Per-Node Hooks
DAG workflow nodes support a hooks field that attaches Claude Agent SDK hooks
to individual nodes. Hooks fire during the node’s AI execution and can control
tool behavior, inject context, modify inputs, and more.
Claude only — Codex nodes will warn and ignore hooks.
Quick Start
Section titled “Quick Start”name: safe-migrationdescription: Generate SQL with guardrailsnodes: - id: generate prompt: "Generate a database migration for $ARGUMENTS" hooks: PreToolUse: - matcher: "Bash" response: hookSpecificOutput: hookEventName: PreToolUse permissionDecision: deny permissionDecisionReason: "No shell access during SQL generation"How It Works
Section titled “How It Works”Each hook matcher has three fields:
matcher(optional): Regex pattern to filter by tool name. Omit to match all tools.response(required): The SDKSyncHookJSONOutputreturned when the hook fires.timeout(optional): Seconds before the hook times out (default: 60).
At runtime, each YAML hook is wrapped in a trivial callback:
async () => responseNo custom DSL — response IS the SDK type, passed through unchanged.
Important: When using hookSpecificOutput, you must include a hookEventName
field that matches the event key (e.g., hookEventName: PreToolUse inside a
PreToolUse hook). This is an SDK requirement — it uses this field to determine
which event-specific fields to process.
Supported Hook Events
Section titled “Supported Hook Events”| Event | Fires When | Matcher Filters On |
|---|---|---|
PreToolUse | Before a tool executes | Tool name (e.g. Bash, Write, Read) |
PostToolUse | After a tool succeeds | Tool name |
PostToolUseFailure | After a tool fails | Tool name |
Notification | System notification | Notification type |
Stop | Agent stops | N/A |
SubagentStart | Subagent spawned | Agent type |
SubagentStop | Subagent finishes | Agent type |
PreCompact | Before context compaction | Trigger (manual/auto) |
SessionStart | Session begins | Source (startup/resume/clear/compact) |
SessionEnd | Session ends | Exit reason |
UserPromptSubmit | User prompt submitted | N/A |
PermissionRequest | Permission prompt would appear | Tool name |
Setup | SDK initialization | Trigger (init/maintenance) |
TeammateIdle | Agent teammate goes idle | N/A |
TaskCompleted | Background task finishes | N/A |
Elicitation | MCP server requests user input | N/A |
ElicitationResult | Elicitation response received | N/A |
ConfigChange | Settings/config file changed | Source (user_settings/project_settings/etc.) |
WorktreeCreate | Git worktree created | Worktree name |
WorktreeRemove | Git worktree removed | Worktree path |
InstructionsLoaded | CLAUDE.md/instructions loaded | Memory type (User/Project/Local/Managed) |
Tool names: Bash, Read, Write, Edit, Glob, Grep, WebFetch,
Agent, plus MCP tools as mcp__<server>__<action>.
Response Format (SDK SyncHookJSONOutput)
Section titled “Response Format (SDK SyncHookJSONOutput)”The response object supports these fields:
| Field | Type | Effect |
|---|---|---|
hookSpecificOutput | object | Event-specific response (see below) |
systemMessage | string | Inject a message visible to the model |
continue | boolean | false stops the agent |
decision | 'approve' / 'block' | Top-level approve/block |
stopReason | string | Reason when stopping |
suppressOutput | boolean | Suppress output emission |
PreToolUse hookSpecificOutput
Section titled “PreToolUse hookSpecificOutput”hookSpecificOutput: hookEventName: PreToolUse permissionDecision: deny | allow | ask # Control whether tool runs permissionDecisionReason: "..." # Why (shown in logs) updatedInput: # Modify tool arguments file_path: "/sandbox/output.ts" additionalContext: "..." # Text injected into model contextPostToolUse hookSpecificOutput
Section titled “PostToolUse hookSpecificOutput”hookSpecificOutput: hookEventName: PostToolUse additionalContext: "..." # Text injected after tool result updatedMCPToolOutput: ... # Override what model sees from toolPostToolUseFailure hookSpecificOutput
Section titled “PostToolUseFailure hookSpecificOutput”hookSpecificOutput: hookEventName: PostToolUseFailure additionalContext: "..." # Context after tool failureElicitation hookSpecificOutput
Section titled “Elicitation hookSpecificOutput”hookSpecificOutput: hookEventName: Elicitation action: accept | decline | cancel # Respond to MCP elicitation content: { ... } # Form field valuesElicitationResult hookSpecificOutput
Section titled “ElicitationResult hookSpecificOutput”hookSpecificOutput: hookEventName: ElicitationResult action: accept | decline | cancel # Override elicitation result content: { ... } # Modified response valuesExamples
Section titled “Examples”Deny a tool entirely
Section titled “Deny a tool entirely”hooks: PreToolUse: - matcher: "Bash" response: hookSpecificOutput: hookEventName: PreToolUse permissionDecision: deny permissionDecisionReason: "Shell access not allowed in this node"Deny tools with a reason message
Section titled “Deny tools with a reason message”hooks: PreToolUse: - matcher: "Write|Edit" response: hookSpecificOutput: hookEventName: PreToolUse permissionDecision: deny permissionDecisionReason: "Only read operations are allowed — do not modify files"Inject context before tool use (without blocking)
Section titled “Inject context before tool use (without blocking)”Note: this does NOT block the tool — it adds guidance the model sees before the tool runs.
hooks: PreToolUse: - matcher: "Write|Edit" response: hookSpecificOutput: hookEventName: PreToolUse additionalContext: "Only write to files in the src/ directory"Redirect file writes (modify tool input)
Section titled “Redirect file writes (modify tool input)”hooks: PreToolUse: - matcher: "Write" response: hookSpecificOutput: hookEventName: PreToolUse permissionDecision: allow updatedInput: file_path: "/sandbox/output.ts"Inject steering instructions after every tool call
Section titled “Inject steering instructions after every tool call”hooks: PostToolUse: - response: systemMessage: "Check: is this output relevant to the task? If not, stop and explain why."Inject context after reading files
Section titled “Inject context after reading files”hooks: PostToolUse: - matcher: "Read" response: hookSpecificOutput: hookEventName: PostToolUse additionalContext: "You just read a file. Do NOT modify it — analysis only."Emergency stop on shell access
Section titled “Emergency stop on shell access”hooks: PreToolUse: - matcher: "Bash" response: continue: false stopReason: "Emergency halt — shell access attempted"Multiple hooks on one node
Section titled “Multiple hooks on one node”hooks: PreToolUse: - matcher: "Bash" response: hookSpecificOutput: hookEventName: PreToolUse permissionDecision: deny permissionDecisionReason: "No shell" - matcher: "Write|Edit" response: hookSpecificOutput: hookEventName: PreToolUse additionalContext: "Only write to files in src/" PostToolUse: - response: systemMessage: "Verify output before continuing"Full workflow example
Section titled “Full workflow example”name: safe-code-reviewdescription: Review code with guardrailsnodes: - id: fetch-diff bash: "git diff main...HEAD"
- id: review prompt: "Review this diff for bugs and security issues: $fetch-diff.output" depends_on: [fetch-diff] hooks: PreToolUse: - matcher: "Bash" response: hookSpecificOutput: hookEventName: PreToolUse permissionDecision: deny permissionDecisionReason: "Code review should not execute commands" - matcher: "Write|Edit" response: hookSpecificOutput: hookEventName: PreToolUse permissionDecision: deny permissionDecisionReason: "Code review is read-only" PostToolUse: - matcher: "Read" response: hookSpecificOutput: hookEventName: PostToolUse additionalContext: "Focus on security issues in this file"
- id: summarize prompt: "Summarize the review findings from $review.output" depends_on: [review] allowed_tools: []Hooks vs allowed_tools/denied_tools
Section titled “Hooks vs allowed_tools/denied_tools”| Feature | allowed_tools/denied_tools | hooks |
|---|---|---|
| Block a tool entirely | Yes | Yes |
| Inject context | No | Yes (additionalContext, systemMessage) |
| Modify tool input | No | Yes (updatedInput) |
| Override tool output | No | Yes (updatedMCPToolOutput) |
| Stop the agent | No | Yes (continue: false) |
| React after tool use | No | Yes (PostToolUse) |
Use allowed_tools/denied_tools for simple include/exclude. Use hooks when you
need context injection, input modification, or post-tool-use reactions.
Limitations
Section titled “Limitations”- Static responses only in YAML — hooks return the same response every time.
For conditional logic, use
when:conditions on downstream nodes or gate execution with upstream bash nodes that emit structured output. - Claude only — Codex nodes warn and ignore hooks.
- No hook event streaming — hook lifecycle events (
hook_started,hook_progress) are not forwarded to the Web UI.
SDK Reference
Section titled “SDK Reference”Refer to the Anthropic Claude Agent SDK documentation for the authoritative SyncHookJSONOutput type, hook event reference, and matcher patterns.
Related
Section titled “Related”- Per-Node MCP Servers —
mcp:field for external tool access - Per-Node Skills —
skills:field for domain knowledge injection