3> This page provides reference documentation for implementing hooks in Claude Code.3> This page provides reference documentation for implementing hooks in Claude Code.
4 4
5<Tip>5<Tip>
6 For a quickstart guide with examples, see [Get started with Claude Code hooks](/en/docs/claude-code/hooks-guide).6 For a quickstart guide with examples, see [Get started with Claude Code hooks](/en/hooks-guide).
7</Tip>7</Tip>
8 8
9## Configuration9## Configuration
10 10
11Claude Code hooks are configured in your [settings files](/en/docs/claude-code/settings):11Claude Code hooks are configured in your [settings files](/en/settings):
12 12
13* `~/.claude/settings.json` - User settings13* `~/.claude/settings.json` - User settings
14* `.claude/settings.json` - Project settings14* `.claude/settings.json` - Project settings
38```38```
39 39
40* **matcher**: Pattern to match tool names, case-sensitive (only applicable for40* **matcher**: Pattern to match tool names, case-sensitive (only applicable for
41 `PreToolUse` and `PostToolUse`)41 `PreToolUse`, `PermissionRequest`, and `PostToolUse`)
42 * Simple strings match exactly: `Write` matches only the Write tool42 * Simple strings match exactly: `Write` matches only the Write tool
43 * Supports regex: `Edit|Write` or `Notebook.*`43 * Supports regex: `Edit|Write` or `Notebook.*`
44 * Use `*` to match all tools. You can also use empty string (`""`) or leave44 * Use `*` to match all tools. You can also use empty string (`""`) or leave
45 `matcher` blank.45 `matcher` blank.
46* **hooks**: Array of commands to execute when the pattern matches46* **hooks**: Array of hooks to execute when the pattern matches
47 * `type`: Currently only `"command"` is supported47 * `type`: Hook execution type - `"command"` for bash commands or `"prompt"` for LLM-based evaluation
48 * `command`: The bash command to execute (can use `$CLAUDE_PROJECT_DIR`48 * `command`: (For `type: "command"`) The bash command to execute (can use `$CLAUDE_PROJECT_DIR` environment variable)
49 environment variable)49 * `prompt`: (For `type: "prompt"`) The prompt to send to the LLM for evaluation
50 * `timeout`: (Optional) How long a command should run, in seconds, before50 * `timeout`: (Optional) How long a hook should run, in seconds, before canceling that specific hook
51 canceling that specific command.51
52 52For events like `UserPromptSubmit`, `Stop`, and `SubagentStop`
53For events like `UserPromptSubmit`, `Notification`, `Stop`, and `SubagentStop`
54that don't use matchers, you can omit the matcher field:53that don't use matchers, you can omit the matcher field:
55 54
56```json theme={null}55```json theme={null}
96 95
97### Plugin hooks96### Plugin hooks
98 97
99[Plugins](/en/docs/claude-code/plugins) can provide hooks that integrate seamlessly with your user and project hooks. Plugin hooks are automatically merged with your configuration when plugins are enabled.98[Plugins](/en/plugins) can provide hooks that integrate seamlessly with your user and project hooks. Plugin hooks are automatically merged with your configuration when plugins are enabled.
100 99
101**How plugin hooks work**:100**How plugin hooks work**:
102 101
141* `${CLAUDE_PROJECT_DIR}`: Project root directory (same as for project hooks)140* `${CLAUDE_PROJECT_DIR}`: Project root directory (same as for project hooks)
142* All standard environment variables are available141* All standard environment variables are available
143 142
144See the [plugin components reference](/en/docs/claude-code/plugins-reference#hooks) for details on creating plugin hooks.143See the [plugin components reference](/en/plugins-reference#hooks) for details on creating plugin hooks.
144
145## Prompt-Based Hooks
146
147In addition to bash command hooks (`type: "command"`), Claude Code supports prompt-based hooks (`type: "prompt"`) that use an LLM to evaluate whether to allow or block an action. Prompt-based hooks are currently only supported for `Stop` and `SubagentStop` hooks, where they enable intelligent, context-aware decisions.
148
149### How prompt-based hooks work
150
151Instead of executing a bash command, prompt-based hooks:
152
1531. Send the hook input and your prompt to a fast LLM (Haiku)
1542. The LLM responds with structured JSON containing a decision
1553. Claude Code processes the decision automatically
156
157### Configuration
158
159```json theme={null}
160{
161 "hooks": {
162 "Stop": [
163 {
164 "hooks": [
165 {
166 "type": "prompt",
167 "prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete."
168 }
169 ]
170 }
171 ]
172 }
173}
174```
175
176**Fields:**
177
178* `type`: Must be `"prompt"`
179* `prompt`: The prompt text to send to the LLM
180 * Use `$ARGUMENTS` as a placeholder for the hook input JSON
181 * If `$ARGUMENTS` is not present, input JSON is appended to the prompt
182* `timeout`: (Optional) Timeout in seconds (default: 30 seconds)
183
184### Response schema
185
186The LLM must respond with JSON containing:
187
188```json theme={null}
189{
190 "decision": "approve" | "block",
191 "reason": "Explanation for the decision",
192 "continue": false, // Optional: stops Claude entirely
193 "stopReason": "Message shown to user", // Optional: custom stop message
194 "systemMessage": "Warning or context" // Optional: shown to user
195}
196```
197
198**Response fields:**
199
200* `decision`: `"approve"` allows the action, `"block"` prevents it
201* `reason`: Explanation shown to Claude when decision is `"block"`
202* `continue`: (Optional) If `false`, stops Claude's execution entirely
203* `stopReason`: (Optional) Message shown when `continue` is false
204* `systemMessage`: (Optional) Additional message shown to the user
205
206### Supported hook events
207
208Prompt-based hooks work with any hook event, but are most useful for:
209
210* **Stop**: Intelligently decide if Claude should continue working
211* **SubagentStop**: Evaluate if a subagent has completed its task
212* **UserPromptSubmit**: Validate user prompts with LLM assistance
213* **PreToolUse**: Make context-aware permission decisions
214* **PermissionRequest**: Intelligently allow or deny permission dialogs
215
216### Example: Intelligent Stop hook
217
218```json theme={null}
219{
220 "hooks": {
221 "Stop": [
222 {
223 "hooks": [
224 {
225 "type": "prompt",
226 "prompt": "You are evaluating whether Claude should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"decision\": \"approve\" or \"block\", \"reason\": \"your explanation\"}",
227 "timeout": 30
228 }
229 ]
230 }
231 ]
232 }
233}
234```
235
236### Example: SubagentStop with custom logic
237
238```json theme={null}
239{
240 "hooks": {
241 "SubagentStop": [
242 {
243 "hooks": [
244 {
245 "type": "prompt",
246 "prompt": "Evaluate if this subagent should stop. Input: $ARGUMENTS\n\nCheck if:\n- The subagent completed its assigned task\n- Any errors occurred that need fixing\n- Additional context gathering is needed\n\nReturn: {\"decision\": \"approve\" or \"block\", \"reason\": \"explanation\"}"
247 }
248 ]
249 }
250 ]
251 }
252}
253```
254
255### Comparison with bash command hooks
256
257| Feature | Bash Command Hooks | Prompt-Based Hooks |
258| --------------------- | ----------------------- | ------------------------------ |
259| **Execution** | Runs bash script | Queries LLM |
260| **Decision logic** | You implement in code | LLM evaluates context |
261| **Setup complexity** | Requires script file | Just configure prompt |
262| **Context awareness** | Limited to script logic | Natural language understanding |
263| **Performance** | Fast (local execution) | Slower (API call) |
264| **Use case** | Deterministic rules | Context-aware decisions |
265
266### Best practices
267
268* **Be specific in prompts**: Clearly state what you want the LLM to evaluate
269* **Include decision criteria**: List the factors the LLM should consider
270* **Test your prompts**: Verify the LLM makes correct decisions for your use cases
271* **Set appropriate timeouts**: Default is 30 seconds, adjust if needed
272* **Use for complex decisions**: Bash hooks are better for simple, deterministic rules
273
274See the [plugin components reference](/en/plugins-reference#hooks) for details on creating plugin hooks.
145 275
146## Hook Events276## Hook Events
147 277
151 281
152**Common matchers:**282**Common matchers:**
153 283
154* `Task` - Subagent tasks (see [subagents documentation](/en/docs/claude-code/sub-agents))284* `Task` - Subagent tasks (see [subagents documentation](/en/sub-agents))
155* `Bash` - Shell commands285* `Bash` - Shell commands
156* `Glob` - File pattern matching286* `Glob` - File pattern matching
157* `Grep` - Content search287* `Grep` - Content search
160* `Write` - File writing290* `Write` - File writing
161* `WebFetch`, `WebSearch` - Web operations291* `WebFetch`, `WebSearch` - Web operations
162 292
293Use [PreToolUse decision control](#pretooluse-decision-control) to allow, deny, or ask for permission to use the tool.
294
295### PermissionRequest
296
297Runs when the user is shown a permission dialog.
298Use [PermissionRequest decision control](#permissionrequest-decision-control) to allow or deny on behalf of the user.
299
300Recognizes the same matcher values as PreToolUse.
301
163### PostToolUse302### PostToolUse
164 303
165Runs immediately after a tool completes successfully.304Runs immediately after a tool completes successfully.
168 307
169### Notification308### Notification
170 309
171Runs when Claude Code sends notifications. Notifications are sent when:310Runs when Claude Code sends notifications. Supports matchers to filter by notification type.
311
312**Common matchers:**
172 313
1731. Claude needs your permission to use a tool. Example: "Claude needs your314* `permission_prompt` - Permission requests from Claude Code
174 permission to use Bash"315* `idle_prompt` - When Claude is waiting for user input (after 60+ seconds of idle time)
1752. The prompt input has been idle for at least 60 seconds. "Claude is waiting316* `auth_success` - Authentication success notifications
176 for your input"317* `elicitation_dialog` - When Claude Code needs input for MCP tool elicitation
318
319You can use matchers to run different hooks for different notification types, or omit the matcher to run hooks for all notifications.
320
321**Example: Different notifications for different types**
322
323```json theme={null}
324{
325 "hooks": {
326 "Notification": [
327 {
328 "matcher": "permission_prompt",
329 "hooks": [
330 {
331 "type": "command",
332 "command": "/path/to/permission-alert.sh"
333 }
334 ]
335 },
336 {
337 "matcher": "idle_prompt",
338 "hooks": [
339 {
340 "type": "command",
341 "command": "/path/to/idle-notification.sh"
342 }
343 ]
344 }
345 ]
346 }
347}
348```
177 349
178### UserPromptSubmit350### UserPromptSubmit
179 351
203 375
204Runs when Claude Code starts a new session or resumes an existing session (which376Runs when Claude Code starts a new session or resumes an existing session (which
205currently does start a new session under the hood). Useful for loading in377currently does start a new session under the hood). Useful for loading in
206development context like existing issues or recent changes to your codebase.378development context like existing issues or recent changes to your codebase, installing dependencies, or setting up environment variables.
207 379
208**Matchers:**380**Matchers:**
209 381
212* `clear` - Invoked from `/clear`384* `clear` - Invoked from `/clear`
213* `compact` - Invoked from auto or manual compact.385* `compact` - Invoked from auto or manual compact.
214 386
387#### Persisting environment variables
388
389SessionStart hooks have access to the `CLAUDE_ENV_FILE` environment variable, which provides a file path where you can persist environment variables for subsequent bash commands.
390
391**Example: Setting individual environment variables**
392
393```bash theme={null}
394#!/bin/bash
395
396if [ -n "$CLAUDE_ENV_FILE" ]; then
397 echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
398 echo 'export API_KEY=your-api-key' >> "$CLAUDE_ENV_FILE"
399 echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
400fi
401
402exit 0
403```
404
405**Example: Persisting all environment changes from the hook**
406
407When your setup modifies the environment (e.g., `nvm use`), capture and persist all changes by diffing the environment:
408
409```bash theme={null}
410#!/bin/bash
411
412ENV_BEFORE=$(export -p | sort)
413
414# Run your setup commands that modify the environment
415source ~/.nvm/nvm.sh
416nvm use 20
417
418if [ -n "$CLAUDE_ENV_FILE" ]; then
419 ENV_AFTER=$(export -p | sort)
420 comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
421fi
422
423exit 0
424```
425
426Any variables written to this file will be available in all subsequent bash commands that Claude Code executes during the session.
427
428<Note>
429 `CLAUDE_ENV_FILE` is only available for SessionStart hooks. Other hook types do not have access to this variable.
430</Note>
431
215### SessionEnd432### SessionEnd
216 433
217Runs when a Claude Code session ends. Useful for cleanup tasks, logging session434Runs when a Claude Code session ends. Useful for cleanup tasks, logging session
235 session_id: string452 session_id: string
236 transcript_path: string // Path to conversation JSON453 transcript_path: string // Path to conversation JSON
237 cwd: string // The current working directory when the hook is invoked454 cwd: string // The current working directory when the hook is invoked
455 permission_mode: string // Current permission mode: "default", "plan", "acceptEdits", or "bypassPermissions"
238 456
239 // Event-specific fields457 // Event-specific fields
240 hook_event_name: string458 hook_event_name: string
251 "session_id": "abc123",469 "session_id": "abc123",
252 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",470 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
253 "cwd": "/Users/...",471 "cwd": "/Users/...",
472 "permission_mode": "default",
254 "hook_event_name": "PreToolUse",473 "hook_event_name": "PreToolUse",
255 "tool_name": "Write",474 "tool_name": "Write",
256 "tool_input": {475 "tool_input": {
257 "file_path": "/path/to/file.txt",476 "file_path": "/path/to/file.txt",
258 "content": "file content"477 "content": "file content"
259 }478 },
479 "tool_use_id": "toolu_01ABC123..."
260}480}
261```481```
262 482
269 "session_id": "abc123",489 "session_id": "abc123",
270 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",490 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
271 "cwd": "/Users/...",491 "cwd": "/Users/...",
492 "permission_mode": "default",
272 "hook_event_name": "PostToolUse",493 "hook_event_name": "PostToolUse",
273 "tool_name": "Write",494 "tool_name": "Write",
274 "tool_input": {495 "tool_input": {
278 "tool_response": {499 "tool_response": {
279 "filePath": "/path/to/file.txt",500 "filePath": "/path/to/file.txt",
280 "success": true501 "success": true
281 }502 },
503 "tool_use_id": "toolu_01ABC123..."
282}504}
283```505```
284 506
289 "session_id": "abc123",511 "session_id": "abc123",
290 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",512 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
291 "cwd": "/Users/...",513 "cwd": "/Users/...",
514 "permission_mode": "default",
292 "hook_event_name": "Notification",515 "hook_event_name": "Notification",
293 "message": "Task completed successfully"516 "message": "Claude needs your permission to use Bash",
517 "notification_type": "permission_prompt"
294}518}
295```519```
296 520
301 "session_id": "abc123",525 "session_id": "abc123",
302 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",526 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
303 "cwd": "/Users/...",527 "cwd": "/Users/...",
528 "permission_mode": "default",
304 "hook_event_name": "UserPromptSubmit",529 "hook_event_name": "UserPromptSubmit",
305 "prompt": "Write a function to calculate the factorial of a number"530 "prompt": "Write a function to calculate the factorial of a number"
306}531}
316{541{
317 "session_id": "abc123",542 "session_id": "abc123",
318 "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",543 "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
544 "permission_mode": "default",
319 "hook_event_name": "Stop",545 "hook_event_name": "Stop",
320 "stop_hook_active": true546 "stop_hook_active": true
321}547}
330{556{
331 "session_id": "abc123",557 "session_id": "abc123",
332 "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",558 "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
559 "permission_mode": "default",
333 "hook_event_name": "PreCompact",560 "hook_event_name": "PreCompact",
334 "trigger": "manual",561 "trigger": "manual",
335 "custom_instructions": ""562 "custom_instructions": ""
342{569{
343 "session_id": "abc123",570 "session_id": "abc123",
344 "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",571 "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
572 "permission_mode": "default",
345 "hook_event_name": "SessionStart",573 "hook_event_name": "SessionStart",
346 "source": "startup"574 "source": "startup"
347}575}
354 "session_id": "abc123",582 "session_id": "abc123",
355 "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",583 "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
356 "cwd": "/Users/...",584 "cwd": "/Users/...",
585 "permission_mode": "default",
357 "hook_event_name": "SessionEnd",586 "hook_event_name": "SessionEnd",
358 "reason": "exit"587 "reason": "exit"
359}588}
361 590
362## Hook Output591## Hook Output
363 592
364There are two ways for hooks to return output back to Claude Code. The output593There are two mutually-exclusive ways for hooks to return output back to Claude Code. The output
365communicates whether to block and any feedback that should be shown to Claude594communicates whether to block and any feedback that should be shown to Claude
366and the user.595and the user.
367 596
369 598
370Hooks communicate status through exit codes, stdout, and stderr:599Hooks communicate status through exit codes, stdout, and stderr:
371 600
372* **Exit code 0**: Success. `stdout` is shown to the user in transcript mode601* **Exit code 0**: Success. `stdout` is shown to the user in verbose mode
373 (CTRL-R), except for `UserPromptSubmit` and `SessionStart`, where stdout is602 (ctrl+o), except for `UserPromptSubmit` and `SessionStart`, where stdout is
374 added to the context.603 added to the context. JSON output in `stdout` is parsed for structured control
375* **Exit code 2**: Blocking error. `stderr` is fed back to Claude to process604 (see [Advanced: JSON Output](#advanced-json-output)).
376 automatically. See per-hook-event behavior below.605* **Exit code 2**: Blocking error. Only `stderr` is used as the error message
377* **Other exit codes**: Non-blocking error. `stderr` is shown to the user and606 and fed back to Claude. The format is `[command]: {stderr}`. JSON in `stdout`
378 execution continues.607 is **not** processed for exit code 2. See per-hook-event behavior below.
608* **Other exit codes**: Non-blocking error. `stderr` is shown to the user in verbose mode (ctrl+o) with
609 format `Failed with non-blocking status code: {stderr}`. If `stderr` is empty,
610 it shows `No stderr output`. Execution continues.
379 611
380<Warning>612<Warning>
381 Reminder: Claude Code does not see stdout if the exit code is 0, except for613 Reminder: Claude Code does not see stdout if the exit code is 0, except for
385#### Exit Code 2 Behavior617#### Exit Code 2 Behavior
386 618
387| Hook Event | Behavior |619| Hook Event | Behavior |
388| ------------------ | ------------------------------------------------------------------ |620| ------------------- | ------------------------------------------------------------------ |
389| `PreToolUse` | Blocks the tool call, shows stderr to Claude |621| `PreToolUse` | Blocks the tool call, shows stderr to Claude |
622| `PermissionRequest` | Denies the permission, shows stderr to Claude |
390| `PostToolUse` | Shows stderr to Claude (tool already ran) |623| `PostToolUse` | Shows stderr to Claude (tool already ran) |
391| `Notification` | N/A, shows stderr to user only |624| `Notification` | N/A, shows stderr to user only |
392| `UserPromptSubmit` | Blocks prompt processing, erases prompt, shows stderr to user only |625| `UserPromptSubmit` | Blocks prompt processing, erases prompt, shows stderr to user only |
398 631
399### Advanced: JSON Output632### Advanced: JSON Output
400 633
401Hooks can return structured JSON in `stdout` for more sophisticated control:634Hooks can return structured JSON in `stdout` for more sophisticated control.
635
636<Warning>
637 JSON output is only processed when the hook exits with code 0. If your hook
638 exits with code 2 (blocking error), `stderr` text is used directly—any JSON in `stdout`
639 is ignored. For other non-zero exit codes, only `stderr` is shown to the user in verbose mode (ctrl+o).
640</Warning>
402 641
403#### Common JSON Fields642#### Common JSON Fields
404 643
440* `"ask"` asks the user to confirm the tool call in the UI.679* `"ask"` asks the user to confirm the tool call in the UI.
441 `permissionDecisionReason` is shown to the user but not to Claude.680 `permissionDecisionReason` is shown to the user but not to Claude.
442 681
682Additionally, hooks can modify tool inputs before execution using `updatedInput`:
683
684* `updatedInput` allows you to modify the tool's input parameters before the tool executes.
685* This is most useful with `"permissionDecision": "allow"` to modify and approve tool calls.
686
443```json theme={null}687```json theme={null}
444{688{
445 "hookSpecificOutput": {689 "hookSpecificOutput": {
446 "hookEventName": "PreToolUse",690 "hookEventName": "PreToolUse",
447 "permissionDecision": "allow" | "deny" | "ask",691 "permissionDecision": "allow"
448 "permissionDecisionReason": "My reason here"692 "permissionDecisionReason": "My reason here",
693 "updatedInput": {
694 "field_to_modify": "new value"
695 }
449 }696 }
450}697}
451```698```
457 `"approve"` and `"block"` map to `"allow"` and `"deny"` respectively.704 `"approve"` and `"block"` map to `"allow"` and `"deny"` respectively.
458</Note>705</Note>
459 706
707#### `PermissionRequest` Decision Control
708
709`PermissionRequest` hooks can allow or deny permission requests shown to the user.
710
711* For `"behavior": "allow"` you can also optionally pass in an `"updatedInput"` that modifies the tool's input parameters before the tool executes.
712* For `"behavior": "deny"` you can also optionally pass in a `"message"` string that tells the model why the permission was denied, and a boolean `"interrupt"` which will stop Claude.
713
714```json theme={null}
715{
716 "hookSpecificOutput": {
717 "hookEventName": "PermissionRequest",
718 "decision": {
719 "behavior": "allow",
720 "updatedInput": {
721 "command": "npm run lint"
722 }
723 }
724 }
725}
726```
727
460#### `PostToolUse` Decision Control728#### `PostToolUse` Decision Control
461 729
462`PostToolUse` hooks can provide feedback to Claude after tool execution.730`PostToolUse` hooks can provide feedback to Claude after tool execution.
478 746
479#### `UserPromptSubmit` Decision Control747#### `UserPromptSubmit` Decision Control
480 748
481`UserPromptSubmit` hooks can control whether a user prompt is processed.749`UserPromptSubmit` hooks can control whether a user prompt is processed and add context.
750
751**Adding context (exit code 0):**
752There are two ways to add context to the conversation:
753
7541. **Plain text stdout** (simpler): Any non-JSON text written to stdout is added
755 as context. This is the easiest way to inject information.
756
7572. **JSON with `additionalContext`** (structured): Use the JSON format below for
758 more control. The `additionalContext` field is added as context.
482 759
483* `"block"` prevents the prompt from being processed. The submitted prompt is760Both methods work with exit code 0. Plain stdout is shown as hook output in
484 erased from context. `"reason"` is shown to the user but not added to context.761the transcript; `additionalContext` is added more discretely.
485* `undefined` allows the prompt to proceed normally. `"reason"` is ignored.762
486* `"hookSpecificOutput.additionalContext"` adds the string to the context if not763**Blocking prompts:**
487 blocked.764
765* `"decision": "block"` prevents the prompt from being processed. The submitted
766 prompt is erased from context. `"reason"` is shown to the user but not added
767 to context.
768* `"decision": undefined` (or omitted) allows the prompt to proceed normally.
488 769
489```json theme={null}770```json theme={null}
490{771{
497}778}
498```779```
499 780
781<Note>
782 The JSON format is not required for simple use cases. To add context, you can
783 just print plain text to stdout with exit code 0. Use JSON when you need to
784 block prompts or want more structured control.
785</Note>
786
500#### `Stop`/`SubagentStop` Decision Control787#### `Stop`/`SubagentStop` Decision Control
501 788
502`Stop` and `SubagentStop` hooks can control whether Claude must continue.789`Stop` and `SubagentStop` hooks can control whether Claude must continue.
590<Note>877<Note>
591 For `UserPromptSubmit` hooks, you can inject context using either method:878 For `UserPromptSubmit` hooks, you can inject context using either method:
592 879
593 * Exit code 0 with stdout: Claude sees the context (special case for `UserPromptSubmit`)880 * **Plain text stdout** with exit code 0: Simplest approach—just print text
594 * JSON output: Provides more control over the behavior881 * **JSON output** with exit code 0: Use `"decision": "block"` to reject prompts,
882 or `additionalContext` for structured context injection
883
884 Remember: Exit code 2 only uses `stderr` for the error message. To block using
885 JSON (with a custom reason), use `"decision": "block"` with exit code 0.
595</Note>886</Note>
596 887
597```python theme={null}888```python theme={null}
668 output = {959 output = {
669 "decision": "approve",960 "decision": "approve",
670 "reason": "Documentation file auto-approved",961 "reason": "Documentation file auto-approved",
671 "suppressOutput": True # Don't show in transcript mode962 "suppressOutput": True # Don't show in verbose mode
672 }963 }
673 print(json.dumps(output))964 print(json.dumps(output))
674 sys.exit(0)965 sys.exit(0)
680## Working with MCP Tools971## Working with MCP Tools
681 972
682Claude Code hooks work seamlessly with973Claude Code hooks work seamlessly with
683[Model Context Protocol (MCP) tools](/en/docs/claude-code/mcp). When MCP servers974[Model Context Protocol (MCP) tools](/en/mcp). When MCP servers
684provide tools, they appear with a special naming pattern that you can match in975provide tools, they appear with a special naming pattern that you can match in
685your hooks.976your hooks.
686 977
726## Examples1017## Examples
727 1018
728<Tip>1019<Tip>
729 For practical examples including code formatting, notifications, and file protection, see [More Examples](/en/docs/claude-code/hooks-guide#more-examples) in the get started guide.1020 For practical examples including code formatting, notifications, and file protection, see [More Examples](/en/hooks-guide#more-examples) in the get started guide.
730</Tip>1021</Tip>
731 1022
732## Security Considerations1023## Security Considerations
778* **Environment**: Runs in current directory with Claude Code's environment1069* **Environment**: Runs in current directory with Claude Code's environment
779 * The `CLAUDE_PROJECT_DIR` environment variable is available and contains the1070 * The `CLAUDE_PROJECT_DIR` environment variable is available and contains the
780 absolute path to the project root directory (where Claude Code was started)1071 absolute path to the project root directory (where Claude Code was started)
1072 * The `CLAUDE_CODE_REMOTE` environment variable indicates whether the hook is running in a remote (web) environment (`"true"`) or local CLI environment (not set or empty). Use this to run different logic based on execution context.
781* **Input**: JSON via stdin1073* **Input**: JSON via stdin
782* **Output**:1074* **Output**:
783 * PreToolUse/PostToolUse/Stop/SubagentStop: Progress shown in transcript (Ctrl-R)1075 * PreToolUse/PermissionRequest/PostToolUse/Stop/SubagentStop: Progress shown in verbose mode (ctrl+o)
784 * Notification/SessionEnd: Logged to debug only (`--debug`)1076 * Notification/SessionEnd: Logged to debug only (`--debug`)
785 * UserPromptSubmit/SessionStart: stdout added as context for Claude1077 * UserPromptSubmit/SessionStart: stdout added as context for Claude
786 1078
829[DEBUG] Hook command completed with status 0: <Your stdout>1121[DEBUG] Hook command completed with status 0: <Your stdout>
830```1122```
831 1123
832Progress messages appear in transcript mode (Ctrl-R) showing:1124Progress messages appear in verbose mode (ctrl+o) showing:
833 1125
834* Which hook is running1126* Which hook is running
835* Command being executed1127* Command being executed
836* Success/failure status1128* Success/failure status
837* Output or error messages1129* Output or error messages
1130
1131
1132---
1133
1134> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://code.claude.com/docs/llms.txt