Claude Code

Hook automation

Hook lifecycle events, matchers, command/http/mcp_tool/prompt/agent handlers, and common automation templates.

Hooks are Claude Code's automation layer. They run scripts, HTTP requests, MCP tools, or model checks at session startup, prompt submission, tool execution, configuration changes, compaction, and other lifecycle points.

Use hooks to turn team conventions into mechanisms: formatting, auditing, blocking commands, recording tool calls, and notifying when long tasks finish.

#Lifecycle

FrequencyExample eventsPurpose
SessionSessionStart, SessionEnd, SetupInitialize environment, show instructions, record sessions
Conversation turnUserPromptSubmit, Stop, StopFailureCheck prompts, notify completion, diagnose errors
Tool callPreToolUse, PermissionRequest, PostToolUse, PostToolUseFailureBlock, audit, format, validate
Batches/subagentsPostToolBatch, SubagentStart, SubagentStop, TaskCreated, TaskCompletedTrack parallel work
Configuration/contextInstructionsLoaded, ConfigChange, CwdChanged, FileChanged, PreCompact, PostCompactObserve settings and memory loading
MCP interactionElicitation, ElicitationResultHandle MCP requests for user input

Common events:

  • PreToolUse: before execution; can prevent an operation.
  • PostToolUse: after success; format or run a lightweight check.
  • UserPromptSubmit: before the prompt reaches the model; validate or add context.
  • Stop: after a response; notify or record completion.
  • ConfigChange: settings, hooks, or rules change.
  • FileChanged: watch selected files such as .envrc or lockfiles.

#Basic configuration

Put hooks in a settings file:

.claude/settings.json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "npm run lint -- --quiet"
          }
        ]
      }
    ]
  }
}

There are three levels:

  1. Event, such as PreToolUse.
  2. Matcher group, such as Bash only.
  3. Handler: command, http, mcp_tool, prompt, or agent.

#Matcher rules

PatternMeaningExample
Omitted, empty, or *Match allEvery tool call
BashExact tool nameBash only
`EditWrite`Multiple tool namesAfter file edits
mcp__memory__.*Regular expressionAll tools from the memory server

For tool events, an if field can narrow the match:

.claude/settings.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(rm *)",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh"
          }
        ]
      }
    ]
  }
}

if uses permission-rule syntax. Compound Bash commands are checked by subcommand, not only against the complete line.

#Handler types

TypeBehaviorUse
commandLocal command receiving JSON on stdinFormatting, auditing, blocking
httpPOST JSON to a URLCentral audits and notifications
mcp_toolCall a connected MCP toolServer-hosted checks
promptSingle-turn model judgmentLightweight semantic checks
agentStart a verifying subagentFile reads and complex checks

Command stdin is event JSON. Output can be empty or a JSON decision.

#Block destructive commands

.claude/hooks/block-rm.sh
#!/usr/bin/env bash
set -euo pipefail

COMMAND=$(jq -r '.tool_input.command // ""')

if echo "$COMMAND" | grep -Eq 'rm -rf (~/|/|\\$HOME)'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Refuse to remove home or root paths"
    }
  }'
fi

Corresponding configuration:

.claude/settings.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(rm *)",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh"
          }
        ]
      }
    ]
  }
}

A silent hook does not approve the action. Empty output or exit 0 continues the normal permission flow. This pattern is an example, not comprehensive shell isolation.

#Automatic formatting

.claude/settings.json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "npm run lint -- --fix"
          }
        ]
      }
    ]
  }
}

Do not run a full build after every edit in a large project. Keep hooks short, stable, and predictable; run comprehensive validation deliberately at task completion.

#Configuration locations

LocationScope
~/.claude/settings.jsonPersonal global hooks
.claude/settings.jsonShared project hooks
.claude/settings.local.jsonPersonal project hooks
Managed settingsEnforced organization hooks
Plugin hooksLoaded when the plugin is enabled
Skill/agent frontmatterLoaded when the skill or agent activates

#Troubleshooting

SymptomCheck
Hook did not fireInspect loaded hooks and matchers in /hooks
Settings edit did not applyCheck ConfigChange and valid JSON
Command not foundUse an absolute path or ${CLAUDE_PROJECT_DIR}
Too slowNarrow with if and avoid unnecessary process starts
MCP tool not matchedUse mcp__server__tool
Suspected custom configuration interferenceTry claude --safe-mode temporarily

#Official references

Support

Need help?

For setup, billing, or model issues, email us. Check the status page for uptime.

WeChat / QQ support is available at the bottom right.