Skip to content

15. Claude Code 工具系统

← 返回目录 | 上一章:Claude Code Agent 与编排系统 | 下一章:Claude Code 状态管理与 UI →

上一章我们看到了 Agent 如何编排查询循环和任务调度。本章深入工具层——那些 Agent 实际调用的"手脚"。Claude Code 拥有 50+ 内置工具和无限扩展的 MCP 工具,通过统一的 buildTool 工厂函数构建,配合多层权限模型和流式并行执行器,形成了当前开源 AI 编码工具中最复杂的工具系统之一。


15.1 Tool 类型系统

Claude Code 的工具定义在 src/Tool.ts(792 行)中,以一个三参数泛型接口为核心:

typescript
type Tool<
  Input extends AnyObject = AnyObject,   // Zod schema
  Output = unknown,                       // 返回值类型
  P extends ToolProgressData = ToolProgressData  // 进度事件
> = {
  name: string
  aliases?: string[]          // 重命名后的向后兼容
  searchHint?: string         // ToolSearch 关键词匹配用
  inputSchema: Input          // Zod v4 schema
  outputSchema?: z.ZodType    // 可选的输出 schema
  maxResultSizeChars: number  // 超限后持久化到磁盘
  strict?: boolean            // 严格模式(API 侧更严格遵循 schema)

  // 核心方法
  call(args, context, canUseTool, parentMessage, onProgress?): Promise<ToolResult<Output>>
  description(input, options): Promise<string>
  prompt(options): Promise<string>

  // 行为声明(用于编排决策)
  isConcurrencySafe(input): boolean    // 能否并行
  isReadOnly(input): boolean           // 是否只读
  isDestructive?(input): boolean       // 是否不可逆
  isEnabled(): boolean                 // 当前环境是否可用
  interruptBehavior?(): 'cancel' | 'block'  // 用户中断时的行为

  // 权限
  checkPermissions(input, context): Promise<PermissionResult>
  validateInput?(input, context): Promise<ValidationResult>
  preparePermissionMatcher?(input): Promise<(pattern: string) => boolean>

  // UI 渲染(React/Ink 组件)
  renderToolUseMessage(input, options): React.ReactNode
  renderToolResultMessage?(content, progress, options): React.ReactNode
  renderToolUseProgressMessage?(progress, options): React.ReactNode
  renderToolUseRejectedMessage?(input, options): React.ReactNode
  renderToolUseErrorMessage?(result, options): React.ReactNode
  renderGroupedToolUse?(toolUses, options): React.ReactNode | null

  // 辅助
  userFacingName(input): string
  getActivityDescription?(input): string | null   // Spinner 文案
  getToolUseSummary?(input): string | null         // 紧凑视图摘要
  toAutoClassifierInput(input): unknown            // 安全分类器输入
  mapToolResultToToolResultBlockParam(content, id): ToolResultBlockParam
  // ...更多
}

这个接口的设计哲学值得注意:

  1. 行为元数据与实现分离isConcurrencySafeisReadOnlyisDestructive 等方法不影响工具自身逻辑,但驱动编排层的调度决策(分批、串行、权限升级)
  2. 输入依赖的行为声明:这些方法接收 input 参数——同一个 BashTool,执行 ls 时是 concurrency-safe + read-only,执行 rm -rf 时则是 destructive + serial
  3. 渲染与逻辑共存:每个工具自带完整的 UI 渲染能力(7 个 render 方法),工具既是功能单元也是 UI 组件

对比 Opencode:Opencode 的工具定义(tool/tool.go)相对精简——NameDescriptionSchemaExecute 四大字段,没有行为元数据也没有渲染逻辑(UI 由 TUI 层统一处理)。这反映了两者的架构取向:Opencode 追求简洁解耦,Claude Code 追求工具层的自描述完备性。


15.2 buildTool 工厂模式

直接实现完整的 Tool 接口需要填充 30+ 个字段,大多数工具的许多字段值是相同的。buildTool 工厂函数解决了这个问题:

typescript
// 7 个可默认化的方法
type DefaultableToolKeys =
  | 'isEnabled'          // 默认 () => true
  | 'isConcurrencySafe'  // 默认 () => false(保守:假设不安全)
  | 'isReadOnly'         // 默认 () => false(保守:假设有写操作)
  | 'isDestructive'      // 默认 () => false
  | 'checkPermissions'   // 默认 allow(交给通用权限系统)
  | 'toAutoClassifierInput'  // 默认 ''(跳过分类器)
  | 'userFacingName'     // 默认使用 tool.name

function buildTool<D extends AnyToolDef>(def: D): BuiltTool<D> {
  return { ...TOOL_DEFAULTS, userFacingName: () => def.name, ...def }
}

默认值的设计遵循**失败安全(fail-closed)**原则:

  • 不确定是否并发安全?默认串行执行
  • 不确定是否只读?默认当作有写操作
  • 没有特殊权限逻辑?默认交给通用权限系统

实际工具定义因此非常简洁,以 FileEditTool 为例:

typescript
export const FileEditTool = buildTool({
  name: FILE_EDIT_TOOL_NAME,
  searchHint: 'modify file contents in place',
  maxResultSizeChars: 100_000,
  strict: true,
  
  // 核心逻辑
  async call(input, context, canUseTool, parentMessage) { /* 625 行实现 */ },
  async description() { return 'A tool for editing files' },
  async prompt() { return getEditToolDescription() },
  
  // 行为声明(覆盖默认值)
  // isConcurrencySafe: 未覆盖 → false(文件编辑不能并行)
  // isReadOnly: 未覆盖 → false(确实有写操作)
  
  // 权限(覆盖默认的 allow)
  async checkPermissions(input, context) {
    return checkWritePermissionForTool(FileEditTool, input, ...)
  },

  // UI 渲染
  renderToolUseMessage,
  renderToolResultMessage,
  renderToolUseRejectedMessage,
  renderToolUseErrorMessage,
  
  // 辅助
  toAutoClassifierInput: (input) => `${input.file_path}: ${input.new_string}`,
  getPath: (input) => input.file_path,
  userFacingName,
  getToolUseSummary,
  // ...
})

对比:Opencode 的工具注册也是声明式的(tool.Define + 结构体),但没有工厂函数和默认值机制——每个工具独立声明所有字段。OmO 的 Skill 系统则更高层:它定义的是"Sisyphus 应该如何使用工具组合",而非工具本身。三者形成了一个抽象层级:Claude Code(工厂+默认值)→ Opencode(直接声明)→ OmO(行为编排指令)。


15.3 工具全景:分类与清单

tools.ts 中的 getAllBaseTools() 是所有内置工具的唯一注册入口。根据功能可分为以下几大类:

文件系统工具

工具功能并发安全特殊说明
FileReadTool读取文件内容maxResultSizeChars: Infinity(永不持久化,避免循环读取)
FileEditTool原地编辑文件625 行实现,含 diff 预览、行尾保持、引号风格保持
FileWriteTool创建/覆盖文件自动创建中间目录
NotebookEditToolJupyter Notebook 编辑专门处理 .ipynb 的 cell 级操作
GlobTool文件名模式搜索当内嵌搜索工具可用时自动隐藏
GrepTool文件内容搜索同上,Ant 内部构建嵌入了 ugrep

Agent 与任务工具

工具功能说明
AgentTool创建子 AgentCoordinator 模式的核心,详见第 14 章
TaskOutputTool获取后台任务输出轮询子 Agent 结果
TaskStopTool终止任务Worker 的 graceful shutdown
TaskCreate/Get/Update/ListToolTodo v2 任务管理feature flag 控制,替代旧版 TodoWriteTool
SkillTool加载/执行 Skill.claude/skills/ 加载专业化指令
SendMessageTool发送消息给 peer跨进程通信(UDS Inbox)
TeamCreate/DeleteTool创建/删除团队Agent Swarms 功能

搜索与网络工具

工具功能说明
WebSearchTool网络搜索返回结构化搜索结果
WebFetchTool抓取网页内容URL → 文本
WebBrowserTool浏览器自动化feature flag 控制
ToolSearchTool搜索延迟加载的工具详见 15.5 节

模式控制工具

工具功能说明
EnterPlanModeTool进入计划模式只规划不执行
ExitPlanModeV2Tool退出计划模式恢复执行权限
EnterWorktreeTool进入 Git Worktree在独立工作树中操作
ExitWorktreeTool退出 Git Worktree合并回主分支
BriefTool切换简洁模式AI 主动调整输出详细度
SnipTool手动裁剪上下文配合 History Snip 压缩策略

Shell 与执行工具

工具功能说明
BashTool执行 Shell 命令18 个文件的子目录,含安全分析、sed 校验、沙箱判断
REPLTool交互式 REPLAnt 内部专用,在 VM 中包装其他原子工具
PowerShellToolWindows PowerShell条件加载
LSPToolLSP 交互环境变量控制启用

特殊工具

工具功能说明
AskUserQuestionTool向用户提问交互式确认和澄清
ConfigTool修改配置Ant 内部专用
CronCreate/Delete/ListTool定时任务管理Agent Triggers 功能
RemoteTriggerTool远程触发器远程事件响应
MonitorTool监控工具后台监控任务
SyntheticOutputTool合成输出内部使用,不直接暴露给模型

条件加载机制

工具清单中大量使用 feature() 函数(来自 bun:bundle)和 process.env 进行条件加载:

typescript
// bun:bundle 编译时死代码消除
const REPLTool = process.env.USER_TYPE === 'ant'
  ? require('./tools/REPLTool/REPLTool.js').REPLTool : null

// feature flag(构建时确定)
const MonitorTool = feature('MONITOR_TOOL')
  ? require('./tools/MonitorTool/MonitorTool.js').MonitorTool : null

// 运行时条件
...(isWorktreeModeEnabled() ? [EnterWorktreeTool, ExitWorktreeTool] : [])

这意味着外部用户看到的工具集与 Anthropic 内部(USER_TYPE=ant)的工具集有显著差异。REPLTool、ConfigTool、TungstenTool 等仅在内部构建中可用。

对比 Opencode:Opencode 的工具集相对精简(~15 个),全部始终可用,没有条件加载。这降低了复杂度,但也限制了针对不同场景的工具优化空间。


15.4 权限模型

Claude Code 的权限系统是三个项目中最复杂的,涉及多个决策层次和多种决策来源。

权限模式(PermissionMode)

系统定义了 7 种权限模式:

模式行为适用场景
default破坏性操作需确认默认交互模式
acceptEdits自动接受文件编辑,其他仍需确认信任编辑但谨慎执行
plan只读,禁止一切写操作规划阶段
dontAsk不确认,不允许的直接拒绝非交互式/CI 场景
bypassPermissions跳过所有权限检查完全信任模式
autoML 分类器自动判断(内部)智能审批
bubble向上冒泡到父级决策子 Agent 场景

权限决策流程

每次工具调用经过以下决策管线:

工具调用请求

① validateInput() — 工具自身的输入校验
    ↓(通过)
② 全局 deny 规则匹配 — 立即拒绝匹配的工具
    ↓(未匹配)
③ tool.checkPermissions() — 工具特定的权限逻辑
    ↓(返回 allow / ask / deny / passthrough)
④ 通用权限系统 — 基于模式和规则的最终决策
    ↓(如果 ask)
⑤ 用户确认 / ML 分类器自动审批

执行 tool.call()

规则系统

权限规则来自多个层级,按优先级从高到低:

typescript
type PermissionRuleSource =
  | 'policySettings'    // 组织策略(最高优先级)
  | 'cliArg'            // 命令行参数
  | 'localSettings'     // 本地设置
  | 'projectSettings'   // 项目设置(.claude/settings.json)
  | 'userSettings'      // 用户全局设置
  | 'session'           // 会话级临时规则
  | 'command'           // 斜杠命令添加
  | 'flagSettings'      // feature flag 设置

每条规则指定工具名和可选的内容匹配模式:

typescript
type PermissionRuleValue = {
  toolName: string       // 如 "Bash", "mcp__server"
  ruleContent?: string   // 如 "git *", "/tmp/**"
}

这使得精细控制成为可能:允许 Bash(git *) 但拒绝 Bash(rm *),允许编辑 src/ 下的文件但拒绝编辑 .env

auto 模式与 ML 分类器

当启用 auto 模式时,权限决策还会经过一个 ML 分类器(toAutoClassifierInput 方法就是为此设计的)。分类器评估工具调用的安全性,可以在用户看到确认对话框之前自动审批安全操作:

typescript
// BashTool 的分类器输入
toAutoClassifierInput: (input) => input.command  // "git status"

// FileEditTool 的分类器输入
toAutoClassifierInput: (input) => `${input.file_path}: ${input.new_string}`

权限决策的溯源

每个决策都携带 PermissionDecisionReason,记录决策来源:

  • rule — 哪条规则匹配了
  • mode — 当前模式的默认行为
  • hook — 哪个 Hook 做出了决策
  • asyncAgent — 后台 Agent 的自动拒绝
  • subcommandResults — BashTool 的子命令级权限聚合

这种溯源机制对调试和审计至关重要——当用户问"为什么这个操作被拒绝了"时,系统能给出精确答案。

对比 Opencode:Opencode 的权限模型简洁得多——dangerous 标记的工具需要确认,其余自动放行。没有多层规则、没有分类器、没有溯源。OmO 通过 Skill 的 MUST NOT DO 指令增加了一层软约束,但这是 prompt 层面的约束而非代码级强制。Claude Code 在这方面的投入,反映了其作为企业级产品对安全合规的严格要求。


15.5 ToolSearch 与延迟加载

当工具数量庞大时(50+ 内置 + MCP 工具),每次请求都发送所有工具定义会消耗大量 token。Claude Code 引入了 ToolSearch 机制解决这个问题:

初始请求时:
  核心工具(~20 个)  → 完整 schema 发送给模型
  可延迟工具(~30 个)→ 仅发送 name + searchHint(defer_loading: true)
  MCP 工具            → 根据 alwaysLoad 标记决定

模型需要某个延迟工具时:
  调用 ToolSearchTool(query="select:NotebookEdit")
  → 返回完整 schema
  → 后续请求中该工具变为完整可用

ToolSearch 支持两种查询模式:

  • 直接选择select:<tool_name> — 精确匹配工具名
  • 关键词搜索:自然语言描述 → 基于 searchHint 和工具名的模糊匹配

工具的 shouldDefer 属性决定是否延迟加载,alwaysLoad 属性可强制始终加载(MCP 工具可通过 _meta['anthropic/alwaysLoad'] 设置)。

这个设计平衡了能力覆盖token 效率:模型知道所有工具的存在(通过名称),但只为实际需要的工具支付完整 schema 的 token 开销。


15.6 工具池装配与 MCP 集成

assembleToolPool:单一装配入口

内置工具和 MCP 工具通过 assembleToolPool 函数统一装配:

typescript
function assembleToolPool(permissionContext, mcpTools): Tools {
  const builtInTools = getTools(permissionContext)
  const allowedMcpTools = filterToolsByDenyRules(mcpTools)
  // 按名称排序,内置工具作为前缀(prompt cache 稳定性)
  // uniqBy name 去重(内置工具优先)
  return uniqBy(
    [...builtInTools].sort(byName).concat(allowedMcpTools.sort(byName)),
    'name',
  )
}

排序策略值得注意:内置工具始终排在 MCP 工具前面形成连续前缀,这确保了 Anthropic API 的 prompt cache 稳定性——当 MCP 工具增减时,不会打乱内置工具的位置导致缓存失效。

MCP 工具的桥接

MCP 工具通过 MCPTool 模板创建,在 mcpClient.ts 中动态覆盖其字段:

typescript
export const MCPTool = buildTool({
  isMcp: true,
  name: 'mcp',          // 实际被覆盖为 mcp__serverName__toolName
  async call() { ... }, // 实际被覆盖为 MCP 协议调用
  async checkPermissions() {
    return { behavior: 'passthrough' }  // 交给通用权限系统
  },
})

MCP 工具名遵循 mcp__<serverName>__<toolName> 的命名约定,权限规则可以按服务器前缀批量控制(如拒绝整个不受信任的 MCP 服务器的所有工具)。

模式感知的工具过滤

getTools() 根据当前运行模式进行额外过滤:

  • Simple 模式--bare):仅保留 Bash + FileRead + FileEdit
  • REPL 模式:隐藏被 REPL 包装的原子工具(由 REPL VM 内部使用)
  • Coordinator 模式:Worker 看到受限工具集(详见 14.4 节)
  • 所有模式都先经过 filterToolsByDenyRules 全局过滤

对比 Opencode + OmO:Opencode 的 MCP 支持同样实现了工具注册和调用的统一接口,但没有 prompt cache 优化排序,也没有模式感知过滤。OmO 通过 skill_mcp 调用封装了 MCP 交互,增加了 Skill 级别的使用约束。三者在 MCP 集成深度上的差异,本质上反映了各自的规模——Claude Code 需要管理几十乃至上百个工具,排序和过滤是性能刚需;Opencode 的工具数量级还不需要这些优化。


15.7 BashTool:最复杂的单体工具

BashTool 是 Claude Code 中工程投入最大的单个工具,其子目录包含 18 个文件,覆盖安全分析、权限验证、沙箱隔离等多个维度:

tools/BashTool/
├── BashTool.tsx              # 主入口与 call() 实现
├── bashPermissions.ts        # Shell 命令的权限判断
├── bashSecurity.ts           # 安全性分析(危险命令检测)
├── bashCommandHelpers.ts     # 命令解析辅助
├── commandSemantics.ts       # 命令语义分析(只读?破坏性?)
├── readOnlyValidation.ts     # 只读命令白名单
├── sedValidation.ts          # sed 命令的特殊校验
├── sedEditParser.ts          # sed 表达式解析
├── destructiveCommandWarning.ts  # 破坏性命令警告
├── modeValidation.ts         # 模式相关校验
├── pathValidation.ts         # 路径安全校验
├── shouldUseSandbox.ts       # 沙箱决策
├── prompt.ts                 # 系统 prompt 生成
├── commentLabel.ts           # 注释标签
├── toolName.ts               # 工具名常量
├── utils.ts                  # 通用工具
├── UI.tsx                    # 渲染组件
└── BashToolResultMessage.tsx # 结果渲染

输入依赖的行为判断

BashTool 的 isConcurrencySafeisReadOnly 方法根据实际命令内容动态判断:

  • lscatfindgrep → concurrency-safe + read-only
  • git statusgit diff → concurrency-safe + read-only
  • npm installpip install → serial + not read-only
  • rmchmodmv → serial + destructive

这种细粒度的语义分析使编排层能够安全地并行执行多个 ls 命令,同时确保 rm 命令串行执行。

Bash 错误的级联取消

在 StreamingToolExecutor 中,BashTool 享有特殊的错误处理逻辑:只有 Bash 错误会触发兄弟工具的级联取消。原因是 Shell 命令常有隐式依赖链(mkdir 失败后续命令无意义),而 FileRead、WebFetch 等工具相互独立,一个失败不应影响其他。

typescript
if (tool.block.name === BASH_TOOL_NAME) {
  this.hasErrored = true
  this.siblingAbortController.abort('sibling_error')
}

对比 Opencode:Opencode 的 Bash 工具实现相对简单——执行命令、捕获输出、超时控制。没有命令语义分析、没有沙箱决策、没有级联取消。这不是缺陷而是取舍:Opencode 信任用户(需手动确认危险命令),Claude Code 则尝试在代码级别理解命令意图。


15.8 小结

Claude Code 的工具系统呈现出鲜明的工业级特征:

  1. 自描述完备:每个工具携带行为元数据(并发安全性、只读性、破坏性)、权限逻辑、UI 渲染能力,编排层无需了解工具内部实现
  2. 工厂 + 默认值buildTool 以 fail-closed 原则提供安全默认值,60+ 个工具定义因此保持简洁
  3. 多层权限:7 种模式 × 8 种规则来源 × ML 分类器 × 决策溯源,满足企业级安全合规需求
  4. 延迟加载:ToolSearch 机制在不牺牲能力覆盖的前提下优化 token 开销
  5. 缓存友好:工具池装配时的排序策略确保 prompt cache 稳定性
  6. 单体深度:BashTool 的 18 文件子目录展示了对单个工具的极致工程投入

如果将三个系统的工具架构画成光谱:

简洁 ←————————————————————————→ 复杂
Opencode          OmO 增强           Claude Code
(~15 工具,         (Skill 编排层,      (50+ 工具,
 扁平注册,          行为约束,            buildTool 工厂,
 二元权限)          多级调度)            多层权限+分类器)

下一章我们将转向 Claude Code 的状态管理与 UI 系统,看看 90+ 字段的全局状态对象和 144+ React 组件是如何协同工作的。


← 返回目录 | 上一章:Claude Code Agent 与编排系统 | 下一章:Claude Code 状态管理与 UI →