Skip to content

Latest commit

 

History

History
271 lines (192 loc) · 17.2 KB

File metadata and controls

271 lines (192 loc) · 17.2 KB
title Configure permissions
source https://code.claude.com/docs/en/agent-sdk/permissions
category code
generated true

Documentation Index

Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt Use this file to discover all available pages before exploring further.

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.

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](./code-agent-sdk/user-input.md).

How permissions are evaluated

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

Run [hooks](./code-agent-sdk/hooks.md) 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. Check `deny` rules (from `disallowed_tools` and [settings.json](./code-settings.md#permission-settings)). 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. Check `ask` rules from [settings.json](./code-settings.md#permission-settings). If an ask rule matches, the call falls through to your [`canUseTool` callback](./code-agent-sdk/user-input.md) 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"]`](./code-mcp.md#require-approval-for-a-specific-tool) 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. {/* min-version: 2.1.199 */}The MCP annotation requires Claude Code v2.1.199 or later.
Apply the active [permission mode](#permission-modes). `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. Check `allow` rules (from `allowed_tools` and settings.json). If a rule matches, the tool is approved. If not resolved by any of the above, call your [`canUseTool` callback](./code-agent-sdk/user-input.md) for a decision. In `dontAsk` mode, this step is skipped and the tool is denied.

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.

As of v2.1.198, if you pass a 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 allowedTools entry such as "Read", which auto-approves that whole tool before the callback is consulted

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. 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.

Option Effect
allowed_tools=["Read", "Grep"] Read and Grep are auto-approved. Tools not listed here still exist and 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 * are denied in every permission mode, including bypassPermissions. Other Bash calls 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.

**Auto-approved tools never reach `canUseTool`.** A tool call approved at any earlier step, by `acceptEdits` or `bypassPermissions`, or by an allow rule, skips your `canUseTool` callback, so permission checks you put there are silently bypassed for that tool. The exception is tools that require user interaction, `AskUserQuestion` and MCP tools marked with [`_meta["anthropic/requiresUserInteraction"]`](./code-mcp.md#require-approval-for-a-specific-tool), which reach the callback even when an allow rule matches. Coverage depends on the entry's form: a bare name like `Read` or `mcp__github__get_issue` auto-approves every call to that tool, while a scoped rule like `Bash(ls *)` auto-approves only matching calls and other `Bash` calls still fall through to the callback. For checks that must run on every tool call, use a [`PreToolUse` hook](./code-agent-sdk/hooks.md): hooks run before every other step, and a hook deny applies even in `bypassPermissions` mode.

For a locked-down agent, pair allowedTools with permissionMode: "dontAsk". Listed tools are approved; anything else is denied outright instead of prompting:

const options = {
  allowedTools: ["Read", "Glob", "Grep"],
  permissionMode: "dontAsk"
};
**`allowed_tools` does not constrain `bypassPermissions`.** `allowed_tools` only pre-approves the tools you list. Unlisted tools are not matched by any allow rule and fall through to the permission mode, where `bypassPermissions` approves them. Setting `allowed_tools=["Read"]` alongside `permission_mode="bypassPermissions"` still approves every tool, including `Bash`, `Write`, and `Edit`. If you need `bypassPermissions` but want specific tools blocked, use `disallowed_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.

Available modes

The SDK supports these permission modes:

Mode Description Tool behavior
default Standard permission behavior No auto-approvals; unmatched tools trigger your canUseTool callback
dontAsk Deny instead of prompting Anything not pre-approved by allowed_tools or rules is denied; 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, unless an explicit ask rule matches (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 each tool call. See Auto mode for availability
**Subagent inheritance:** When the parent uses `bypassPermissions`, `acceptEdits`, or `auto`, all subagents inherit that mode and it cannot be overridden per subagent. Subagents may have different system prompts and less constrained behavior than your main agent, so inheriting `bypassPermissions` grants them full, autonomous system access. An explicit [`ask` rule](#how-permissions-are-evaluated) still forces a prompt.

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.
<CodeGroup>
  ```python Python theme={null}
  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())
  ```

  ```typescript TypeScript theme={null}
  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();
  ```
</CodeGroup>
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.
<CodeGroup>
  ```python Python theme={null}
  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())
  ```

  ```typescript TypeScript theme={null}
  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();
  ```
</CodeGroup>

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. 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. 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.

Use with extreme caution. Claude has full system access in this mode. Only use in controlled environments where you trust all possible operations.

allowed_tools does not constrain this mode. Every tool is approved, not just the ones you listed. Deny rules (disallowed_tools), explicit ask rules, and hooks are evaluated before the mode check and can still block a tool.

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. 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: