
1. 为什么要在 Claude Code 里折腾 HooksClaude Code 每次调用工具、等待输入、结束会话都会触发对应的生命周期事件。你可以在这些事件上挂脚本拿到上下文 JSON决定 Claude 要不要继续执行。这句话听起来抽象落到实际场景里就是Claude 准备跑rm -rf的时候你能拦下来Claude 跑完一条 Bash 命令你能记一笔日志Claude 停下来等你回复你能收到一条系统通知。Hooks 的本质是给 Claude Code 的执行流程装了几个「检查站」。Claude 要调用工具时Claude Code 把当时的状态打包成 JSON通过 stdin 传给你配置的脚本等脚本退出再继续。脚本退出码是 0 就放行是 2 就阻断其他非 0 值只记录错误但不阻断。这个机制让「可观测」和「可干预」两件事同时成立。适合谁看如果你已经在本地用 Claude Code 写代码想让工具调用过程更透明、更安全或者想把 Claude Code 接到统一 Key/API 通道上调用模型这篇就是给你写的。我会从 settings.json 的配置骨架开始把 PreToolUse 和 PostToolUse 两个最常用的事件跑通再接入 TaoToken 的统一通道最后给出逐条验证动作和排错清单。全程可复制不需要你额外造轮子。2. TaoToken 前置统一 Key 与 API 通道Claude Code 默认走 Anthropic 的官方通道但很多团队希望把模型调用收敛到一个统一入口方便做额度管理、日志审计和成本核算。TaoToken 提供的就是这样一个统一 Key/API 通道Claude Code 通过它调用模型Hooks 则负责在本地拦截和记录工具调用事件。两者配合一个管「调用去哪」一个管「调用前后干什么」。你需要先拿到一个可用的 API Key。打开控制台在 API Keys 页面创建一个新 Key复制保存。这个 Key 后面会写进 Claude Code 的环境变量或配置里Hooks 脚本本身不直接持有 Key它只处理 Claude Code 传过来的事件 JSON所以 Key 的暴露面很小。接入文档里有完整的参数说明和示例建议先扫一遍再动手。如果你只是想先验证模型能不能通可以直接用模型对话页面发一条消息确认 Key 有效。长期在本地做编码和 Agent 任务的话Coding Plan 会更划算额度模型和调用方式在文档里都有说明。这里要强调一点Hooks 和 TaoToken 是两层东西。Hooks 跑在本地处理的是 Claude Code 的工具调用事件TaoToken 处理的是模型请求的转发和鉴权。你不需要在 Hook 脚本里写任何网络请求去调模型Hook 只负责读 stdin、写 stdout、给退出码。把这两层分清楚后面配置就不会乱。3. 可复制配置settings.json 里的 PreToolUse 与 PostToolUseClaude Code 的 Hooks 配置写在~/.claude/settings.json里。如果你之前没建过这个文件直接新建一个加入hooks字段。下面是一份可以直接复制的最小骨架包含 PreToolUse 和 PostToolUse 两个事件每个事件挂一个 Python 脚本。{ hooks: { PreToolUse: [ { matcher: Bash, hooks: [ { type: command, command: python3 /Users/you/.claude/hooks/pre_bash_guard.py } ] } ], PostToolUse: [ { matcher: Bash, hooks: [ { type: command, command: python3 /Users/you/.claude/hooks/post_bash_log.py } ] } ] } }几个关键点逐条说明。matcher字段决定这个 Hook 对哪些工具生效写Bash就只监听 Bash 工具Edit、Read 等其他工具不会触发。如果你想让所有工具都走这个 Hook把matcher去掉或者写成*。每个事件可以配多个脚本按数组顺序依次执行前一个脚本的退出码会影响后续行为所以顺序有讲究。type目前固定是command表示执行一条命令。command里写脚本的绝对路径用python3显式指定解释器避免 shebang 在不同环境下失效。脚本文件需要可执行权限虽然用python3调用时权限不是必须的但养成习惯没坏处mkdir -p ~/.claude/hooks chmod x ~/.claude/hooks/pre_bash_guard.py chmod x ~/.claude/hooks/post_bash_log.pyPreToolUse 的脚本负责「调用前判断」PostToolUse 的脚本负责「调用后记录」。下面分别给出两个脚本的完整内容。先看 PreToolUse 的守卫脚本它检测到rm -rf就阻断#!/usr/bin/env python3 import json import sys data json.load(sys.stdin) if data.get(tool_name) Bash: command data.get(tool_input, {}).get(command, ) if rm -rf in command: print(拦截禁止执行 rm -rf, filesys.stderr) sys.exit(2) sys.exit(0)退出码 2 是 PreToolUse 的阻断信号。Claude 读到 stderr把它当作拒绝原因展示给用户然后停止这次工具调用。退出码 1 或其他非 0 值只会记录错误Claude 仍会继续执行所以想真正拦住必须用 2。再看 PostToolUse 的日志脚本它把每条 Bash 命令追加到日志文件#!/usr/bin/env python3 import json import sys from datetime import datetime data json.load(sys.stdin) if data.get(tool_name) Bash: command data.get(tool_input, {}).get(command, ) with open(/tmp/claude_commands.log, a) as f: f.write(f{datetime.now()} | {command}\n) sys.exit(0)PostToolUse 的退出码不影响工具执行结果因为工具已经跑完了。它的价值在于记录和补充上下文。你还可以通过 stdout 输出 JSON用additionalContext字段向 Claude 注入信息print(json.dumps({ additionalContext: 当前磁盘剩余空间 2GB请谨慎执行大文件操作 })) sys.exit(0)这段内容会直接注入到 Claude 的上下文里Claude 读完再决定下一步。适合在工具调用前后补充环境信息比如磁盘空间、当前分支、最近一次构建结果。4. 验证请求逐条确认 Hook 真的被触发配置写完不代表生效。Claude Code 只在会话启动时加载 settings.json已有会话需要重启才能加载新配置。所以第一步是退出当前会话重新启动一个新的 Claude Code 会话。启动后先做一次「无害触发」验证。让 Claude 执行一条普通命令比如echo hello。如果 PostToolUse 的日志脚本配置正确/tmp/claude_commands.log里应该多出一行记录cat /tmp/claude_commands.log预期输出类似2025-01-15 10:23:41.123456 | echo hello如果日志文件不存在或者没有新行说明 Hook 没被触发。这时候先检查三件事settings.json 的 JSON 格式是否合法、脚本路径是否写对、会话是否重启过。JSON 格式可以用python3 -m json.tool ~/.claude/settings.json快速校验。接着验证 PreToolUse 的阻断能力。让 Claude 执行一条包含rm -rf的命令比如rm -rf /tmp/test。预期结果是 Claude 收到阻断信号在终端显示「拦截禁止执行 rm -rf」并且不执行这条命令。如果你看到命令被拒绝的提示说明 PreToolUse 的退出码 2 生效了。再验证additionalContext注入。在 PostToolUse 脚本里临时加一段 stdout 输出让 Claude 执行任意 Bash 命令观察 Claude 的回复里是否引用了你注入的上下文。这一步能确认 stdout JSON 通道是通的。最后验证 TaoToken 通道。在 Claude Code 的环境变量里配置好 API Key 和 Base URL重启会话后发一条简单消息确认模型能正常回复。如果模型回复正常说明统一 Key/API 通道已经接通Hooks 和模型调用两条链路都跑通了。调试时有个小技巧在脚本里加一行print(json.dumps(data, indent2), filesys.stderr)把收到的完整数据打到 stderr。Claude Code 会把 stderr 输出显示在终端你就能看到每个事件传过来的 JSON 长什么样字段名和结构一目了然。5. 本篇常见错排查配置 Hooks 时最容易踩的坑集中在几个地方我按出现频率排一下。第一个坑是 JSON 格式错误。settings.json 对格式很敏感多一个逗号、少一个引号都会导致整个文件解析失败Hooks 全部不生效。用python3 -m json.tool校验是最快的办法。另外注意hooks字段的层级它和matcher、hooks数组的嵌套关系容易写错对照上面的骨架逐层检查。第二个坑是脚本路径写成了相对路径。Claude Code 执行 Hook 时的工作目录不一定是你的项目目录相对路径会找不到文件。统一用绝对路径~在 JSON 里不会自动展开要写成/Users/you/...的完整形式。第三个坑是退出码用错。PreToolUse 想阻断必须用sys.exit(2)用sys.exit(1)只会记录错误但不会阻断。很多人以为非 0 就能拦结果命令照跑。记住 2 是阻断其他非 0 是记录。第四个坑是忘了重启会话。settings.json 的改动不会热加载必须新开会话。如果你改完配置发现没反应先重启再说。第五个坑是 matcher 写错工具名。工具名是大小写敏感的Bash不能写成bash。如果你不确定某个工具的确切名称可以在 Hook 脚本里先把tool_name打出来看看。第六个坑是脚本没有读 stdin。Claude Code 把 JSON 写到脚本的 stdin如果脚本不读就直接退出Claude Code 可能会等待或报错。确保脚本第一件事就是json.load(sys.stdin)。第七个坑是 TaoToken 的 Base URL 配错。Claude Code 通过环境变量读取 API 地址如果地址写错或者 Key 无效模型调用会失败但 Hooks 本身还是正常触发的。这时候要区分是 Hook 问题还是模型通道问题看终端报错信息就能判断。6. 把 Hooks 和 TaoToken 串起来用Hooks 跑通之后你可以把事件转发到本地 socket让一个常驻进程处理所有状态变化驱动自定义 UI 或状态面板。转发脚本只需几行#!/usr/bin/env python3 import json import socket import sys data sys.stdin.read() sock socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.connect(/tmp/my_app.sock) sock.sendall((data \n).encode()) response sock.recv(1024).decode().strip() sock.close() sys.exit(2 if response block else 0)常驻进程在 socket 上监听收到 PreToolUse 弹出确认窗口用户点通过就回ok点拒绝就回blockHook 脚本根据返回值决定退出码。这样就能在 Claude 执行过程中插入任意交互界面不限于终端。如果你想把模型调用也收敛到统一通道在 Claude Code 的环境变量里配置 TaoToken 的 API 地址和 Key重启会话即可。API 地址是https://taotoken.net/apiKey 从控制台的 API Keys 页面获取。接入文档里有完整的环境变量示例和参数说明照着配就行。长期在本地做编码和 Agent 任务的话Coding Plan 的额度模型更适合高频调用场景。如果只是想先验证模型通不通用模型对话页面发一条消息最快。Hooks 负责本地可观测TaoToken 负责统一调用两层各管各的配合起来就是一个完整的本地开发闭环。一个脚本、一个退出码就能接入 Claude Code 的整个执行流程。你的脚本负责判断逻辑Claude Code 负责触发和等待各管各的。