本页目录12
- Hooks 通过 options.hooks(TypeScript)或 ClaudeAgentOptions(hooks=...)(Python)注册,结构是事件名 → 匹配器数组,每个匹配器包含 matcher/hooks/timeout。
- 官方列出的 hook 事件已扩展到 30 余种,但只有约一半(PreToolUse、PostToolUse、PostToolUseFailure、UserPromptSubmit、Stop、SubagentStart/Stop、PreCompact、PermissionRequest、Notification)在 Python SDK 中可用,其余仅 TypeScript SDK 支持。
- 回调返回值分两类:所有事件通用的顶层字段(systemMessage、continue/continue_),以及依事件而异的 hookSpecificOutput(如 PreToolUse 的 permissionDecision/updatedInput,PostToolUse 的 updatedToolOutput)。
- 默认超时 600 秒,UserPromptSubmit 为 30 秒,MessageDisplay 为 10 秒,SessionEnd 在关机阶段仅有 1.5 秒预算;返回 async:true 可让副作用型 hook 不阻塞 agent 继续执行。
- SessionStart/SessionEnd 等 TypeScript-only 事件在 Python 中需改用 settings 文件里的 shell command hook 或以首条消息作为初始化触发点来变通。
本文是对 Claude Agent SDK 官方文档「Hooks」页的中文整理,完整与最新内容以原文为准:https://code.claude.com/docs/en/agent-sdk/hooks
说明:该页面篇幅较大,本整理以官方页面中可核实的字段名、类型、默认值与结构说明为主;文中「结构示意」代码块仅用占位名标注确认过的字段/键名,并非逐字复制的原文示例,如需可直接复制运行的完整示例代码,请查阅原文对应小节。
概述
Hooks 是在 agent 执行的关键节点运行你自己代码的回调函数,响应诸如工具调用、会话启动、执行停止等事件。典型用途包括:
- 在危险操作执行前拦截(如破坏性 shell 命令、未授权文件访问)
- 记录/审计每次工具调用,用于合规、调试或分析
- 转换输入输出:清洗数据、注入凭据、重定向文件路径
- 为敏感操作(数据库写入、API 调用)要求人工审批
- 跟踪会话生命周期:管理状态、清理资源、发送通知
Hook 触发流程
- 事件触发(An event fires):agent 执行过程中发生某事,SDK 触发对应事件,例如工具即将被调用(
PreToolUse)、工具返回结果(PostToolUse)、子代理启动或停止、agent 空闲,或执行结束。 - SDK 收集已注册的 hooks:SDK 检查该事件类型下已注册的 hooks,包括你在
options.hooks中传入的回调 hook,以及在启用对应settingSources(TypeScript)/setting_sources(Python)时,来自 settings 文件的 shell command hook(默认query()选项下该项是启用的)。 - matcher 过滤哪些 hooks 会运行:如果某个 hook 设置了
matcher模式(例如Write|Edit),SDK 会用它匹配事件的目标(例如工具名)。没有设置 matcher 的 hook 会对该事件类型的每次发生都运行。
可用的 Hook 事件
SDK 为 agent 执行的不同阶段提供 hooks。部分 hook 在两种 SDK 中都可用,部分仅限 TypeScript。
| Hook 事件 | Python SDK | TypeScript SDK | 触发条件 | 典型用途 |
|---|---|---|---|---|
PreToolUse | 是 | 是 | 工具调用请求(可阻止或修改) | 阻止危险的 shell 命令 |
PostToolUse | 是 | 是 | 工具执行结果 | 将所有文件变更记录到审计日志 |
PostToolUseFailure | 是 | 是 | 工具执行失败 | 处理或记录工具错误 |
PostToolBatch | 否 | 是 | 一整批工具调用全部完成后触发,每批一次,发生在下一次模型调用之前 | 为整批调用统一注入一次约定/规范 |
UserPromptSubmit | 是 | 是 | 用户提交提示词 | 向提示词注入额外上下文 |
UserPromptExpansion | 否 | 是 | 用户输入的命令或 MCP 提示在到达 Claude 前展开为提示词;Claude 自己调用 skill 时不会触发 | 阻止某命令被直接调用,或在输入 skill 时添加上下文 |
MessageDisplay | 否 | 是 | 一条助手消息的文本完成时触发,每条消息一次,带完整消息文本 | 在不改变 transcript 的前提下屏蔽或重排显示文本 |
Stop | 是 | 是 | agent 执行停止 | 退出前保存会话状态 |
StopFailure | 否 | 是 | 本轮以 API 错误而非正常方式结束 | 记录失败或发送告警 |
SubagentStart | 是 | 是 | 子代理初始化 | 跟踪并行任务的派生 |
SubagentStop | 是 | 是 | 子代理完成 | 汇总并行任务结果 |
PreCompact | 是 | 是 | 会话压缩请求 | 摘要前先归档完整 transcript |
PostCompact | 否 | 是 | 会话压缩完成 | 记录生成的摘要 |
PermissionRequest | 是 | 是 | 某次工具调用需要权限决策 | 自定义权限处理逻辑 |
PermissionDenied | 否 | 是 | 自动模式拒绝了某次工具调用,包括没有分类器裁决的拒绝 | 记录拒绝情况,或告知模型可重试;对无裁决的拒绝,Claude Code 会忽略 retry: true |
SessionStart | 否 | 是 | 会话初始化 | 初始化日志与遥测 |
SessionEnd | 否 | 是 | 会话终止 | 清理临时资源 |
Notification | 是 | 是 | agent 状态消息 | 将 agent 状态更新发送到 Slack 或 PagerDuty |
Setup | 否 | 是 | 会话设置/维护 | 运行初始化任务 |
TeammateIdle | 否 | 是 | 队友(teammate)变为空闲 | 重新分配工作或发通知 |
TaskCreated | 否 | 是 | 通过 TaskCreate 工具创建了任务 | 强制任务命名规范 |
TaskCompleted | 否 | 是 | 任务被标记为完成 | 要求测试通过后才能关闭任务 |
Elicitation | 否 | 是 | MCP 服务器在任务中途请求用户输入 | 以程序方式响应 MCP 输入请求 |
ElicitationResult | 否 | 是 | 用户对 MCP elicitation 做出响应 | 在响应返回服务器前修改或阻止它 |
ConfigChange | 否 | 是 | 配置文件发生变化 | 动态重新加载设置 |
InstructionsLoaded | 否 | 是 | CLAUDE.md 或规则文件被加载进上下文 | 审计加载了哪些指令文件 |
WorktreeCreate | 否 | 是 | 创建了 Git worktree | 跟踪隔离的工作区 |
WorktreeRemove | 否 | 是 | 移除了 Git worktree | 清理工作区资源 |
CwdChanged | 否 | 是 | 会话期间工作目录发生变化 | 按目录重新加载环境变量 |
FileChanged | 否 | 是 | 被监视的文件被修改、创建或删除 | 项目文件变化时重新加载配置 |
DirectoryAdded | 否 | 是 | 会话期间添加了新的工作目录 | 为中途添加的仓库安装依赖 |
配置 Hook
把 hook 传入 agent 选项的 hooks 字段(Python 中是 ClaudeAgentOptions,TypeScript 中是 options 对象)即可完成配置。hooks 在 Python 中是一个 dict,在 TypeScript 中是一个 object,其中:
- 键(Keys):hook 事件名称,例如
'PreToolUse'、'PostToolUse'、'Stop' - 值(Values):matcher 数组,每个 matcher 包含一个可选的过滤模式以及你的回调函数
Matcher(匹配器)
用 matcher 过滤回调何时触发。matcher 字段匹配的目标随 hook 事件类型而不同——例如基于工具的 hook 匹配工具名,而 Notification hook 匹配通知类型。SDK 中的 matcher 遵循与 settings 文件中 matcher 相同的规则(即精确字符串匹配与正则匹配两条路径,以及各事件类型对应的匹配值)。
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
matcher | string | undefined | 匹配事件的过滤字段,规则与 settings 文件中的 matcher 一致。对工具类 hook,匹配的是工具名;内置工具包括 Bash、Read、Write、Edit、Glob、Grep、WebFetch、Agent 等。MCP 工具使用 mcp__<server>__<action> 模式 |
hooks | HookCallback[] | 无(必填) | 模式匹配时要执行的回调函数数组 |
timeout | number | undefined | 超时时间(秒)。不设置时使用该事件的默认超时;SDK 回调遵循 command 类型 hook 的默认值 |
应尽量使用 matcher 精确指定要作用的工具:设为 'Bash' 只会对 Bash 命令生效,省略该模式则会对该事件类型的每一次发生都运行回调。
回调函数
输入(Inputs)
每个 hook 回调都会收到三个参数:
- 输入数据:一个带类型的对象,包含事件详情。不同 hook 类型的输入形状不同,例如
PreToolUseHookInput包含tool_name与tool_input,而NotificationHookInput包含message。- 所有 hook 输入都共享
session_id、cwd、hook_event_name。 agent_id与agent_type在 hook 于子代理内触发时才会被填充。TypeScript 中这两个字段位于基础 hook input 类型上,对所有 hook 类型都可用;Python 中它们是PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest的可选字段,是SubagentStart、SubagentStop的必填字段。
- 所有 hook 输入都共享
- Tool use ID(
str | None/string | undefined):用于关联同一次工具调用对应的PreToolUse与PostToolUse事件。 - Context:TypeScript 中包含一个
signal属性(AbortSignal)用于取消;Python 中该参数目前保留,供未来使用。
输出(Outputs)
回调返回的对象包含两类字段:
- 顶层字段,对所有事件通用:
systemMessage向用户展示一条消息;continue(Python 中为continue_)决定该 hook 运行后 agent 是否继续执行。部分事件会忽略这些字段,或将其投递到别处。 hookSpecificOutput:控制当前操作,内部字段随 hook 事件类型而不同。- 对
PreToolUse:可设置permissionDecision(取值allow、deny、ask、defer)、permissionDecisionReason、updatedInput。返回defer会结束当前 query,以便你之后再恢复它。 - 对
PostToolUse:可设置additionalContext向工具结果追加信息;设置updatedToolOutput可在 Claude 看到结果前替换任意工具(两种 SDK 均支持)的输出;较旧的updatedMCPToolOutput字段仅替换 MCP 工具输出,已废弃。
- 对
返回 {} 表示放行且不做任何修改。SDK 回调 hook 使用与 Claude Code shell command hook 相同的 JSON 输出格式。
异步输出(Asynchronous output)
默认情况下,agent 会等待你的 hook 返回结果后才继续。如果 hook 只是执行副作用(如记录日志、发送 webhook),不需要影响 agent 行为,可以返回异步输出,让 agent 立即继续而无需等待 hook 完成:
| 字段 | 类型 | 说明 |
|---|---|---|
async | true | 表示异步模式,agent 不等待即继续执行。Python 中用 async_ 以避开保留字 |
asyncTimeout | number | 后台操作的超时时间(毫秒),可选 |
Hook 超时
Claude Code 为每个回调设置超时,可通过 HookMatcher 上的 timeout 字段以秒为单位设置。不设置时使用该事件的默认值:
- 大多数事件:600 秒
UserPromptSubmit:30 秒MessageDisplay:10 秒SessionEnd:在关机阶段运行,使用更短的超时预算,默认 1.5 秒
TypeScript 中可通过 context 里的 AbortSignal 处理取消逻辑。
示例场景一览
官方文档在「Examples」一节给出以下场景(均为对 PreToolUse/PostToolUse/Notification 等 hook 的具体用法,建议直接查阅原文获取可运行代码):
| 示例 | 说明 |
|---|---|
| Modify tool input | 拦截 Write 工具调用,重写 file_path 参数(如加上沙箱前缀),通过 updatedInput 与 permissionDecision: allow 重定向文件写入位置 |
| Add context and block a tool | 用 permissionDecision: deny、permissionDecisionReason、systemMessage 阻止特定操作(如写入系统目录),并向模型/用户说明原因 |
| Auto-approve specific tools | 对只读工具(读取、搜索、查找等)返回 permissionDecision: allow,免去用户确认 |
| Register multiple hooks | 在同一事件上注册多个独立检查回调,其中任意一个返回 deny 都会阻止整个工具调用 |
| Filter with multi-tool matchers | 用管道分隔的精确工具列表、正则表达式,或省略 matcher,为一组相关工具共享同一回调 |
| Track subagent activity | 用 SubagentStop hook 监控子代理完成情况,记录每次完成的摘要信息 |
| Make HTTP requests from hooks | 在 hook 中执行异步操作(如 HTTP 请求),需要在 hook 内部捕获错误,避免失败请求中断 agent |
| Forward notifications to Slack | 用 Notification hook 接收系统通知,通过 Slack webhook 转发 agent 状态消息到指定频道 |
常见问题排查
| 问题 | 排查/解决思路 |
|---|---|
| Hook not firing(hook 不触发) | 检查事件名大小写、matcher 是否精确匹配工具名、hook 是否配置在正确位置;确认会话未因 max_turns 限制而提前结束 |
| Matcher not filtering as expected(matcher 过滤不符合预期) | matcher 只按工具名过滤,不匹配文件路径等参数;需在 hook 内部检查 tool_input.file_path 等具体字段做进一步过滤 |
| Hook timeout(hook 超时) | 可用 timeout 字段以秒为单位调高;不同事件类型超时后的处理策略不同,参见「Hook 超时」一节 |
| Tool blocked unexpectedly(工具被意外阻止) | 检查所有 PreToolUse hook 的返回值是否含 deny;记录 permissionDecisionReason 排查;检查 matcher 是否过宽 |
| Modified input not applied(修改后的输入未生效) | 确认 updatedInput 放在 hookSpecificOutput 内部;不要与 permissionDecision: defer 同时使用;输出中应带上 hookEventName 标明 hook 类型 |
| Session hooks not available in Python(Python 中没有 session hooks) | SessionStart/SessionEnd 仅 TypeScript 可用;Python 需通过 settings 文件把它们定义为 shell command hook,或用第一条消息充当初始化触发点 |
| Subagent permission prompts multiplying(子代理权限提示反复出现) | 用 PreToolUse hook 自动批准特定工具,或配置权限规则让子代理继承父级权限设置 |
| Recursive hook loops with subagents(子代理引发递归 hook 循环) | 在 hook 内检查子代理标识、用共享变量跟踪执行上下文,或只为顶层 agent 会话启用该 hook |
| systemMessage not appearing in output(systemMessage 未出现在输出中) | 该字段是给用户看的,不会传给模型;v2.1.227 及以后可能在消息流中以 SDKInformationalMessage 形式出现(是否出现取决于具体事件,见原文各事件小节);若要传上下文给模型,应使用 additionalContext |