这份文档详细介绍了 Claude Code Hooks 的配置与使用方法。Hooks 提供了一种灵活的机制,让你能够在 Claude Code 的不同事件触发点(如工具调用前后、会话开始或结束、用户提交提示时等)自动执行自定义脚本或命令。通过合理配置钩子,你可以扩展 Claude Code 的能力,实现工作流自动化、代码质量检查、安全策略执行以及项目环境定制化等功能。文档同时涵盖了输入输出规范、安全注意事项、调试方法以及与 MCP 工具的集成方式,帮助你在保证安全和稳定的前提下,充分发挥 Claude Code 的可扩展性。
配置 🔗
Claude Code 的钩子通过你的 设置文件 来配置:
- ~/.claude/settings.json- 用户设置
- .claude/settings.json- 项目设置
- .claude/settings.local.json- 本地项目设置(不会被提交)
- 企业托管的策略设置
结构 🔗
钩子通过匹配器(matcher)来组织,每个匹配器可以包含多个钩子:
{
 "hooks": {
    "EventName": [
        {
            "matcher": "ToolPattern",
            "hooks": [
                {
                "type": "command",
                "command": "your-command-here"
                }
            ]
        }
    ]
 }
} 
- 
matcher: 用于匹配工具名称的模式,区分大小写(仅适用于 PreToolUse和PostToolUse)- 简单字符串完全匹配:Write仅匹配 Write 工具
- 支持正则:Edit|Write或Notebook.*
- 使用 *匹配所有工具。你也可以使用空字符串 ("") 或者省略matcher。
 
- 简单字符串完全匹配:
- 
hooks: 当模式匹配时要执行的命令数组 - type: 当前仅支持- "command"
- command: 要执行的 bash 命令(可使用- $CLAUDE_PROJECT_DIR环境变量)
- timeout: (可选)命令的最长运行时间,单位为秒,超时则取消该命令。
 
对于像 UserPromptSubmit、Notification、Stop 和 SubagentStop 这类不使用匹配器的事件,可以省略 matcher 字段:
{
 "hooks": {
 "UserPromptSubmit": [
    {
    "hooks": [
        {
        "type": "command",
        "command": "/path/to/prompt-validator.py"
        }
      ]
     }
    ]
 }
} 
项目专用钩子脚本 🔗
你可以使用环境变量 CLAUDE_PROJECT_DIR(仅当 Claude Code 调用钩子命令时可用)来引用存储在项目中的脚本,确保它们无论 Claude 当前目录如何都能运行:
{
 "hooks": {
    "PostToolUse": [
        {
            "matcher": "Write|Edit",
            "hooks": [
                {
                "type": "command",
                "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/check-style.sh"
                }
            ]
        }
    ]
 }
} 
钩子事件 🔗
PreToolUse 🔗
在 Claude 创建工具参数之后、调用工具之前运行。常见匹配器:
- Task- 子代理任务(见 子代理文档)
- Bash- Shell 命令
- Glob- 文件模式匹配
- Grep- 内容搜索
- Read- 文件读取
- Edit,- MultiEdit- 文件编辑
- Write- 文件写入
- WebFetch,- WebSearch- 网络操作
PostToolUse 🔗
在工具成功完成后立即运行。识别与 PreToolUse 相同的匹配器值。
Notification 🔗
在 Claude Code 发送通知时运行。通知发送的场景包括:
- Claude 需要你授权才能使用某个工具。例如:“Claude 需要你的许可才能使用 Bash”
- 输入提示(prompt)空闲至少 60 秒时。“Claude 正在等待你的输入”
UserPromptSubmit 🔗
当用户提交提示时运行,在 Claude 处理之前。这样可以基于提示或对话添加额外上下文,验证提示,或屏蔽某些类型的提示。
Stop 🔗
当主 Claude Code 代理完成响应时运行。如果因用户中断而停止则不会运行。
SubagentStop 🔗
当 Claude Code 子代理(Task 工具调用)完成响应时运行。
PreCompact 🔗
在 Claude Code 即将执行压缩操作之前运行。匹配器:
- manual- 由- /compact调用
- auto- 由自动压缩调用(因为上下文窗口已满)
SessionStart 🔗
当 Claude Code 开启新会话或恢复会话时运行(当前底层总是会开启一个新会话)。常用于加载开发上下文,如现有问题或代码库的最近更改。匹配器:
- startup- 由启动调用
- resume- 由- --resume、- --continue或- /resume调用
- clear- 由- /clear调用
- compact- 由自动或手动压缩调用
SessionEnd 🔗
当 Claude Code 会话结束时运行。常用于清理任务、记录会话统计或保存会话状态。钩子输入中的 reason 字段可能是:
- clear- 使用 /clear 命令清除会话
- logout- 用户登出
- prompt_input_exit- 用户在提示输入可见时退出
- other- 其他退出原因
钩子输入 🔗
钩子通过 stdin 接收 JSON 数据,其中包含会话信息和事件相关数据:
{
  // 通用字段
  "session_id": "string",
  "transcript_path": "string", // 对话 JSON 文件路径
  "cwd": "string",             // 调用钩子时的当前工作目录
  // 事件相关字段
  "hook_event_name": "string",
  ...
}
PreToolUse 输入 🔗
tool_input 的具体结构依赖于工具。
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PreToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  }
}
PostToolUse 输入 🔗
tool_input 和 tool_response 的具体结构依赖于工具。
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  },
  "tool_response": {
    "filePath": "/path/to/file.txt",
    "success": true
  }
}
Notification 输入 🔗
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Notification",
  "message": "Task completed successfully"
}
UserPromptSubmit 输入 🔗
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "Write a function to calculate the factorial of a number"
}
Stop 和 SubagentStop 输入 🔗
当 Claude Code 因 Stop 钩子而继续执行时,stop_hook_active 会为 true。可以检查此值或处理对话记录,以避免 Claude Code 无限运行。
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "hook_event_name": "Stop",
  "stop_hook_active": true
}
PreCompact 输入 🔗
对于 manual,custom_instructions 来自用户在 /compact 中输入的内容。对于 auto,custom_instructions 为空。
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "hook_event_name": "PreCompact",
  "trigger": "manual",
  "custom_instructions": ""
}
SessionStart 输入 🔗
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "hook_event_name": "SessionStart",
  "source": "startup"
}
SessionEnd 输入 🔗
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "SessionEnd",
  "reason": "exit"
}
钩子输出 🔗
钩子有两种方式将输出返回给 Claude Code。输出用于传达是否阻止操作以及要显示给 Claude 和用户的反馈信息。
简单方式:退出码 🔗
钩子通过 退出码 (exit code)、stdout 和 stderr 来传递状态:
- 退出码 0:成功。stdout会在对话模式(CTRL-R)中显示给用户,除了UserPromptSubmit和SessionStart,在这两种情况下 stdout 会被加入上下文。
- 退出码 2:阻塞性错误。stderr会反馈给 Claude 自动处理。具体行为见下表。
- 其他退出码:非阻塞错误。stderr显示给用户,执行继续。
退出码 2 的行为 🔗
| 钩子事件 | 行为 | 
|---|---|
| PreToolUse | 阻止工具调用,将 stderr 显示给 Claude | 
| PostToolUse | 将 stderr 显示给 Claude(工具已运行) | 
| Notification | 不适用,仅将 stderr 显示给用户 | 
| UserPromptSubmit | 阻止提示处理,清空提示,仅将 stderr 显示给用户 | 
| Stop | 阻止停止,将 stderr 显示给 Claude | 
| SubagentStop | 阻止子代理停止,将 stderr 显示给 Claude 子代理 | 
| PreCompact | 不适用,仅将 stderr 显示给用户 | 
| SessionStart | 不适用,仅将 stderr 显示给用户 | 
| SessionEnd | 不适用,仅将 stderr 显示给用户 | 
高级方式:JSON 输出 🔗
钩子可以在 stdout 中返回结构化 JSON,用于更复杂的控制:
通用 JSON 字段 🔗
所有钩子类型都可以包含以下可选字段:
{
  "continue": true,           // Claude 是否在钩子执行后继续(默认: true)
  "stopReason": "string",     // 当 continue = false 时,显示给用户的原因
  "suppressOutput": true,     // 是否隐藏 stdout 的输出(默认: false)
  "systemMessage": "string"   // 可选,显示给用户的警告信息
}
如果 continue = false,Claude 会在钩子执行后停止处理。
- 对于 PreToolUse,这不同于"permissionDecision": "deny",后者只阻止某个工具调用,并提供自动反馈给 Claude。
- 对于 PostToolUse,这不同于"decision": "block",后者提供自动反馈给 Claude。
- 对于 UserPromptSubmit,这会阻止提示被处理。
- 对于 Stop和SubagentStop,这优先于任何"decision": "block"输出。
- 在所有情况下,"continue": false的优先级高于"decision": "block"。
stopReason 与 continue 一起使用,显示给用户而不会显示给 Claude。
PreToolUse 决策控制 🔗
PreToolUse 钩子可以控制工具调用是否继续:
- "allow":绕过权限系统。- permissionDecisionReason显示给用户,但不显示给 Claude。
- "deny":阻止工具调用执行。- permissionDecisionReason显示给 Claude。
- "ask":在 UI 中请求用户确认工具调用。- permissionDecisionReason显示给用户,但不显示给 Claude。
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow" | "deny" | "ask",
    "permissionDecisionReason": "My reason here"
  }
}
PostToolUse 决策控制 🔗
PostToolUse 钩子可以在工具执行后为 Claude 提供反馈:
- "block":自动提示 Claude 并传递- reason。
- undefined:什么也不做,- reason被忽略。
- "hookSpecificOutput.additionalContext":为 Claude 提供额外上下文。
{
  "decision": "block" | undefined,
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "Additional information for Claude"
  }
}
UserPromptSubmit 决策控制 🔗
UserPromptSubmit 钩子可以控制用户提示是否被处理。
- "block"阻止提示被处理。提交的提示会从上下文中清除,- "reason"显示给用户,但不会加入上下文。
- undefined允许提示正常处理,- "reason"会被忽略。
- "hookSpecificOutput.additionalContext"在未阻止时,会将字符串加入上下文。
{
  "decision": "block" | undefined,
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "My additional context here"
  }
}
Stop/SubagentStop 决策控制 🔗
Stop 和 SubagentStop 钩子可以控制 Claude 是否必须继续执行。
- "block"阻止 Claude 停止。必须提供- reason,以便 Claude 知道如何继续。
- undefined允许 Claude 停止,- reason会被忽略。
{
  "decision": "block" | undefined,
  "reason": "Must be provided when Claude is blocked from stopping"
}
SessionStart 决策控制 🔗
SessionStart 钩子允许在会话开始时加载上下文。
- "hookSpecificOutput.additionalContext"将字符串加入上下文。
- 多个钩子返回的 additionalContext会被拼接。
{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "My additional context here"
  }
}
SessionEnd 决策控制 🔗
SessionEnd 钩子会在会话结束时运行。它们不能阻止会话终止,但可以执行清理任务。
退出码示例:Bash 命令验证 🔗
#!/usr/bin/env python3
import json
import re
import sys
# 定义验证规则,形式为 (正则模式, 消息) 的元组列表
VALIDATION_RULES = [
    (
        r"\bgrep\b(?!.*\|)",
        "使用 'rg' (ripgrep) 替代 'grep' 以获得更好的性能和功能",
    ),
    (
        r"\bfind\s+\S+\s+-name\b",
        "使用 'rg --files | rg pattern' 或 'rg --files -g pattern' 替代 'find -name' 以获得更好的性能",
    ),
]
def validate_command(command: str) -> list[str]:
    issues = []
    for pattern, message in VALIDATION_RULES:
        if re.search(pattern, command):
            issues.append(message)
    return issues
try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
    sys.exit(1)
tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
command = tool_input.get("command", "")
if tool_name != "Bash" or not command:
    sys.exit(1)
# 验证命令
issues = validate_command(command)
if issues:
    for message in issues:
        print(f"• {message}", file=sys.stderr)
    # 退出码 2 阻止工具调用,并将 stderr 显示给 Claude
    sys.exit(2) 
JSON 输出示例:UserPromptSubmit 添加上下文和验证 🔗
#!/usr/bin/env python3
import json
import sys
import re
import datetime
# 从 stdin 加载输入
try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
    sys.exit(1)
prompt = input_data.get("prompt", "")
# 检查敏感模式
sensitive_patterns = [
    (r"(?i)\b(password|secret|key|token)\s*[:=]", "提示包含潜在的敏感信息"),
]
for pattern, message in sensitive_patterns:
    if re.search(pattern, prompt):
        # 使用 JSON 输出阻止并提供原因
        output = {
            "decision": "block",
            "reason": f"安全策略违规: {message}。请重新表述请求,不要包含敏感信息。"
        }
        print(json.dumps(output))
        sys.exit(0)
# 将当前时间加入上下文
context = f"Current time: {datetime.datetime.now()}"
print(context)
"""
以下写法也等效:
print(json.dumps({
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": context,
  },
}))
"""
# 允许提示继续,并附带额外上下文
sys.exit(0) 
JSON 输出示例:PreToolUse 自动批准 🔗
#!/usr/bin/env python3
import json
import sys
# 从 stdin 加载输入
try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
    sys.exit(1)
tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
# 示例:自动批准对文档文件的读取
if tool_name == "Read":
    file_path = tool_input.get("file_path", "")
    if file_path.endswith((".md", ".mdx", ".txt", ".json")):
        # 使用 JSON 输出自动批准工具调用
        output = {
            "decision": "approve",
            "reason": "文档文件已自动批准",
            "suppressOutput": True  # 不在对话模式中显示
        }
        print(json.dumps(output))
        sys.exit(0)
# 其他情况交给正常的权限流程
sys.exit(0) 
Claude Code 钩子与 Model Context Protocol (MCP) 工具 无缝配合。当 MCP 服务器提供工具时,它们会以特殊的命名模式出现,你可以在钩子中进行匹配。
MCP 工具命名 🔗
MCP 工具遵循命名模式 mcp__<server>__<tool>,例如:
- mcp__memory__create_entities- Memory 服务器的实体创建工具
- mcp__filesystem__read_file- Filesystem 服务器的文件读取工具
- mcp__github__search_repositories- GitHub 服务器的仓库搜索工具
为 MCP 工具配置钩子 🔗
你可以针对特定的 MCP 工具,或整个 MCP 服务器配置钩子:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__memory__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
          }
        ]
      },
      {
        "matcher": "mcp__.*__write.*",
        "hooks": [
          {
            "type": "command",
            "command": "/home/user/scripts/validate-mcp-write.py"
          }
        ]
      }
    ]
  }
}
示例 🔗
安全注意事项 🔗
免责声明 🔗
使用风险自负:Claude Code 钩子会在你的系统上自动执行任意 shell 命令。使用钩子即表示你已知晓并同意:
- 你需对自己配置的命令负全部责任
- 钩子可能修改、删除或访问你用户账户能访问的任何文件
- 恶意或写得不当的钩子可能导致数据丢失或系统损坏
- Anthropic 不提供任何担保,也不对因使用钩子导致的任何损害承担责任
- 你应当在安全环境中充分测试钩子后再在生产环境中使用
在将钩子命令加入配置前,务必先审查并理解其作用。
安全最佳实践 🔗
以下是编写更安全钩子的一些关键做法:
- 验证并清理输入 —— 永远不要盲目信任输入数据
- 始终给 shell 变量加引号 —— 使用 "$VAR"而不是$VAR
- 阻止路径遍历 —— 检查文件路径中是否包含 ..
- 使用绝对路径 —— 为脚本指定完整路径(项目路径可用 $CLAUDE_PROJECT_DIR)
- 跳过敏感文件 —— 避免处理 .env、.git/、密钥等文件
配置安全性 🔗
直接修改设置文件中的钩子不会立即生效。Claude Code 的行为是:
- 在启动时捕获钩子的快照
- 会话期间始终使用该快照
- 如果外部修改了钩子,会发出警告
- 需要在 /hooks菜单中确认后,修改才会生效
这样可以防止恶意钩子修改影响你当前的会话。
钩子执行细节 🔗
- 
超时:默认 60 秒执行上限,可按命令单独配置 - 某个命令超时不会影响其他命令
 
- 
并行执行:所有匹配的钩子会并行运行 
- 
去重:多个相同的钩子命令会自动去重 
- 
环境:在当前目录下运行,并使用 Claude Code 的环境 - 环境变量 CLAUDE_PROJECT_DIR可用,包含项目根目录的绝对路径(Claude Code 启动位置)
 
- 环境变量 
- 
输入:通过 stdin 提供 JSON 
- 
输出: - PreToolUse / PostToolUse / Stop / SubagentStop: 进度显示在对话模式 (Ctrl-R)
- Notification / SessionEnd: 仅记录到调试日志 (--debug)
- UserPromptSubmit / SessionStart: stdout 会作为上下文传给 Claude
 
调试 🔗
基础排查 🔗
如果钩子没有生效:
- 检查配置 —— 运行 /hooks查看钩子是否已注册
- 验证语法 —— 确保 JSON 设置合法
- 测试命令 —— 先手动运行钩子命令
- 检查权限 —— 确认脚本是可执行的
- 查看日志 —— 使用 claude --debug查看钩子执行详情
常见问题:
- 引号未转义 —— 在 JSON 字符串中使用 \"
- 匹配器错误 —— 检查工具名是否完全匹配(区分大小写)
- 找不到命令 —— 脚本使用完整路径
高级调试 🔗
对于复杂的钩子问题:
- 检查钩子执行 —— 使用 claude --debug查看详细执行信息
- 验证 JSON 模式 —— 使用外部工具测试钩子输入/输出
- 检查环境变量 —— 确认 Claude Code 的环境正确
- 测试边界情况 —— 用异常文件路径或输入测试钩子
- 监控系统资源 —— 检查钩子执行时是否资源耗尽
- 使用结构化日志 —— 在钩子脚本中实现日志记录
调试输出示例 🔗
使用 claude --debug 查看钩子执行详情:
[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Getting matching hook commands for PostToolUse with query: Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 60000ms
[DEBUG] Hook command completed with status 0: <Your stdout>
进度消息会显示在对话模式 (Ctrl-R),包括:
- 哪个钩子正在运行
- 执行的命令
- 成功/失败状态
- 输出或错误信息