Skip to content

Hooks 改参数要守规矩:updatedInput 现在会被校验 ​

Claude Code 的钩子不只能「放行 / 拦截」,还能改写工具参数:

  • PreToolUse 钩子在 hookSpecificOutput 里返回 updatedInput,替换工具即将使用的参数;
  • PermissionRequest 钩子在 decision 对象里返回 updatedInput,在批准的同时改掉输入。

常见用法包括:给 npm test 自动加上 --silent、把危险路径改写到沙箱目录、替用户回答 AskUserQuestion 等。

CLI 2.1.300 收紧了这条通道:钩子返回的 updatedInput 现在会像 Claude 自己生成的输入一样被校验。

具体变了什么 ​

按更新日志,新规则有两条:

  1. 未知字段会被丢弃:你在 updatedInput 里多塞的、工具 schema 里没有的键,直接去掉,不会传给工具;
  2. 工具不接受的输入会拒绝这次调用:类型不对、缺必填项、值不合法,这次工具调用会被拒绝,而不是带着坏参数硬跑。

以前钩子的改写在一定程度上是「你写什么就用什么」,现在多了一道和 Claude 输入同等级别的检查。

为什么要这样 ​

  • 少一种隐蔽失败:钩子拼错字段名(比如把 command 写成 cmd),以前可能导致工具拿到奇怪的输入、行为难以预料;
  • 不给工具塞私货:多余字段被丢弃,避免钩子无意中把调试信息、内部标记带进工具调用;
  • 失败时偏向安全:非法输入直接拒绝,而不是放过一个形状不对的调用。这和 2.1.295 的 onFailure: "block" 思路一致——钩子出问题时宁可拦住。

钩子作者要自查的几点 ​

1. updatedInput 是整体替换,别只写改动的字段

官方文档一直强调:updatedInput 会替换整个输入对象,没改的字段也要原样带上。以前漏写字段也许还能凑合跑,现在如果漏掉的是必填项,调用会被直接拒绝。

json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "updatedInput": {
      "command": "npm test -- --silent",
      "description": "Run tests quietly"
    }
  }
}

更稳妥的写法是:读入原始 tool_input,在它上面修改,再整体返回。比如用 jq:

bash
#!/usr/bin/env bash
input=$(cat)
echo "$input" | jq '{
  hookSpecificOutput: {
    hookEventName: "PreToolUse",
    permissionDecision: "allow",
    updatedInput: (.tool_input + { command: (.tool_input.command + " --silent") })
  }
}'

2. 字段名和类型对照工具定义

不确定某个工具接受哪些参数,可以在 --debug 日志或会话记录里看 Claude 正常调用时传的输入,照着结构改。数字就给数字,布尔就给布尔,不要都写成字符串。

3. 别再靠自定义字段传「暗号」

如果你之前在 updatedInput 里加了自定义键,指望下游的另一个钩子或 MCP 工具读到——现在这些键会被丢掉。需要传递额外信息,考虑用 additionalContext 告诉 Claude,或者在钩子自己的状态文件里记录。

4. 修改后的输入仍然要过权限规则

文档写明,Claude Code 会用钩子返回的输入去评估权限规则(以及 Bash 命令能否自动转后台),而不是 Claude 原本发的那份。所以改写成一个被 deny 规则命中的命令,照样会被拒绝;PermissionRequest 里返回的 updatedInput 也会重新对照 deny 和 ask 规则。

同版本相关改动 ​

2.1.300 还调整了一处钩子行为:在 Cowork 云端会话里,PermissionRequest 钩子批准时附带的 setMode(切换权限模式)会被忽略,其余批准内容照常生效。本地终端用户不受影响。

升级后怎么验证 ​

  1. claude --version 确认 ≥ 2.1.300;
  2. 用 claude --debug 跑一遍会触发你钩子的操作,看工具是否按预期执行;
  3. 如果某个原本能用的改写开始被拒,优先检查:是否漏了必填字段、字段名拼写、值的类型。

小结 ​

  • 2.1.300 起,PreToolUse 和 PermissionRequest 钩子的 updatedInput 按工具自身的规则校验;
  • 未知字段被丢弃,非法输入直接拒绝调用;
  • 写钩子时在原始 tool_input 基础上修改并整体返回,最不容易出错。

Claude-cn.org,专注于 Claude Code 中文教程