Agent SDK Hooks
Prompts guide behavior. Hooks, Permissions, Callbacks, and Application logic enforce behavior.
1. Core Concept
Where to enforce rules in an agentic system.
A weak answer says:
A strong answer says:
Use hooks, permission rules, approval callbacks, validators, and deterministic application logic to block, modify, approve, or audit actions at runtime.
Anthropic’s Agent SDK docs describe hooks as callback functions that run on agent events such as tool calls, session start, subagent start/stop, compaction, or execution stop.
Hooks can block dangerous operations, log/audit tool calls, transform inputs or outputs, require approval, and manage lifecycle state. (Claude Code)
2. Mental Model
Hook = Lifecycle Interception Point
A hook is code that runs automatically at a specific point in the agent lifecycle.
Example:
Claude wants to run:
Bash("rm -rf /tmp/build")
PreToolUse hook fires first.
Hook inspects command.
Hook returns deny.
Tool does not execute.
Claude receives the denial reason.
The important point:
Hooks run outside the model’s free-form reasoning. They are programmatic control points.
Claude can propose an action, but the hook can block or modify it before execution.
3. Hooks Vs Prompts
| Requirement | Is Prompt enough? | Better enforcement |
|---|---|---|
| Preferred writing style | Yes | System prompt / instructions |
| “Be careful with files” | No | PreToolUse hook or permission rule |
Block writes to .env |
No | PreToolUse hook or deny rule |
| Log all tool calls | No | PostToolUse hook |
| Require human approval for deploy | No | canUseTool, ask rule, approval workflow |
| Run tests after edits | No | PostToolUse hook |
| Prevent completion until tests pass | No | Stop hook |
| Prevent subagent from finishing early | No | SubagentStop hook |
| Enforce regulated workflow | No | Application logic + hooks + approval gates |
Heuristic:
If violation has real consequences, do not rely only on prompting.
4. Where Hooks Fit in the Permission Flow
When Claude requests a tool, the SDK evaluates permissions in a specific order. A hook can deny a call outright, but a hook returning allow does not skip later deny or ask rules. (Claude)
High-yield implications:
| Situation | Correct interpretation |
|---|---|
Hook returns allow, but deny rule matches |
Tool is still denied |
Deny rule matches in bypassPermissions |
Tool is still blocked |
allowed_tools lists Read only, but mode is bypassPermissions |
Other tools can still run unless denied |
dontAsk mode and unapproved tool |
Denied, not prompted |
plan mode and file edit |
Prompts through approval callback; edits are not auto-approved |
The exam will likely test precedence. Do not assume one allow setting overrides all other controls.
5. Programmatic Enforcement Layers
Think of enforcement as a stack.
| Layer | Best for | Example |
|---|---|---|
| System prompt | Behavioural guidance | “Prefer minimal changes.” |
| Allowed tools | Pre-approve safe tools | Auto-allow Read, Glob, Grep |
| Deny rules | Static hard blocks | Block Bash(rm *) |
| PreToolUse hook | Dynamic runtime validation before execution | Block writes outside /src |
| PostToolUse hook | Audit or react after execution | Run linter after file edit |
| Stop hook | Prevent premature completion | Continue until tests pass |
| Subagent hooks | Track or constrain subagents | Log subagent output or block early stop |
| canUseTool callback | Human approval or UI-mediated decisions | Ask user before deploy |
| Application logic | Business-critical deterministic rules | Refund > $500 requires manager approval |
Use the lowest reliable enforcement layer. For example, “never delete production data” should be enforced in code or permission policy, not just in a model instruction.
6. Must-Know Hook Events
Anthropic’s hooks reference divides events across the session lifecycle, per-turn lifecycle, and tool-call lifecycle. Common exam-relevant events include UserPromptSubmit, PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, PostToolBatch, SubagentStart, SubagentStop, Stop, PreCompact, and SessionEnd. (Claude Code)
High-Yield Subset
| Hook event | Fires when | Exam use case |
|---|---|---|
UserPromptSubmit |
Before Claude processes user prompt | Reject prompt containing forbidden data or inject context |
PreToolUse |
Before a tool executes | Block, allow, ask, defer, or modify tool input |
PermissionRequest |
Permission dialog would appear | External approval notification |
PostToolUse |
After successful tool execution | Logging, audit, post-edit checks |
PostToolUseFailure |
After failed tool execution | Error logging, recovery signal |
PostToolBatch |
After a batch of parallel tool calls | Inject conventions before next model turn |
Stop |
Claude is about to stop | Check completion criteria; prevent stopping |
SubagentStart |
Subagent starts | Track spawning |
SubagentStop |
Subagent finishes | Validate subagent completion |
PreCompact |
Before compaction | Archive transcript before summarization |
Notification |
Status notification | Send Slack/PagerDuty update |
Shortcut:
Before execution → PreToolUse
After execution → PostToolUse
Before finishing → Stop
Before subagent finishes → SubagentStop
Before prompt enters model → UserPromptSubmit
Before compaction → PreCompact
6.1. PreToolUse: The Enforcement Workhorse
Use PreToolUse when you must decide before a tool runs.
Best for:
- Blocking destructive shell commands
- Preventing writes to protected files
- Validating API request parameters
- Enforcing path restrictions
- Requiring approval for sensitive tools
- Sanitizing or redirecting tool inputs
- Controlling MCP tool calls.
Example policy:
Anthropic’s hook docs show a PreToolUse hook that blocks writes to .env files by returning permissionDecision: "deny" with a reason. (Claude Code)
Trap:
Do not use
PostToolUseto block dangerous execution. By then, the tool already ran.
6.2. PostToolUse: Audit and Reaction, Not Prevention
Use PostToolUse after the tool succeeds.
Best for:
- Logging tool use
- Recording audit trails
- Running lint/test checks after edits
- Sending webhook notifications
- Adding context after a tool result
- Detecting tool output anomalies
But PostToolUse cannot prevent the original action because it has already happened. Claude Code’s hook reference explicitly distinguishes events that can block from those that cannot; PostToolUse cannot block the action and can only show feedback after the fact. (Claude Code)
Good use:
Bad use:6.3. Stop Hook: Completion Enforcement
A Stop hook runs when Claude is about to finish. Use it to enforce completion criteria.
Good cases:
- Tests must pass before Claude stops
- Required checklist must be complete
- Output must include required sections
- All requested files must be updated
- No unresolved TODOs remain
Anthropic’s hooks reference gives an example of a multi-criteria Stop hook that checks whether all user-requested tasks are complete, whether errors need addressing, and whether follow-up work is needed; if not OK, Claude continues working. (Claude Code)
Trap:
A
Stophook is not the same asstop_reason.
stop_reasoncontrols the API agentic loop. AStophook can intercept Claude Code / Agent SDK lifecycle completion and enforce additional criteria.
6.4. SubagentStart And SubagentStop
From D1.2, subagents are isolated workers and all coordination should flow through the coordinator. For D1.5, hooks let you observe or enforce subagent lifecycle behavior.
Use subagent hooks for:
- Logging spawned subagents
- Tracking parallel work
- Preventing premature subagent completion
- Validating subagent output
- Auditing subagent tool usage
Anthropic’s Agent SDK hook docs list SubagentStart and SubagentStop and show a SubagentStop example that logs subagent completion, transcript path, and tool-use ID. (Claude Code)
Important trap:
Subagents do not automatically inherit every permission assumption safely. Be careful with broad permission modes.
Anthropic’s permission docs warn that when a parent uses bypassPermissions, acceptEdits, or auto, subagents inherit that mode and it cannot be overridden per subagent; in particular, inherited bypassPermissions can give subagents full autonomous system access. (Claude)
6.5 Hook Decisions
For SDK callback hooks, a PreToolUse hook can return:
| Decision | Meaning |
|---|---|
allow |
Approve the operation |
deny |
Block the operation |
ask |
Escalate to user approval |
defer |
End query so it can be resumed later |
updatedInput |
Modify tool input before execution |
permissionDecisionReason |
Tell Claude why a tool was denied |
additionalContext |
Add context after a tool result |
updatedToolOutput |
Replace tool output before Claude sees it |
Anthropic documents these fields under hook outputs and notes that updatedToolOutput replaces tool output before Claude sees it, while additionalContext can append information to tool results. It also states that when multiple hook or permission results apply, the priority is deny > defer > ask > allow. (Claude Code)
Implication:
If any matching hook denies, the operation is blocked even if another hook allows.
6.6 Command Hook Exit Codes
For Claude Code command hooks, exit codes matter.
| Exit code / output | Meaning |
|---|---|
0 with no JSON |
No decision; normal flow continues |
0 with valid JSON |
Structured control decision |
2 |
Blocking error for blockable events |
| Other non-zero | Usually non-blocking error |
The hooks reference says exit 2 is the “stop, don’t do this” signal for blockable events such as PreToolUse, PermissionRequest, UserPromptSubmit, Stop, SubagentStop, and PreCompact; it also says PostToolUse cannot block because the tool already ran. (Claude Code)
Exam trap:
Exit code
1is not the standard enforcement signal in Claude Code hooks. Useexit 2or structured JSON for blocking.
7 Permissions: Allow, Deny, Ask, and Modes
Hooks are only one enforcement mechanism. Permission rules and modes are equally relevant.
7.1 Allow and Deny Rules
| Rule | Meaning |
|---|---|
allowed_tools=["Read", "Grep"] |
Auto-approve these tools |
disallowed_tools=["Bash"] |
Remove Bash tool from Claude’s available tools |
disallowed_tools=["Bash(rm *)"] |
Keep Bash available, but block matching rm calls |
disallowed_tools=["*"] |
Remove every tool |
Anthropic’s permission docs distinguish allow rules from deny rules: allow rules pre-approve matching tools, while deny rules can remove a whole tool or block scoped patterns; scoped deny rules still apply even in bypassPermissions. (Claude)
7.2 Permission Modes
| Mode | Exam meaning |
|---|---|
default |
Standard permission behaviour |
dontAsk |
Deny anything not pre-approved |
acceptEdits |
Auto-accept file edits/filesystem operations in scope |
bypassPermissions |
Auto-approve tools unless denied/asked by earlier controls; dangerous |
plan |
Explore and plan; file edits are not auto-approved |
Use dontAsk for headless locked-down agents.
Use plan when Claude should inspect and propose but not modify files.
Use bypassPermissions only in isolated controlled environments, because it grants broad system access unless deny/ask/hooks stop it. (Claude)
7.3. canUseTool: Human Approval and User Input
Use canUseTool when your application needs to surface tool approval requests or clarifying questions to the user.
Anthropic’s docs say Claude may need input when it wants permission for a tool or when it asks a clarifying question through AskUserQuestion; both trigger canUseTool, which pauses execution until the callback returns. (Claude Code)
Good use cases:
- User approval before deletion
- User approval before deployment
- User selects which database/environment to use
- User confirms an irreversible action
- UI-mediated “allow / deny / modify” decision
Anthropic documents that a callback can allow, deny, approve with changes, remember a permission update, reject with guidance, or redirect Claude through application logic. (Claude Code)
Exam distinction:
| Need | Use |
|---|---|
Automatically block every .env write |
Hook or deny rule |
| Ask user whether to allow a specific file delete | canUseTool |
| Ask user which output format they want | AskUserQuestion via canUseTool |
| Send Slack message when approval is needed | PermissionRequest or notification hook |
7.4 Async Hooks
Async hooks are useful for side effects that should not block Claude.
Examples:
- Logging
- Sending metrics
- Starting background tests
- Notifying Slack
- Firing analytics
Async hooks cannot block, modify, or inject context into the current operation because Claude continues immediately.
Anthropic’s docs explicitly state that async hook decisions such as permissionDecision and continue have no effect after the triggering action has proceeded. (Claude Code)
Trap:
Do not use an async hook for a mandatory security check.
Use synchronous PreToolUse or permission rules for enforcement.
8. Hook Types
Claude Code hooks can be implemented as command hooks, HTTP hooks, MCP tool hooks, prompt hooks, or agent hooks.
- Command hooks run shell commands;
- HTTP hooks call endpoints;
- MCP tool hooks call connected MCP tools;
- Prompt hooks use an LLM for single-turn evaluation;
- Agent hooks spawn a subagent to verify conditions.
| Hook type | Best use |
|---|---|
| Command | Deterministic local scripts, validation, linting |
| HTTP | Centralized policy service or audit endpoint |
| MCP tool | Use connected MCP capability as validator |
| Prompt | Soft judgement / rubric check |
| Agent | Richer verification requiring file inspection |
| Async command | Logging or long-running side effect |
Anthropic marks agent hooks as experimental and says production workflows should prefer command hooks. (Claude Code)
9. Security Best Practices
Hooks can be powerful and dangerous. Command hooks run with the user’s system permissions, so hook scripts must be reviewed and tested.
Anthropic’s hooks reference recommends validating and sanitizing inputs, quoting shell variables, blocking path traversal, using absolute paths, and skipping sensitive files like .env, .git, and keys. (Claude Code)
Traps:
- Do not pass raw tool input into shell commands without quoting
- Do not trust hook input blindly
- Do not assume hooks run in a sandbox unless your environment enforces one
- Do not use a prompt hook as a hard security boundary
- Do not let
bypassPermissionsrun outside a controlled environment - Do not assume
allowed_toolsconstrainsbypassPermissions
10. Decision Table
| Scenario wording | Best answer |
|---|---|
| “Block dangerous command before execution” | PreToolUse hook or deny rule |
| “Log every file change” | PostToolUse hook |
| “Prevent Claude from stopping before tests pass” | Stop hook |
| “Validate subagent output before accepting it” | SubagentStop hook |
| “Ask user before deleting files” | canUseTool callback / ask rule |
| “Headless agent with only Read/Grep allowed” | allowed_tools + dontAsk |
| “Claude should plan but not edit” | plan mode |
| “Need auto-approve file edits in isolated repo” | acceptEdits |
| “Need full speed in isolated sandbox but still block rm” | bypassPermissions + deny rules/hooks |
| “Need security policy to apply to all developers” | Managed policy settings / project hooks |
| “Need external approval service” | HTTP hook or canUseTool integration |
| “Need non-blocking analytics” | Async hook |