Claude Code 的工具系统是整个 runtime 的执行底座。模型能做的事情,最终都要落到 Tool Contract、权限系统和编排器上。
src/tools.ts工具注册表src/Tool.tsTool 类型与公共 contractsrc/services/tools/toolOrchestration.ts多个 tool_use 的批次编排src/services/tools/toolExecution.ts单个工具执行、权限、hook、标准化结果
flowchart TB
Model[模型输出 tool_use]
Registry[getAllBaseTools]
Orch[toolOrchestration]
Exec[toolExecution]
Perm[permission system]
Hook[pre/post hooks]
Result[tool_result]
Model --> Orch
Registry --> Orch
Orch --> Exec
Exec --> Perm
Exec --> Hook
Exec --> Result
src/tools.ts 的 getAllBaseTools() 是全局工具总表。
它做的不只是“列一个数组”,还包含:
- feature flag 裁剪
- 环境差异分发
- ant-only / external build 差异
- team/worktree/lsp/tool search 等能力按条件注入
所以工具可见性是动态生成的,而不是固定常量。
在 src/Tool.ts 里,每个工具都需要至少提供:
- 名称
- schema
- prompt/description
- call 实现
- 可选并发安全信息
- 权限上下文说明
这让不同工具能共享一套运行时,而不是每个工具自己处理校验、日志和结果封装。
src/services/tools/toolOrchestration.ts 的一个核心设计是:不是看到多个 tool_use 就傻跑,而是先按并发安全性分批。
逻辑是:
- 连续只读或 concurrency-safe 工具可以并发
- 非 concurrency-safe 工具单独串行
- 运行中维护
inProgressToolUseIDs
sequenceDiagram
participant Q as query.ts
participant O as toolOrchestration
participant E as toolExecution
participant P as permission
participant T as tool impl
Q->>O: runTools(tool_use blocks)
O->>O: partitionToolCalls()
O->>E: runToolUse(...)
E->>P: hasPermissionsToUseTool(...)
P-->>E: allow / deny / ask
E->>T: tool.call(...)
T-->>E: raw result
E-->>Q: standardized tool_result
toolExecution.ts 不是简单的 tool.call() 包装,它还负责:
- schema parse
- 权限判定
- pre-tool hooks
- 进度消息
- telemetry
- 错误分类
- result block 标准化
- post-tool hooks
因此工具实现文件本身可以尽量只关心业务动作。
Claude Code 的权限系统是横切层,不依赖某个工具自己写 prompt。
表现为:
- 执行前统一判定
- deny / ask / allow 统一返回结构
- 决策来源可追踪
- worker/subagent/coordinator 模式下行为不同
看它如何按 feature 和环境拼装工具池。
重点看:
partitionToolCalls()runTools()runToolsConcurrently()runToolsSerially()
重点看:
- 错误分类
- 权限来源映射
- hook 调用链
- registry、orchestration、execution 三层分离
- 工具并发是运行时决策,不是模型自己决定
- 权限、hook、telemetry 全部集中处理
- 工具可被主 agent、subagent、worker、MCP bridge 复用
建议从 src/tools.ts -> src/Tool.ts -> toolOrchestration.ts -> toolExecution.ts 这条线读。