Permissions
Control tool use with permission modes and interactive approvals
The SDK gives your application two levers over what an agent’s tools can do: a session-wide permission mode, and a canUseTool callback that approves, denies, or edits individual tool calls — interactively, if your application involves a human.
Permission modes
Section titled “Permission modes”Set permissionMode at agent creation or per session:
| Mode | Behavior |
|---|---|
standard | Default. Tool calls that need approval flow through canUseTool |
acceptEdits | File edits are auto-approved; other approvals still flow through canUseTool |
unrestricted | Auto-allows tool calls that do not require user input; input-requesting tools still reach canUseTool |
strict | The most restrictive mode; nothing is auto-approved |
await using session = client.createSession(agentId, { permissionMode: "standard",});allowedTools is a separate, coarser lever: an availability filter that controls which tools the session exposes at all. See MCP and client tools for how it interacts with client and MCP tools.
Approve, deny, or edit tool calls
Section titled “Approve, deny, or edit tool calls”Use canUseTool to decide each tool call that isn’t auto-approved. The callback can be fully interactive: return a promise that resolves after your UI or server receives a user’s decision. The SDK keeps the approval pending until the callback returns.
await using session = client.createSession(agentId, { permissionMode: "standard", canUseTool: async (toolName, toolInput) => { if (toolName === "Bash") { return { behavior: "deny", message: "Denied by the example approval policy", }; } return { behavior: "allow" }; },});An "allow" response can pass updatedInput to edit the tool call before it runs. A "deny" response can pass interrupt: true to also stop the turn:
canUseTool: async (toolName, toolInput) => { if (toolName === "Bash") { return { behavior: "allow", updatedInput: { ...toolInput, timeout: 30_000 }, }; } return { behavior: "deny", message: "Not allowed", interrupt: true };},The callback’s optional third argument carries context for richer approval UIs: permissionSuggestions, blockedPath, and diffs for file edits.
Tools that ask the user for input also flow through canUseTool (even under unrestricted), so your app can render the prompt and return the user’s response. Interactive user-input tools like AskUserQuestion are always excluded from SDK sessions regardless of allowedTools.
Recover pending approvals
Section titled “Recover pending approvals”If your client disconnects while an approval is pending — a mobile app backgrounded mid-approval, a browser tab reloaded — the approval survives on the runtime. When the client returns:
await using session = client.resumeSession(conversationId, { canUseTool });
// Fetch IDs, model, history, and pending-approval state in one requestconst state = await session.bootstrapState();
// Replay any approval that was pending when the connection droppedawait session.recoverPendingApprovals();recoverPendingApprovals() re-delivers the pending request to your canUseTool callback so the user can finish the decision in the new session.
What to read next
Section titled “What to read next”- Sending messages — the stream where tool calls and results appear
- MCP and client tools — defining the tools these permissions govern
- SDK reference —
CanUseToolCallbackand session option types