Skip to content

Step 2:工具系统

分析目标

理解 Claude Code 工具系统的完整架构:工具接口定义、注册机制、条件编译和权限管控。

核心文件

文件角色
src/Tool.tsTool 接口和类型定义
src/tools.ts工具注册中心
src/tools/*/50+ 工具实现
src/utils/permissions/permissions.ts权限规则引擎

Tool 接口

typescript
// src/Tool.ts (核心定义)
export interface Tool {
  name: string
  description: string
  inputSchema: ToolInputJSONSchema
  isEnabled(): boolean
  input(input: any, context: ToolContext): AsyncGenerator<ToolResult>

  // 可选的挂载点
  permissionCheck?: (context: ToolPermissionContext) => PermissionResult
  mcpInfo?: { serverName: string; toolName: string }
}

关键设计

input() 是 AsyncGenerator

typescript
async *input(input: any, context: ToolContext) {
  // 可以产生多个中间结果
  yield { type: 'progress', progress: '正在执行...' }
  const result = await doSomething(input)
  yield { type: 'result', data: result }
}

这让工具可以在执行过程中向模型推送进度更新,而不仅仅返回最终结果。

isEnabled() 是运行时检查

typescript
class WebSearchTool implements Tool {
  isEnabled(): boolean {
    // 检查 API key 是否可用
    return !!process.env.WEB_SEARCH_API_KEY
  }
}

这也意味着工具可以随环境变化动态启用/禁用。

注册机制

加载图表中...

静态注册

typescript
// 来自静态 import 的工具
export function getAllBaseTools(): Tools {
  return [
    AgentTool,         // 子代理
    BashTool,          // 终端
    FileReadTool,      // 读文件
    FileEditTool,      // 编辑文件
    FileWriteTool,     // 写文件
    WebFetchTool,      // 网页抓取
    WebSearchTool,     // 网页搜索
    AskUserQuestionTool, // 询问用户
    SkillTool,         // 技能
    GlobTool,          // 文件搜索
    GrepTool,          // 文本搜索
    // ... 更多
  ]
}

静态导入的工具在模块加载时就会被执行,因此它们需要在模块顶级完成所有初始化。

条件注册

typescript
// 编译时 DCE
const WebBrowserTool = feature('WEB_BROWSER_TOOL')
  ? require('./tools/WebBrowserTool/WebBrowserTool.js').WebBrowserTool
  : null

// 运行时门控
const REPLTool = process.env.USER_TYPE === 'ant'
  ? require('./tools/REPLTool/REPLTool.js').REPLTool
  : null

条件工具在 tools.ts 中通过三元运算符条件加载,然后通过扩展运算符(spread)添加到 getAllBaseTools() 的返回数组中:

typescript
// 条件加载
...(WebBrowserTool ? [WebBrowserTool] : []),
...(SleepTool ? [SleepTool] : []),
...cronTools,  // cronTools 本身已经经过条件判断

惰性加载

typescript
// 惰性 require 打破循环依赖
const getTeamCreateTool = () =>
  require('./tools/TeamCreateTool/TeamCreateTool.js').TeamCreateTool

const getTeamDeleteTool = () =>
  require('./tools/TeamDeleteTool/TeamDeleteTool.js').TeamDeleteTool

这些工具直到实际被调用时才执行 require(),避免了模块加载时的循环依赖。

权限系统

加载图表中...

权限规则来源

来源文件优先级
用户配置~/.claude/settings.json
项目配置.claude/settings.json
CLI 参数--allowed-tools / --disallowed-tools

工具级别过滤

typescript
export function filterToolsByDenyRules<T extends { name: string }>(
  tools: readonly T[],
  permissionContext: ToolPermissionContext
): T[] {
  return tools.filter(tool => !getDenyRuleForTool(permissionContext, tool))
}

这个过滤发生在工具列表构建时,而非工具调用时。被 deny 的工具在模型看到它们之前就被移除了。

简单模式 (Simple Mode)

CLAUDE_CODE_SIMPLE=1 时,工具列表缩减为三个核心工具:

typescript
const simpleTools: Tool[] = [BashTool, FileReadTool, FileEditTool]

这是通过 getTools() 函数的早期返回实现的。在协调器模式下,还会额外包含 AgentToolTaskStopTool

练习

  1. tools.ts 中统计条件工具的数量,区分 feature()USER_TYPE 两种模式
  2. 找到所有使用惰性 require() 的工具,分析它们为什么需要惰性加载
  3. 添加一个简单的日志工具 LogTool,记录工具调用日志到文件,然后注册到 getAllBaseTools()