Hooks
Shell commands Claude Code runs automatically in response to events — e.g., before/after a tool call or on session end.
What it is
A hook is a deterministic handler that fires on a named Claude Code lifecycle event. Events include PreToolUse, PostToolUse, UserPromptSubmit, Stop, SubagentStop, Notification, SessionStart, and (v2.1.152, 2026-05-27) MessageDisplay — which can transform or hide assistant message text as it’s rendered. Handlers can be command (shell), prompt, agent, http, or mcp_tool (call an MCP tool directly). A PreToolUse hook that exits 2 blocks the pending tool call — this is the main guardrail mechanism. As of May 2026, PostToolUse hooks can also replace tool output for any tool (previously MCP-only) via hookSpecificOutput.updatedToolOutput, and any hook can fire desktop notifications, set window titles, or ring the terminal bell via the new terminalSequence JSON field (works even with no controlling terminal). SessionStart hooks can now return reloadSkills: true (so a hook that installs skills makes them available in the same session) and set the session title via hookSpecificOutput.sessionTitle.
Hooks run outside the model. They see structured JSON on stdin and respond via exit codes or stdout JSON. PostToolUse and PostToolUseFailure input now includes duration_ms (tool execution only, excluding permission prompts and PreToolUse hook time); Stop and SubagentStop input now includes background_tasks and session_crons. The active effort level is available as effort.level and $CLAUDE_EFFORT.
When to use it
- Enforce a policy (block
rm -rf, refuse edits to protected paths). - Auto-format or lint files after
Write/Edit/MultiEdit. - Log tool calls for audit.
- Redact secrets from tool output before Claude sees it.
- Route notifications to Slack or a desktop alert.
How to install / enable
The interactive path is easiest:
# inside a Claude Code session
/hooks
Or edit .claude/settings.json (project, committed) or ~/.claude/settings.json (personal) directly:
{
"hooks": {
"PostToolUse": [{
"matcher": "Write|Edit|MultiEdit",
"hooks": [{
"type": "command",
"command": "npx prettier --write \"$CLAUDE_TOOL_INPUT_FILE_PATH\""
}]
}]
}
}
Reload by restarting the session or running /hooks again.
Common pitfalls
- Putting secrets in
.claude/settings.json— it’s designed to be committed. - Omitting the
matcherregex, which causes the hook to fire for every tool call. - Relying on
PostToolUseto undo an action — it can’t. UsePreToolUseto block. - Letting a hook hang. Long-running commands stall the session; background or short-circuit them.
- Looping
Stophooks that keep blocking — Claude Code now ends the turn after 8 consecutive blocks (override withCLAUDE_CODE_STOP_HOOK_BLOCK_CAP). - Echoing tool input back unchanged from a
commandhook and assuming it bypasses permissions; use thepermissionDecisionJSON shape instead.
See also
- Plugins — distribute hooks alongside skills and MCP
- Authentication
- Hooks reference — canonical docs
Sources
- Automate workflows with hooks — Anthropic docs; verified 2026-05-22 (this run).
- Hooks reference — Anthropic docs; verified 2026-05-22.
- Claude Code changelog (May 2026) —
type: "mcp_tool"hooks,PostToolUseduration_ms,Stop/SubagentStopbackground_tasks+session_crons, PostToolUseupdatedToolOutputfor all tools, Stop-hook loop cap,$CLAUDE_EFFORT,terminalSequencefield,MessageDisplayevent +SessionStart.reloadSkills+sessionTitle(v2.1.152, 2026-05-27); verified 2026-05-29 (this run).