claude_hooks 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,34 +1,1310 @@
1
- Documentation
1
+ [Skip to main content](https://code.claude.com/docs/en/hooks#content-area)
2
2
 
3
- Page
3
+ [Claude Code Docs home page![light logo](https://mintcdn.com/claude-code/o69F7a6qoW9vboof/logo/light.svg?fit=max&auto=format&n=o69F7a6qoW9vboof&q=85&s=536eade682636e84231afce2577f9509)![dark logo](https://mintcdn.com/claude-code/o69F7a6qoW9vboof/logo/dark.svg?fit=max&auto=format&n=o69F7a6qoW9vboof&q=85&s=0766b3221061e80143e9f300733e640b)](https://code.claude.com/docs)
4
4
 
5
- ## Page Not Found
5
+ ![US](https://d3gk2c5xim1je2.cloudfront.net/flags/US.svg)
6
6
 
7
- ### The requested page could not be found.
7
+ English
8
8
 
9
- Go back
9
+ Search...
10
10
 
11
- Ask Docs
12
- ![Chat avatar](https://platform.claude.com/docs/images/book-icon-light.svg)
11
+ Ctrl K
13
12
 
14
- a.claude.ai
13
+ Search...
15
14
 
16
- # a.claude.ai is blocked
15
+ Navigation
17
16
 
18
- **a.claude.ai** refused to connect.
17
+ Reference
19
18
 
20
- ERR\_BLOCKED\_BY\_RESPONSE
19
+ Hooks reference
21
20
 
22
- **a.claude.ai** refused to connect.
21
+ [Getting started](https://code.claude.com/docs/en/overview) [Build with Claude Code](https://code.claude.com/docs/en/sub-agents) [Deployment](https://code.claude.com/docs/en/third-party-integrations) [Administration](https://code.claude.com/docs/en/setup) [Configuration](https://code.claude.com/docs/en/settings) [Reference](https://code.claude.com/docs/en/cli-reference) [Resources](https://code.claude.com/docs/en/legal-and-compliance)
23
22
 
24
- ![](<Base64-Image-Removed>)![](<Base64-Image-Removed>)
23
+ On this page
25
24
 
26
- Invalid domain for site key.
25
+ - [Configuration](https://code.claude.com/docs/en/hooks#configuration)
26
+ - [Structure](https://code.claude.com/docs/en/hooks#structure)
27
+ - [Project-Specific Hook Scripts](https://code.claude.com/docs/en/hooks#project-specific-hook-scripts)
28
+ - [Plugin hooks](https://code.claude.com/docs/en/hooks#plugin-hooks)
29
+ - [Prompt-Based Hooks](https://code.claude.com/docs/en/hooks#prompt-based-hooks)
30
+ - [How prompt-based hooks work](https://code.claude.com/docs/en/hooks#how-prompt-based-hooks-work)
31
+ - [Configuration](https://code.claude.com/docs/en/hooks#configuration-2)
32
+ - [Response schema](https://code.claude.com/docs/en/hooks#response-schema)
33
+ - [Supported hook events](https://code.claude.com/docs/en/hooks#supported-hook-events)
34
+ - [Example: Intelligent Stop hook](https://code.claude.com/docs/en/hooks#example%3A-intelligent-stop-hook)
35
+ - [Example: SubagentStop with custom logic](https://code.claude.com/docs/en/hooks#example%3A-subagentstop-with-custom-logic)
36
+ - [Comparison with bash command hooks](https://code.claude.com/docs/en/hooks#comparison-with-bash-command-hooks)
37
+ - [Best practices](https://code.claude.com/docs/en/hooks#best-practices)
38
+ - [Hook Events](https://code.claude.com/docs/en/hooks#hook-events)
39
+ - [PreToolUse](https://code.claude.com/docs/en/hooks#pretooluse)
40
+ - [PermissionRequest](https://code.claude.com/docs/en/hooks#permissionrequest)
41
+ - [PostToolUse](https://code.claude.com/docs/en/hooks#posttooluse)
42
+ - [Notification](https://code.claude.com/docs/en/hooks#notification)
43
+ - [UserPromptSubmit](https://code.claude.com/docs/en/hooks#userpromptsubmit)
44
+ - [Stop](https://code.claude.com/docs/en/hooks#stop)
45
+ - [SubagentStop](https://code.claude.com/docs/en/hooks#subagentstop)
46
+ - [PreCompact](https://code.claude.com/docs/en/hooks#precompact)
47
+ - [SessionStart](https://code.claude.com/docs/en/hooks#sessionstart)
48
+ - [Persisting environment variables](https://code.claude.com/docs/en/hooks#persisting-environment-variables)
49
+ - [SessionEnd](https://code.claude.com/docs/en/hooks#sessionend)
50
+ - [Hook Input](https://code.claude.com/docs/en/hooks#hook-input)
51
+ - [PreToolUse Input](https://code.claude.com/docs/en/hooks#pretooluse-input)
52
+ - [PostToolUse Input](https://code.claude.com/docs/en/hooks#posttooluse-input)
53
+ - [Notification Input](https://code.claude.com/docs/en/hooks#notification-input)
54
+ - [UserPromptSubmit Input](https://code.claude.com/docs/en/hooks#userpromptsubmit-input)
55
+ - [Stop and SubagentStop Input](https://code.claude.com/docs/en/hooks#stop-and-subagentstop-input)
56
+ - [PreCompact Input](https://code.claude.com/docs/en/hooks#precompact-input)
57
+ - [SessionStart Input](https://code.claude.com/docs/en/hooks#sessionstart-input)
58
+ - [SessionEnd Input](https://code.claude.com/docs/en/hooks#sessionend-input)
59
+ - [Hook Output](https://code.claude.com/docs/en/hooks#hook-output)
60
+ - [Simple: Exit Code](https://code.claude.com/docs/en/hooks#simple%3A-exit-code)
61
+ - [Exit Code 2 Behavior](https://code.claude.com/docs/en/hooks#exit-code-2-behavior)
62
+ - [Advanced: JSON Output](https://code.claude.com/docs/en/hooks#advanced%3A-json-output)
63
+ - [Common JSON Fields](https://code.claude.com/docs/en/hooks#common-json-fields)
64
+ - [PreToolUse Decision Control](https://code.claude.com/docs/en/hooks#pretooluse-decision-control)
65
+ - [PermissionRequest Decision Control](https://code.claude.com/docs/en/hooks#permissionrequest-decision-control)
66
+ - [PostToolUse Decision Control](https://code.claude.com/docs/en/hooks#posttooluse-decision-control)
67
+ - [UserPromptSubmit Decision Control](https://code.claude.com/docs/en/hooks#userpromptsubmit-decision-control)
68
+ - [Stop/SubagentStop Decision Control](https://code.claude.com/docs/en/hooks#stop%2Fsubagentstop-decision-control)
69
+ - [SessionStart Decision Control](https://code.claude.com/docs/en/hooks#sessionstart-decision-control)
70
+ - [SessionEnd Decision Control](https://code.claude.com/docs/en/hooks#sessionend-decision-control)
71
+ - [Exit Code Example: Bash Command Validation](https://code.claude.com/docs/en/hooks#exit-code-example%3A-bash-command-validation)
72
+ - [JSON Output Example: UserPromptSubmit to Add Context and Validation](https://code.claude.com/docs/en/hooks#json-output-example%3A-userpromptsubmit-to-add-context-and-validation)
73
+ - [JSON Output Example: PreToolUse with Approval](https://code.claude.com/docs/en/hooks#json-output-example%3A-pretooluse-with-approval)
74
+ - [Working with MCP Tools](https://code.claude.com/docs/en/hooks#working-with-mcp-tools)
75
+ - [MCP Tool Naming](https://code.claude.com/docs/en/hooks#mcp-tool-naming)
76
+ - [Configuring Hooks for MCP Tools](https://code.claude.com/docs/en/hooks#configuring-hooks-for-mcp-tools)
77
+ - [Examples](https://code.claude.com/docs/en/hooks#examples)
78
+ - [Security Considerations](https://code.claude.com/docs/en/hooks#security-considerations)
79
+ - [Disclaimer](https://code.claude.com/docs/en/hooks#disclaimer)
80
+ - [Security Best Practices](https://code.claude.com/docs/en/hooks#security-best-practices)
81
+ - [Configuration Safety](https://code.claude.com/docs/en/hooks#configuration-safety)
82
+ - [Hook Execution Details](https://code.claude.com/docs/en/hooks#hook-execution-details)
83
+ - [Debugging](https://code.claude.com/docs/en/hooks#debugging)
84
+ - [Basic Troubleshooting](https://code.claude.com/docs/en/hooks#basic-troubleshooting)
85
+ - [Advanced Debugging](https://code.claude.com/docs/en/hooks#advanced-debugging)
86
+ - [Debug Output Example](https://code.claude.com/docs/en/hooks#debug-output-example)
27
87
 
28
- ERROR for site owner:
88
+ For a quickstart guide with examples, see [Get started with Claude Code hooks](https://code.claude.com/docs/en/hooks-guide).
29
89
 
30
- Invalid domain for site key
90
+ ## [​](https://code.claude.com/docs/en/hooks\#configuration) Configuration
31
91
 
32
- reCAPTCHA
92
+ Claude Code hooks are configured in your [settings files](https://code.claude.com/docs/en/settings):
33
93
 
34
- [Privacy](https://www.google.com/intl/en/policies/privacy/) \- [Terms](https://www.google.com/intl/en/policies/terms/)
94
+ - `~/.claude/settings.json` \- User settings
95
+ - `.claude/settings.json` \- Project settings
96
+ - `.claude/settings.local.json` \- Local project settings (not committed)
97
+ - Enterprise managed policy settings
98
+
99
+ Enterprise administrators can use `allowManagedHooksOnly` to block user, project, and plugin hooks. See [Hook configuration](https://code.claude.com/docs/en/settings#hook-configuration).
100
+
101
+ ### [​](https://code.claude.com/docs/en/hooks\#structure) Structure
102
+
103
+ Hooks are organized by matchers, where each matcher can have multiple hooks:
104
+
105
+ Copy
106
+
107
+ Ask AI
108
+
109
+ ```
110
+ {
111
+ "hooks": {
112
+ "EventName": [\
113
+ {\
114
+ "matcher": "ToolPattern",\
115
+ "hooks": [\
116
+ {\
117
+ "type": "command",\
118
+ "command": "your-command-here"\
119
+ }\
120
+ ]\
121
+ }\
122
+ ]
123
+ }
124
+ }
125
+ ```
126
+
127
+ - **matcher**: Pattern to match tool names, case-sensitive (only applicable for
128
+ `PreToolUse`, `PermissionRequest`, and `PostToolUse`)
129
+
130
+ - Simple strings match exactly: `Write` matches only the Write tool
131
+ - Supports regex: `Edit|Write` or `Notebook.*`
132
+ - Use `*` to match all tools. You can also use empty string (`""`) or leave
133
+ `matcher` blank.
134
+ - **hooks**: Array of hooks to execute when the pattern matches
135
+
136
+ - `type`: Hook execution type - `"command"` for bash commands or `"prompt"` for LLM-based evaluation
137
+ - `command`: (For `type: "command"`) The bash command to execute (can use `$CLAUDE_PROJECT_DIR` environment variable)
138
+ - `prompt`: (For `type: "prompt"`) The prompt to send to the LLM for evaluation
139
+ - `timeout`: (Optional) How long a hook should run, in seconds, before canceling that specific hook
140
+
141
+ For events like `UserPromptSubmit`, `Stop`, and `SubagentStop`
142
+ that don’t use matchers, you can omit the matcher field:
143
+
144
+ Copy
145
+
146
+ Ask AI
147
+
148
+ ```
149
+ {
150
+ "hooks": {
151
+ "UserPromptSubmit": [\
152
+ {\
153
+ "hooks": [\
154
+ {\
155
+ "type": "command",\
156
+ "command": "/path/to/prompt-validator.py"\
157
+ }\
158
+ ]\
159
+ }\
160
+ ]
161
+ }
162
+ }
163
+ ```
164
+
165
+ ### [​](https://code.claude.com/docs/en/hooks\#project-specific-hook-scripts) Project-Specific Hook Scripts
166
+
167
+ You can use the environment variable `CLAUDE_PROJECT_DIR` (only available when
168
+ Claude Code spawns the hook command) to reference scripts stored in your project,
169
+ ensuring they work regardless of Claude’s current directory:
170
+
171
+ Copy
172
+
173
+ Ask AI
174
+
175
+ ```
176
+ {
177
+ "hooks": {
178
+ "PostToolUse": [\
179
+ {\
180
+ "matcher": "Write|Edit",\
181
+ "hooks": [\
182
+ {\
183
+ "type": "command",\
184
+ "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-style.sh"\
185
+ }\
186
+ ]\
187
+ }\
188
+ ]
189
+ }
190
+ }
191
+ ```
192
+
193
+ ### [​](https://code.claude.com/docs/en/hooks\#plugin-hooks) Plugin hooks
194
+
195
+ [Plugins](https://code.claude.com/docs/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.**How plugin hooks work**:
196
+
197
+ - Plugin hooks are defined in the plugin’s `hooks/hooks.json` file or in a file given by a custom path to the `hooks` field.
198
+ - When a plugin is enabled, its hooks are merged with user and project hooks
199
+ - Multiple hooks from different sources can respond to the same event
200
+ - Plugin hooks use the `${CLAUDE_PLUGIN_ROOT}` environment variable to reference plugin files
201
+
202
+ **Example plugin hook configuration**:
203
+
204
+ Copy
205
+
206
+ Ask AI
207
+
208
+ ```
209
+ {
210
+ "description": "Automatic code formatting",
211
+ "hooks": {
212
+ "PostToolUse": [\
213
+ {\
214
+ "matcher": "Write|Edit",\
215
+ "hooks": [\
216
+ {\
217
+ "type": "command",\
218
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh",\
219
+ "timeout": 30\
220
+ }\
221
+ ]\
222
+ }\
223
+ ]
224
+ }
225
+ }
226
+ ```
227
+
228
+ Plugin hooks use the same format as regular hooks with an optional `description` field to explain the hook’s purpose.
229
+
230
+ Plugin hooks run alongside your custom hooks. If multiple hooks match an event, they all execute in parallel.
231
+
232
+ **Environment variables for plugins**:
233
+
234
+ - `${CLAUDE_PLUGIN_ROOT}`: Absolute path to the plugin directory
235
+ - `${CLAUDE_PROJECT_DIR}`: Project root directory (same as for project hooks)
236
+ - All standard environment variables are available
237
+
238
+ See the [plugin components reference](https://code.claude.com/docs/en/plugins-reference#hooks) for details on creating plugin hooks.
239
+
240
+ ## [​](https://code.claude.com/docs/en/hooks\#prompt-based-hooks) Prompt-Based Hooks
241
+
242
+ In 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.
243
+
244
+ ### [​](https://code.claude.com/docs/en/hooks\#how-prompt-based-hooks-work) How prompt-based hooks work
245
+
246
+ Instead of executing a bash command, prompt-based hooks:
247
+
248
+ 1. Send the hook input and your prompt to a fast LLM (Haiku)
249
+ 2. The LLM responds with structured JSON containing a decision
250
+ 3. Claude Code processes the decision automatically
251
+
252
+ ### [​](https://code.claude.com/docs/en/hooks\#configuration-2) Configuration
253
+
254
+ Copy
255
+
256
+ Ask AI
257
+
258
+ ```
259
+ {
260
+ "hooks": {
261
+ "Stop": [\
262
+ {\
263
+ "hooks": [\
264
+ {\
265
+ "type": "prompt",\
266
+ "prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete."\
267
+ }\
268
+ ]\
269
+ }\
270
+ ]
271
+ }
272
+ }
273
+ ```
274
+
275
+ **Fields:**
276
+
277
+ - `type`: Must be `"prompt"`
278
+ - `prompt`: The prompt text to send to the LLM
279
+
280
+ - Use `$ARGUMENTS` as a placeholder for the hook input JSON
281
+ - If `$ARGUMENTS` is not present, input JSON is appended to the prompt
282
+ - `timeout`: (Optional) Timeout in seconds (default: 30 seconds)
283
+
284
+ ### [​](https://code.claude.com/docs/en/hooks\#response-schema) Response schema
285
+
286
+ The LLM must respond with JSON containing:
287
+
288
+ Copy
289
+
290
+ Ask AI
291
+
292
+ ```
293
+ {
294
+ "decision": "approve" | "block",
295
+ "reason": "Explanation for the decision",
296
+ "continue": false, // Optional: stops Claude entirely
297
+ "stopReason": "Message shown to user", // Optional: custom stop message
298
+ "systemMessage": "Warning or context" // Optional: shown to user
299
+ }
300
+ ```
301
+
302
+ **Response fields:**
303
+
304
+ - `decision`: `"approve"` allows the action, `"block"` prevents it
305
+ - `reason`: Explanation shown to Claude when decision is `"block"`
306
+ - `continue`: (Optional) If `false`, stops Claude’s execution entirely
307
+ - `stopReason`: (Optional) Message shown when `continue` is false
308
+ - `systemMessage`: (Optional) Additional message shown to the user
309
+
310
+ ### [​](https://code.claude.com/docs/en/hooks\#supported-hook-events) Supported hook events
311
+
312
+ Prompt-based hooks work with any hook event, but are most useful for:
313
+
314
+ - **Stop**: Intelligently decide if Claude should continue working
315
+ - **SubagentStop**: Evaluate if a subagent has completed its task
316
+ - **UserPromptSubmit**: Validate user prompts with LLM assistance
317
+ - **PreToolUse**: Make context-aware permission decisions
318
+ - **PermissionRequest**: Intelligently allow or deny permission dialogs
319
+
320
+ ### [​](https://code.claude.com/docs/en/hooks\#example:-intelligent-stop-hook) Example: Intelligent Stop hook
321
+
322
+ Copy
323
+
324
+ Ask AI
325
+
326
+ ```
327
+ {
328
+ "hooks": {
329
+ "Stop": [\
330
+ {\
331
+ "hooks": [\
332
+ {\
333
+ "type": "prompt",\
334
+ "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\"}",\
335
+ "timeout": 30\
336
+ }\
337
+ ]\
338
+ }\
339
+ ]
340
+ }
341
+ }
342
+ ```
343
+
344
+ ### [​](https://code.claude.com/docs/en/hooks\#example:-subagentstop-with-custom-logic) Example: SubagentStop with custom logic
345
+
346
+ Copy
347
+
348
+ Ask AI
349
+
350
+ ```
351
+ {
352
+ "hooks": {
353
+ "SubagentStop": [\
354
+ {\
355
+ "hooks": [\
356
+ {\
357
+ "type": "prompt",\
358
+ "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\"}"\
359
+ }\
360
+ ]\
361
+ }\
362
+ ]
363
+ }
364
+ }
365
+ ```
366
+
367
+ ### [​](https://code.claude.com/docs/en/hooks\#comparison-with-bash-command-hooks) Comparison with bash command hooks
368
+
369
+ | Feature | Bash Command Hooks | Prompt-Based Hooks |
370
+ | --- | --- | --- |
371
+ | **Execution** | Runs bash script | Queries LLM |
372
+ | **Decision logic** | You implement in code | LLM evaluates context |
373
+ | **Setup complexity** | Requires script file | Configure prompt |
374
+ | **Context awareness** | Limited to script logic | Natural language understanding |
375
+ | **Performance** | Fast (local execution) | Slower (API call) |
376
+ | **Use case** | Deterministic rules | Context-aware decisions |
377
+
378
+ ### [​](https://code.claude.com/docs/en/hooks\#best-practices) Best practices
379
+
380
+ - **Be specific in prompts**: Clearly state what you want the LLM to evaluate
381
+ - **Include decision criteria**: List the factors the LLM should consider
382
+ - **Test your prompts**: Verify the LLM makes correct decisions for your use cases
383
+ - **Set appropriate timeouts**: Default is 30 seconds, adjust if needed
384
+ - **Use for complex decisions**: Bash hooks are better for simple, deterministic rules
385
+
386
+ See the [plugin components reference](https://code.claude.com/docs/en/plugins-reference#hooks) for details on creating plugin hooks.
387
+
388
+ ## [​](https://code.claude.com/docs/en/hooks\#hook-events) Hook Events
389
+
390
+ ### [​](https://code.claude.com/docs/en/hooks\#pretooluse) PreToolUse
391
+
392
+ Runs after Claude creates tool parameters and before processing the tool call.**Common matchers:**
393
+
394
+ - `Task` \- Subagent tasks (see [subagents documentation](https://code.claude.com/docs/en/sub-agents))
395
+ - `Bash` \- Shell commands
396
+ - `Glob` \- File pattern matching
397
+ - `Grep` \- Content search
398
+ - `Read` \- File reading
399
+ - `Edit` \- File editing
400
+ - `Write` \- File writing
401
+ - `WebFetch`, `WebSearch` \- Web operations
402
+
403
+ Use [PreToolUse decision control](https://code.claude.com/docs/en/hooks#pretooluse-decision-control) to allow, deny, or ask for permission to use the tool.
404
+
405
+ ### [​](https://code.claude.com/docs/en/hooks\#permissionrequest) PermissionRequest
406
+
407
+ Runs when the user is shown a permission dialog.
408
+ Use [PermissionRequest decision control](https://code.claude.com/docs/en/hooks#permissionrequest-decision-control) to allow or deny on behalf of the user.Recognizes the same matcher values as PreToolUse.
409
+
410
+ ### [​](https://code.claude.com/docs/en/hooks\#posttooluse) PostToolUse
411
+
412
+ Runs immediately after a tool completes successfully.Recognizes the same matcher values as PreToolUse.
413
+
414
+ ### [​](https://code.claude.com/docs/en/hooks\#notification) Notification
415
+
416
+ Runs when Claude Code sends notifications. Supports matchers to filter by notification type.**Common matchers:**
417
+
418
+ - `permission_prompt` \- Permission requests from Claude Code
419
+ - `idle_prompt` \- When Claude is waiting for user input (after 60+ seconds of idle time)
420
+ - `auth_success` \- Authentication success notifications
421
+ - `elicitation_dialog` \- When Claude Code needs input for MCP tool elicitation
422
+
423
+ You can use matchers to run different hooks for different notification types, or omit the matcher to run hooks for all notifications.**Example: Different notifications for different types**
424
+
425
+ Copy
426
+
427
+ Ask AI
428
+
429
+ ```
430
+ {
431
+ "hooks": {
432
+ "Notification": [\
433
+ {\
434
+ "matcher": "permission_prompt",\
435
+ "hooks": [\
436
+ {\
437
+ "type": "command",\
438
+ "command": "/path/to/permission-alert.sh"\
439
+ }\
440
+ ]\
441
+ },\
442
+ {\
443
+ "matcher": "idle_prompt",\
444
+ "hooks": [\
445
+ {\
446
+ "type": "command",\
447
+ "command": "/path/to/idle-notification.sh"\
448
+ }\
449
+ ]\
450
+ }\
451
+ ]
452
+ }
453
+ }
454
+ ```
455
+
456
+ ### [​](https://code.claude.com/docs/en/hooks\#userpromptsubmit) UserPromptSubmit
457
+
458
+ Runs when the user submits a prompt, before Claude processes it. This allows you
459
+ to add additional context based on the prompt/conversation, validate prompts, or
460
+ block certain types of prompts.
461
+
462
+ ### [​](https://code.claude.com/docs/en/hooks\#stop) Stop
463
+
464
+ Runs when the main Claude Code agent has finished responding. Does not run if
465
+ the stoppage occurred due to a user interrupt.
466
+
467
+ ### [​](https://code.claude.com/docs/en/hooks\#subagentstop) SubagentStop
468
+
469
+ Runs when a Claude Code subagent (Task tool call) has finished responding.
470
+
471
+ ### [​](https://code.claude.com/docs/en/hooks\#precompact) PreCompact
472
+
473
+ Runs before Claude Code is about to run a compact operation.**Matchers:**
474
+
475
+ - `manual` \- Invoked from `/compact`
476
+ - `auto` \- Invoked from auto-compact (due to full context window)
477
+
478
+ ### [​](https://code.claude.com/docs/en/hooks\#sessionstart) SessionStart
479
+
480
+ Runs when Claude Code starts a new session or resumes an existing session (which
481
+ currently does start a new session under the hood). Useful for loading in
482
+ development context like existing issues or recent changes to your codebase, installing dependencies, or setting up environment variables.**Matchers:**
483
+
484
+ - `startup` \- Invoked from startup
485
+ - `resume` \- Invoked from `--resume`, `--continue`, or `/resume`
486
+ - `clear` \- Invoked from `/clear`
487
+ - `compact` \- Invoked from auto or manual compact.
488
+
489
+ #### [​](https://code.claude.com/docs/en/hooks\#persisting-environment-variables) Persisting environment variables
490
+
491
+ SessionStart 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.**Example: Setting individual environment variables**
492
+
493
+ Copy
494
+
495
+ Ask AI
496
+
497
+ ```
498
+ #!/bin/bash
499
+
500
+ if [ -n "$CLAUDE_ENV_FILE" ]; then
501
+ echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
502
+ echo 'export API_KEY=your-api-key' >> "$CLAUDE_ENV_FILE"
503
+ echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
504
+ fi
505
+
506
+ exit 0
507
+ ```
508
+
509
+ **Example: Persisting all environment changes from the hook**When your setup modifies the environment (for example, `nvm use`), capture and persist all changes by diffing the environment:
510
+
511
+ Copy
512
+
513
+ Ask AI
514
+
515
+ ```
516
+ #!/bin/bash
517
+
518
+ ENV_BEFORE=$(export -p | sort)
519
+
520
+ # Run your setup commands that modify the environment
521
+ source ~/.nvm/nvm.sh
522
+ nvm use 20
523
+
524
+ if [ -n "$CLAUDE_ENV_FILE" ]; then
525
+ ENV_AFTER=$(export -p | sort)
526
+ comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
527
+ fi
528
+
529
+ exit 0
530
+ ```
531
+
532
+ Any variables written to this file will be available in all subsequent bash commands that Claude Code executes during the session.
533
+
534
+ `CLAUDE_ENV_FILE` is only available for SessionStart hooks. Other hook types do not have access to this variable.
535
+
536
+ ### [​](https://code.claude.com/docs/en/hooks\#sessionend) SessionEnd
537
+
538
+ Runs when a Claude Code session ends. Useful for cleanup tasks, logging session
539
+ statistics, or saving session state.The `reason` field in the hook input will be one of:
540
+
541
+ - `clear` \- Session cleared with /clear command
542
+ - `logout` \- User logged out
543
+ - `prompt_input_exit` \- User exited while prompt input was visible
544
+ - `other` \- Other exit reasons
545
+
546
+ ## [​](https://code.claude.com/docs/en/hooks\#hook-input) Hook Input
547
+
548
+ Hooks receive JSON data via stdin containing session information and
549
+ event-specific data:
550
+
551
+ Copy
552
+
553
+ Ask AI
554
+
555
+ ```
556
+ {
557
+ // Common fields
558
+ session_id: string
559
+ transcript_path: string // Path to conversation JSON
560
+ cwd: string // The current working directory when the hook is invoked
561
+ permission_mode: string // Current permission mode: "default", "plan", "acceptEdits", "dontAsk", or "bypassPermissions"
562
+
563
+ // Event-specific fields
564
+ hook_event_name: string
565
+ ...
566
+ }
567
+ ```
568
+
569
+ ### [​](https://code.claude.com/docs/en/hooks\#pretooluse-input) PreToolUse Input
570
+
571
+ The exact schema for `tool_input` depends on the tool.
572
+
573
+ Copy
574
+
575
+ Ask AI
576
+
577
+ ```
578
+ {
579
+ "session_id": "abc123",
580
+ "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
581
+ "cwd": "/Users/...",
582
+ "permission_mode": "default",
583
+ "hook_event_name": "PreToolUse",
584
+ "tool_name": "Write",
585
+ "tool_input": {
586
+ "file_path": "/path/to/file.txt",
587
+ "content": "file content"
588
+ },
589
+ "tool_use_id": "toolu_01ABC123..."
590
+ }
591
+ ```
592
+
593
+ ### [​](https://code.claude.com/docs/en/hooks\#posttooluse-input) PostToolUse Input
594
+
595
+ The exact schema for `tool_input` and `tool_response` depends on the tool.
596
+
597
+ Copy
598
+
599
+ Ask AI
600
+
601
+ ```
602
+ {
603
+ "session_id": "abc123",
604
+ "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
605
+ "cwd": "/Users/...",
606
+ "permission_mode": "default",
607
+ "hook_event_name": "PostToolUse",
608
+ "tool_name": "Write",
609
+ "tool_input": {
610
+ "file_path": "/path/to/file.txt",
611
+ "content": "file content"
612
+ },
613
+ "tool_response": {
614
+ "filePath": "/path/to/file.txt",
615
+ "success": true
616
+ },
617
+ "tool_use_id": "toolu_01ABC123..."
618
+ }
619
+ ```
620
+
621
+ ### [​](https://code.claude.com/docs/en/hooks\#notification-input) Notification Input
622
+
623
+ Copy
624
+
625
+ Ask AI
626
+
627
+ ```
628
+ {
629
+ "session_id": "abc123",
630
+ "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
631
+ "cwd": "/Users/...",
632
+ "permission_mode": "default",
633
+ "hook_event_name": "Notification",
634
+ "message": "Claude needs your permission to use Bash",
635
+ "notification_type": "permission_prompt"
636
+ }
637
+ ```
638
+
639
+ ### [​](https://code.claude.com/docs/en/hooks\#userpromptsubmit-input) UserPromptSubmit Input
640
+
641
+ Copy
642
+
643
+ Ask AI
644
+
645
+ ```
646
+ {
647
+ "session_id": "abc123",
648
+ "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
649
+ "cwd": "/Users/...",
650
+ "permission_mode": "default",
651
+ "hook_event_name": "UserPromptSubmit",
652
+ "prompt": "Write a function to calculate the factorial of a number"
653
+ }
654
+ ```
655
+
656
+ ### [​](https://code.claude.com/docs/en/hooks\#stop-and-subagentstop-input) Stop and SubagentStop Input
657
+
658
+ `stop_hook_active` is true when Claude Code is already continuing as a result of
659
+ a stop hook. Check this value or process the transcript to prevent Claude Code
660
+ from running indefinitely.
661
+
662
+ Copy
663
+
664
+ Ask AI
665
+
666
+ ```
667
+ {
668
+ "session_id": "abc123",
669
+ "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
670
+ "permission_mode": "default",
671
+ "hook_event_name": "Stop",
672
+ "stop_hook_active": true
673
+ }
674
+ ```
675
+
676
+ ### [​](https://code.claude.com/docs/en/hooks\#precompact-input) PreCompact Input
677
+
678
+ For `manual`, `custom_instructions` comes from what the user passes into
679
+ `/compact`. For `auto`, `custom_instructions` is empty.
680
+
681
+ Copy
682
+
683
+ Ask AI
684
+
685
+ ```
686
+ {
687
+ "session_id": "abc123",
688
+ "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
689
+ "permission_mode": "default",
690
+ "hook_event_name": "PreCompact",
691
+ "trigger": "manual",
692
+ "custom_instructions": ""
693
+ }
694
+ ```
695
+
696
+ ### [​](https://code.claude.com/docs/en/hooks\#sessionstart-input) SessionStart Input
697
+
698
+ Copy
699
+
700
+ Ask AI
701
+
702
+ ```
703
+ {
704
+ "session_id": "abc123",
705
+ "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
706
+ "permission_mode": "default",
707
+ "hook_event_name": "SessionStart",
708
+ "source": "startup"
709
+ }
710
+ ```
711
+
712
+ ### [​](https://code.claude.com/docs/en/hooks\#sessionend-input) SessionEnd Input
713
+
714
+ Copy
715
+
716
+ Ask AI
717
+
718
+ ```
719
+ {
720
+ "session_id": "abc123",
721
+ "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
722
+ "cwd": "/Users/...",
723
+ "permission_mode": "default",
724
+ "hook_event_name": "SessionEnd",
725
+ "reason": "exit"
726
+ }
727
+ ```
728
+
729
+ ## [​](https://code.claude.com/docs/en/hooks\#hook-output) Hook Output
730
+
731
+ There are two mutually exclusive ways for hooks to return output back to Claude Code. The output
732
+ communicates whether to block and any feedback that should be shown to Claude
733
+ and the user.
734
+
735
+ ### [​](https://code.claude.com/docs/en/hooks\#simple:-exit-code) Simple: Exit Code
736
+
737
+ Hooks communicate status through exit codes, stdout, and stderr:
738
+
739
+ - **Exit code 0**: Success. `stdout` is shown to the user in verbose mode
740
+ (ctrl+o), except for `UserPromptSubmit` and `SessionStart`, where stdout is
741
+ added to the context. JSON output in `stdout` is parsed for structured control
742
+ (see [Advanced: JSON Output](https://code.claude.com/docs/en/hooks#advanced-json-output)).
743
+ - **Exit code 2**: Blocking error. Only `stderr` is used as the error message
744
+ and fed back to Claude. The format is `[command]: {stderr}`. JSON in `stdout`
745
+ is **not** processed for exit code 2. See per-hook-event behavior below.
746
+ - **Other exit codes**: Non-blocking error. `stderr` is shown to the user in verbose mode (ctrl+o) with
747
+ format `Failed with non-blocking status code: {stderr}`. If `stderr` is empty,
748
+ it shows `No stderr output`. Execution continues.
749
+
750
+ Reminder: Claude Code does not see stdout if the exit code is 0, except for
751
+ the `UserPromptSubmit` hook where stdout is injected as context.
752
+
753
+ #### [​](https://code.claude.com/docs/en/hooks\#exit-code-2-behavior) Exit Code 2 Behavior
754
+
755
+ | Hook Event | Behavior |
756
+ | --- | --- |
757
+ | `PreToolUse` | Blocks the tool call, shows stderr to Claude |
758
+ | `PermissionRequest` | Denies the permission, shows stderr to Claude |
759
+ | `PostToolUse` | Shows stderr to Claude (tool already ran) |
760
+ | `Notification` | N/A, shows stderr to user only |
761
+ | `UserPromptSubmit` | Blocks prompt processing, erases prompt, shows stderr to user only |
762
+ | `Stop` | Blocks stoppage, shows stderr to Claude |
763
+ | `SubagentStop` | Blocks stoppage, shows stderr to Claude subagent |
764
+ | `PreCompact` | N/A, shows stderr to user only |
765
+ | `SessionStart` | N/A, shows stderr to user only |
766
+ | `SessionEnd` | N/A, shows stderr to user only |
767
+
768
+ ### [​](https://code.claude.com/docs/en/hooks\#advanced:-json-output) Advanced: JSON Output
769
+
770
+ Hooks can return structured JSON in `stdout` for more sophisticated control.
771
+
772
+ JSON output is only processed when the hook exits with code 0. If your hook
773
+ exits with code 2 (blocking error), `stderr` text is used directly—any JSON in `stdout`
774
+ is ignored. For other non-zero exit codes, only `stderr` is shown to the user in verbose mode (ctrl+o).
775
+
776
+ #### [​](https://code.claude.com/docs/en/hooks\#common-json-fields) Common JSON Fields
777
+
778
+ All hook types can include these optional fields:
779
+
780
+ Copy
781
+
782
+ Ask AI
783
+
784
+ ```
785
+ {
786
+ "continue": true, // Whether Claude should continue after hook execution (default: true)
787
+ "stopReason": "string", // Message shown when continue is false
788
+
789
+ "suppressOutput": true, // Hide stdout from transcript mode (default: false)
790
+ "systemMessage": "string" // Optional warning message shown to the user
791
+ }
792
+ ```
793
+
794
+ If `continue` is false, Claude stops processing after the hooks run.
795
+
796
+ - For `PreToolUse`, this is different from `"permissionDecision": "deny"`, which
797
+ only blocks a specific tool call and provides automatic feedback to Claude.
798
+ - For `PostToolUse`, this is different from `"decision": "block"`, which
799
+ provides automated feedback to Claude.
800
+ - For `UserPromptSubmit`, this prevents the prompt from being processed.
801
+ - For `Stop` and `SubagentStop`, this takes precedence over any
802
+ `"decision": "block"` output.
803
+ - In all cases, `"continue" = false` takes precedence over any
804
+ `"decision": "block"` output.
805
+
806
+ `stopReason` accompanies `continue` with a reason shown to the user, not shown
807
+ to Claude.
808
+
809
+ #### [​](https://code.claude.com/docs/en/hooks\#pretooluse-decision-control) `PreToolUse` Decision Control
810
+
811
+ `PreToolUse` hooks can control whether a tool call proceeds.
812
+
813
+ - `"allow"` bypasses the permission system. `permissionDecisionReason` is shown
814
+ to the user but not to Claude.
815
+ - `"deny"` prevents the tool call from executing. `permissionDecisionReason` is
816
+ shown to Claude.
817
+ - `"ask"` asks the user to confirm the tool call in the UI.
818
+ `permissionDecisionReason` is shown to the user but not to Claude.
819
+
820
+ Additionally, hooks can modify tool inputs before execution using `updatedInput`:
821
+
822
+ - `updatedInput` allows you to modify the tool’s input parameters before the tool executes.
823
+ - This is most useful with `"permissionDecision": "allow"` to modify and approve tool calls.
824
+
825
+ Copy
826
+
827
+ Ask AI
828
+
829
+ ```
830
+ {
831
+ "hookSpecificOutput": {
832
+ "hookEventName": "PreToolUse",
833
+ "permissionDecision": "allow"
834
+ "permissionDecisionReason": "My reason here",
835
+ "updatedInput": {
836
+ "field_to_modify": "new value"
837
+ }
838
+ }
839
+ }
840
+ ```
841
+
842
+ The `decision` and `reason` fields are deprecated for PreToolUse hooks.
843
+ Use `hookSpecificOutput.permissionDecision` and
844
+ `hookSpecificOutput.permissionDecisionReason` instead. The deprecated fields
845
+ `"approve"` and `"block"` map to `"allow"` and `"deny"` respectively.
846
+
847
+ #### [​](https://code.claude.com/docs/en/hooks\#permissionrequest-decision-control) `PermissionRequest` Decision Control
848
+
849
+ `PermissionRequest` hooks can allow or deny permission requests shown to the user.
850
+
851
+ - For `"behavior": "allow"` you can also optionally pass in an `"updatedInput"` that modifies the tool’s input parameters before the tool executes.
852
+ - 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.
853
+
854
+ Copy
855
+
856
+ Ask AI
857
+
858
+ ```
859
+ {
860
+ "hookSpecificOutput": {
861
+ "hookEventName": "PermissionRequest",
862
+ "decision": {
863
+ "behavior": "allow",
864
+ "updatedInput": {
865
+ "command": "npm run lint"
866
+ }
867
+ }
868
+ }
869
+ }
870
+ ```
871
+
872
+ #### [​](https://code.claude.com/docs/en/hooks\#posttooluse-decision-control) `PostToolUse` Decision Control
873
+
874
+ `PostToolUse` hooks can provide feedback to Claude after tool execution.
875
+
876
+ - `"block"` automatically prompts Claude with `reason`.
877
+ - `undefined` does nothing. `reason` is ignored.
878
+ - `"hookSpecificOutput.additionalContext"` adds context for Claude to consider.
879
+
880
+ Copy
881
+
882
+ Ask AI
883
+
884
+ ```
885
+ {
886
+ "decision": "block" | undefined,
887
+ "reason": "Explanation for decision",
888
+ "hookSpecificOutput": {
889
+ "hookEventName": "PostToolUse",
890
+ "additionalContext": "Additional information for Claude"
891
+ }
892
+ }
893
+ ```
894
+
895
+ #### [​](https://code.claude.com/docs/en/hooks\#userpromptsubmit-decision-control) `UserPromptSubmit` Decision Control
896
+
897
+ `UserPromptSubmit` hooks can control whether a user prompt is processed and add context.**Adding context (exit code 0):**
898
+ There are two ways to add context to the conversation:
899
+
900
+ 1. **Plain text stdout** (simpler): Any non-JSON text written to stdout is added
901
+ as context. This is the easiest way to inject information.
902
+ 2. **JSON with `additionalContext`** (structured): Use the JSON format below for
903
+ more control. The `additionalContext` field is added as context.
904
+
905
+ Both methods work with exit code 0. Plain stdout is shown as hook output in
906
+ the transcript; `additionalContext` is added more discretely.**Blocking prompts:**
907
+
908
+ - `"decision": "block"` prevents the prompt from being processed. The submitted
909
+ prompt is erased from context. `"reason"` is shown to the user but not added
910
+ to context.
911
+ - `"decision": undefined` (or omitted) allows the prompt to proceed normally.
912
+
913
+ Copy
914
+
915
+ Ask AI
916
+
917
+ ```
918
+ {
919
+ "decision": "block" | undefined,
920
+ "reason": "Explanation for decision",
921
+ "hookSpecificOutput": {
922
+ "hookEventName": "UserPromptSubmit",
923
+ "additionalContext": "My additional context here"
924
+ }
925
+ }
926
+ ```
927
+
928
+ The JSON format isn’t required for simple use cases. To add context, you can print plain text to stdout with exit code 0. Use JSON when you need to
929
+ block prompts or want more structured control.
930
+
931
+ #### [​](https://code.claude.com/docs/en/hooks\#stop/subagentstop-decision-control) `Stop`/`SubagentStop` Decision Control
932
+
933
+ `Stop` and `SubagentStop` hooks can control whether Claude must continue.
934
+
935
+ - `"block"` prevents Claude from stopping. You must populate `reason` for Claude
936
+ to know how to proceed.
937
+ - `undefined` allows Claude to stop. `reason` is ignored.
938
+
939
+ Copy
940
+
941
+ Ask AI
942
+
943
+ ```
944
+ {
945
+ "decision": "block" | undefined,
946
+ "reason": "Must be provided when Claude is blocked from stopping"
947
+ }
948
+ ```
949
+
950
+ #### [​](https://code.claude.com/docs/en/hooks\#sessionstart-decision-control) `SessionStart` Decision Control
951
+
952
+ `SessionStart` hooks allow you to load in context at the start of a session.
953
+
954
+ - `"hookSpecificOutput.additionalContext"` adds the string to the context.
955
+ - Multiple hooks’ `additionalContext` values are concatenated.
956
+
957
+ Copy
958
+
959
+ Ask AI
960
+
961
+ ```
962
+ {
963
+ "hookSpecificOutput": {
964
+ "hookEventName": "SessionStart",
965
+ "additionalContext": "My additional context here"
966
+ }
967
+ }
968
+ ```
969
+
970
+ #### [​](https://code.claude.com/docs/en/hooks\#sessionend-decision-control) `SessionEnd` Decision Control
971
+
972
+ `SessionEnd` hooks run when a session ends. They cannot block session termination
973
+ but can perform cleanup tasks.
974
+
975
+ #### [​](https://code.claude.com/docs/en/hooks\#exit-code-example:-bash-command-validation) Exit Code Example: Bash Command Validation
976
+
977
+ Copy
978
+
979
+ Ask AI
980
+
981
+ ```
982
+ #!/usr/bin/env python3
983
+ import json
984
+ import re
985
+ import sys
986
+
987
+ # Define validation rules as a list of (regex pattern, message) tuples
988
+ VALIDATION_RULES = [\
989
+ (\
990
+ r"\bgrep\b(?!.*\|)",\
991
+ "Use 'rg' (ripgrep) instead of 'grep' for better performance and features",\
992
+ ),\
993
+ (\
994
+ r"\bfind\s+\S+\s+-name\b",\
995
+ "Use 'rg --files | rg pattern' or 'rg --files -g pattern' instead of 'find -name' for better performance",\
996
+ ),\
997
+ ]
998
+
999
+ def validate_command(command: str) -> list[str]:
1000
+ issues = []
1001
+ for pattern, message in VALIDATION_RULES:
1002
+ if re.search(pattern, command):
1003
+ issues.append(message)
1004
+ return issues
1005
+
1006
+ try:
1007
+ input_data = json.load(sys.stdin)
1008
+ except json.JSONDecodeError as e:
1009
+ print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
1010
+ sys.exit(1)
1011
+
1012
+ tool_name = input_data.get("tool_name", "")
1013
+ tool_input = input_data.get("tool_input", {})
1014
+ command = tool_input.get("command", "")
1015
+
1016
+ if tool_name != "Bash" or not command:
1017
+ sys.exit(1)
1018
+
1019
+ # Validate the command
1020
+ issues = validate_command(command)
1021
+
1022
+ if issues:
1023
+ for message in issues:
1024
+ print(f"• {message}", file=sys.stderr)
1025
+ # Exit code 2 blocks tool call and shows stderr to Claude
1026
+ sys.exit(2)
1027
+ ```
1028
+
1029
+ #### [​](https://code.claude.com/docs/en/hooks\#json-output-example:-userpromptsubmit-to-add-context-and-validation) JSON Output Example: UserPromptSubmit to Add Context and Validation
1030
+
1031
+ For `UserPromptSubmit` hooks, you can inject context using either method:
1032
+
1033
+ - **Plain text stdout** with exit code 0: Simplest approach, prints text
1034
+ - **JSON output** with exit code 0: Use `"decision": "block"` to reject prompts,
1035
+ or `additionalContext` for structured context injection
1036
+
1037
+ Remember: Exit code 2 only uses `stderr` for the error message. To block using
1038
+ JSON (with a custom reason), use `"decision": "block"` with exit code 0.
1039
+
1040
+ Copy
1041
+
1042
+ Ask AI
1043
+
1044
+ ```
1045
+ #!/usr/bin/env python3
1046
+ import json
1047
+ import sys
1048
+ import re
1049
+ import datetime
1050
+
1051
+ # Load input from stdin
1052
+ try:
1053
+ input_data = json.load(sys.stdin)
1054
+ except json.JSONDecodeError as e:
1055
+ print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
1056
+ sys.exit(1)
1057
+
1058
+ prompt = input_data.get("prompt", "")
1059
+
1060
+ # Check for sensitive patterns
1061
+ sensitive_patterns = [\
1062
+ (r"(?i)\b(password|secret|key|token)\s*[:=]", "Prompt contains potential secrets"),\
1063
+ ]
1064
+
1065
+ for pattern, message in sensitive_patterns:
1066
+ if re.search(pattern, prompt):
1067
+ # Use JSON output to block with a specific reason
1068
+ output = {
1069
+ "decision": "block",
1070
+ "reason": f"Security policy violation: {message}. Please rephrase your request without sensitive information."
1071
+ }
1072
+ print(json.dumps(output))
1073
+ sys.exit(0)
1074
+
1075
+ # Add current time to context
1076
+ context = f"Current time: {datetime.datetime.now()}"
1077
+ print(context)
1078
+
1079
+ """
1080
+ The following is also equivalent:
1081
+ print(json.dumps({
1082
+ "hookSpecificOutput": {
1083
+ "hookEventName": "UserPromptSubmit",
1084
+ "additionalContext": context,
1085
+ },
1086
+ }))
1087
+ """
1088
+
1089
+ # Allow the prompt to proceed with the additional context
1090
+ sys.exit(0)
1091
+ ```
1092
+
1093
+ #### [​](https://code.claude.com/docs/en/hooks\#json-output-example:-pretooluse-with-approval) JSON Output Example: PreToolUse with Approval
1094
+
1095
+ Copy
1096
+
1097
+ Ask AI
1098
+
1099
+ ```
1100
+ #!/usr/bin/env python3
1101
+ import json
1102
+ import sys
1103
+
1104
+ # Load input from stdin
1105
+ try:
1106
+ input_data = json.load(sys.stdin)
1107
+ except json.JSONDecodeError as e:
1108
+ print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
1109
+ sys.exit(1)
1110
+
1111
+ tool_name = input_data.get("tool_name", "")
1112
+ tool_input = input_data.get("tool_input", {})
1113
+
1114
+ # Example: Auto-approve file reads for documentation files
1115
+ if tool_name == "Read":
1116
+ file_path = tool_input.get("file_path", "")
1117
+ if file_path.endswith((".md", ".mdx", ".txt", ".json")):
1118
+ # Use JSON output to auto-approve the tool call
1119
+ output = {
1120
+ "decision": "approve",
1121
+ "reason": "Documentation file auto-approved",
1122
+ "suppressOutput": True # Don't show in verbose mode
1123
+ }
1124
+ print(json.dumps(output))
1125
+ sys.exit(0)
1126
+
1127
+ # For other cases, let the normal permission flow proceed
1128
+ sys.exit(0)
1129
+ ```
1130
+
1131
+ ## [​](https://code.claude.com/docs/en/hooks\#working-with-mcp-tools) Working with MCP Tools
1132
+
1133
+ Claude Code hooks work seamlessly with
1134
+ [Model Context Protocol (MCP) tools](https://code.claude.com/docs/en/mcp). When MCP servers
1135
+ provide tools, they appear with a special naming pattern that you can match in
1136
+ your hooks.
1137
+
1138
+ ### [​](https://code.claude.com/docs/en/hooks\#mcp-tool-naming) MCP Tool Naming
1139
+
1140
+ MCP tools follow the pattern `mcp__<server>__<tool>`, for example:
1141
+
1142
+ - `mcp__memory__create_entities` \- Memory server’s create entities tool
1143
+ - `mcp__filesystem__read_file` \- Filesystem server’s read file tool
1144
+ - `mcp__github__search_repositories` \- GitHub server’s search tool
1145
+
1146
+ ### [​](https://code.claude.com/docs/en/hooks\#configuring-hooks-for-mcp-tools) Configuring Hooks for MCP Tools
1147
+
1148
+ You can target specific MCP tools or entire MCP servers:
1149
+
1150
+ Copy
1151
+
1152
+ Ask AI
1153
+
1154
+ ```
1155
+ {
1156
+ "hooks": {
1157
+ "PreToolUse": [\
1158
+ {\
1159
+ "matcher": "mcp__memory__.*",\
1160
+ "hooks": [\
1161
+ {\
1162
+ "type": "command",\
1163
+ "command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"\
1164
+ }\
1165
+ ]\
1166
+ },\
1167
+ {\
1168
+ "matcher": "mcp__.*__write.*",\
1169
+ "hooks": [\
1170
+ {\
1171
+ "type": "command",\
1172
+ "command": "/home/user/scripts/validate-mcp-write.py"\
1173
+ }\
1174
+ ]\
1175
+ }\
1176
+ ]
1177
+ }
1178
+ }
1179
+ ```
1180
+
1181
+ ## [​](https://code.claude.com/docs/en/hooks\#examples) Examples
1182
+
1183
+ For practical examples including code formatting, notifications, and file protection, see [More Examples](https://code.claude.com/docs/en/hooks-guide#more-examples) in the get started guide.
1184
+
1185
+ ## [​](https://code.claude.com/docs/en/hooks\#security-considerations) Security Considerations
1186
+
1187
+ ### [​](https://code.claude.com/docs/en/hooks\#disclaimer) Disclaimer
1188
+
1189
+ **USE AT YOUR OWN RISK**: Claude Code hooks execute arbitrary shell commands on
1190
+ your system automatically. By using hooks, you acknowledge that:
1191
+
1192
+ - You are solely responsible for the commands you configure
1193
+ - Hooks can modify, delete, or access any files your user account can access
1194
+ - Malicious or poorly written hooks can cause data loss or system damage
1195
+ - Anthropic provides no warranty and assumes no liability for any damages
1196
+ resulting from hook usage
1197
+ - You should thoroughly test hooks in a safe environment before production use
1198
+
1199
+ Always review and understand any hook commands before adding them to your
1200
+ configuration.
1201
+
1202
+ ### [​](https://code.claude.com/docs/en/hooks\#security-best-practices) Security Best Practices
1203
+
1204
+ Here are some key practices for writing more secure hooks:
1205
+
1206
+ 1. **Validate and sanitize inputs** \- Never trust input data blindly
1207
+ 2. **Always quote shell variables** \- Use `"$VAR"` not `$VAR`
1208
+ 3. **Block path traversal** \- Check for `..` in file paths
1209
+ 4. **Use absolute paths** \- Specify full paths for scripts (use
1210
+ “$CLAUDE\_PROJECT\_DIR” for the project path)
1211
+ 5. **Skip sensitive files** \- Avoid `.env`, `.git/`, keys, etc.
1212
+
1213
+ ### [​](https://code.claude.com/docs/en/hooks\#configuration-safety) Configuration Safety
1214
+
1215
+ Direct edits to hooks in settings files don’t take effect immediately. Claude
1216
+ Code:
1217
+
1218
+ 1. Captures a snapshot of hooks at startup
1219
+ 2. Uses this snapshot throughout the session
1220
+ 3. Warns if hooks are modified externally
1221
+ 4. Requires review in `/hooks` menu for changes to apply
1222
+
1223
+ This prevents malicious hook modifications from affecting your current session.
1224
+
1225
+ ## [​](https://code.claude.com/docs/en/hooks\#hook-execution-details) Hook Execution Details
1226
+
1227
+ - **Timeout**: 60-second execution limit by default, configurable per command.
1228
+
1229
+ - A timeout for an individual command does not affect the other commands.
1230
+ - **Parallelization**: All matching hooks run in parallel
1231
+ - **Deduplication**: Multiple identical hook commands are deduplicated automatically
1232
+ - **Environment**: Runs in current directory with Claude Code’s environment
1233
+
1234
+ - The `CLAUDE_PROJECT_DIR` environment variable is available and contains the
1235
+ absolute path to the project root directory (where Claude Code was started)
1236
+ - 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.
1237
+ - **Input**: JSON via stdin
1238
+ - **Output**:
1239
+
1240
+ - PreToolUse/PermissionRequest/PostToolUse/Stop/SubagentStop: Progress shown in verbose mode (ctrl+o)
1241
+ - Notification/SessionEnd: Logged to debug only (`--debug`)
1242
+ - UserPromptSubmit/SessionStart: stdout added as context for Claude
1243
+
1244
+ ## [​](https://code.claude.com/docs/en/hooks\#debugging) Debugging
1245
+
1246
+ ### [​](https://code.claude.com/docs/en/hooks\#basic-troubleshooting) Basic Troubleshooting
1247
+
1248
+ If your hooks aren’t working:
1249
+
1250
+ 1. **Check configuration** \- Run `/hooks` to see if your hook is registered
1251
+ 2. **Verify syntax** \- Ensure your JSON settings are valid
1252
+ 3. **Test commands** \- Run hook commands manually first
1253
+ 4. **Check permissions** \- Make sure scripts are executable
1254
+ 5. **Review logs** \- Use `claude --debug` to see hook execution details
1255
+
1256
+ Common issues:
1257
+
1258
+ - **Quotes not escaped** \- Use `\"` inside JSON strings
1259
+ - **Wrong matcher** \- Check tool names match exactly (case-sensitive)
1260
+ - **Command not found** \- Use full paths for scripts
1261
+
1262
+ ### [​](https://code.claude.com/docs/en/hooks\#advanced-debugging) Advanced Debugging
1263
+
1264
+ For complex hook issues:
1265
+
1266
+ 1. **Inspect hook execution** \- Use `claude --debug` to see detailed hook
1267
+ execution
1268
+ 2. **Validate JSON schemas** \- Test hook input/output with external tools
1269
+ 3. **Check environment variables** \- Verify Claude Code’s environment is correct
1270
+ 4. **Test edge cases** \- Try hooks with unusual file paths or inputs
1271
+ 5. **Monitor system resources** \- Check for resource exhaustion during hook
1272
+ execution
1273
+ 6. **Use structured logging** \- Implement logging in your hook scripts
1274
+
1275
+ ### [​](https://code.claude.com/docs/en/hooks\#debug-output-example) Debug Output Example
1276
+
1277
+ Use `claude --debug` to see hook execution details:
1278
+
1279
+ Copy
1280
+
1281
+ Ask AI
1282
+
1283
+ ```
1284
+ [DEBUG] Executing hooks for PostToolUse:Write
1285
+ [DEBUG] Getting matching hook commands for PostToolUse with query: Write
1286
+ [DEBUG] Found 1 hook matchers in settings
1287
+ [DEBUG] Matched 1 hooks for query "Write"
1288
+ [DEBUG] Found 1 hook commands to execute
1289
+ [DEBUG] Executing hook command: <Your command> with timeout 60000ms
1290
+ [DEBUG] Hook command completed with status 0: <Your stdout>
1291
+ ```
1292
+
1293
+ Progress messages appear in verbose mode (ctrl+o) showing:
1294
+
1295
+ - Which hook is running
1296
+ - Command being executed
1297
+ - Success/failure status
1298
+ - Output or error messages
1299
+
1300
+ Was this page helpful?
1301
+
1302
+ YesNo
1303
+
1304
+ [Checkpointing](https://code.claude.com/docs/en/checkpointing) [Plugins reference](https://code.claude.com/docs/en/plugins-reference)
1305
+
1306
+ Ctrl+I
1307
+
1308
+ Assistant
1309
+
1310
+ Responses are generated using AI and may contain mistakes.