工具系统解析:Tool 抽象与 tools 注册表
Claude Code 的执行力来自工具,不只来自模型
很多人讨论 AI 编程工具时,容易把注意力全部放在模型上。
但从 Claude Code 源码来看,真正让它“能干活”的,是模型外面的工具系统。
如果没有工具,Claude Code 再强也只是会分析代码。
有了工具,它才能真正读文件、改文件、跑命令、接 MCP、问用户、开子任务。
Tool.ts 负责定义统一协议
Tool.ts 不是某个具体工具实现,而是全系统的工具抽象层。
这里最重要的价值有两个:
- 统一工具的输入、输出、上下文和权限语义
- 给所有工具提供相同的运行契约
文件里能看到很多关键类型:
ToolInputJSONSchemaToolUseContextToolPermissionContext- 各类进度与状态类型
这些类型说明 Claude Code 设计工具时,不是把工具当命令快捷方式,而是把它们当系统级能力对象。
对应源码片段
export type ToolInputJSONSchema = {
[x: string]: unknown
type: 'object'
properties?: {
[x: string]: unknown
}
}
这段定义虽然短,但非常关键。
它说明 Claude Code 的工具不是随便塞一段文本描述,而是要求:
- 输入结构明确
- 参数可枚举
- 工具契约可被系统理解
这正是工具化系统和随意 prompt 技巧的区别。
Tool.ts 真正定义的是一套执行契约
从这些类型可以反推出,Claude Code 对工具的预期非常清晰:
- 工具要有明确输入 schema
- 工具要能拿到统一上下文
- 工具要能被权限系统约束
- 工具执行过程要能反馈进度
- 工具结果要能回到消息流里
这套契约是后面所有能力扩展的基础。
ToolUseContext 很能说明问题
ToolUseContext 里挂了大量运行时资源,例如:
- 当前工具集合
- 命令集合
- MCP client 与 resource
- AppState 读写方法
- 通知能力
- 中断控制
- 消息历史
- 文件读取限制
- attribution / fileHistory 更新器
这意味着一个工具并不是孤立执行的,它是运行在完整会话上下文里的。
对应源码片段
export type ToolUseContext = {
options: {
commands: Command[]
debug: boolean
mainLoopModel: string
tools: Tools
verbose: boolean
thinkingConfig: ThinkingConfig
mcpClients: MCPServerConnection[]
mcpResources: Record<string, ServerResource[]>
}
abortController: AbortController
readFileState: FileStateCache
getAppState(): AppState
setAppState(f: (prev: AppState) => AppState): void
messages: Message[]
}
这段类型直接说明:
一个工具执行时,实际上是被放进完整运行时里的,而不是孤立调用一个小函数。
tools.ts 是系统的工具目录
如果说 Tool.ts 定义的是协议,那 tools.ts 定义的就是“这次系统到底有哪些工具”。
从注册表里能看到 Claude Code 的能力面相当宽:
- 文件工具:读、写、编辑、Notebook
- 终端工具:Bash、PowerShell
- 搜索工具:Glob、Grep
- 网络工具:WebFetch、WebSearch、WebBrowser
- 交互工具:AskUserQuestion
- 管理工具:Todo、Task、Plan、Worktree
- 集成工具:MCP、LSP、ToolSearch
- 智能体工具:Agent、Team、SendMessage
这已经明显不是“几个简单 function calling 工具”的规模了。
对应源码片段
export function getAllBaseTools(): Tools {
return [
AgentTool,
TaskOutputTool,
BashTool,
...(hasEmbeddedSearchTools() ? [] : [GlobTool, GrepTool]),
ExitPlanModeV2Tool,
FileReadTool,
FileEditTool,
FileWriteTool,
WebFetchTool,
TodoWriteTool,
WebSearchTool,
AskUserQuestionTool,
SkillTool,
EnterPlanModeTool,
...(isEnvTruthy(process.env.ENABLE_LSP_TOOL) ? [LSPTool] : []),
ListMcpResourcesTool,
ReadMcpResourceTool,
]
}
这里最值得注意的是两点:
- Claude Code 的能力面非常宽,不止读写文件和 Bash
- 工具集不是死的,会受环境变量和 feature 条件影响
这些工具大致可以分成 4 组
1. 基础执行工具
- FileRead
- FileEdit
- FileWrite
- Bash
- Glob
- Grep
2. 会话控制工具
- AskUserQuestion
- TodoWrite
- EnterPlanMode
- ExitPlanMode
3. 扩展接入工具
- ListMcpResources
- ReadMcpResource
- LSPTool
- ToolSearchTool
4. 协作与任务工具
- AgentTool
- SendMessageTool
- TaskCreate / Get / Update / List
- TeamCreate / TeamDelete
工具不是全量暴露,而是动态筛选
tools.ts 里能看到很多 feature gate、环境判断和权限过滤逻辑。
这说明工具系统有两个重要特征:
- 工具集是可配置、可裁剪的
- 模型看到的工具集不一定等于代码里存在的工具集
也就是说,Claude Code 在“给模型什么能力”这件事上非常克制。
对应源码片段
export function filterToolsByDenyRules<
T extends {
name: string
mcpInfo?: { serverName: string; toolName: string }
},
>(tools: readonly T[], permissionContext: ToolPermissionContext): T[] {
return tools.filter(tool => !getDenyRuleForTool(permissionContext, tool))
}
这段代码很重要,因为它说明安全不是只在执行时介入。
有些工具会在模型看到它们之前,就先被权限规则过滤掉。
这里体现了 Claude Code 的工程哲学
从工具系统设计上,你能读出几个非常明显的工程取向:
- 能力统一封装:先抽象协议,再接入具体能力
- 权限前置:不是执行时再补安全,而是能力暴露阶段就开始约束
- 按环境启用:不同构建、不同平台、不同 feature 下工具集不同
- 扩展优先:MCP、LSP、Team、Workflow 都能接进这套协议
这也是它后面能不断长出新能力的前提。
为什么统一工具协议特别重要
因为一旦协议稳定,新增能力的方式就会变得非常可预测:
- 新能力实现自己的 Tool
- 接入统一上下文和权限体系
- 注册到工具目录
- 在合适条件下暴露给模型
这比给模型单独塞一堆特例能力,要稳定得多。
小结
Claude Code 的工具系统可以概括成一句话:
用统一的 Tool 协议,把文件系统、终端、搜索、外部集成和智能体能力包装成模型可调用、可控、可扩展的执行层。
理解了这层,你就能明白它为什么不是“聊天 + 插件”的简单组合,而是一个真正可操作的工程代理。