Defining Hooks
Hook Implementation Step by Step
Implementing a hook requires understanding three things: how to configure it, what data it receives, and how to communicate back. Each hook is a command that receives tool call data as JSON via stdin and responds with exit codes.
- 1.Choose your hook type: pre-tool use (can block) or post-tool use (feedback only)
- 2.Identify which tool names to target (e.g., 'read', 'grep', 'edit', 'write')
- 3.Write a script that reads JSON from stdin containing tool_name and input parameters
- 4.Parse the JSON and apply your logic (e.g., check if a file path is restricted)
- 5.Exit with code 0 (allow/success) or code 2 (block, pre-hooks only)
- 6.Send feedback via stderr (console.error) -- this goes back to Claude as a message
The Tool Call Data Structure
When a hook is triggered, it receives a JSON object via stdin containing details about the tool call that Claude is about to make (pre-hook) or just made (post-hook).
// JSON received via stdin
{
"session_id": "abc-123",
"tool_name": "read",
"tool_input": {
"file_path": "/project/src/.env",
"encoding": "utf-8"
}
}// hooks/read_hook.js -- Block .env file access
const data = JSON.parse(
require('fs').readFileSync('/dev/stdin', 'utf8')
);
const filePath = data.tool_input?.file_path || '';
if (filePath.includes('.env')) {
console.error('Blocked: Cannot read .env files.');
process.exit(2); // Exit code 2 = block
}
process.exit(0); // Exit code 0 = allow| Exit Code | Meaning | Effect |
|---|---|---|
| 0 | Allow / Success | Tool call proceeds normally |
| 2 | Block (pre-hooks only) | Tool call is prevented, stderr sent to Claude as feedback |
| Other | Error | Treated as allow (fail-open for safety) |
Exit code reference for hook scripts
Tool Discovery
Not sure which tool names to use in your matcher? Ask Claude directly: 'What tool names are available for hooks?' Claude will list all available tools including read, grep, edit, write, bash, and any MCP server tools.
Key Takeaways
- ✓Hooks receive tool call data as JSON via stdin with tool_name and tool_input fields
- ✓Exit code 0 allows the operation, exit code 2 blocks it (pre-hooks only)
- ✓Send feedback to Claude via stderr (console.error) -- this becomes a message in the conversation
- ✓The matcher field uses pipe-separated tool names: 'read|grep' targets both tools
- ✓Ask Claude for available tool names rather than guessing -- it knows all registered tools
Check Your Understanding
Test what you learned in this lesson.
Q1.How does a hook script receive information about the tool call?
Q2.How does a hook script send feedback back to Claude?
Q3.Why should you check multiple path field names (file_path, path) in your hook script?