Design current Claude Code hooks with nested matcher-entry schema, five handler types, matcher/if boundaries, permissions-aware policy, portable scripts, MCP timing checks, and deterministic validation.
Design Claude Code hooks that run at lifecycle events without relying on the model to
remember a rule. The current official configuration shape is nested: each event key maps
to an array of matcher entries, and each matcher entry contains an inner hooks array of
handler objects. Flat event arrays of handlers are legacy and must be migrated. [DOC]
Use hook-engineering to design or repair a hook configuration; then audit the resulting
shape and local skill package with the toolkit gate ${CLAUDE_PLUGIN_ROOT}/scripts/check.sh at the plugin root.
Reusable local assets are listed in assets/manifest.json; load assets/README.md for the
asset inventory. [CONFIG]
Local reference: assets/hook-engineering-policy.json (the bundled policy) and
knowledge/body-of-knowledge.md. The event list below is the single set declared in that
policy — treat it as one tier, not a confirmed/candidate split. [CONFIG]
Event names (case-sensitive), the 22 in the vendored official spec and in
scripts/validate_hooks.py (EVENTS): SessionStart, UserPromptSubmit, PreToolUse,
PermissionRequest, PostToolUse, PostToolUseFailure, Notification, SubagentStart,
SubagentStop, Stop, StopFailure, TeammateIdle, TaskCompleted, InstructionsLoaded,
ConfigChange, WorktreeCreate, WorktreeRemove, PreCompact, PostCompact,
Elicitation, ElicitationResult, SessionEnd. A hooks.json naming any other event is
rejected by the toolkit gate (HOOKS-CRITICAL unknown event name), so this list and the
validator are one declaration. coverage_gap: documented surface, not runtime-probed; before
relying on a less common event confirm it fires in the target session. [CÓDIGO]
Deterministic assets (2.16): do not hand-write the JSON. hook-scaffold emits and validates a
hooks block (its scaffold_hook and validate_hook scripts); hook-rules turns a prose
guideline into a validated hookify rule (its scaffold_rule and validate_rule scripts). This skill owns the design rationale; those two own the artifact. [CÓDIGO]
Handler types:
| Type | Required fields | Deterministic use |
|---|---|---|
command | command | Best default for deterministic local policy and scripts. |
http | url | Deterministic only if the endpoint is stable and controlled. |
mcp_tool | server, tool | Calls an already-connected MCP server; tolerate missing server/tool results. |
prompt | prompt | LLM judgment, not deterministic enforcement. |
agent | prompt | Experimental agentic verifier; use cautiously and prefer command hooks for production policy. |
Common handler fields are type, if, timeout, statusMessage, and once. [DOC]
Handler/event compatibility matters. Per the bundled policy
(handler_event_compatibility), all five handler types are accepted on thirteen events:
PreToolUse, PostToolUse, PostToolUseFailure, PostToolBatch, PermissionRequest,
PermissionDenied, Stop, SubagentStop, TaskCompleted, TaskCreated, TeammateIdle,
UserPromptSubmit, and UserPromptExpansion. A second group accepts command, http,
and mcp_tool only, and SessionStart/Setup accept command and mcp_tool only.
Outside the all-five group, prompt/agent handlers do not apply. [CONFIG]
coverage_gap: this compatibility map is documented schema from the bundled policy, not
runtime-probed in a live session. The installed Claude Code version may expose fewer events
or narrower handler support. Before relying on a less common event or on a prompt/agent
handler, confirm it fires in the target session and record the residual gap. [SUPUESTO]
Source discrepancy [coverage_gap]: a second documented source (internal-plugin-quality-source / official-specs/ official-hook-spec.md) restricts prompt/agent to the 3 ToolUseContext events ONLY
(PreToolUse, PostToolUse, PermissionRequest) — narrower than the bundled-policy list above. The
gate ${CLAUDE_PLUGIN_ROOT}/scripts/validate_hooks.py enforces the conservative (3-event) rule, treating a prompt/
agent hook elsewhere as a "silent bomb". When they conflict, prefer the conservative rule and verify
against the live runtime. Full matrix: references/hook-compatibility-matrix.md. [DOC]
PostToolUse.UserPromptExpansion before the expanded
prompt reaches Claude. coverage_gap: confirm the event fires in the target session
before depending on it, else fall back to UserPromptSubmit. [SUPUESTO]mcp_tool, while handling
connection timing as a non-blocking risk. [DOC]Do not use hooks as a way to grant authority. Permission rules remain the baseline
authority boundary. A PreToolUse hook can tighten restrictions with
permissionDecision: "deny" even under permissive modes, but a hook allow cannot
override deny rules from settings or managed policy. [DOC]
Use the nested matcher-entry schema:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(git push *)",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/check-git-push.py",
"args": []
}
]
}
]
}
}Keep matcher and if separate. matcher filters a matcher entry by event-specific
fields such as tool name, command name, notification type, or MCP server name. if is
a single permission-rule string on individual handlers and, per the bundled policy
(tool_filtered_events_for_if), is evaluated only on the tool-filtered events
PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, and
PermissionDenied. On any other event a handler with if set never runs, so omit if
there. Note this is a separate axis from handler availability: an event may still accept
command, http, or mcp_tool handlers even where if does not evaluate — if
support is narrower than handler support. [CONFIG]
Use permissions for coarse allow/deny policy. Use hooks for deterministic checks,
automation, narrower runtime denies, audit, and normalization. Never claim a hook
allow bypasses permission policy. [DOC]
Put slash-command expansion controls on UserPromptExpansion, not only
UserPromptSubmit, when the risk is in the generated prompt. Match by command name
and block unsafe expansions before Claude receives them. coverage_gap:
confirm UserPromptExpansion fires in the target session, and keep a
UserPromptSubmit fallback. [SUPUESTO]
Make command hooks portable. Prefer ${CLAUDE_PROJECT_DIR}, ${CLAUDE_PLUGIN_ROOT},
or ${CLAUDE_PLUGIN_DATA} over absolute local paths; use exec form with args when
quoting or path placeholders matter; verify scripts exist and are executable. [DOC]
Treat mcp_tool handlers as integration hooks. The MCP server must already be
connected; hooks do not start OAuth or connection flows. Avoid SessionStart and
Setup unless the handler tolerates a not-connected result. [DOC]
Prefer deterministic command or controlled http handlers for policy. Use prompt
or agent only when judgment is needed; document that prompt/agent results depend on
model behavior and that agent hooks are experimental. [DOC]
Keep examples and evals concrete: include the exact event, matcher, handler type,
policy boundary, validation command, and residual coverage_gap when runtime version
or MCP availability is unknown. [CONFIG]
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/protect-client-paths.py",
"args": [],
"timeout": 10,
"statusMessage": "Checking protected client paths"
}
]
}
],
"UserPromptExpansion": [
{
"matcher": "publish-client-deck",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/check-command-expansion.py",
"args": [],
"timeout": 10
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "mcp_tool",
"server": "audit-ledger",
"tool": "record_hook_event",
"input": {
"event": "${hook_event_name}",
"path": "${toolInput.file_path}"
},
"timeout": 15
}
]
}
]
}
}Interpolation tokens follow a camelCase convention
(${toolInput.file_path}, not snake_case ${tool_input.file_path}). coverage_gap: the
exact token set beyond toolInput and hook_event_name is not enumerated in the local
reference; confirm field names against the installed version before depending on them. [SUPUESTO]
The command scripts read JSON from stdin and return structured JSON only when they must block, rewrite, or add context. A path guard that denies a protected write should return:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Protected client deliverable path requires explicit approval."
}
}{ "PreToolUse": [{ "type": "command", "command": "..." }] }.if on Stop, SessionStart, or UserPromptSubmit; only PreToolUse, PostToolUse,
PostToolUseFailure, PermissionRequest, and PermissionDenied evaluate if, so the
handler never runs elsewhere.allow and claims it overrides project or managed deny rules./Users/name/project/.claude/hooks/check.sh in committed
hook configuration.mcp_tool on SessionStart without a fallback for servers not connected yet.hooks array.type is one of command, http, mcp_tool, prompt, or agent.matcher and if are not treated as interchangeable.if appears only on the tool-filtered events (PreToolUse, PostToolUse,
PostToolUseFailure, PermissionRequest, PermissionDenied) and holds one permission
rule.prompt/agent handlers appear only on the events the bundled policy lists under
all_five_types; never on command/mcp_tool-only events.UserPromptExpansion) carry a coverage_gap and a
confirmed fallback when used for slash command expansion checks.mcp_tool handlers document connected-server timing and non-blocking failure
behavior.coverage_gap.tool-permission-policy for baseline permission design.mcp-engineering for MCP server and tool availability checks.katas-context-dilution-mitigationkatas-path-conditional-ruleskatas-plan-mode-explorationkatas-pretooluse-guardrailskatas-posttooluse-normalization${CLAUDE_PROJECT_DIR}, ${CLAUDE_PLUGIN_ROOT} o ${CLAUDE_PLUGIN_DATA}. [EXPLICIT]tool-permission-policy. [EXPLICIT]assets/hook-engineering-policy.json), no probada en runtime; coverage_gap: confirmar en la sesión destino que el evento usado dispara antes de depender de él [SUPUESTO]. [SUPUESTO]Capas del packet, cargables bajo demanda (disciplina ICM: una capa por vez, nunca todas juntas): references/ guías de profundidad (cargar UNA por etapa) · knowledge/ cuerpo de conocimiento · prompts/ prompts listos · examples/ salida de ejemplo · agents/ subagentes del packet · templates/ plantilla de output · assets/ recursos estáticos.
e8f986b
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.