SpyBara
Go Premium

agent-sdk/permissions.md 2026-09-27 23:59 UTC to 2026-09-28 07:58 UTC

This page contains 1 addition and 1 deletion.

2026
Sat 5 14:59 Wed 9 22:58 Fri 11 23:01 Tue 15 23:58 Thu 24 22:57 Sat 26 23:59 Mon 28 08:57

Configure permissions

Control how your agent uses tools with permission modes, hooks, and declarative allow/deny rules.

The Claude Agent SDK provides permission controls to manage how Claude uses tools. Use permission modes and rules to define what's allowed automatically, and the canUseTool callback to handle everything else at runtime.

How permissions are evaluated

When Claude requests a tool, the SDK checks permissions in this order:

1

Hooks

Run hooks first. A hook can deny the call outright or pass it on. A hook that returns allow does not skip the deny and ask rules below; those are evaluated regardless of the hook result. A PreToolUse hook allow also can't approve an rm or rmdir removal targeting a critical path.

2

Deny rules

Check deny rules (from disallowed_tools and settings.json). If a deny rule matches, the tool is blocked, even in bypassPermissions mode. Bare-name deny rules like Bash remove the tool from Claude's context before this evaluation begins, so only scoped rules like Bash(rm *) are checked at this step.

3

Ask rules

Check ask rules from settings.json. If an ask rule matches, the call falls through to your canUseTool callback for confirmation, even in bypassPermissions mode.

Tools that require user interaction behave the same way: AskUserQuestion and MCP tools whose server sets _meta["anthropic/requiresUserInteraction"] always fall through to the callback, even when an allow rule matches. In dontAsk mode both cases are denied instead, because that mode never prompts. The MCP annotation requires Claude Code v2.1.199 or later.

claude.ai connector tools your organization has set to ask also leave the flow at this step. Every call falls through to the callback, even in bypassPermissions mode and even when an allow rule matches. The callback receives the reason Your organization requires approval for this tool. In dontAsk mode the call is denied instead, because that mode never prompts.

4

Permission mode

Apply the active permission mode:

  • In bypassPermissions mode, Claude Code approves everything that reaches this step except rm and rmdir removals targeting a critical path, which fall through instead.
  • In acceptEdits mode, Claude Code approves the file operations listed under Accept edits mode.
  • In plan mode, Claude Code sends file-edit and shell-write tools to your canUseTool callback regardless of allow rules, so write operations can't be auto-approved while planning.
  • In other modes, the request falls through.
5

Allow rules

Check allow rules (from allowed_tools and settings.json). If a rule matches, the tool is approved. A call the tool approves on its own is resolved at this step too, with no rule needed: for example a file read inside your working directories or a read-only Bash command.

rm and rmdir removals targeting a critical path are never approved by an allow rule. Whether they then reach your callback depends on the permission mode: in an Agent SDK session in auto mode, for example, Claude Code denies them by default without calling it. The Critical paths mode table lists what each mode does with them.

6

canUseTool callback

If not resolved by any of the above, call your canUseTool callback for a decision. In dontAsk mode, this step is skipped and the tool is denied.

In the TypeScript SDK, if you set permissionPrompts: 'none', your callback isn't called at this step. A PermissionRequest hook still gets a chance to decide, and if it doesn't, Claude Code denies the call. The option requires Claude Code v2.1.259 or later.

Diagram of the six-step permission evaluation flow matching the steps above: a tool request passes through hooks, deny rules, ask rules, permission mode, allow rules, and canUseTool. Hooks, deny rules, and canUseTool can route down to Blocked; permission mode bypass, allow rules, and canUseTool can route up to Execute; ask rules route to canUseTool. Diagram of the six-step permission evaluation flow matching the steps above: a tool request passes through hooks, deny rules, ask rules, permission mode, allow rules, and canUseTool. Hooks, deny rules, and canUseTool can route down to Blocked; permission mode bypass, allow rules, and canUseTool can route up to Execute; ask rules route to canUseTool.

If you pass a canUseTool callback in a configuration where the TypeScript SDK expects the evaluation order to auto-approve calls before the callback is consulted, the SDK emits a Node.js process warning once when the query is constructed. The warning's code is CLAUDE_SDK_CAN_USE_TOOL_SHADOWED. Two configurations trigger it:

  • permissionMode: 'bypassPermissions', which auto-approves every call that reaches the permission mode step apart from the actions no mode auto-approves
  • Each bare allowedTools entry such as "Read", which auto-approves that whole tool before the callback is consulted, apart from the actions no mode auto-approves

Entries with a specifier such as Bash(ls *) and the acceptEdits mode don't trigger it, and allow rules coming from settings files aren't visible to the check.

Listen with process.on('warning', ...) and match the code to log or suppress it. To gate every tool call regardless of mode and rules, use a PreToolUse hook instead.

This page focuses on allow and deny rules and permission modes. For the other steps:

Allow and deny rules

allowed_tools and disallowed_tools (TypeScript: allowedTools / disallowedTools) add entries to the allow and deny rule lists in the evaluation flow above. If you name one of the task-tracking tools in allowed_tools, Claude Code also opts the session in. Any other tool not listed in allowed_tools is still available to Claude, and a call to it that needs approval falls through to the permission mode. Deny rules behave differently depending on whether they name a tool or scope a pattern within one.

Option Effect
allowed_tools=["Read", "Grep"] Read and Grep are auto-approved. Other tools not listed here still exist, and calls to them that need approval fall through to the permission mode and canUseTool.
disallowed_tools=["Bash"] The Bash tool definition is removed from the request. Claude does not see the tool and cannot attempt it.
disallowed_tools=["Bash(rm *)"] Bash stays available. Calls matching rm * as written are denied in every permission mode, including bypassPermissions. Other Bash calls, including /bin/rm, fall through to the permission mode.
disallowed_tools=["*"] Every tool definition is removed from the request. Tool-name globs are supported in deny rules: "*" matches every tool and "mcp__*" matches every MCP tool across all servers.

Allow rules accept tool-name globs only after a literal mcp__<server>__ prefix. The server segment must be glob-free so the rule names a specific server you configured: mcp__puppeteer__* matches every tool from the puppeteer server, and mcp__github__get_* matches its get_ tools. An unanchored entry like allowed_tools=["*"] or allowed_tools=["mcp__*"] is ignored with a startup warning and does not auto-approve anything.

Scoped rules for Read and Edit take a path pattern. Edit(path) rules govern all built-in tools that write files, including Write and NotebookEdit; a Write(path) rule is never matched by the file permission checks.

Use //path for an absolute filesystem path: a deny rule of Edit(//secrets/**) blocks writes anywhere under /secrets on disk. With a single leading slash, Edit(/secrets/**) anchors at the rule's source instead. For rules passed through allowed_tools or disallowed_tools, that means the session's working directory, so the rule doesn't block /secrets on disk. See Read and Edit rules for the four anchor forms and how rules from settings files resolve.

For a locked-down agent, pair allowedTools with permissionMode: "dontAsk":

const options = {
  allowedTools: ["Read", "Glob", "Grep"],
  permissionMode: "dontAsk"
};

Listed tools are approved, apart from the actions no mode auto-approves, and every other call that would prompt is denied instead. Calls that need no approval in default mode run whether or not you list them, such as read-only Bash commands, tools like Agent that don't ask before running, and file reads inside your working directories. To put a tool out of Claude's reach entirely, add its bare name to disallowedTools.

You can also configure allow, deny, and ask rules declaratively in .claude/settings.json. These rules are read when the project setting source is enabled, which it is for default query() options. If you set setting_sources (TypeScript: settingSources) explicitly, include "project" for them to apply. See Permission settings for the rule syntax.

Permission modes

Permission modes provide global control over how Claude uses tools. You can set the permission mode when calling query() or change it dynamically during streaming sessions.

Available modes

The SDK supports these permission modes:

Mode Description Tool behavior
default Standard permission behavior No mode-based auto-approvals; calls that need approval and match no allow rule trigger your canUseTool callback
dontAsk Deny instead of prompting Any call that would otherwise prompt is denied. Calls approved by allowed_tools or rules run, and so do calls that need no approval in default mode; connector tools your organization set to ask and tools that require user interaction are denied even if you've pre-approved them, as are rm and rmdir removals targeting a critical path. canUseTool is never called
acceptEdits Auto-accept file edits File edits and filesystem operations (mkdir, rm, mv, etc.) are automatically approved
bypassPermissions Bypass permission checks Tools run without permission prompts, except for the actions no mode auto-approves. Use with caution
plan Planning mode Claude explores and plans without editing your source files; file edits are never auto-approved and prompt through your canUseTool callback
auto Model-classified approvals A model classifier approves or denies permission prompts. See Auto mode for availability

Set permission mode

You can set the permission mode once when starting a query, or change it dynamically while the session is active.

Pass permission_mode (Python) or permissionMode (TypeScript) when creating a query. This mode applies for the entire session unless changed dynamically.

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions


async def main():
async for message in query(
prompt="Help me refactor this code",
options=ClaudeAgentOptions(
permission_mode="default",  # Set the mode here
),
):
if hasattr(message, "result"):
print(message.result)


asyncio.run(main())

Mode details

Accept edits mode (acceptEdits)

Auto-approves file operations so Claude can edit code without prompting. Other tools (like Bash commands that aren't filesystem operations) still require normal permissions.

Auto-approved operations:

  • File edits (Edit, Write tools)
  • Filesystem commands: mkdir, touch, rm, rmdir, mv, cp, sed

Both apply only to paths inside the working directory or additionalDirectories. In acceptEdits mode, Claude Code doesn't auto-approve the request when Claude:

  • Works on a path outside that scope
  • Writes to a protected path
  • Removes a critical path with rm or rmdir

Use when: you trust Claude's edits and want faster iteration, such as during prototyping or when working in an isolated directory.

Don't ask mode (dontAsk)

Converts any permission prompt into a denial, without calling canUseTool. Tools pre-approved by allowed_tools, settings.json allow rules, or a hook run as normal, and so do calls that need no approval in default mode, such as file reads inside your working directories and calls to Agent. Connector tools your organization set to ask, tools that require user interaction, and rm and rmdir removals targeting a critical path are denied even when an allow rule matches. A PreToolUse hook allow doesn't clear a critical-path removal either.

Use when: you want a fixed, explicit tool surface for a headless agent and prefer a hard deny over silent reliance on canUseTool being absent.

Bypass permissions mode (bypassPermissions)

Auto-approves tool uses without prompting, except the cases listed in the warning below. Hooks still execute and can block operations if needed. On Linux and macOS, Claude Code refuses to start in this mode as root or under sudo outside a recognized sandbox, and the query fails before the first turn.

Plan mode (plan)

Claude explores the codebase and produces a plan without editing your source files. Read-only tools run as they do in the default permission mode.

File edits are never auto-approved in plan mode, even when an allow rule matches. They prompt through your canUseTool callback instead. On Claude Code v2.1.212 or later, shell commands that modify files, such as touch and rm, reach your canUseTool callback the same way.

In the TypeScript SDK, if you set allowDangerouslySkipPermissions: true alongside permissionMode: 'plan', file edits and shell commands that modify files still reach your canUseTool callback. The option lets you switch to bypassPermissions later with setPermissionMode().

Claude may use AskUserQuestion to clarify requirements before finalizing the plan. See Handle approvals and user input for handling these prompts.

Use when: you want Claude to propose changes without executing them, such as during code review or when you need to approve changes before they're made.

For the other steps in the permission evaluation flow: