随笔

Claude Code 源码解读 03:工具调用为什么必须过权限

从工具池、输入校验、PreToolUse Hook、权限判断到 tool_result 回灌,解释 Claude Code 如何把模型行动变成可拒绝、可审计的行动。

2026-04-23 Claude Code源码解读工具权限

模型输出 tool_use 只是提出行动,不是行动已经被允许。

快速了解

  • 模型提出工具调用,不代表工具已经执行。
  • 工具调用先过 schema、工具自身校验、PreToolUse Hook、权限判断,最后才到 Tool.call
  • Hook 的 allow 不能绕过 deny / ask,这是权限治理的关键。
  • 工具失败也要回灌成 tool_result,否则 query loop 的轨迹会断。

如果只想快速理解,可以先读这一节;如果要看源码机制,再往下看“主线与场景插入分类”和机制图。

机制图
机制图
权限判断顺序
权限判断顺序

设问与回应

设问回应
模型看到工具前,工具池是否已经被权限和 deny rule 收缩?是的,模型看到的工具面并不是原始全集。工具池会先经过可用性、场景条件和 deny rule 收缩,模型只能在被暴露的工具集合里提出行动。
输入校验是在权限前还是权限后?输入校验发生在最终权限判断之前。这样做的原因是,权限系统不能只面对自然语言参数,它需要先得到结构化、可判断的工具输入。
Hook 的 allow 能不能绕过 deny / ask?Hook 的 allow 不能绕过 deny / ask。PreToolUse Hook 可以补上下文、改写输入、提出 allow/deny/ask,但最终仍要回到权限合并逻辑里。
bypassPermissions 是不是无限通行?bypassPermissions 不是无限通行。它会改变部分询问路径,但仍受工具自身语义、高风险 opt-in、环境约束和某些安全边界限制。

主线与场景插入分类

机制/模块分类我保留的判断
StreamingToolExecutor主线功能query loop 收到 tool_use 后的执行调度层
checkPermissionsAndCallTool()主线功能串起 schema 校验、工具校验、Hook、权限、调用和结果回灌
runPreToolUseHooks()安全/治理横切在最终权限决策前介入,可 deny、ask、改 input、加 context
resolveHookPermissionDecision()安全/治理横切Hook allow 仍需检查 deny / ask 规则
assembleToolPool()主线 + 场景插入聚合点内置工具是主线;MCP、REPL、coordinator、worktree 按场景加入

源码链路拆解

最关键的顺序是:先找工具,再校验输入,再跑 PreToolUse Hook,再合并权限判断,最后才进入 Tool.call。这说明权限不是一个弹窗,而是一组前后有序的判断。
tool_result 是协议边界。工具调用成功或失败,都要映射成 API 可消费的结果块。

延伸问题:Permission 是否像 Unix 一样抽象 read / write

Claude Code 的 permission 不是简单 read / write。每个 Tool 还带有 isReadOnly(input)isDestructive(input)isOpenWorld(input)checkPermissions(input, context) 等语义。这比粗粒度 read / write 更适合 agent。