Claude Code 工具系统与权限架构
工具系统和权限架构是 Claude Code 的执行层核心——它们决定了 AI agent 能做什么、不能做什么、以及如何安全地做。
一、工具接口契约
每个工具实现统一的 Tool<Input, Output, P> 接口(约 40 个方法/属性):
interface Tool<Input, Output, P> {
// 身份
name: string
description: string
inputSchema: ZodSchema<Input>
// 行为控制
isConcurrencySafe: boolean // 是否可并发执行
shouldDefer: boolean // 是否延迟加载(需 ToolSearch)
alwaysLoad: boolean // 是否始终加载
interruptBehavior: 'cancel' | 'block'
// 生命周期
checkPermissions(ctx): PermissionResult
call(input, ctx): Promise<Output>
prompt(ctx): string // 工具描述(注入 system prompt)
// 渲染
renderToolUse(input): ReactNode
renderToolResult(output): ReactNode
// ... 更多 render 方法
}
关键设计:buildTool() 工厂函数填充默认实现,开发者只需覆盖关心的方法。
二、工具编排层
runTools() — 并发分区
async function runTools(toolUseBlocks: ToolUseBlock[]) {
// 分区
const concurrent = blocks.filter(b => b.tool.isConcurrencySafe)
const serial = blocks.filter(b => !b.tool.isConcurrencySafe)
// 并发安全组:Promise.all 并行
const concurrentResults = await Promise.all(
concurrent.map(b => runToolUse(b))
)
// 非并发安全组:串行执行
const serialResults = []
for (const block of serial) {
serialResults.push(await runToolUse(block))
}
}
并发安全工具(可并行):FileRead、Glob、Grep、WebFetch、WebSearch、TodoWrite
非并发安全工具(必须串行):BashTool、FileEdit、FileWrite、NotebookEdit
StreamingToolExecutor — 流式预执行
当 API 流式返回多个 tool_use blocks 时:
- 维护
pendingTools队列 - Block 流完整后立即启动执行(不等待整个响应完成)
- 兄弟 abort:一个 Bash 出错 → 取消同轮未启动的兄弟工具
- Progress message 转发给上层
三、工具钩子系统
三种钩子运行器覆盖工具执行的完整生命周期:
PreToolUse
工具执行前触发。钩子可以:
- 返回
{ allow: true }→ 跳过权限检查 - 返回
{ deny: true }→ 拦截执行 - 修改 input
安全约束:resolveHookPermissionDecision() 确保钩子的 allow 不会覆盖全局 deny 规则。即使钩子返回 allow,如果存在显式 deny 规则,deny 仍然生效。
PostToolUse
工具执行成功后触发,可修改输出。
PostToolUseFailure
工具执行失败后触发,可提供恢复建议。
四、权限系统架构
四层纵深防御
┌─────────────────────────────────────────┐
│ Layer 4: System Prompt 层 │
│ 行为规范(概率性遵循) │
├─────────────────────────────────────────┤
│ Layer 3: Permission Rules 层 │
│ 确定性执行(allow/deny 规则) │
├─────────────────────────────────────────┤
│ Layer 2: Tool 层 │
│ checkPermissions() 方法 │
├─────────────────────────────────────────┤
│ Layer 1: 用户确认层 │
│ 交互式 dialog / auto-approve classifier │
└─────────────────────────────────────────┘
权限上下文 — resolve-once 模式
createPermissionContext() 使用 resolve-once 模式:多个判定源竞争同一个 Promise,第一个解决的结果生效,其他被忽略。
三种权限处理器
| 处理器 | 适用场景 | 策略 |
|---|---|---|
| interactiveHandler | 主 agent | 4 源竞赛(UI/Bridge/Channel/Hooks) |
| coordinatorHandler | Coordinator 模式 | 自动化优先,fallback 到交互 |
| swarmWorkerHandler | Swarm worker | 转发给 leader |
interactiveHandler 详解
最复杂的处理器,同时竞赛 4 个判定源:
┌─── 本地 UI(terminal allow/deny 按钮)
│
Promise.race ──────├─── Bridge(Claude.ai / CCR 远程控制)
│
├─── Channel(Telegram/iMessage 等)
│
└─── Hooks + Classifier(自动化规则)
第一个 resolve 的生效,AbortController 取消其他。
coordinatorHandler
自动化优先策略:
1. 先尝试 hooks 判定 → 如果有结果,直接返回
2. 再尝试 classifier 判定 → 如果有结果,直接返回
3. 两者都不做判定 → fallback 到交互式 dialog
swarmWorkerHandler
Worker 将权限请求转发给 leader:
Worker 需要权限
↓
通过 mailbox 发送权限请求
↓
registerPermissionCallback() 注册回调
↓
useSwarmPermissionPoller 每 500ms 轮询
↓
Leader 响应到达 → 回调触发 → 权限判定完成
权限模式
| 模式 | 行为 | 触发方式 |
|---|---|---|
| default | 交互式,每次确认 | 默认 |
| auto | 分类器自动判定 | GrowthBook gate |
| bypassPermissions | 跳过所有检查 | --dangerously-skip-permissions |
| acceptEdits | 自动接受编辑 | 配置 |
| bubble | 冒泡到父终端 | fork 子 agent |
| plan | 需要计划审批 | teammate plan mode |
五、Auto Mode 分类器
Auto mode 是 Claude Code 的”YOLO mode”——通过分类器自动判定工具调用是否安全:
// 分类器规则示例
{
tool: "Bash",
command_pattern: "^(cat|ls|find|grep|rg|head|tail|wc)\\b",
decision: "allow" // 只读命令自动允许
}
{
tool: "FileEdit",
path_pattern: "^/tmp/",
decision: "allow" // /tmp 下的编辑自动允许
}
CLI 子命令:
claude auto-mode defaults— 输出默认分类器规则claude auto-mode config— 输出合并后的有效配置claude auto-mode critique— 用 LLM 评审用户自定义规则的质量
六、ToolSearch — 延迟加载机制
为了节省 token,非常用工具不加载到初始 prompt 中:
初始加载的工具(alwaysLoad=true):
FileRead, FileWrite, FileEdit, Bash, Grep, Glob, AgentTool, ...
延迟加载的工具(shouldDefer=true):
CronCreate, CronDelete, EnterPlanMode, ExitPlanMode,
EnterWorktree, ExitWorktree, Monitor, NotebookEdit,
PushNotification, RemoteTrigger, SendMessage,
TaskCreate/Get/List/Output/Stop/Update,
TeamCreate, TeamDelete, WebFetch, WebSearch, ...
模型需要使用延迟工具时,先调用 ToolSearch:
ToolSearch({ query: "select:TeamCreate,SendMessage" })
↓
工具 schema 加载到当前上下文
↓
模型可以调用 TeamCreate / SendMessage
七、Services 层
API 服务
- 重试逻辑(
withRetry.ts) - Prompt cache 断裂检测(
promptCacheBreakDetection.ts) - 用量追踪(
usage.ts) - 超限额度授予(
overageCreditGrant.ts)
上下文压缩
- 自动压缩(
autoCompact.ts):上下文窗口满时触发 - 微压缩(
microCompact.ts):更细粒度的压缩 - 基于时间的配置(
timeBasedMCConfig.ts) - 分组策略(
grouping.ts):决定哪些消息可以合并
MCP 集成
- 客户端(
client.ts) - 连接管理器(
MCPConnectionManager.tsx) - OAuth 认证(
auth.ts) - In-process 传输(
InProcessTransport.ts)
记忆服务
- SessionMemory:会话记忆
- extractMemories:从对话中提取值得记忆的信息
- teamMemorySync:团队记忆同步 + secret 扫描
八、设计模式总结
1. Fail-closed 安全
所有工具默认需要权限。没有显式 allow 规则的操作一律拒绝。这与传统的 fail-open 设计(默认允许,显式拒绝)相反。
2. 竞赛式权限判定
多个判定源通过 Promise.race 竞争,第一个结果生效。这允许最快的判定源(如本地 hooks)立即响应,而不需要等待所有源。
3. 并发安全标记
工具自声明是否可并发执行(isConcurrencySafe),编排层据此自动分区。这比全局锁或手动管理并发更优雅。
4. 钩子不可覆盖 deny
即使 PreToolUse 钩子返回 allow,全局 deny 规则仍然生效。这确保了安全策略的不可绕过性。
5. 延迟加载节省 token
通过 ToolSearch 按需加载工具 schema,初始 prompt 只包含常用工具。这在 43+ 工具的规模下显著节省 token 成本。
参考资源
| 资源 | 链接 |
|---|---|
| 工具系统逆向 | github.com/zong0728/claude-code-internals |
| 权限系统分析 | deepwiki.com/ai-ml-architect/claude-code/3-permission-system |
| Hook 系统文档 | code.claude.com/docs/en/hooks |
| Auto Mode 配置 | code.claude.com/docs/en/auto-mode |
| 学术论文 | arXiv:2604.14228 Section 4.2 |