Claude Code 的 PermissionRequest hook 会在 Claude Code 即将弹出权限提示时运行,并能替你应答。要允许,hook 以 0 退出并输出 {"hookSpecificOutput": {"hookEventName": "PermissionRequest", "decision": {"behavior": "allow"}}};要拒绝,则使用 "behavior": "deny",并附上给 Claude 的 message。它按工具名匹配,无法覆盖你的 deny 规则,退出码 2 在这个事件上不起作用。如果 hook 没有输出决定,就会照常显示提示。
01PermissionRequest 与 PreToolUse、Notification 的对比
有三个 hook 事件与权限相关,各自分工不同:
PreToolUse每次工具调用前,无论是否需要权限能:allow、deny、ask 或 deferPermissionRequest仅在 Claude Code 即将询问你时能:allow 或 denyNotification(permission_prompt)你不在时,提示等待约六秒之后否如果你关心的是提示本身——应答、记录或转发到别处——就用 PermissionRequest。它会立即触发,没有六秒延迟。对于应作用于每次调用的规则,比如“永远不要运行 rm -rf”,用 PreToolUse,因为即使不会弹出提示它也会运行。该事件的触发时机见 Notification hook 指南。
02hook 收到的内容
除了通用字段,PermissionRequest hook 还会收到 Claude 想要发起的调用的 tool_name 和 tool_input,以及一个可选的 permission_suggestions 数组:
{
"session_id": "abc123",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PermissionRequest",
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf node_modules",
"description": "Remove node_modules directory"
},
"permission_suggestions": [
{
"type": "addRules",
"rules": [{ "toolName": "Bash", "ruleContent": "rm -rf node_modules" }],
"behavior": "allow",
"destination": "localSettings"
}
]
}
permission_suggestions 包含 Claude Code 针对此请求建议的规则变更。它并不完全等同于对话框中的选项,因为每个对话框会自行生成选项。
03decision 对象
在 hookSpecificOutput 中返回一个 decision:
behavior"allow" 或 "deny"updatedInput仅限 allow。会替换工具的整个输入,所以未改动的字段也要包含在内updatedPermissions仅限 allow。要应用的权限变更,比如添加规则或切换模式message仅限 deny。告诉 Claude 原因interrupt仅限 deny。设为 true 时还会让 Claude 停止有两条规则要记住:
- deny 和 ask 规则依然生效。hook 返回
"allow"并不能覆盖设置里匹配的 deny 规则,修改后的输入也会再次按这些规则检查。 - 这里会忽略退出码
2。在大多数事件中,退出码2会阻止操作,但如果PermissionRequesthook 以2退出且没有返回decision对象,提示会保持原样。要拒绝,就返回 decision。
04示例:自动批准某一个提示
Anthropic 官方的示例会自动批准 ExitPlanMode,也就是 Claude 做完计划后弹出的那个提示,这样就不用每次都被问:
{
"hooks": {
"PermissionRequest": [
{
"matcher": "ExitPlanMode",
"hooks": [
{
"type": "command",
"command": "echo '{\"hookSpecificOutput\": {\"hookEventName\": \"PermissionRequest\", \"decision\": {\"behavior\": \"allow\"}}}'"
}
]
}
]
}
}
matcher 要尽量窄。空 matcher 或 .* 再加上一律 allow,会批准所有提示,包括写文件和 shell 命令。
05示例:根据命令内容做决定
这个脚本(需要 jq)会拒绝任何涉及 git push 的请求,并附上 Claude 能读到的理由;其他提示则什么都不输出,交给你自己处理:
#!/bin/bash
# ~/.claude/hooks/permission-check.sh
input=$(cat)
cmd=$(jq -r '.tool_input.command // empty' <<<"$input")
case "$cmd" in
*"git push"*)
jq -nc '{hookSpecificOutput: {hookEventName: "PermissionRequest",
decision: {behavior: "deny",
message: "Pushing is not allowed from this session. Commit and stop."}}}'
;;
esac
exit 0
{
"hooks": {
"PermissionRequest": [
{
"matcher": "Bash",
"hooks": [{ "type": "command", "command": "~/.claude/hooks/permission-check.sh" }]
}
]
}
}
用 jq 生成 JSON,不要拼接字符串,这样消息里的引号就不会把它弄坏。另外要确保没有其他内容输出到 stdout。如果你的 shell profile 会 echo 一行内容,Claude Code 就读不到 decision 了。
06示例:允许并在本次会话中记住
如果想批准请求,并让 Claude 在会话结束前不再询问,可以添加一个带 destination: "session" 的 updatedPermissions 条目。hook 可以原样返回收到的某条 permission_suggestions,只改 destination:
#!/bin/bash
input=$(cat)
jq -c '{hookSpecificOutput: {hookEventName: "PermissionRequest",
decision: {behavior: "allow",
updatedPermissions: [(.permission_suggestions // [])[]
| select(.type == "addRules")
| .destination = "session"]}}}' <<<"$input"
按这个写法,它会允许收到的所有请求,所以一定要配一个窄的 matcher。destination 也可以是 localSettings、projectSettings 或 userSettings,这样规则会写入设置文件,而不是只保存在内存里。
07很多时候,权限规则才是更合适的工具
如果答案永远不变,就不需要 hook。把规则写进 settings.json,Claude Code 无需运行任何东西就会应用它:
{
"permissions": {
"allow": ["Bash(npm run *)"],
"deny": ["Bash(git push *)"]
}
}
规则的判定顺序是先 deny,再 ask,最后 allow。当决定取决于规则看不到的东西时,hook 才有用武之地,比如当前时间、所在分支,或者某个人在别处作答。其他内置选项可参考 权限模式详解。
08容易踩的坑
- 非交互式运行。使用
claude -p时,没有人可以回应提示。PermissionRequesthook 仍会运行,如果没有任何 hook 返回 decision,调用会被拒绝。 - 超时。command hook 默认最多可以运行 600 秒,Claude 会一直等。如果 hook 要等待较慢的东西(比如人),记得设置
timeout(单位为秒)。 - 有些提示不会触发它。沙盒命令发起的网络请求不会运行
PermissionRequest。这种情况请用permission_prompt通知类型。 - 安全。返回
"allow"的 hook 是在以你的名义、用你的用户权限批准操作。要像审查其他能执行命令的代码一样审查它。
09Claude Code Notifier 的定位
写 PermissionRequest hook 最常见的原因,是想在不在电脑前时也能回应提示。自己动手做的话,hook 需要把请求发到某处、等待回复,并在超时前返回结果。这是可行的,有人就用聊天机器人实现过。
Claude Code Notifier 可以帮你搞定这件事。它的免费命令行工具 ccn 支持 Mac、Windows 和 Linux,会把 hook 安装到 ~/.claude/settings.json。配对 iPhone 或 Android app 后,权限请求会推送到手机上,你可以选择允许一次、本次会话内允许或拒绝;ccn 会通过 PermissionRequest hook 返回这个决定。如果你在过去 30 秒内敲过键盘,就不会等待,terminal 照常询问。如果你不在,terminal 会等待手机回复,最长等到你设定的上限(hold_ceiling_seconds,默认 600),而一旦你碰键盘就立即停止等待。它是独立产品,并非 Anthropic 出品。具体流程见 在手机上批准权限,安全方面见 远程批准安全吗?。
10常见问题
如何用 hook 自动批准权限?
添加一个 matcher 足够窄的 PermissionRequest hook,让它输出一个 JSON decision,在 hookSpecificOutput 中包含 "behavior": "allow",然后以 0 退出。如果同一条命令总是要允许,直接在 settings.json 里加一条 permissions.allow 规则更简单,不需要 hook。
为什么退出码 2 不会拒绝请求?
PermissionRequest 属于不认退出码 2 的事件之一。Claude Code 会忽略它,照常显示提示。应该在 decision 对象里返回 "behavior": "deny"。
PermissionRequest hook 能覆盖我的 deny 规则吗?
不能。deny 和 ask 规则依然会被判定,所以 hook 返回的 "allow" 绕不过匹配的 deny 规则。你返回的任何 updatedInput 也会按这些规则检查。
PermissionRequest 和 PreToolUse 有什么区别?
PreToolUse 在每次工具调用前运行,可以 allow、deny、ask 或 defer。PermissionRequest 只在 Claude Code 即将显示提示时运行,可以 allow 或 deny。策略控制用 PreToolUse,回应提示用 PermissionRequest。
如果 hook 什么都不输出会怎样?
什么都不会变。Claude Code 会在 terminal 中照常显示权限提示,就像 hook 不存在一样。