The Claude Agent SDK evaluates tool-use permission requests through a fixed six-step order — hooks, then deny rules, then ask rules, then the active permission mode, then allow rules, then a canUseTool callback — and a request approved early in that chain never reaches the steps after it. Two consequences of that ordering are documented directly in Anthropic's own docs and are easy to miss when configuring a production deployment.

What the permission model actually is

Every tool call a Claude Agent SDK session attempts is evaluated, not assumed. According to Anthropic's current documentation, the evaluation runs through six steps in this fixed order: hooks first, then deny rules, then ask rules, then the active permission mode, then allow rules, and finally the canUseTool callback (Claude Docs — Configure permissions, as of late August 2026).

Order matters here in a specific, non-obvious way: a hook returning allow does not skip the deny or ask steps that come after it in the sequence. A hook can approve something at its own stage, but a later deny rule can still block it — the steps are additive filters, not a first-match-wins chain that stops as soon as one step says yes.

  1. Hooks run first, before every other check — including when the session is running in bypassPermissions mode. A PreToolUse hook is the only point in the sequence that executes unconditionally.
  2. Deny rules are evaluated next and block matching tool calls outright.
  3. Ask rules prompt for explicit approval on matching calls.
  4. Permission mode is applied — the session-wide setting (below) that governs default behavior for anything not already matched by an earlier rule.
  5. Allow rules approve matching calls that survived the earlier steps.
  6. The canUseTool callback runs last, for whatever remains unresolved after every prior step.

The six permission modes

The SDK exposes six modes, each governing how much gets auto-approved before a call would otherwise reach the canUseTool callback (Claude Docs, as of late August 2026):

Mode Behavior
default No auto-approvals; nothing bypasses the full evaluation chain
dontAsk Denies anything not pre-approved; never calls the canUseTool callback
acceptEdits Auto-approves file edits and filesystem commands
bypassPermissions Approves nearly everything; the docs describe Claude as having "full system access" in this mode and warn to use it "with extreme caution"
plan Read-only exploration; any edit still prompts for approval
auto A model classifier approves or denies each request

bypassPermissions is the mode most worth pausing on before enabling in any production configuration — not because it's undocumented, but because the two footguns below both compound specifically under it.

Footgun #1: allowed_tools doesn't constrain bypassPermissions

A team configuring allowed_tools=["Read"] alongside permissionMode: "bypassPermissions" might reasonably expect the tool list to still act as a ceiling. It doesn't. The documentation is explicit that bypassPermissions still approves every tool — including Bash, Write, and Edit — regardless of what allowed_tools specifies (Claude Docs, as of late August 2026). The docs' own stated fix is to use disallowed_tools instead when specific tools must remain blocked even under bypass mode — an allow-list and a bypass mode do not compose the way an allow-list intuitively suggests they should.

Footgun #2: subagents inherit the parent's permission mode

Subagents inherit the parent session's permission mode by default, and — this is the part worth flagging to anyone building multi-agent workflows on the SDK — an AgentDefinition's own permissionMode setting cannot override the parent when the parent session is running bypassPermissions, acceptEdits, or auto (Claude Docs, as of late August 2026). Concretely: a subagent configured with a tighter, more restrictive permission mode, running under a parent session in bypassPermissions mode, does not get the tighter mode. It inherits the parent's bypass instead.

The docs frame the risk directly: a subagent can carry "a different system prompt and less constrained behavior than your main agent," and under this inheritance rule it can end up with full, autonomous system access purely because of what mode the parent happened to be running — not because anyone explicitly granted the subagent that authority. For teams that treat subagents as a way to delegate narrower, lower-trust tasks, this inheritance behavior is the opposite of what that delegation pattern implies.

The callback isn't a universal safety net

A related, easy-to-miss consequence of the ordering: any tool call auto-approved by an earlier step — via acceptEdits, bypassPermissions, or a matching allow rule — never reaches the canUseTool callback at all (Claude Docs, as of late August 2026). If a team implements custom authorization logic only inside that callback, assuming it runs on every tool call, that logic is silently skipped for anything an earlier step already approved. The only step in the sequence that runs unconditionally, including under bypassPermissions, is a PreToolUse hook — which is why the docs position hooks as the enforcement point of last resort, not the callback.

What this means for teams building on the SDK

The practical takeaway is that permission configuration on the Claude Agent SDK is a sequence to reason about, not a single setting to get right. Three checks follow directly from the two footguns above:

  • If any part of your deployment runs bypassPermissions, verify that anything you actually need blocked is in disallowed_tools, not just absent from allowed_tools.
  • If your architecture spawns subagents, check what permission mode the parent session runs under before assuming a subagent's own permissionMode setting is enforced — it may not be.
  • If authorization logic lives only in a canUseTool callback, confirm it's not being silently bypassed for tool calls an earlier step already approved; move enforcement you cannot afford to skip into a PreToolUse hook instead.

None of this is a criticism of the model itself — a six-step evaluation order with hooks running unconditionally first is a defensible, well-documented design, and the docs are explicit about both footguns rather than leaving them to be discovered in production. The risk is entirely in configuration: teams reasonably assume an allow-list constrains a bypass mode, or that a subagent's own settings are respected, because that's how permission systems in most other tooling behave. The Claude Agent SDK's docs say otherwise for these two specific cases, and a permission review that doesn't explicitly check both is incomplete.

This pattern — permission decisions evaluated through an ordered chain where an early "yes" can outrank a later, more specific check — is not unique to this SDK. It's the same class of misconfiguration risk covered generally in excessive agency controls for OWASP LLM06, and the same reasoning that applies to scoping any credentialed, tool-calling process applies here: default to the narrowest mode the task needs, and treat broader modes as an explicit, reviewed exception rather than a convenience default. The same discipline — one credential per agent, narrowly scoped, reviewed rather than assumed — is what the identity for AI agents guide covers for agent identity generally, and it applies just as directly to how permission modes are assigned per session and per subagent.

Framework-specific tool scoping decisions like these also intersect with the broader MCP tool-authorization surface: an agent's permission mode governs what the SDK itself allows, but any MCP servers it connects to carry their own tool-level authorization that should be reviewed independently — see the MCP server security checklist for that layer. And because subagents can carry distinct behavior from a main agent, the same vetting discipline documented for Agent Skills — know what a delegated unit of work can actually do before it runs — applies to subagent definitions as well.

FAQ

Does a deny rule always win over an allow rule in the Claude Agent SDK? Yes for calls it matches — deny rules are evaluated before allow rules in the six-step order, so a matching deny blocks the call regardless of what a later allow rule would have approved.

If I set allowed_tools to a short list, is that list enforced under every permission mode? No. Under bypassPermissions, the documented behavior is that every tool is approved regardless of allowed_tools; use disallowed_tools if specific tools must stay blocked in that mode.

Do subagents always run with the same permission mode as their parent? They inherit it by default, and cannot override it when the parent runs bypassPermissions, acceptEdits, or auto — a subagent's own permissionMode setting only takes effect outside those three parent modes.