canUseTool callback to handle everything else at runtime.
This page covers permission modes and rules. To build interactive approval flows where users approve or deny tool requests at runtime, see Handle approvals and user input.
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.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.
bypassPermissions approves everything that reaches this step. acceptEdits approves file operations. plan routes file-edit and shell-write tools to your canUseTool callback regardless of allow rules, so write operations cannot be auto-approved while planning. Other modes fall through.5
Allow rules
Check
allow rules (from allowed_tools and settings.json). If a rule matches, the tool is approved.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.canUseTool callback that this evaluation order can never reach, the TypeScript 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- Each bare
allowedToolsentry such as"Read", which auto-approves that whole tool before the callback is consulted
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:
- Hooks: run custom code to allow, deny, or modify tool requests. See Control execution with hooks.
- canUseTool callback: prompt users for approval at runtime, when no earlier step resolves the call. See Handle approvals and user input.
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. Allow rules only affect approval: a tool not listed in allowed_tools is still available to Claude and falls through to the permission mode. Deny rules behave differently depending on whether they name a tool or scope a pattern within one.
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". Listed tools are approved, apart from the always-prompt tools in the Warning above; anything else is denied outright instead of prompting:
.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 callingquery() or change it dynamically during streaming sessions.
Available modes
The SDK supports these permission modes:Set permission mode
You can set the permission mode once when starting a query, or change it dynamically while the session is active.- At query time
- During streaming
Pass
permission_mode (Python) or permissionMode (TypeScript) when creating a query. This mode applies for the entire session unless changed dynamically.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
additionalDirectories. Paths outside that scope and writes to protected paths still prompt.
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. Tools pre-approved by allowed_tools, settings.json allow rules, or a hook run as normal. Connector tools your organization set to ask and tools that require user interaction are denied even when an allow rule matches. Everything else is denied without calling canUseTool.
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 all tool uses without prompts. Hooks still execute and can block operations if needed.
Plan mode (plan)
Claude explores the codebase and produces a plan without editing your source files. Read-only tools run as in default 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.
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.
Related resources
For the other steps in the permission evaluation flow:- Handle approvals and user input: interactive approval prompts and clarifying questions
- Hooks guide: run custom code at key points in the agent lifecycle
- Permission rules: declarative allow/deny rules in
settings.json