乐于分享
好东西不私藏

Claude Code源码系列:4、1-关键功能模块-工具系统

Claude Code源码系列:4、1-关键功能模块-工具系统

概述 (Overview)

Claude Code 的工具系统是整个 AI Agent 与外部世界交互的核心桥梁。它不仅提供了类型安全的工具定义机制,还实现了完整的权限控制体系、并发执行策略、流式进度报告以及 MCP (Model Context Protocol) 工具集成。本文将从源码级别深入剖析这一系统的设计哲学、核心架构与关键实现细节。

设计哲学

工具系统遵循以下核心设计原则:

  1. 类型安全优先:使用 TypeScript 泛型 + Zod Schema 实现从定义到执行的全程类型校验
  2. 默认安全策略(Fail-closed)isConcurrencySafe 默认 falseisReadOnly 默认 false,确保新工具必须显式声明安全属性
  3. 延迟加载优化:通过 lazySchema 和 shouldDefer 机制减少初始提示词成本
  4. 权限分层治理validateInput → checkPermissions → canUseTool 三层检查确保细粒度控制

系统全景架构图

+-----------------------------------------------------------------------+|                           Claude Code 工具系统架构                                  |+-----------------------------------------------------------------------+|                                                                                   ||  +------------------+     +------------------+     +------------------+            ||  |   API Layer      |     |  Tool Registry   |     |  Permission      |            ||  | (Anthropic SDK)  |---->|  (tools.ts)      |---->|  Control Layer   |            ||  +------------------+     +------------------+     +------------------+            ||          |                        |                        |                      ||          v                        v                        v                      ||  +------------------+     +------------------+     +------------------+            ||  | StreamingTool    |     |  Tool Pool       |     |  Permission      |            ||  | Executor         |<----|  Assembly        |<----|  Context         |            ||  +------------------+     +------------------+     +------------------+            ||          |                        |                        |                      ||          v                        v                        v                      ||  +------------------+     +------------------+     +------------------+            ||  |  Tool Execution  |     |  Built-in Tools  |     |  Rule Matching   |            ||  |  (toolExecution) |---->|  (FileReadTool,  |---->|  (permissions.ts)|            ||  +------------------+     |   BashTool, ...) |     +------------------+            ||          |               +------------------+              |                      ||          v                        |                        v                      ||  +------------------+             |               +------------------+            ||  |  Progress/Result |             |               |  Hook System     |            ||  |  Streaming       |<------------+---------------|  (PreToolUse,    |            ||  +------------------+                             |   PostToolUse)   |            ||                                                   +------------------+            ||                                                                                   ||  +------------------+     +------------------+     +------------------+            ||  |  MCP Tool        |     |  Agent Tool      |     |  Skill Tool      |            ||  |  Integration     |     |  Subagent        |     |  Execution       |            ||  +------------------+     +------------------+     +------------------+            ||                                                                                   |+-----------------------------------------------------------------------+

1. 核心基类与类型设计 (Core Types & Interfaces)

1.1 Tool 泛型接口 – 三层类型架构

文件src/Tool.ts:362-695

// src/Tool.ts:362-366export type Tool<  Input extends AnyObject = AnyObject,   // Zod Schema 类型约束  Output = unknown,                      // 输出类型(默认 unknown,灵活性高)  P extends ToolProgressData = ToolProgressData, // 进度数据类型> = {// 基本信息字段  readonly name: string// 工具唯一标识名  aliases?: string[]                     // 别名列表(向后兼容用)  searchHint?: string// ToolSearch 关键词匹配提示(3-10词)// Schema 定义  readonly inputSchema: Input            // Zod 输入 Schema  readonly inputJSONSchema?: ToolInputJSONSchema  // MCP 工具可直接使用 JSON Schema  outputSchema?: z.ZodType<unknown>      // 输出 Schema(可选)// 核心执行方法  call(    args: z.infer<Input>,    context: ToolUseContext,    canUseTool: CanUseToolFn,    parentMessage: AssistantMessage,    onProgress?: ToolCallProgress<P>,  ): Promise<ToolResult<Output>>// 描述生成方法  description(    input: z.infer<Input>,    options: {      isNonInteractiveSession: boolean      toolPermissionContext: ToolPermissionContext      tools: Tools    },  ): Promise<string>// 工具特性判断方法  isConcurrencySafe(input: z.infer<Input>): boolean// 是否可并发执行  isEnabled(): boolean// 是否启用  isReadOnly(input: z.infer<Input>): boolean// 是否只读操作  isDestructive?(input: z.infer<Input>): boolean// 是否破坏性操作// 权限相关方法  checkPermissions(    input: z.infer<Input>,    context: ToolUseContext,  ): Promise<PermissionResult>  validateInput?(    input: z.infer<Input>,    context: ToolUseContext,  ): Promise<ValidationResult>  preparePermissionMatcher?(input: z.infer<Input>): Promise<(pattern: string) =>boolean>// UI 渲染方法  renderToolUseMessage(input: Partial<z.infer<Input>>, options): React.ReactNode  renderToolResultMessage?(content: Output, progressMessages, options): React.ReactNode  renderToolUseProgressMessage?(progressMessages, options): React.ReactNode  renderToolUseRejectedMessage?(input, options): React.ReactNode  renderToolUseErrorMessage?(result, options): React.ReactNode  renderGroupedToolUse?(toolUses, options): React.ReactNode | null// 其他辅助方法  prompt(options): Promise<string>  userFacingName(input: Partial<z.infer<Input>>): string  getToolUseSummary?(input: Partial<z.infer<Input>>): string | null  getActivityDescription?(input: Partial<z.infer<Input>>): string | null  toAutoClassifierInput(input: z.infer<Input>): unknown  mapToolResultToToolResultBlockParam(content: Output, toolUseID: string): ToolResultBlockParam// MCP 相关属性  isMcp?: boolean// 是否 MCP 工具  isLsp?: boolean// 是否 LSP 工具  mcpInfo?: { serverName: string; toolName: string }  // MCP 元信息// 延迟加载属性  readonly shouldDefer?: boolean// 是否延迟加载(需 ToolSearch)  readonly alwaysLoad?: boolean// 是否始终加载(即使启用 ToolSearch)// 结果限制  maxResultSizeChars: number// 结果持久化阈值  readonly strict?: boolean// 是否启用 API 严格模式}

泛型设计深度解析

泛型参数
类型约束
设计意图
Input extends AnyObject
确保输入必须是 Zod 对象 Schema,提供类型校验
Output = unknown
默认 unknown 允许工具自定义返回类型,灵活性最大化
P extends ToolProgressData
进度类型约束,不同工具可定义专属进度结构

1.2 AnyObject 类型约束

文件src/Tool.ts:343

// src/Tool.ts:343exporttype AnyObject = z.ZodType<{ [key: string]: unknown }>

这是一个关键的类型约束设计:它确保所有工具的输入必须是 Zod 对象类型 Schema,而非原始类型或数组。这保证了:

  • 工具输入始终是可验证的对象结构
  • 权限规则可以基于字段级别进行匹配
  • Hook 系统可以访问结构化的输入参数

1.3 ToolResult 输出类型

文件src/Tool.ts:321-336

// src/Tool.ts:321-336export type ToolResult<T> = {  data: T                                // 泛型输出数据// 工具执行过程中产生的消息(追加到对话历史)  newMessages?: (    | UserMessage    | AssistantMessage    | AttachmentMessage    | SystemMessage  )[]// 上下文修改器(仅对非并发安全工具有效)// 用于修改后续工具执行的上下文(如模型切换、权限调整)  contextModifier?: (context: ToolUseContext) => ToolUseContext// MCP 协议元数据传递  mcpMeta?: {    _meta?: Record<string, unknown>    structuredContent?: Record<string, unknown>  }}

关键设计点

  1. data: T – 泛型输出数据,由 outputSchema 定义类型
  2. newMessages – 支持工具产生对话消息(如 SkillTool 扩展提示词)
  3. contextModifier – 仅对非并发安全工具有效,因为并发工具无法保证修改顺序
  4. mcpMeta – MCP 协议的元数据透传通道

1.4 ToolProgress 进度类型体系

文件src/Tool.ts:307-340

// src/Tool.ts:307-310exporttype ToolProgress<P extends ToolProgressData> = {  toolUseID: string// 工具调用唯一 ID  data: P                                 // 进度数据(泛型)}// src/Tool.ts:338-340exporttype ToolCallProgress<P extends ToolProgressData = ToolProgressData> = (  progress: ToolProgress<P>,) => void

集中定义的进度类型 (src/types/tools.ts):

// 进度类型集中定义(从 src/types/tools.ts 导入)export type BashProgress = {type'bash_progress'  pid: number  output: string  isInteractiveCheck?: boolean}export type AgentToolProgress = {type'agent_progress'  message: Message  agentId: string}export type WebSearchProgress = {type'web_search_progress'  status: 'searching' | 'processing' | 'complete'  resultsCount?: number}

1.5 Tools 集合类型

文件src/Tool.ts:701

// src/Tool.ts:701exporttype Tools = readonly Tool[]

使用 readonly 确保工具集合不可变,防止意外修改。这是工具池传递过程中的重要安全保证。

1.6 工具发现与名称匹配

文件src/Tool.ts:348-360

// src/Tool.ts:348-353/** * 检查工具是否匹配给定名称(主名称或别名) */exportfunctiontoolMatchesName(  tool: { name: string; aliases?: string[] },  name: string,): boolean{return tool.name === name || (tool.aliases?.includes(name) ?? false)}// src/Tool.ts:358-360/** * 从工具列表中按名称或别名查找工具 */exportfunctionfindToolByName(tools: Tools, name: string): Tool | undefined{return tools.find(t => toolMatchesName(t, name))}

2. 核心机制与工厂模式 (Core Mechanisms & Initialization)

2.1 buildTool 工厂函数 – 类型安全的部分实现

文件src/Tool.ts:783-792

// src/Tool.ts:783-792/** * 从部分定义构建完整的 Tool,填充安全默认值 * * 默认值(关键安全属性采用 fail-closed 策略): * - `isEnabled` → `true` * - `isConcurrencySafe` → `false` (假设不安全) * - `isReadOnly` → `false` (假设写入操作) * - `isDestructive` → `false` * - `checkPermissions` → `{ behavior: 'allow', updatedInput }` (透传到通用权限系统) * - `toAutoClassifierInput` → `''` (跳过分类器) * - `userFacingName` → `name` */exportfunctionbuildTool<DextendsAnyToolDef>(def: D): BuiltTool<D{// 运行时展开:默认值 + 用户定义return {    ...TOOL_DEFAULTS,    userFacingName: () => def.name,    ...def,  } as BuiltTool<D>}

2.2 TOOL_DEFAULTS 默认值配置 – Fail-closed 安全策略

文件src/Tool.ts:757-769

// src/Tool.ts:757-769const TOOL_DEFAULTS = {// 工具启用状态:默认启用  isEnabled: () => true,// 【关键安全】并发安全:默认 false// 新工具必须显式声明为 true 才能并发执行  isConcurrencySafe: (_input?: unknown) => false,// 【关键安全】只读属性:默认 false// 新工具必须显式声明为 true 才会被视为读操作  isReadOnly: (_input?: unknown) => false,// 破坏性操作:默认 false  isDestructive: (_input?: unknown) => false,// 权限检查:默认允许,透传到通用权限系统  checkPermissions: (    input: { [key: string]: unknown },    _ctx?: ToolUseContext,  ): Promise<PermissionResult> =>Promise.resolve({ behavior: 'allow', updatedInput: input }),// 分类器输入:默认空字符串(跳过安全分类器)  toAutoClassifierInput: (_input?: unknown) => '',// 用户可见名称:默认空,buildTool 会覆盖为工具名  userFacingName: (_input?: unknown) => '',}

Fail-closed 策略深度解读

属性
默认值
安全含义
isConcurrencySafe false
新工具默认需要独占执行,避免并发冲突
isReadOnly false
新工具默认视为写操作,触发更严格的权限检查
isDestructive false
新工具默认不触发破坏性操作警告

2.3 ToolDef 类型 – 可选实现的设计

文件src/Tool.ts:707-726

// src/Tool.ts:707-714/** * 可由 buildTool 提供默认值的方法键 * ToolDef 可省略这些方法,buildTool 会自动填充 */type DefaultableToolKeys =  | 'isEnabled'  | 'isConcurrencySafe'  | 'isReadOnly'  | 'isDestructive'  | 'checkPermissions'  | 'toAutoClassifierInput'  | 'userFacingName'// src/Tool.ts:721-726/** * buildTool 接受的工具定义类型 * 与 Tool 形状相同,但默认方法可选 */export type ToolDef<  Input extends AnyObject = AnyObject,  Output = unknown,  P extends ToolProgressData = ToolProgressData,> = Omit<Tool<Input, Output, P>, DefaultableToolKeys> &  Partial<Pick<Tool<Input, Output, P>, DefaultableToolKeys>>

2.4 BuiltTool 类型推导 – 类型级展开镜像

文件src/Tool.ts:735-741

// src/Tool.ts:735-741/** * 类型级展开,镜像运行时 `{...TOOL_DEFAULTS, ...def}` * 对于每个可默认键: * - 如果 D 提供了(required),D 的类型优先 * - 如果 D 略了或 optional,默认类型填充 */type BuiltTool<D> = Omit<D, DefaultableToolKeys> & {  [K in DefaultableToolKeys]-?: K extends keyof D    ? undefinedextends D[K]      ? ToolDefaults[K]              // D 有但 optional → 用默认      : D[K]                         // D 有且 required → 用 D 的    : ToolDefaults[K]                // D 没有 → 用默认}

2.5 lazySchema 延迟初始化 – 避免模块加载开销

文件src/utils/lazySchema.ts

// src/utils/lazySchema.ts (全文)/** * 延迟 Schema 构造工厂 * 避免 Zod Schema 在模块初始化时立即构造的性能开销 * * 所有工具的 inputSchema/outputSchema 都使用此工厂 */exportfunctionlazySchema<T>(factory: () => T): () => T{let cached: T | undefinedreturn() => (cached ??= factory())}

使用示例 (src/tools/FileReadTool/FileReadTool.ts:227-243):

// src/tools/FileReadTool/FileReadTool.ts:227-243const inputSchema = lazySchema(() =>  z.strictObject({    file_path: z.string().describe('The absolute path to the file to read'),    offset: semanticNumber(z.number().int().nonnegative().optional()).describe('The line number to start reading from (default: 1)',    ),    limit: semanticNumber(z.number().int().positive().optional()).describe('The number of lines to read',    ),    pages: z.string().optional().describe('Page range for PDF files'),  }),)type InputSchema = ReturnType<typeof inputSchema>export type Input = z.infer<InputSchema>

2.6 延迟加载机制 (shouldDefer / alwaysLoad)

文件src/Tool.ts:441-450

// src/Tool.ts:441-443/** * 延迟加载标记:工具需要 ToolSearch 才能调用 * 设置为 true 时,初始提示词不发送完整 Schema */readonly shouldDefer?: boolean// src/Tool.ts:448-450/** * 始终加载标记:即使启用 ToolSearch 也发送完整 Schema * 用于模型必须在第一轮看到的工具(如 Bash、Read) */readonly alwaysLoad?: boolean

3. 编排与执行流程 (Orchestration & Execution Flow)

3.1 StreamingToolExecutor 流式执行器

文件src/services/tools/StreamingToolExecutor.ts

// src/services/tools/StreamingToolExecutor.ts:40-62/** * 流式工具执行器:随着 API 流入的工具调用动态执行 * * 并发控制策略: * - 并发安全工具可与其他并发安全工具并行执行 * - 非并发安全工具必须独占执行(exclusive access) * - 结果按工具接收顺序产出(保持时序一致性) */export class StreamingToolExecutor {private tools: TrackedTool[] = []           // 跟踪的工具列表private toolUseContext: ToolUseContext      // 执行上下文private hasErrored = false// 是否有工具出错private erroredToolDescription = ''// 错误工具描述private siblingAbortController: AbortController  // 兄弟进程中止控制器private discarded = false// 是否丢弃(fallback 场景)private progressAvailableResolve?: () => void// 进度可用唤醒回调constructor(private readonly toolDefinitions: Tools,private readonly canUseTool: CanUseToolFn,    toolUseContext: ToolUseContext,) {this.toolUseContext = toolUseContextthis.siblingAbortController = createChildAbortController(      toolUseContext.abortController,    )  }}

3.2 TrackedTool 状态跟踪

文件src/services/tools/StreamingToolExecutor.ts:19-32

// src/services/tools/StreamingToolExecutor.ts:19-32type ToolStatus = 'queued' | 'executing' | 'completed' | 'yielded'type TrackedTool = {  id: string// 工具调用 ID  block: ToolUseBlock                      // API 工具调用块  assistantMessage: AssistantMessage       // 关联的助手消息  status: ToolStatus                       // 执行状态  isConcurrencySafe: boolean// 是否并发安全  promise?: Promise<void>                  // 执行 Promise  results?: Message[]                      // 结果消息列表  pendingProgress: Message[]               // 待产出的进度消息(立即 yield)  contextModifiers?: Array<(context: ToolUseContext) => ToolUseContext>  // 上下文修改器}

3.3 并发控制核心逻辑

文件src/services/tools/StreamingToolExecutor.ts:129-135

// src/services/tools/StreamingToolExecutor.ts:129-135/** * 检查工具是否可以执行(基于当前并发状态) * * 条件: * 1. 无正在执行的工具 → 可以执行 * 2. 工具并发安全 + 所有正在执行的工具都并发安全 → 可以执行 */private canExecuteTool(isConcurrencySafe: boolean): boolean {const executingTools = this.tools.filter(t => t.status === 'executing')return (    executingTools.length === 0 ||    (isConcurrencySafe && executingTools.every(t => t.isConcurrencySafe))  )}

3.4 工具添加流程

文件src/services/tools/StreamingToolExecutor.ts:76-124

// src/services/tools/StreamingToolExecutor.ts:76-124/** * 添加工具到执行队列 * 立即开始执行如果条件允许 */addTool(block: ToolUseBlock, assistantMessage: AssistantMessage): void {const toolDefinition = findToolByName(this.toolDefinitions, block.name)// 工具不存在 → 直接返回错误结果if (!toolDefinition) {this.tools.push({      id: block.id,      block,      assistantMessage,      status: 'completed',      isConcurrencySafe: true,      pendingProgress: [],      results: [        createUserMessage({          content: [            {type'tool_result',              content: `<tool_use_error>Error: No such tool available: ${block.name}</tool_use_error>`,              is_error: true,              tool_use_id: block.id,            },          ],          toolUseResult: `Error: No such tool available: ${block.name}`,          sourceToolAssistantUUID: assistantMessage.uuid,        }),      ],    })return  }// 解析输入并判断并发安全性const parsedInput = toolDefinition.inputSchema.safeParse(block.input)const isConcurrencySafe = parsedInput?.success    ? (() => {try {returnBoolean(toolDefinition.isConcurrencySafe(parsedInput.data))        } catch {returnfalse// 异常时默认不安全        }      })()    : false  // 加入跟踪队列this.tools.push({    id: block.id,    block,    assistantMessage,    status: 'queued',    isConcurrencySafe,    pendingProgress: [],  })  // 异步处理队列voidthis.processQueue()}

3.5 队列处理与执行调度

文件src/services/tools/StreamingToolExecutor.ts:140-151

// src/services/tools/StreamingToolExecutor.ts:140-151/** * 处理队列,当并发条件允许时启动工具 */private async processQueue(): Promise<void> {for (const tool of this.tools) {if (tool.status !== 'queued'continueif (this.canExecuteTool(tool.isConcurrencySafe)) {await this.executeTool(tool)  // 可以执行 → 启动    } else {// 无法执行此工具// 非并发安全工具需保持顺序 → 停止等待if (!tool.isConcurrencySafe) break    }  }}

3.6 完整执行流程 ASCII 图

+-----------------------------------------------------------------------+|                        工具执行流程                                    |+-----------------------------------------------------------------------+|                                                                                   ||  API Stream                                                                       ||      |                                                                            ||      v                                                                            ||  +--------------------------------------------------------+                       ||  |  StreamingToolExecutor.addTool(block, assistantMsg)   |                       ||  |                                                        |                       ||  |  1. findToolByName() 查找工具定义                      |                       ||  |  2. inputSchema.safeParse() 解析并校验输入             |                       ||  |  3. isConcurrencySafe() 判断并发安全性                 |                       ||  |  4. 加入 TrackedTool 队列 (status='queued')           |                       ||  |  5. 触发 processQueue()                               |                       ||  +--------------------------------------------------------+                       ||      |                                                                            ||      v                                                                            ||  +--------------------------------------------------------+                       ||  |  processQueue() - 并发控制调度                        |                       ||  |                                                        |                       ||  |  +----------------------------------------------+     |                       ||  |  | canExecuteTool(isConcurrencySafe)           |     |                       ||  |  |                                              |     |                       ||  |  | 条件判断:                                    |     |                       ||  |  | - executingTools.length === 0  → 可以执行    |     |                       ||  |  | - 并发安全 + 全部并发安全       → 可以执行    |     |                       ||  |  | - 其他情况                     → 需等待      |     |                       ||  |  +----------------------------------------------+     |                       ||  |                                                        |                       ||  |  调度策略:                                            |                       ||  |  - 可执行 → executeTool(tool)                        |                       ||  |  - 不可执行 + 非并发安全 → break (保持顺序)          |                       ||  |  - 不可执行 + 并发安全 → 继续检查下一个              |                       ||  +--------------------------------------------------------+                       ||      |                                                                            ||      v                                                                            ||  +--------------------------------------------------------+                       ||  |  executeTool(tool)                                    |                       ||  |                                                        |                       ||  |  1. 创建 toolAbortController (子中止控制器)           |                       ||  |  2. 更新状态: status = 'executing'                    |                       ||  |  3. 调用 runToolUse() 开始执行                        |                       ||  |  4. 监听进度消息 (onProgress 回调)                    |                       ||  |     - 进度消息 → pendingProgress 数组                 |                       ||  |  5. 处理执行结果                                      |                       ||  |     - 成功 → results = resultingMessages              |                       ||  |     - 失败 → hasErrored = true, 中止兄弟进程          |                       ||  |  6. 更新状态: status = 'completed'                    |                       ||  +--------------------------------------------------------+                       ||      |                                                                            ||      v                                                                            ||  +--------------------------------------------------------+                       ||  |  runToolUse() [toolExecution.ts]                      |                       ||  |                                                        |                       ||  |  1. findToolByName() 查找工具                         |                       ||  |  2. 工具不存在 → 返回错误消息                          |                       ||  |  3. 调用 streamedCheckPermissionsAndCallTool()        |                       ||  +--------------------------------------------------------+                       ||      |                                                                            ||      v                                                                            ||  +--------------------------------------------------------+                       ||  |  streamedCheckPermissionsAndCallTool()                |                       ||  |                                                        |                       ||  |  +------------------+  +------------------+            |                       ||  |  | 1. validateInput |  | 2. PreToolUse    |            |                       ||  |  |    (工具特定)     |->|    Hooks        |            |                       ||  |  |    - 路径验证     |  |    - 用户钩子   |            |                       ||  |  |    - 格式校验     |  |    - 自动批准   |            |                       ||  |  +------------------+  +------------------+            |                       ||  |           |                    |                      |                       ||  |           v                    v                      |                       ||  |  +------------------+  +------------------+            |                       ||  |  | 3. checkPermiss- |  | 4. canUseTool    |            |                       ||  |  |    ions(工具)     |->|    (权限决策)   |            |                       ||  |  |    - 工具规则     |  |    - allow      |            |                       ||  |  |    - 内容规则     |  |    - deny       |            |                       ||  |  |    - passthrough  |  |    - ask (交互) |            |                       ||  |  +------------------+  +------------------+            |                       ||  |           |                    |                      |                       ||  |           +--------------------+                      |                       ||  |                              |                        |                       ||  |                              v                        |                       ||  |  +------------------+                                 |                       ||  |  | 权限决策         |                                 |                       ||  |  | - allow → 继续   |                                 |                       ||  |  | - deny → 返回错误|                                 |                       ||  |  | - ask → 等待用户 |                                 |                       ||  |  +------------------+                                 |                       ||  |           |                                           |                       ||  |           v (权限允许)                                |                       ||  |  +------------------+                                 |                       ||  |  | 5. tool.call()   |                                 |                       ||  |  |    执行核心逻辑   |                                 |                       ||  |  |    - 文件操作     |                                 |                       ||  |  |    - 命令执行     |                                 |                       ||  |  |    - 网络请求     |                                 |                       ||  |  +------------------+                                 |                       ||  |           |                                           |                       ||  |           v                                           |                       ||  |  +------------------+  +------------------+            |                       ||  |  | 6. PostToolUse   |  | 7. 结果序列化   |            |                       ||  |  |    Hooks        |->|    mapToolResult|            |                       ||  |  |    - 结果钩子    |  |    ToBlockParam |            |                       ||  |  +------------------+  +------------------+            |                       ||  +--------------------------------------------------------+                       ||      |                                                                            ||      v                                                                            ||  +--------------------------------------------------------+                       ||  |  getCompletedResults() / getRemainingResults()         |                       ||  |                                                        |                       ||  |  流式产出策略:                                        |                       ||  |  1. 进度消息立即 yield (pendingProgress)               |                       ||  |  2. 完成结果按顺序 yield (results)                     |                       ||  |  3. 等待执行完成或进度可用 (progressPromise)            |                       ||  +--------------------------------------------------------+                       ||      |                                                                            ||      v                                                                            ||  API Response (tool_result blocks)                                               ||                                                                                   |+-----------------------------------------------------------------------+

3.7 进度消息流式产出

文件src/services/tools/StreamingToolExecutor.ts:412-440

// src/services/tools/StreamingToolExecutor.ts:412-440/** * 获取已完成的结果(Generator 模式) * 进度消息立即产出,不等待工具完成 */*getCompletedResults(): Generator<MessageUpdate, void> {for (const tool of this.tools) {// 进度消息立即 yield(实时反馈)while (tool.pendingProgress.length > 0) {const progressMessage = tool.pendingProgress.shift()!yield {        message: progressMessage,        newContext: this.toolUseContext,      }    }// 完成的工具产出结果if (tool.status === 'completed' && tool.results) {for (const message of tool.results) {yield {          message,          newContext: tool.contextModifiers            ? this.applyContextModifiers(tool.contextModifiers)            : this.toolUseContext,        }      }      tool.status = 'yielded'// 标记已产出    }  }}

4. 关键内置实现示例 (Built-in Implementations)

4.1 FileReadTool – 多格式文件读取神器

文件src/tools/FileReadTool/FileReadTool.ts:337-718

// src/tools/FileReadTool/FileReadTool.ts:337-395export const FileReadTool = buildTool({  name: FILE_READ_TOOL_NAME,  searchHint: 'read files, images, PDFs, notebooks',// 输出自 bounded,永不持久化(避免循环 Read→file→Read)  maxResultSizeChars: Infinity,  strict: true,// Schema 定义get inputSchema(): InputSchema { return inputSchema() },get outputSchema(): OutputSchema { return outputSchema() },// 安全特性  isConcurrencySafe() { return true },   // 读操作可并发  isReadOnly() { return true },          // 纯读操作// 分类器输入  toAutoClassifierInput(input) { return input.file_path },// UI 分类  isSearchOrReadCommand() { return { isSearch: false, isRead: true } },// 路径获取  getPath({ file_path }): string { return file_path || getCwd() },// 路径扩展(防止 Hook 绕过)  backfillObservableInput(input) {if (typeof input.file_path === 'string') {      input.file_path = expandPath(input.file_path)    }  },// 权限匹配器准备async preparePermissionMatcher({ file_path }) {return pattern => matchWildcardPattern(pattern, file_path)  },// 权限检查async checkPermissions(input, context): Promise<PermissionDecision> {const appState = context.getAppState()return checkReadPermissionForTool(      FileReadTool,      input,      appState.toolPermissionContext,    )  },})

validateInput 输入验证深度解析 (src/tools/FileReadTool/FileReadTool.ts:418-494):

// src/tools/FileReadTool/FileReadTool.ts:418-494async validateInput({ file_path, pages }, toolUseContext: ToolUseContext) {// 1. PDF 页码参数验证(纯字符串解析,无 I/O)if (pages !== undefined) {const parsed = parsePDFPageRange(pages)if (!parsed) {return {        result: false,        message: `Invalid pages parameter: "${pages}". Use formats like "1-5", "3", or "10-20".`,        errorCode: 7,      }    }// 页数限制检查const rangeSize = parsed.lastPage - parsed.firstPage + 1if (rangeSize > PDF_MAX_PAGES_PER_READ) {return {        result: false,        message: `Page range exceeds maximum of ${PDF_MAX_PAGES_PER_READ} pages.`,        errorCode: 8,      }    }  }// 2. 路径扩展 + Deny 规则检查const fullFilePath = expandPath(file_path)const appState = toolUseContext.getAppState()const denyRule = matchingRuleForInput(    fullFilePath,    appState.toolPermissionContext,'read','deny',  )if (denyRule !== null) {return {      result: false,      message: 'File is in a directory denied by permission settings.',      errorCode: 1,    }  }// 3. 【安全关键】UNC 路径检查(防止 NTLM 凭证泄露)// Windows SMB 认证在 fs 操作时触发,需在权限批准后才执行const isUncPath = fullFilePath.startsWith('\\\\') || fullFilePath.startsWith('//')if (isUncPath) {return { result: true }  // 跳过后续 I/O 检查  }// 4. 二进制文件扩展名检查(PDF/图片除外)const ext = path.extname(fullFilePath).toLowerCase()if (    hasBinaryExtension(fullFilePath) &&    !isPDFExtension(ext) &&    !IMAGE_EXTENSIONS.has(ext.slice(1))  ) {return {      result: false,      message: `Cannot read binary ${ext} files. Use appropriate tools.`,      errorCode: 4,    }  }// 5. 【安全关键】阻塞设备文件检查// /dev/random, /dev/zero 等会产生无限输出或阻塞if (isBlockedDevicePath(fullFilePath)) {return {      result: false,      message: `Cannot read '${file_path}': device file would block.`,      errorCode: 9,    }  }return { result: true }}

安全防护 – 阻塞设备文件列表 (src/tools/FileReadTool/FileReadTool.ts:98-115):

// src/tools/FileReadTool/FileReadTool.ts:98-115/** * 会阻塞或产生无限输出的设备文件路径 * 这些文件必须被阻止读取,否则会导致工具卡死 */const BLOCKED_DEVICE_PATHS = new Set(['/dev/zero',       // 无限零字节'/dev/random',     // 无限随机字节'/dev/urandom',    // 无限随机字节'/dev/full',       // 无限写入阻塞'/dev/stdin',      // 标准输入阻塞'/dev/tty',        // 终端设备阻塞'/dev/console',    // 控制台阻塞])

智能去重机制 (src/tools/FileReadTool/FileReadTool.ts:523-572):

// src/tools/FileReadTool/FileReadTool.ts:523-572/** * 去重检查:避免重复发送相同文件内容 * * 如果已读取过相同范围且文件未修改: * - 返回 file_unchanged stub(节省 cache_creation tokens) * - 原始 tool_result 已在上下文中 * * 统计显示 ~18% 的 Read 调用是相同文件碰撞 */const readTimestamp = readFileState.get(fullFilePath)if (readTimestamp) {const currentMtime = getFileModificationTime(fullFilePath)// 文件未修改 + 范围匹配 → 返回 stubif (currentMtime === readTimestamp.timestamp) {const rangeMatch =      (offset === readTimestamp.offset || offset === 1) &&      (limit === readTimestamp.limit || limit === undefined)if (rangeMatch) {return {        data: {type'file_unchanged',          file: { filePath: file_path },        },      }    }  }}

4.2 FileEditTool – 读-改-写原子性保证

文件src/tools/FileEditTool/FileEditTool.ts:86-595

// src/tools/FileEditTool/FileEditTool.ts:86-132export const FileEditTool = buildTool({  name: FILE_EDIT_TOOL_NAME,  searchHint: 'modify file contents in place',  maxResultSizeChars: 100_000,  strict: true,// 分类器输入:文件路径 + 新内容  toAutoClassifierInput(input) {return `${input.file_path}${input.new_string}`  },  getPath(input): string { return input.file_path },// 路径扩展  backfillObservableInput(input) {if (typeof input.file_path === 'string') {      input.file_path = expandPath(input.file_path)    }  },// 权限匹配器async preparePermissionMatcher({ file_path }) {return pattern => matchWildcardPattern(pattern, file_path)  },async checkPermissions(input, context): Promise<PermissionDecision> {const appState = context.getAppState()return checkWritePermissionForTool(      FileEditTool,      input,      appState.toolPermissionContext,    )  },})

并发修改检测 – 原子性核心 (src/tools/FileEditTool/FileEditTool.ts:275-311):

// src/tools/FileEditTool/FileEditTool.ts:275-311/** * 并发修改检测:确保读-改-写原子性 * * 机制: * 1. 检查文件最后修改时间 * 2. 与上次读取时间戳比较 * 3. Windows 兼容:时间戳不可靠时使用内容比对 */const readTimestamp = toolUseContext.readFileState.get(fullFilePath)if (!readTimestamp || readTimestamp.isPartialView) {return {    result: false,    behavior: 'ask',    message: FILE_UNREAD_ERROR,    errorCode: 6,  }}// 检查并发修改const lastWriteTime = getFileModificationTime(fullFilePath)if (lastWriteTime > readTimestamp.timestamp) {// Windows timestamp fallback:内容比对避免误报const isFullRead = !readTimestamp.offset && !readTimestamp.limitif (!isFullRead) {// 部分读取 → 无法比对,必须警告return {      result: false,      behavior: 'ask',      message: FILE_UNEXPECTEDLY_MODIFIED_ERROR,      errorCode: 11,    }  }// 全量读取 → 内容比对if (fileContent !== readTimestamp.content) {return {      result: false,      behavior: 'ask',      message: FILE_UNEXPECTEDLY_MODIFIED_ERROR,      errorCode: 11,    }  }}

多处匹配检测 (src/tools/FileEditTool/FileEditTool.ts:312-361):

// src/tools/FileEditTool/FileEditTool.ts:312-361/** * 多处匹配检测:防止意外替换多个位置 * * 场景: * - old_string 在文件中出现多次 * - 未设置 replace_all=true * - 需要明确提示用户 */const matches = countMatches(fileContent, old_string)if (matches > 1 && !replace_all) {return {    result: false,    behavior: 'ask',    message: `Found ${matches} matches. Use replace_all to replace all occurrences.`,    errorCode: 12,  }}// 无匹配检测if (matches === 0) {// 智能提示:可能原因const suggestions = []// 1. 弯引号 vs 直引号if (old_string.includes("'") || old_string.includes('"')) {    suggestions.push('Check for curly quotes vs straight quotes')  }// 2. 行尾符差异if (fileContent.includes('\r\n') && !old_string.includes('\r\n')) {    suggestions.push('File has CRLF line endings')  }return {    result: false,    behavior: 'ask',    message: `No matches found. ${suggestions.join('. ')}`,    errorCode: 13,  }}

4.3 BashTool – Shell 命令执行与后台任务

文件src/tools/BashTool/BashTool.tsx:227-294

// src/tools/BashTool/BashTool.tsx:227-259// 完整输入 Schema(包含内部字段)const fullInputSchema = lazySchema(() => z.strictObject({  command: z.string().describe('The command to execute'),  timeout: semanticNumber(z.number().optional())    .describe(`Optional timeout in milliseconds (max ${getMaxTimeoutMs()})`),  description: z.string().optional()    .describe('Clear, concise description of what this command does'),  run_in_background: semanticBoolean(z.boolean().optional())    .describe('Set to true to run this command in the background'),  dangerouslyDisableSandbox: semanticBoolean(z.boolean().optional())    .describe('Set to true to dangerously override sandbox mode'),// 内部字段:sed 编辑预览结果  _simulatedSedEdit: z.object({    filePath: z.string(),    newContent: z.string()  }).optional(),}))// 对外 Schema:移除内部字段const inputSchema = lazySchema(() =>  isBackgroundTasksDisabled    ? fullInputSchema().omit({ run_in_background: true, _simulatedSedEdit: true })    : fullInputSchema().omit({ _simulatedSedEdit: true }))// src/tools/BashTool/BashTool.tsx:279-294// 输出 Schemaconst outputSchema = lazySchema(() => z.object({  stdout: z.string().describe('Standard output'),  stderr: z.string().describe('Standard error output'),  rawOutputPath: z.string().optional().describe('Path for large MCP outputs'),  interrupted: z.boolean().describe('Whether command was interrupted'),  isImage: z.boolean().optional().describe('stdout contains image data'),  backgroundTaskId: z.string().optional().describe('Background task ID'),  backgroundedByUser: z.boolean().optional().describe('User manually backgrounded'),  assistantAutoBackgrounded: z.boolean().optional().describe('Auto-backgrounded'),  persistedOutputPath: z.string().optional().describe('Persisted output path'),  persistedOutputSize: z.number().optional().describe('Total output size'),}))

智能分类 – 搜索/读取命令识别 (src/tools/BashTool/BashTool.tsx:95-172):

// src/tools/BashTool/BashTool.tsx:95-172/** * 判断 Bash 命令是否为搜索/读取类型 * 用于 UI 折叠显示 */export functionisSearchOrReadBashCommand(command: string): boolean{// 静默命令列表(只读、无副作用)const BASH_SILENT_COMMANDS = new Set(['ls''dir''cat''head''tail''less''more','grep''rg''find''locate''which''whereis','wc''stat''file''du''df''pwd','git status''git log''git show''git diff','npm list''pip list''brew list',  ])// 解析复合命令const partsWithOperators = splitCommandWithOperators(command)for (const part of partsWithOperators) {if (part === '||' || part === '&&' || part === '|' || part === ';') {continue// 跳过操作符    }const baseCommand = part.trim().split(/\s+/)[0]if (!BASH_SILENT_COMMANDS.has(baseCommand)) {return false// 非静默命令 → 不是纯搜索/读取    }  }returntrue}

4.4 GlobTool – 文件名模式搜索

文件src/tools/GlobTool/GlobTool.ts:57-198

// src/tools/GlobTool/GlobTool.ts:26-52// 输入 Schemaconst inputSchema = lazySchema(() =>  z.strictObject({    pattern: z.string()      .describe('The glob pattern to match files against (e.g., "**/*.js")'),    path: z.string().optional()      .describe('The directory to search in (default: current working directory)'),  }))// 输出 Schemaconst outputSchema = lazySchema(() =>  z.object({    filenames: z.array(z.string())      .describe('Matching file paths, relative to the search directory'),    durationMs: z.number().describe('Search duration in milliseconds'),    numFiles: z.number().describe('Number of matching files found'),    truncated: z.boolean().describe('Whether results were truncated'),  }))// src/tools/GlobTool/GlobTool.ts:57-149export const GlobTool = buildTool({  name: GLOB_TOOL_NAME,  searchHint: 'find files by name pattern or wildcard',  maxResultSizeChars: 100_000,  isConcurrencySafe() { return true },  isReadOnly() { return true },  isSearchOrReadCommand() {return { isSearch: true, isRead: false }  },  toAutoClassifierInput(input) { return input.pattern },async validateInput({ path }, context) {// UNC 路径安全检查if (path?.startsWith('\\\\') || path?.startsWith('//')) {return { result: true }    }// 路径扩展const fullPath = path ? expandPath(path) : getCwd()// Deny 规则检查const denyRule = matchingRuleForInput(fullPath, ...)if (denyRule !== null) {return { result: false, message: 'Directory denied by permissions', errorCode: 1 }    }return { result: true }  },async call(input, { abortController, globLimits }) {const limit = globLimits?.maxResults ?? 100const { files, truncated } = await glob(      input.pattern,      path,      { limit, offset: 0 },      abortController.signal,    )// 相对路径转换(节省 token)const filenames = files.map(toRelativePath)return {      data: {        filenames,        durationMs,        numFiles: filenames.length,        truncated,      },    }  },})

4.5 GrepTool – 内容搜索神器

文件src/tools/GrepTool/GrepTool.ts:33-156

// src/tools/GrepTool/GrepTool.ts:33-89// 输入 Schemaconst inputSchema = lazySchema(() =>  z.strictObject({    pattern: z.string()      .describe('The regular expression pattern to search for'),    path: z.string().optional()      .describe('The directory to search in'),    output_mode: z.enum(['content''files_with_matches''count'])      .default('content')      .describe('Output mode: content (lines), files_with_matches, or count'),    glob: z.string().optional()      .describe('Glob pattern to filter files'),type: z.string().optional()      .describe('File type filter (js, py, rust, etc.)'),    head_limit: semanticNumber(z.number().int().positive().optional())      .describe('Limit output to first N results (default: 250)'),  }))// 默认结果限制const DEFAULT_HEAD_LIMIT = 250// src/tools/GrepTool/GrepTool.ts:160-577export const GrepTool = buildTool({  name: GREP_TOOL_NAME,  searchHint: 'search file contents with regex (ripgrep)',  maxResultSizeChars: 100_000,  isConcurrencySafe() { return true },  isReadOnly() { return true },  isSearchOrReadCommand() {return { isSearch: true, isRead: false }  },async call(input, context) {const args = ['--hidden''--max-columns''500']// 排除版本控制目录for (const dir of ['.git''.svn''.hg''.jj']) {      args.push('--glob'`!${dir}`)    }// 输出模式处理if (output_mode === 'files_with_matches') {      args.push('-l')  // 只输出文件名// 按修改时间排序(最近修改优先)const sortedMatches = results.sort((a, b) => b.mtimeMs - a.mtimeMs)    }if (output_mode === 'count') {      args.push('-c')  // 统计匹配数    }// 应用结果限制const limit = head_limit ?? DEFAULT_HEAD_LIMIT    args.push('--max-count'String(limit))return {      data: {        mode: output_mode,        filenames,        content,        numMatches,      },    }  },})

4.6 AgentTool – 子代理执行与 Fork 优化

文件src/tools/AgentTool/AgentTool.tsx:82-300

// src/tools/AgentTool/AgentTool.tsx:82-138// 输入 Schemaconst baseInputSchema = lazySchema(() =>  z.strictObject({    description: z.string()      .describe('A short (3-5 word) description of what the agent will do'),    prompt: z.string()      .describe('The task for the agent to perform'),    subagent_type: z.string().optional()      .describe('Specialized agent type (general-purpose, Explore, Plan, etc.)'),    model: z.enum(['sonnet''opus''haiku']).optional()      .describe('Optional model override for the subagent'),    run_in_background: z.boolean().optional()      .describe('Run the agent in the background'),    isolation: z.enum(['worktree''remote']).optional()      .describe('Isolation mode: worktree (git worktree) or remote (CCR)'),  }))// src/tools/AgentTool/AgentTool.tsx:196-300export const AgentTool = buildTool({  name: AGENT_TOOL_NAME,  searchHint: 'spawn sub-agent for parallel work',  maxResultSizeChars: 100_000,// 并发安全  isConcurrencySafe() { return true },async call(input, context) {// Fork 优化:共享父进程的 prompt cacheif (isForkSubagentEnabled()) {const messages = buildForkedMessages(prompt, context)// 使用父进程的 renderedSystemPrompt 避免 cache bust    }// 隔离模式:Worktreeif (input.isolation === 'worktree') {const worktreePath = await createAgentWorktree()// 在独立的 git worktree 中执行    }// 隔离模式:Remote(CCR 远程执行)if (input.isolation === 'remote') {return await teleportToRemote(taskDef)    }// 后台执行if (input.run_in_background) {const taskId = await spawnBackgroundAgent(...)// 完成后通知主线程    }return {      data: {        result: agentResult,// ...      },    }  },})

4.7 MCPTool – 动态工具模板

文件src/tools/MCPTool/MCPTool.ts:1-77

// src/tools/MCPTool/MCPTool.ts:1-77/** * MCP 工具模板 * * 这是一个占位工具定义,在 MCP 客户端连接时 * 会动态覆盖 name、description、call 等方法 */export const MCPTool = buildTool({  isMcp: true,  name: 'mcp',  // 在 mcpClient.ts 中动态覆盖  maxResultSizeChars: 100_000,// 动态 Schema:接受任意输入  inputSchema: lazySchema(() => z.object({}).passthrough()),  outputSchema: lazySchema(() => z.string()),// 权限透传:让通用权限系统处理async checkPermissions(): Promise<PermissionResult> {return { behavior: 'passthrough', message: 'MCPTool requires permission.' }  },// 占位实现,在 mcpClient.ts 中动态注入async call() {return { data: '' }  },async description() {return DESCRIPTION  },async prompt() {return PROMPT  },})

关键设计:MCP 工具是动态创建的模板,在 MCP 客户端连接时被实例化为具体工具,通过 mcpInfo 属性记录原始服务器和工具名称。

4.8 ToolSearchTool – 工具发现系统

文件src/tools/ToolSearchTool/ToolSearchTool.ts:304-471

// src/tools/ToolSearchTool/ToolSearchTool.ts:21-45// 输入 Schemaconst inputSchema = lazySchema(() =>  z.strictObject({    query: z.string()      .describe('Query to find deferred tools. Use "select:ToolName" for direct selection'),    max_results: semanticNumber(z.number().int().positive().optional())      .describe('Maximum number of results (default: 5)'),  }))// src/tools/ToolSearchTool/ToolSearchTool.ts:304-471export const ToolSearchTool = buildTool({  name: TOOL_SEARCH_TOOL_NAME,async call({ query, max_results }, context) {const deferredTools = tools.filter(isDeferredTool)// 直接选择:select:ToolName 格式if (query.startsWith('select:')) {const toolName = query.slice('select:'.length)return {        matches: [toolName],        total_deferred_tools: deferredTools.length,      }    }// 关键词搜索const matches = await searchToolsWithKeywords(query, deferredTools)return {      matches: matches.slice(0, max_results ?? 5),      total_deferred_tools: deferredTools.length,    }  },// 特殊序列化:返回 tool_reference 类型  mapToolResultToToolResultBlockParam(content, toolUseID) {return {type'tool_result',      tool_use_id: toolUseID,      content: content.matches.map(name => ({type'tool_reference',        tool_name: name,      })),    }  },})

5. 关键上下文/系统设计 (Context & Sub-systems)

5.1 ToolUseContext 执行上下文详解

文件src/Tool.ts:158-300

// src/Tool.ts:158-300export type ToolUseContext = {// ==================== 配置选项 ====================  options: {    commands: Command[]                    // 可用命令列表    debug: boolean// 调试模式    mainLoopModel: string// 主循环模型    tools: Tools                           // 可用工具列表    verbose: boolean// 详细模式    thinkingConfig: ThinkingConfig         // 思考配置    mcpClients: MCPServerConnection[]      // MCP 客户端连接    mcpResources: Record<string, ServerResource[]>  // MCP 资源    isNonInteractiveSession: boolean// 非交互会话标志    agentDefinitions: AgentDefinitionsResult  // 代理定义    maxBudgetUsd?: number// 预算限制    customSystemPrompt?: string// 自定义系统提示词    appendSystemPrompt?: string// 追加系统提示词    querySource?: QuerySource              // 查询来源追踪    refreshTools?: () => Tools             // 工具刷新回调  }// ==================== 状态管理 ====================  abortController: AbortController         // 中止控制器  readFileState: FileStateCache            // 文件读取状态缓存  getAppState(): AppState                  // 获取应用状态  setAppState(f: (prev: AppState) => AppState): void// 设置应用状态  setAppStateForTasks?: (f: (prev: AppState) => AppState) => void// 任务状态设置// ==================== UI 集成 ====================  setToolJSX?: SetToolJSXFn                // 设置工具 JSX 渲染  addNotification?: (notif: Notification) => void// 添加通知  appendSystemMessage?: (msg) => void// 追加系统消息  sendOSNotification?: (opts) => void// 发送 OS 通知// ==================== 技能触发器 ====================  nestedMemoryAttachmentTriggers?: Set<string>  // CLAUDE.md 嵌入触发  loadedNestedMemoryPaths?: Set<string>    // 已加载的嵌套记忆路径  dynamicSkillDirTriggers?: Set<string>    // 动态技能目录触发  discoveredSkillNames?: Set<string>       // 已发现技能名称// ==================== 消息与状态 ====================  userModified?: boolean// 用户修改标志  setInProgressToolUseIDs: (f: (prev: Set<string>) => Set<string>) => void  setHasInterruptibleToolInProgress?: (v: boolean) => void  setResponseLength: (f: (prev: number) => number) => void  messages: Message[]                      // 消息历史// ==================== 限制配置 ====================  fileReadingLimits?: {    maxTokens?: number    maxSizeBytes?: number  }  globLimits?: {    maxResults?: number  }// ==================== 权限追踪 ====================  toolDecisions?: Map<string, {    source: string    decision: 'accept' | 'reject'    timestamp: number  }>  queryTracking?: QueryChainTracking       // 查询链追踪  localDenialTracking?: DenialTrackingState  // 本地拒绝追踪  contentReplacementState?: ContentReplacementState  // 内容替换状态// ==================== 其他 ====================  requestPrompt?: (sourceName, toolInputSummary) => (request) => Promise<PromptResponse>  handleElicitation?: (serverName, params, signal) => Promise<ElicitResult>  toolUseId?: string// 工具调用 ID  agentId?: AgentId                        // 代理 ID(子代理专用)  agentType?: string// 代理类型  renderedSystemPrompt?: SystemPrompt      // 父进程渲染的系统提示词(Fork 优化)}

5.2 ToolPermissionContext 权限上下文

文件src/Tool.ts:123-138

// src/Tool.ts:123-138/** * 工具权限上下文 * 使用 DeepImmutable 确保配置不可变 */export type ToolPermissionContext = DeepImmutable<{  mode: PermissionMode                    // 权限模式 (default, accept, plan, auto)  additionalWorkingDirectories: Map<string, AdditionalWorkingDirectory>  // 额外工作目录// ==================== 规则集 ====================  alwaysAllowRules: ToolPermissionRulesBySource    // 始终允许规则  alwaysDenyRules: ToolPermissionRulesBySource     // 始终拒绝规则  alwaysAskRules: ToolPermissionRulesBySource      // 始终询问规则// ==================== 特殊标志 ====================  isBypassPermissionsModeAvailable: boolean// 绕过权限模式可用  isAutoModeAvailable?: boolean// 自动模式可用  strippedDangerousRules?: ToolPermissionRulesBySource  // 剥离的危险规则  shouldAvoidPermissionPrompts?: boolean// 避免权限提示(后台代理)  awaitAutomatedChecksBeforeDialog?: boolean// 等待自动检查后再显示对话框  prePlanMode?: PermissionMode               // 计划模式前的权限模式}>

5.3 工具池组装机制

文件src/tools.ts:193-367

// src/tools.ts:193-251/** * 获取所有基础工具列表 * 这是所有工具的注册中心 * * 注意:必须与 Statsig 配置保持同步以支持系统提示词缓存 */export functiongetAllBaseTools(): Tools{return [    AgentTool,    TaskOutputTool,    BashTool,    ...(hasEmbeddedSearchTools() ? [] : [GlobTool, GrepTool]),    ExitPlanModeV2Tool,    FileReadTool,    FileEditTool,    FileWriteTool,    NotebookEditTool,    WebFetchTool,    TodoWriteTool,    WebSearchTool,    TaskStopTool,    AskUserQuestionTool,    SkillTool,    EnterPlanModeTool,    ...(process.env.USER_TYPE === 'ant' ? [ConfigTool] : []),    ...(process.env.USER_TYPE === 'ant' ? [TungstenTool] : []),// ... 条件性工具(根据 feature flags)    ...(isTodoV2Enabled()      ? [TaskCreateTool, TaskGetTool, TaskUpdateTool, TaskListTool]      : []),    ...(isWorktreeModeEnabled() ? [EnterWorktreeTool, ExitWorktreeTool] : []),    getSendMessageTool(),    ...(isAgentSwarmsEnabled()      ? [getTeamCreateTool(), getTeamDeleteTool()]      : []),    ListMcpResourcesTool,    ReadMcpResourceTool,    ...(isToolSearchEnabledOptimistic() ? [ToolSearchTool] : []),  ]}// src/tools.ts:262-269/** * 按 Deny 规则过滤工具 * 工具级拒绝规则:规则无内容时匹配整个工具 */export functionfilterToolsByDenyRules<Textends{ name: string; mcpInfo?: { serverName: string; toolName: string } },>(tools: readonly T[], permissionContext: ToolPermissionContext): T[] {return tools.filter(tool => !getDenyRuleForTool(permissionContext, tool))}// src/tools.ts:345-367/** * 组装完整工具池(内置 + MCP) * * 这是工具池组装的单一真实来源: * 1. 获取内置工具(根据权限过滤) * 2. 过滤 MCP 工具 * 3. 去重合并(内置工具优先) * 4. 排序(Prompt Cache 稳定性) */export functionassembleToolPool(  permissionContext: ToolPermissionContext,  mcpTools: Tools,): Tools{const builtInTools = getTools(permissionContext)const allowedMcpTools = filterToolsByDenyRules(mcpTools, permissionContext)// 排序策略:按名称排序,内置工具作为连续前缀// Prompt Cache 在最后一个内置工具后设置断点const byName = (a: Tool, b: Tool) => a.name.localeCompare(b.name)return uniqBy(    [...builtInTools].sort(byName).concat(allowedMcpTools.sort(byName)),'name',  )}

5.4 工具可用性控制常量

文件src/constants/tools.ts

// src/constants/tools.ts:36-71/** * 所有代理禁用的工具集 * 这些工具不能在子代理中使用 */export const ALL_AGENT_DISALLOWED_TOOLS = new Set(['TaskOutput',      // 防止递归调用'ExitPlanMode',    // 计划模式是主线程抽象'TaskStop',        // 需要主线程状态'Agent',           // 防止嵌套(ant 类型除外)])/** * 异步代理允许的工具集 * 后台代理可以使用的工具列表 */export const ASYNC_AGENT_ALLOWED_TOOLS = new Set(['Read''Write''Edit''Bash''Grep''Glob','WebFetch''WebSearch''Skill''NotebookEdit',])/** * 协调器模式允许的工具集 * 协调器(Coordinator)专用工具 */export const COORDINATOR_MODE_ALLOWED_TOOLS = new Set(['Agent',           // 协调器核心:派发任务给工作者'TaskStop',        // 停止工作者任务'SendMessage',     // 发送消息给工作者'SyntheticOutput'// 合成输出工具])

6. 关键代码片段总结 (Code Snippets Cheatsheet)

6.1 工具定义模板

// 完整工具定义模板// src/Tool.ts:783-792 + src/tools/GlobTool/GlobTool.ts:57+export const MyTool = buildTool({  name: 'MyTool',  searchHint: 'brief capability description (3-10 words)',  maxResultSizeChars: 100_000,  strict: true,// Schema 定义get inputSchema(): InputSchema { return inputSchema() },get outputSchema(): OutputSchema { return outputSchema() },// 安全属性  isConcurrencySafe() { return true },   // 或 false  isReadOnly() { return true },          // 或 false  isDestructive() { return false },      // 仅破坏性操作设 true// 分类器输入  toAutoClassifierInput(input) { return input.someField },// UI 分类(可选)  isSearchOrReadCommand() { return { isSearch: true, isRead: false } },// 路径类工具必须实现  getPath(input): string { return input.file_path },  backfillObservableInput(input) {if (typeof input.file_path === 'string') {      input.file_path = expandPath(input.file_path)    }  },async preparePermissionMatcher({ file_path }) {return pattern => matchWildcardPattern(pattern, file_path)  },// 权限检查async checkPermissions(input, context): Promise<PermissionDecision> {const appState = context.getAppState()return checkWritePermissionForTool(MyTool, input, appState.toolPermissionContext)  },// 输入验证(可选但推荐)async validateInput(input, context): Promise<ValidationResult> {// 1. 路径安全检查const fullPath = expandPath(input.file_path)// 2. Deny 规则检查// 3. 格式/范围验证return { result: true }  },// 核心执行async call(input, context, canUseTool, parentMessage, onProgress) {// 执行逻辑return {      data: result,      newMessages?: [...],      contextModifier?: (ctx) => modifiedCtx,    }  },// UI 渲染方法  renderToolUseMessage,  renderToolResultMessage,  mapToolResultToToolResultBlockParam(data, toolUseID) {return {type'tool_result',      tool_use_id: toolUseID,      content: serializedContent,    }  },})

6.2 lazySchema 使用模式

// src/utils/lazySchema.ts + 实际使用示例const inputSchema = lazySchema(() =>  z.strictObject({    file_path: z.string().describe('Absolute path...'),    offset: semanticNumber(z.number().int().nonnegative().optional()),    limit: semanticNumber(z.number().int().positive().optional()),  }))type InputSchema = ReturnType<typeof inputSchema>exporttype Input = z.infer<InputSchema>

6.3 并发控制核心逻辑

// src/services/tools/StreamingToolExecutor.ts:129-135private canExecuteTool(isConcurrencySafe: boolean): boolean {const executingTools = this.tools.filter(t => t.status === 'executing')return (    executingTools.length === 0 ||    (isConcurrencySafe && executingTools.every(t => t.isConcurrencySafe))  )}

6.4 文件编辑原子性保证

// src/tools/FileEditTool/FileEditTool.ts:275-311// 并发修改检测const lastWriteTime = getFileModificationTime(fullFilePath)const lastRead = readFileState.get(fullFilePath)if (lastWriteTime > lastRead.timestamp) {// Windows fallback: 内容比对const isFullRead = !lastRead.offset && !lastRead.limitif (!isFullRead || fileContent !== lastRead.content) {throw new Error(FILE_UNEXPECTEDLY_MODIFIED_ERROR)  }}

6.5 UNC 路径安全检查

// src/tools/FileReadTool/FileReadTool.ts:463-467 + FileEditTool:179-181// 防止 NTLM 凭证泄露const isUncPath = fullFilePath.startsWith('\\\\') || fullFilePath.startsWith('//')if (isUncPath) {return { result: true }  // 跳过 I/O 检查,等权限批准后再操作}

6.6 权限规则匹配

// src/utils/permissions/permissions.ts:238-269functiontoolMatchesRule(tool: Pick<Tool, 'name' | 'mcpInfo'>, rule: PermissionRule): boolean{// 无内容规则 → 匹配整个工具if (rule.ruleValue.ruleContent === undefined) {return rule.ruleValue.toolName === tool.name  }// MCP server-level 匹配const ruleInfo = mcpInfoFromString(rule.ruleValue.toolName)const toolInfo = mcpInfoFromString(tool.name)return ruleInfo?.serverName === toolInfo?.serverName}

6.7 进度消息回调

// 进度报告模式async call(input, context, canUseTool, parentMessage, onProgress) {// 执行过程中报告进度  onProgress?.({    toolUseID: parentMessage.message.id,    data: {type'bash_progress',      pid: process.pid,      output: partialOutput,    },  })// 最终返回结果return { data: finalResult }}

7. 总结 (Summary)

设计模式总结表

设计模式
应用位置
价值
工厂模式 buildTool

 函数
统一创建入口、默认值填充、类型安全保证
策略模式 isConcurrencySafe

/validateInput/checkPermissions
工具可自定义行为策略,灵活性高
观察者模式 ToolCallProgress

 回调
实时进度报告、UI 流式更新
模板方法模式 Tool

 接口生命周期
标准化执行流程、工具只需实现差异化部分
延迟初始化 lazySchema

 工厂
避免模块加载开销、提升启动性能

安全最佳实践总结表

安全机制
实现位置
保护目标
UNC 路径防护
FileReadTool:463-467, FileEditTool:179-181
防止 NTLM 凭证泄露
设备文件阻断
FileReadTool:98-115
防止无限输出/阻塞
并发修改检测
FileEditTool:275-311
读-改-写原子性
多处匹配警告
FileEditTool:312-361
防止意外批量替换
二进制文件拒绝
FileReadTool:469-482
防止无效操作
路径遍历检测
expandPath + matchingRuleForInput
防止非法路径访问
沙箱隔离
BashTool 相关辅助文件
限制危险命令执行

性能优化策略总结表

优化策略
实现方式
效果
延迟加载 lazySchema

 + shouldDefer
减少初始提示词成本
智能去重
readFileState + mtime 检查
节省 ~18% 重复读取
相对路径
filenames.map(toRelativePath)
节省 token
结果持久化
maxResultSizeChars 阈值
大结果存磁盘,避免 token 消耗
并发执行
isConcurrencySafe 分组
最大化并行度
Prompt Cache 稳定排序
byName 排序 + 内置工具前缀
提高缓存命中率

核心文件索引表

功能模块
文件路径
核心行号范围
Tool 类型定义 src/Tool.ts
362-695 (接口), 757-792 (工厂)
工具注册中心 src/tools.ts
193-251 (注册), 345-367 (组装)
流式执行器 src/services/tools/StreamingToolExecutor.ts
40-62 (类), 129-135 (并发)
权限类型 src/types/permissions.ts
44-266 (类型定义)
权限检查 src/utils/permissions/permissions.ts
122-390 (规则匹配)
FileReadTool src/tools/FileReadTool/FileReadTool.ts
337-718 (完整定义)
FileEditTool src/tools/FileEditTool/FileEditTool.ts
86-595 (完整定义)
BashTool src/tools/BashTool/BashTool.tsx
180-294 (Schema)
AgentTool src/tools/AgentTool/AgentTool.tsx
82-300 (完整定义)
工具常量 src/constants/tools.ts
36-113 (禁用/允许集)

Claude Code 的工具系统展现了一个成熟的 AI Agent 工具框架应有的设计水平:类型安全的泛型架构、分层治理的权限体系、智能的并发调度、流式的进度反馈、以及完善的 MCP 集成。这个架构值得任何构建 AI Agent 工具系统的开发者深入学习和借鉴。