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
[!NOTE]
If you load a plugin that contains a mod, see where a mod's decisions fit among rules and hooks.
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 doesn't skip the deny and ask rules below, which are evaluated regardless of the hook result.
A PreToolUse hook that returns allow also can't approve an rm or rmdir command whose target Claude Code recognizes as a critical path, such as rm -rf ~. For what that allow does in plan mode, see Hook approvals in plan mode.
2. Deny rules
If a deny rule from disallowed_tools or settings.json matches, the call is denied and canUseTool isn't called, even in bypassPermissions mode and even when a hook returned allow.
In the TypeScript SDK, if you set the permission_policy of a custom tool to always_deny, a call to that tool is denied 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.
In the TypeScript SDK, if you set a custom tool's permission_policy to always_ask, no allow rule approves its calls, not even an allowedTools entry. In dontAsk mode Claude Code denies those calls, and in auto, plan, or bypassPermissions mode it can approve them before your callback runs.
4. Permission mode
Apply the active permission mode:
- In
bypassPermissionsmode, Claude Code approves everything that reaches this step except recognized critical-path removals and Bash commands flaggedDangerous rm operation not ruled out, which fall through instead. - In
acceptEditsmode, Claude Code approves the file operations listed under Accept edits mode. - In
planmode, Claude Code sends file-edit tools to yourcanUseToolcallback regardless of allow rules. - 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.
Recognized critical-path removals aren't approved by an allow rule, and commands flagged Dangerous rm operation not ruled out aren't approved by a Bash or Bash(*) 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.
An allow rule doesn't approve a read from a network path.
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, though a mod you installed can still approve some such calls.
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.
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': the warning text starts withcanUseTool will not be invoked: permissionMode 'bypassPermissions'. Tool calls are approved beforecanUseToolis consulted, so your callback runs for only a few calls, such as the calls listed under Bypass permissions mode. To check the approved calls too, put your callback's checks in aPreToolUsehook- An
allowedToolsentry that is only a tool name, such as"Read": the warning lists each such entry aftercanUseTool will not be invoked for:. Calls to a listed tool are approved beforecanUseToolis consulted, apart from the calls an allow rule never approves. To have your callback run for a listed tool's calls that need approval, remove that tool's entry fromallowedTools
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.
The warning doesn't block any call or change any option. If your callback already receives every call you want to check, leave your configuration as it is. To log the warning, listen with process.on('warning', ...) and match the CLAUDE_SDK_CAN_USE_TOOL_SHADOWED code.
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. 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, apart from reads from network paths. 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 and doesn't 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.
[!WARNING]
Auto-approved tools never reach
canUseTool. A tool call approved at any earlier step, byacceptEditsorbypassPermissions, or by an allow rule, skips yourcanUseToolcallback, so permission checks you put there are silently bypassed for that tool.If calls to a tool in
allowed_toolsstill reach yourcanUseToolcallback, or are denied indontAskmode, check whether they're reads from network paths or among the following calls. No allow rule approves these calls. IndontAskmode, which never prompts, Claude Code denies them without callingcanUseTool, and in the other permission modes you handle them as each item says:
AskUserQuestion, MCP tools marked_meta["anthropic/requiresUserInteraction"], and connector tools your organization set toask: answer them in the callback. Handle clarifying questions shows how to answerAskUserQuestion, and a connector call arrives with the reasonYour organization requires approval for this toolCustom tools your application marks
always_ask: decide the call in the callback. To stop these calls arriving, change the policy mid-session. Inauto,plan, andbypassPermissionsmode the call can instead be approved before the callback, in the cases listed under Permission modes that can approve analways_askcallRecognized
rmandrmdircritical-path removals: indefault,acceptEdits, andbypassPermissionsmode, allow or deny the removal in the callback, wheredecisionReasonstarts withDangerous rm. Forautoandplanmode, see Critical-path removals in each permission mode. The check that recognizes these removals can miss some commandsBare name: an entry like
Readormcp__github__get_issueauto-approves every call to that tool apart from the actions no mode auto-approves and reads from network paths.Scoped rule: an entry like
Bash(npm test *)auto-approves only matching calls, and otherBashcalls that need approval still fall through to the callback.For checks that must run on every tool call, use a
PreToolUsehook: hooks run before every other step, and a hook deny applies even inbypassPermissionsmode.
For a locked-down agent, pair allowedTools with permissionMode: "dontAsk":
const options = {
allowedTools: ["Read", "Glob", "Grep"],
permissionMode: "dontAsk"
};
The listed Read, Glob, and Grep calls are approved, apart from the actions no mode auto-approves and reads from network paths. Every other call that would prompt is denied instead, though a mod you installed can still approve some of those calls.
Calls that need no approval in default mode run whether or not you list them. These include read-only Bash commands, tools like Agent that don't ask before running, and file reads inside your working directories. To hide a built-in tool from Claude entirely rather than deny its calls, add its bare name to disallowedTools.
[!WARNING]
allowed_toolsdoes not constrainbypassPermissions.allowed_toolspre-approves the tools you list. Other unlisted tools are not matched by any allow rule and fall through to the permission mode, wherebypassPermissionsapproves them. Settingallowed_tools=["Read"]alongsidepermission_mode="bypassPermissions"still approves every tool, includingBash,Write, andEdit. If you needbypassPermissionsbut want specific tools blocked, usedisallowed_tools.
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.
With Claude Code v2.1.302 or later, your app starts in default mode unless you set a permission mode or it gets one from permissions.defaultMode in its settings files. In default mode, Claude Code denies any tool call needing approval unless you approve it through an allow rule, hook, canUseTool callback, or permission prompt tool. If tool calls are denied, see Tool calls denied in claude -p and Agent SDK runs.
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 without calling canUseTool, though a mod you installed can still approve some of those calls. Calls approved by allowed_tools or settings.json allow rules run, and so do calls that need no approval in default mode. Some calls, such as reads from network paths, are denied even when allowed_tools or an allow rule matches them. For the list, see Don't ask mode |
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 and, outside a cloud session, custom tools your application marks always_ask. 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 reviews actions such as shell commands and network requests, allowing or blocking each one it reviews. See Auto mode for availability and the decision order |
[!WARNING]
Subagent inheritance: If your main session is in plan mode, a subagent whose
AgentDefinitionsetspermissionMode: "acceptEdits"still runs in plan mode. That subagent'sEditandWritecalls on your source files still go to yourcanUseToolcallback. Plan mode holds for the subagents you define too, so that handing work to one doesn't get around reviewing the plan first.The subagent inherits
plan,acceptEdits,auto, orbypassPermissionsfrom the main session, and under adefault- ordontAsk-mode main session it runs in the mode you set. Even then, Claude Code never applies a"bypassPermissions"value, and applies an"auto"value only when auto mode is available to that subagent. ThebypassPermissionsexception requires Claude Code v2.1.267 or later.Subagents may have different system prompts and less constrained behavior than your main agent, so inheriting
bypassPermissionsgrants them full, autonomous system access. The actions no mode auto-approves still apply.
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
Pass permission_mode (Python) or permissionMode (TypeScript) when creating a query.
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())
import { query } from "@anthropic-ai/claude-agent-sdk";
async function main() {
for await (const message of query({
prompt: "Help me refactor this code",
options: {
permissionMode: "default" // Set the mode here
}
})) {
if ("result" in message) {
console.log(message.result);
}
}
}
main();
During streaming
Call set_permission_mode() (Python) or setPermissionMode() (TypeScript) to change the mode mid-session. The new mode takes effect immediately for all subsequent tool requests. This lets you start restrictive and loosen permissions as trust builds, for example switching to acceptEdits after reviewing Claude's initial approach.
import asyncio
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
async def main():
async with ClaudeSDKClient(
options=ClaudeAgentOptions(
permission_mode="default", # Start in default mode
)
) as client:
await client.query("Help me refactor this code")
# Change mode dynamically mid-session
await client.set_permission_mode("acceptEdits")
# Process messages with the new permission mode
async for message in client.receive_response():
if hasattr(message, "result"):
print(message.result)
asyncio.run(main())
import { query } from "@anthropic-ai/claude-agent-sdk";
async function main() {
const q = query({
prompt: "Help me refactor this code",
options: {
permissionMode: "default" // Start in default mode
}
});
// Change mode dynamically mid-session
await q.setPermissionMode("acceptEdits");
// Process messages with the new permission mode
for await (const message of q) {
if ("result" in message) {
console.log(message.result);
}
}
}
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 path that Claude Code recognizes as a critical path with
rmorrmdir - Runs
touchwith, for example, an option, a glob, or atimeout 5orLANG=Cprefix
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, though a mod you installed can still approve some of those calls. Tools pre-approved by allowed_tools, settings.json allow rules, or a hook run as normal. Calls that need no approval in default mode also run as normal, such as file reads inside your working directories and calls to Agent.
These calls are denied even when an allow rule matches:
- Connector tools your organization set to
ask - Tools that require user interaction, such as
AskUserQuestionand MCP tools whose server sets_meta["anthropic/requiresUserInteraction"] - Custom tools whose
permission_policyyour application sets toalways_ask - Reads from network paths
- Recognized critical-path removals
If your PreToolUse hook returns allow for a recognized critical-path removal, a read from a network path, or a call to a tool marked always_ask, the call is still denied. The check that recognizes these removals can miss some commands.
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 actions no mode auto-approves. 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.
[!WARNING]
Use with extreme caution. Claude has full system access in this mode. Only use in controlled environments where you trust all possible operations.
allowed_toolsdoes not constrain this mode. Every tool is approved, not just the ones you listed. These controls still apply:
- Deny rules, explicit
askrules, and hooks are evaluated before the mode check and can still block a tool.- Connector tools your organization set to
ask, tools that require user interaction, and recognized critical-path removals still fall through to yourcanUseToolcallback. The check that recognizes these removals can miss some commands. Outside a cloud session, calls to custom tools your application marksalways_askfall through to the callback too.- The cross-session messaging safeguards still apply.
Plan mode (plan)
Claude explores the codebase and produces a plan without editing your source files. File edits are never auto-approved in plan mode, even when an allow rule matches or a PreToolUse hook returns allow. They prompt through your canUseTool callback instead.
While auto mode is available to the session, commands Claude runs during planning don't reach your callback by default, because the classifier model that auto mode uses reviews them instead. With that review on, the classifier can also approve read-only calls that would otherwise reach your callback, such as a WebFetch call with no allow rule.
[!NOTE]
In the TypeScript SDK, if you set
allowDangerouslySkipPermissions: truealongsidepermissionMode: 'plan', file edits still reach yourcanUseToolcallback. To switch a session that starts in plan mode tobypassPermissionslater withsetPermissionMode(), you needallowDangerouslySkipPermissionsset from the start.
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.
Auto mode (auto)
A classifier model reviews actions such as shell commands and network requests before they run, and allows or blocks each one it reviews. For the models and providers auto mode requires and the order it evaluates actions in, see Eliminate permission prompts with auto mode.
If you switch a session that's in auto mode to a model that auto mode doesn't support, using set_model(), setModel(), or applyFlagSettings(), the session changes to default mode:
- What you see: a
systemmessage with subtypestatusandpermissionModeset to"default", which the TypeScript SDK types asSDKStatusMessage. From then on, calls that need approval and match no allow rule reach yourcanUseToolcallback. - To return to auto mode: switch to a supported model, then call
set_permission_mode("auto")orsetPermissionMode("auto"). If you switch the model back without making that call, the session stays indefaultmode. While the unsupported model is active, the call fails withCannot set permission mode to auto: auto mode unavailable for this model.
The change to default mode after a set_model(), setModel(), or applyFlagSettings() call requires Claude Code v2.1.299 or later.
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