Claude Code 之所以“能做事”,不是因为模型会魔法,而是因为它被接上了一套统一的工具系统
如果你第一次接触 AI Agent 系统,很容易产生一个误解:模型既然这么聪明,应该可以直接操作电脑吧?
事实恰恰相反。
模型本身只是一个”文本生成器”。它可以理解你的需求,可以规划步骤,可以生成代码,但它完全无法直接操作你的电脑:
- 不能读文件(它看不到你的文件系统)
- 不能写代码(它生成的代码只是文本,不会自动保存)
- 不能跑命令(它没有权限访问你的 Shell)
- 不能访问外部服务(它被隔离在 API 调用的边界内)
那它是怎么”帮你做事”的?
答案是:通过一套精心设计的工具系统。
模型不能直接做事,但它可以”请求做事”。它会输出一个结构化的请求:”我想调用 FileReadTool,参数是 src/app.ts”。然后 Claude Code 收到这个请求,在本地执行真正的文件读取,再把结果返回给模型。
这就是为什么工具系统不是”附属功能”,而是”产品主体的一部分”。
没有工具系统,Claude Code 就只是一个”会聊天的终端程序”。有了工具系统,它才能真正成为”会做事的编程助手”。
| 文件 | 作用 |
|---|---|
src/Tool.ts |
定义工具契约、工具上下文、工具构建基础能力 |
src/tools.ts |
统一注册内置工具 |
src/services/tools/* |
工具执行与调度相关逻辑 |
src/tools/BashTool/* |
最复杂、最有代表性的工具实现 |
从源码设计上看,一个工具至少要回答 4 个问题:
- 我叫什么:模型如何引用我
- 我接收什么参数:输入 Schema 是什么
- 我什么时候能执行:权限和模式限制是什么
- 我执行后返回什么:结果如何回传给模型
这说明 Claude Code 的工具不是“随便暴露一个函数”,而是一个规范化接口。
对于模型来说,工具不是代码实现,而是一个“可调用能力描述”。
Claude Code 需要把工具变成模型能理解的形式,所以每个工具都不只是一个 call():
- 有名称
- 有描述
- 有输入 Schema
- 有结果格式
- 有权限语义
- 有 UI 渲染方式
这也是为什么工具系统会单独抽象成 src/Tool.ts,而不是散落在各个功能目录里。
src/tools.ts 是内置工具注册中心。
它负责把各种工具按规则收集起来,例如:
- 始终可用的核心工具
- 由 Feature Flag 控制的实验性工具
- 用懒加载打破循环依赖的工具
这一步非常关键,因为模型看到的并不是“仓库里有哪些工具文件”,而是当前这次会话里最终被注册出来、最终被开放出来的工具集合。
getTools() 很重要,但它不等于本轮真正可执行的全部工具池。
更准确地说:
getTools():给出经过 mode / deny / enable 过滤后的 built-in toolsassembleToolPool():把 built-in 和 MCP tools 合并、去重toolUseContext.options.tools:本轮主链真正会被执行层使用的工具池
适合核心能力,例如:
BashToolFileReadToolFileWriteToolFileEditToolGlobToolGrepTool
这些能力足够基础,几乎可以视为 Claude Code 的标配。
源码中很多工具要看 feature('...') 是否开启,例如:
- 主动模式相关工具
- Cron/Trigger 相关工具
- KAIROS 相关工具
- 监控、推送、简报类工具
这说明工具系统不仅服务当前产品,也服务未来产品实验。
有些工具不是因为“不重要”才延迟加载,而是因为:
- 启动时不想拉起太多重量级模块
- 要规避循环依赖
例如 TeamCreateTool、TeamDeleteTool 这类和多 Agent 体系耦合更深的工具,就用了懒加载思路。
从 Tool.ts 和执行层的调用链看,工具查找至少有一条明确规则:
- 优先在当前会话真实可用的工具池里查
- 名字匹配支持主名和 alias
- 如果当前工具池里找不到,只会做很有限的旧 alias 兼容回退
- 再找不到,就直接生成错误
tool_result
对于模型来说,工具最重要的不是 TypeScript 实现,而是“工具说明文本”。
工具说明会告诉模型:
- 这个工具适合什么时候用
- 参数该怎么填
- 有哪些常见失败方式
- 什么时候不该用它
所以工具描述本质上是一种非常具体的 Prompt Engineering。
因为 Claude Code 并没有一个独立的工具选择器替模型做路由。模型能不能选对工具,很大程度取决于工具描述写得够不够清楚。
Claude Code 的工具设计不是把能力裸露给模型,而是把能力做成一层有边界的 API。
举个典型例子:
FileReadTool和FileEditTool明确分开- 读文件和改文件是两个不同权限等级的动作
- 工具层先把边界切清,权限系统才有可能做细粒度控制
这是读工具系统时最值得看清的一层分工。
- 这一轮有哪些
tool_use - 哪些调用能并发,哪些必须串行
- 并发结果按什么顺序回放
这里要特别注意:
单个 loop 的默认语义仍然是偏顺序推进。
编排层会判断并发安全性,但这不等于“Claude Code 默认把一轮里所有工具都并发跑掉”。
如果你前面已经读过 02 — Agent Loop,可以把它理解成:
02讲的是主链默认语义偏顺序03讲的是执行层在少数安全前提下如何做更细的调度
- 找到具体工具定义
- 校验输入 schema
- 调用工具自己的权限检查
- 真正执行
tool.call() - 把结果整理成统一结构
- 读取文件
- 写入文件
- 精准编辑文件
- 搜索文件名
- 搜索文件内容
- 编辑 Notebook
- 走 LSP 获取代码诊断与跳转
这是 Claude Code 最核心的一组工具,因为编码任务大多围绕它们展开。
BashTool 是最重要也最敏感的工具之一。
因为一旦能执行命令,Claude Code 的能力会非常强,但风险也会骤增。所以这类工具的实现通常最厚、最严。
WebFetchToolWebSearchTool- 各类 MCP 工具
这组工具让模型不只会看本地代码,还能查外部资料、调用外部系统。
AgentToolTeamCreateToolSendMessageToolTaskCreateToolTodoWriteTool
这组工具把 Claude Code 从单体 CLI 助手往多 Agent 协作系统推进了一步。
如果你只能挑一个工具深入看,优先看 BashTool。
原因不是它最常用,而是它最能体现 Claude Code 的工程思路:
- 命令语义需要分析
- 路径需要验证
- 破坏性动作要识别
- 只读模式要强约束
- 沙箱和权限模式都要兼容
换句话说,Claude Code 不是“把 shell 开给模型”,而是“把一层受控 shell 能力开放给模型”。
这里也能反向看出工具系统的设计边界:
- 工具系统不是只负责“路由”
- 权限系统也不是完全脱离工具系统独立运行
- 两者是在执行层内部咬合起来的
如果只是 TypeScript 世界里的函数,接口够用就行。
但 Claude Code 里的工具面对的是模型,所以还必须考虑:
- 描述是否足够清楚
- 输入是否足够结构化
- 返回是否足够稳定
- 权限语义是否足够明确
- UI 层是否知道怎么展示工具调用过程
这也是为什么工具系统往往会同时涉及:
- 类型系统
- JSON Schema
- Prompt 描述
- 权限控制
- 渲染逻辑
它没有设计一个外部模块去先判断:
- 应该用哪个工具
- 调用顺序是什么
- 要不要先搜索再编辑
这些决策大多直接交给模型。
工具系统做的事情是:
- 把能力清晰暴露出来
- 把边界和说明写清楚
- 在执行时严格检查
也就是说,Claude Code 选择的是:模型负责决策,工具系统负责提供清晰、稳定、安全的执行接口。
源码里的边界至少有三层:
- 工具原始输出
ToolResult<T>
- 协议层结果块
tool_result
- 外层消息包络
- 最终会落进 user-side message
所以“工具返回什么” 和 “模型最后看到什么” 不是同一个层级的问题。
工具不只是代码模块,也会进入模型上下文。
这就带来一个重要问题:
- 如果工具描述会跟着环境频繁变化
- 那模型侧看到的工具 Schema 也会变化
- 进而影响缓存和稳定性
这也是源码里把某些动态信息从工具描述中迁走的原因,比如动态 agent 列表。
更准确的说法是:
- built-in 和 MCP tools 会进入同一个执行池
- 两者共用主链上的查找、调用和结果装配框架
- 但在结果后处理和元数据透传上,MCP 会保留一些协议特例
不要一上来逐个看 40 多个工具。优先抓下面 4 件事:
- 工具契约怎么定义
- 工具是怎么注册进系统的
- 工具执行前后有哪些钩子和权限检查
- 为什么某些工具需要独立的安全子系统
getTools()、真实执行池、tool_result这三个概念千万不要混
工具系统是 Claude Code 的执行层核心,它做的不只是“提供功能”,而是:
- 用统一契约暴露能力
- 用 Prompt 描述引导模型调用
- 用 Schema 限制输入形状
- 用权限系统决定是否执行
- 用渲染层把工具过程反馈给用户
- 把“工具池构建”“工具调度”“单次工具执行”明确拆层
- 让 built-in 与 MCP 工具共享一套主链,只在少数协议点分叉
- 04 — 权限安全模型:继续看工具调用在真正执行前还要经过哪些检查
- 07 — 多 Agent 协作:继续看
AgentTool与团队工具如何把单体工具系统扩展成多 Agent 系统