Overview
The Agent SDK controls tool use through permission modes, declarative allow/deny
rules, hooks, and the canUseTool callback. Designing these well is how you get a
least-privilege agent that runs autonomously where it is safe and asks (or is
denied) where it is not.
Evaluation order
When Claude requests a tool, the SDK checks, in order:
- Hooks - can deny outright or pass on (a hook
allow does not skip later
deny/ask rules).
- Deny rules - from
disallowedTools and settings; a match blocks the tool
even in bypassPermissions.
- Permission mode -
bypassPermissions approves; acceptEdits approves file
ops; others fall through.
- Allow rules - from
allowedTools and settings; a match approves.
- canUseTool callback - decides anything unresolved (skipped in
dontAsk,
which denies).
Allow and deny rules
allowedTools: ["Read", "Grep"] - auto-approves those; unlisted tools fall
through to the mode.
disallowedTools: ["Bash"] - removes the tool from context entirely.
disallowedTools: ["Bash(rm *)"] - keeps Bash but denies matching calls in
every mode.
Critically, allowedTools does not constrain bypassPermissions; to block
specific tools there, use disallowedTools.
Permission modes
| Mode |
Behavior |
default |
No auto-approvals; unmatched tools hit canUseTool. |
dontAsk |
Anything not pre-approved is denied; canUseTool never called. |
acceptEdits |
Auto-approves file edits and filesystem commands in scope. |
bypassPermissions |
Approves everything (hooks/deny still apply); use with caution. |
plan |
Read-only; Claude plans without editing. |
auto (TS) |
A model classifier approves or denies each call. |
Set it with permissionMode at query time, or change it mid-session with
setPermissionMode() / set_permission_mode().
Lock down an agent
For a headless, fixed tool surface, pair an allow list with dontAsk:
const options = { allowedTools: ["Read", "Glob", "Grep"], permissionMode: "dontAsk" };
Listed tools are approved; anything else is denied outright rather than prompting.
Interactive approvals
For runtime approval flows, implement the canUseTool callback (returns allow or
deny). For deterministic guardrails, use hooks. Declarative allow/ask/deny rules
can also live in .claude/settings.json when the project setting source is
enabled.
Source