Tool Hook Events
Tool Events
These events fire before a tool runs. They also fire when a permission prompt shows or auto mode denies a call.
They are the main place to approve, change, or block a tool call.
MCP tool names have the form mcp__<server>__<tool>.
PreToolUse
Fires after Claude builds the tool input and before the tool runs.
It does not fire for @ file mentions.
Exit 2 blocks the call, and stderr is the reason.
Handlers: all five types.
| Field | Status | Direction | What it does |
|---|---|---|---|
tool_ | Documented: An official docs page names this item. | Input | The tool name. Any tool except End. |
tool_ | Documented: An official docs page names this item. | Input | The tool arguments. The shape depends on the tool (see below). File paths are absolute. |
tool_ | Documented: An official docs page names this item. | Input | The tool call ID. |
permission | Documented: An official docs page names this item. | Output, in hook | allow, deny, ask, or defer. allow skips the prompt but not deny or ask rules. Order of strength: deny, then defer, then ask, then allow. defer works only in - with one tool call. |
permission | Documented: An official docs page names this item. | Output, in hook | The reason text. For ask it shows in the prompt. For deny it goes to Claude. Otherwise it goes to the debug log. |
updated | Documented: An official docs page names this item. | Output, in hook | Replaces the whole tool input. Rules and background checks use the new input. Needed with allow for Ask and Exit. Claude Code ignores it with defer. |
additional | Documented: An official docs page names this item. | Output, in hook | Text placed next to the tool result. Claude Code ignores it with defer. |
permission | Undocumented: Only the program shows this item. It can change without notice. | Output, in hook | Words for a permission card. For the desktop host only. An object with title (200 characters), description (2000), and display (64). Claude Code ignores it, with a debug line, if it cannot show the text. |
Top-level decision and reason | Differs: The official docs and the program disagree. The row says how. Deprecated for this event. The program schema accepts the enum on all events. | Output | approve maps to allow. block maps to deny. Use permission instead. |
ask label | Documented: An official docs page names this item. | Behavior | The prompt shows [settings], [plugin:, or [skill]. An ask also forces a prompt in auto mode. |
Tool Input Shapes
The tool names link to Tools, which holds the status and the full parameters of each tool.
| Tool | Status | Keys in tool_input |
|---|---|---|
Bash | Documented: An official docs page names this item. | command, description, timeout (milliseconds), run_ |
Power | Documented: An official docs page names this item. Gated: Works only behind a flag or a server gate. CLAUDE_CODE_USE_POWERSHELL_TOOL | The same keys as Bash. Match with Bash|Power. |
Write | Documented: An official docs page names this item. | file_, content |
Edit | Documented: An official docs page names this item. | file_, old_, new_, replace_ |
Read | Documented: An official docs page names this item. | file_, offset, limit |
Glob | Documented: An official docs page names this item. | pattern, path |
Grep | Documented: An official docs page names this item. | pattern, path, glob, output_, -, multiline |
Web | Documented: An official docs page names this item. Gated: Works only behind a flag or a server gate. CLAUDE_CODE_DISABLE_WEB_FETCH=1 turns it off | url, prompt, offset (2.1.290) |
Web | Documented: An official docs page names this item. Gated: Works only behind a flag or a server gate. off for the gateway provider. Vertex depends on the model | query, allowed_, blocked_ |
Agent | Documented: An official docs page names this item. | prompt, description, subagent_, model |
Ask | Documented: An official docs page names this item. | questions, and answers (set only through updated) |
Exit | Documented: An official docs page names this item. Gated: Works only behind a flag or a server gate. internal enable check | Claude Code adds plan and plan. Claude Code accepts the old allowed key. It ignores the key. |
The docs name Workflow as a tool you can match, but give no input shape for it.
PermissionRequest
Fires when a permission prompt would show, or when Claude Code would deny a call that cannot prompt.
This event does not honor exit 2. Deny with the decision object instead.
We checked this in the interactive screen. The hook fires before the dialog opens. allow skips the dialog. deny returns message to the model. Claude Code ignores a top-level decision: "block". See Input and Output.
There is no event for a sandbox network request.
Handlers: command, http, mcp_tool, and prompt. Claude Code skips an agent hook.
| Field | Status | Direction | What it does |
|---|---|---|---|
tool_, tool_ | Documented: An official docs page names this item. | Input | The same as in Pre. There is no tool_. |
permission_ | Documented: An official docs page names this item. | Input | Updates that Claude Code suggests. The same shape as updated. A dialog may show other options. |
decision. | Documented: An official docs page names this item. | Output, in hook | allow or deny. Deny and ask rules still apply. |
decision. | Documented: An official docs page names this item. | Output | New tool input. allow only. Checked again against deny and ask rules. |
decision. | Documented: An official docs page names this item. | Output | Permission updates to apply. allow only. |
decision. | Documented: An official docs page names this item. | Output | The reason for the denial. deny only. Goes to Claude. |
decision. | Documented: An official docs page names this item. | Output | Stops Claude. deny only. |
Permission Update Entries
updatedPermissions and permission_suggestions use these entries.
| Entry | Status | What it does | Fields |
|---|---|---|---|
add, replace, remove | Documented: An official docs page names this item. | Change permission rules. | rules (each {tool), behavior (allow, deny, ask), destination |
set | Documented: An official docs page names this item. | Change the permission mode. | mode: default, auto, accept, dont, bypass, plan. bypass works only if bypass was on when Claude Code started. |
add, remove | Documented: An official docs page names this item. | Change working directories. | directories |
destination | Differs: The official docs and the program disagree. The row says how. The docs list four values. The program type also includes cliArg. | Where Claude Code keeps the change. | session, local, project, user |
PermissionDenied
Fires only when auto mode denies a call, including a denial with no classifier verdict.
It does not fire for manual denials, PreToolUse blocks, or deny rules.
Claude Code ignores the exit code and stderr.
We checked this in the interactive screen. An auto mode denial fired it. A manual "No" did not. With no classifier verdict (the classifier failed), retry: true changed nothing in the model's input. With a verdict (a stub answered the classifier with a block and the reason [Data Exfiltration]), the denial stood. The tool did not run. The model's next request got the extra text The PermissionDenied hook indicated you may retry this tool call. after the denied tool result. Without retry that text was absent. systemMessage was not shown.
This event accepts all five handler types. Claude Code drops output from prompt and agent.
| Field | Status | Direction | What it does |
|---|---|---|---|
tool_, tool_, tool_, mcp_ | Documented: An official docs page names this item. | Input | As in Pre. |
reason | Documented: An official docs page names this item. | Input | Why auto mode denied. A rule name in brackets, such as [Data Exfiltration]. |
retry | Documented: An official docs page names this item. | Output, in hook | true tells the model it may retry. The denial stands. Ignored for denials with no verdict. |
Tool Result Events
These events fire after a tool runs. PostToolUse and PostToolUseFailure match on tool name. PostToolBatch has no matcher.
All three accept the five handler types.
PostToolUse
Fires after a tool call succeeds.
Exit 2 shows stderr to Claude but does not undo the tool.
It does not see writes made by Bash or by outside processes. Use FileChanged for those.
| Field | Status | Direction | What it does |
|---|---|---|---|
tool_, tool_, tool_, mcp_ | Documented: An official docs page names this item. | Input | As in Pre. |
tool_ | Documented: An official docs page names this item. | Input | The structured tool output. The shape depends on the tool. |
duration_ | Documented: An official docs page names this item. | Input | Run time in milliseconds. Excludes permission prompts and Pre hooks. Optional. |
decision, reason | Documented: An official docs page names this item. | Output | block adds the reason next to the result. Claude still sees the original output. |
additional | Documented: An official docs page names this item. | Output, in hook | Text next to the result. Claude Code does not deliver it for async hooks. |
updated | Documented: An official docs page names this item. | Output, in hook | Replaces what Claude sees. Must match the tool output shape. For built-in tools, Claude Code ignores a bad shape. The tool already ran. |
updated | Documented: An official docs page names this item. | Output, in hook | The same, for MCP tools only. Prefer updated. |
classifier | Documented: An official docs page names this item. | Output, in hook | A note for the auto mode classifier. Needs 2.1.236. At most 2,000 characters for one call. Dropped for async hooks and read-only calls. Treated as unverified context. |
tool_ | Documented: An official docs page names this item. | Input | Files that a Bash run changed. Needs 2.1.269, a public beta, and the setting bash. Keys: changed (200), files (5), more, unavailable, skipped, shared. |
tool_ of Agent | Documented: An official docs page names this item. | Input | status (completed or async_), agent, content, resolved, models, total, total, total, usage, and output when the agent starts in the background. |
tool_ of Exit | Documented: An official docs page names this item. | Input | plan and file. |
Subagent tool | Documented: An official docs page names this item. | Matcher | The tool that carries a subagent report in auto mode. Match it to read tool_. Needs 2.1.271. |
PostToolUseFailure
Fires when a started tool fails, or an MCP tool returns an error.
It does not fire for unknown tools, schema errors, or permission denials.
Exit 2 shows stderr to Claude.
| Field | Status | Direction | What it does |
|---|---|---|---|
tool_, tool_, tool_, mcp_ | Documented: An official docs page names this item. | Input | As in Post. |
error | Documented: An official docs page names this item. | Input | What went wrong. The same text as the tool result. Bash and Power errors start with Exit code N. |
is_ | Documented: An official docs page names this item. | Input | true for an abort. Canceling a running tool does not fire this hook. |
duration_ | Documented: An official docs page names this item. | Input | Run time in milliseconds. Optional. |
additional | Documented: An official docs page names this item. | Output, in hook | Text next to the error. A top-level decision: also works. |
PostToolBatch
Fires once after all tool calls of a batch resolve, before the next model request.
Exit 2, decision: "block", or continue: false stops the loop.
| Field | Status | Direction | What it does |
|---|---|---|---|
tool_ | Documented: An official docs page names this item. | Input | One entry per call, with tool_, tool_, tool_, and tool_. |
tool_ | Documented: An official docs page names this item. | Input | What the model gets in the tool_. A serialized string or content blocks. It is not the Post object. |
additional | Documented: An official docs page names this item. | Output, in hook | Context for the next request. Claude Code sends it once. |
PostToolUse tool_response Shapes
We logged the stdin of a PostToolUse command hook in -p on 2.1.305, with one scripted call per tool. This is the tool_response you can read, and the shape updatedToolOutput replaces.
| Tool | Status | tool_response |
|---|---|---|
Bash | Documented: An official docs page names this item. | stdout, stderr, interrupted, is, and no. |
Read (text) | Undocumented: Only the program shows this item. It can change without notice. | type (text) and file with file, content, num, start, and total. |
Write (new file) | Undocumented: Only the program shows this item. It can change without notice. | type (create), file, content, structured (empty), original (null), and user. |
Edit | Undocumented: Only the program shows this item. It can change without notice. | file, old, new, original, structured (items with old, old, new, new, lines), user, replace, and content. The last key shows only when the file had a partial Read (with offset and limit) or no Read. tool_ also has replace_. |
Tool | Undocumented: Only the program shows this item. It can change without notice. | matches (tool names), query, and total_. tool_ also has max_. |
Agent | Undocumented: Only the program shows this item. It can change without notice. | First the async start: is, status (async_), agent, description, resolved, prompt, output, can, and can. Then, for a finished subagent: status (completed), prompt, agent, agent, content (text blocks), resolved, total, total, total, usage, can, harness, harness, and harness. |
List | Undocumented: Only the program shows this item. It can change without notice. | listing (text), sections, and notes. |
Cron | Undocumented: Only the program shows this item. It can change without notice. | id, human, recurring, durable, and next. |
Cron | Undocumented: Only the program shows this item. It can change without notice. | jobs. |
A second probe ran the same hook in the interactive screen (2.1.305, acceptEdits, one scripted call per tool). It confirmed the shapes of Bash, Read, Write, and Edit above, with one difference: the Edit result after a Read had no contentNotInModelContext key. It also confirmed that an MCP tool's tool_response is the array of content blocks the server returned ([{"type":"text","text":"echo:OK"}]), and that the hook input has mcp_server. Every PostToolUse input had hook_event_name, tool_name, tool_input, tool_response, tool_use_id, and duration_ms. On 2.1.305 an Edit of a file that was never read was not an error, in acceptEdits and in the default mode. The file changed, PostToolUse fired, and no PostToolUseFailure fired. That tool_response had contentNotInModelContext: true, which the Edit after a Read did not have. The interactive probe did not run Glob, Grep, NotebookEdit, WebFetch, or WebSearch (this build's tool list there had no Glob or Grep), and did not test whether updatedToolOutput accepts these shapes. See Interactive Probes.
Related Pages
- Hook Events: all 33 events and where each one is documented.
- Input and Output: common fields, decisions, and exit codes.
- Configuration: matchers and timeouts.