章节导航 第 5 章 / 共 23 章
工具系统:42+ 工具的设计哲学
从 Tool Schema 到权限上下文,从工具注册到执行生命周期——拆解 Claude Code 工具系统的三层架构
5.1 三层架构
Claude Code 的工具系统不是一个简单的”函数注册表”。它是一个三层架构,每一层有明确的职责:
| 层 | 职责 | 核心文件 |
|---|---|---|
| 接口层 | 定义工具的标准化结构 | Tool.ts |
| 管理层 | 动态加载、权限过滤、工具池装配 | tools.ts |
| 执行层 | 工具调用的生命周期管理 | toolOrchestration.ts / toolExecution.ts |
这三层的依赖方向是单向的:执行层依赖管理层,管理层依赖接口层。这保证了每一层可以独立演进。
源码引用:工具系统三层架构参见 Tool.ts、tools.ts、toolOrchestration.ts。
5.2 Tool 接口:每个工具的身份证
Tool.ts 定义了每个工具必须遵循的契约。这不是一个轻量接口——它包含了从 Schema 到权限到执行的完整定义:
// Tool.ts 中的核心接口(简化重构)
interface Tool {
// 身份
name: string;
description: string;
inputSchema: ZodSchema; // Zod 运行时验证
// 行为标记
isConcurrencySafe: boolean | ((input) => boolean);
isReadOnly: boolean;
isDestructive: boolean;
shouldDefer: boolean; // 延迟加载
alwaysLoad: boolean; // 始终加载
// 权限
checkPermissions(input): PermissionResult;
toAutoClassifierInput(): string; // 给 AI 分类器的描述
// 执行
call(input): Promise<ToolResult>;
validateInput(input): ValidationResult;
}
几个关键设计决策:
Zod Schema 做运行时验证。TypeScript 的类型系统只在编译时有效,但模型返回的工具参数是运行时数据。Zod 补上了这个缺口——当模型生成了不符合 Schema 的参数时,Zod 会在运行时捕获并给出结构化错误信息,这个错误信息可以被送回给模型让它修正。
行为标记支持静态声明和动态计算两种模式。从接口定义可以看到,isConcurrencySafe 的类型是 boolean | ((input) => boolean)——它既可以是静态布尔值(大多数工具的做法),也可以是接收输入参数的函数。Bash 工具就使用了函数形式:它通过 shell-quote 库解析命令内容,判断 ls、cat、grep 等只读命令为并发安全(返回 true),而 rm、mv、写操作命令为非并发安全(返回 false)。这是工具接口设计中最精妙的部分之一——同一个行为标记在不同工具中有不同的计算策略。
Fail-Closed 默认值。buildTool 函数为所有工具填充默认值时,选择更安全的选项:
| 属性 | 默认值 | 含义 |
|---|---|---|
isConcurrencySafe | false | 假设不安全,串行执行 |
isReadOnly | false | 假设会写入 |
isDestructive | false | 假设非破坏性 |
这意味着:如果工具开发者忘记设置某个标记,系统会自动选择更保守的行为。这是安全工程中的基本原则——默认安全。
Fail-Closed 还要问一句:拒绝的语义被分类对了吗(v2.1.277)。 上面讨论的 fail-closed 都是”缺信息时选保守值”。官方修过一类不同性质的缺陷——保守值选对了,但错误的种类说错了:
Fixed the Write tool silently ending the turn as a declined permission when the target path is an existing directory; it now reports a clear error
Write 的目标是已存在的目录时,旧实现静默把这一轮当作 declined permission 结束。目录已存在是一个路径错误,却被表达成权限拒绝。后果不只是报错难读:模型读到”用户拒绝了这次写入”,会据此改变后续策略——可能换个路径重试,也可能放弃写入,而真正该做的是检查路径。
由此可以把 fail-closed 的讨论从”是否拒绝”扩展到”拒绝的语义是否被正确分类”。一个被错误分类的拒绝,比一个清晰的报错危险得多:前者会把上层决策带偏,而且不留下任何痕迹。
5.3 九大类工具
42+ 内置工具按功能分为 9 大类,此外 Plugin 系统引入了可安装的第三方工具作为新的来源类别(详见第 12 章):
| 类别 | 工具 | 并发安全 | 只读 |
|---|---|---|---|
| 文件操作 | FileRead / FileWrite / FileEdit / NotebookEdit | Read:✓ | Read:✓ |
| 代码搜索 | Grep / Glob | ✓ | ✓ |
| 网络工具 | WebFetch / WebSearch | ✓ | ✓ |
| 命令执行 | Bash / PowerShell / Sleep | 动态¹ | ✗ |
| 任务管理 | TaskCreate / Update / List / Stop / Output / Get | ✓ | 部分 |
| MCP 集成 | MCPTool / McpAuth / ListMcpResources / ReadMcpResource / LSP | 部分 | 部分 |
| 代理与团队 | Agent / Skill / TeamCreate / TeamDelete / SendMessage | ✗ | ✗ |
| 工作流控制 | EnterPlanMode / ExitPlanMode / EnterWorktree / ExitWorktree | ✗ | ✗ |
| 用户交互 | AskUserQuestion / TodoWrite / Config / ScheduleCron | 部分 | 部分 |
v2.1.214 新增 EndConversation 工具。该工具允许 Claude Code 在遭遇高度恶意用户或越狱尝试时主动结束会话——这是一个全新的工具类别方向(“安全防御”),在传统的九大类工具分类中不存在。EndConversation 的引入标志着工具系统从”被动执行”向”主动防御”的演进,与第 7 章权限安全模型形成互补——权限系统是”事前+事中”防护,EndConversation 是”事中终止”。
v2.1.247 新增 SendFeedback 工具(2026-08-26):会话中出错时,Claude 可起草一份反馈报告,供用户在 /feedback 审阅后发送;可通过 feedbackDrafts 设置关闭。官方原文:
Added the
SendFeedbacktool: when something goes wrong in a session, Claude can draft a feedback report for you to review and send from/feedback(turn off with thefeedbackDraftssetting)
它与 EndConversation 属于同一趋势的两端:2026 下半年新增的工具不再只是”让 Agent 能做事”,而是”让 Agent 能自我约束与自我上报”——EndConversation 是事中终止,SendFeedback 是事后上报。
还有一个设计细节值得单独学:它不直接发送。流程是”起草 → 用户审阅 → 用户发送”,自动化保留了人工闸门。这与 5.8 第 7 条”自主性降级是可逆能力”是同一设计哲学——给模型动作能力的同时,把最终决定权留在人手里。顺带一提,/feedback 这条通道本身也意味着会话内容可以外发到厂商,这是 feedbackDrafts 设置存在的理由。
几个值得注意的设计:
FileEdit 与 FileWrite 分离。FileWrite 是整文件覆写,FileEdit 是精确替换——old_str → new_str 模式。分离的好处是权限可以更细粒度:允许编辑但不允许覆写,或者反过来。
Bash 的并发安全是动态判断的(表中标注 ¹)。Bash 工具的 isConcurrencySafe 不是简单返回 false,而是解析命令内容做动态判断:ls、cat、grep、find 等只读命令被识别为并发安全,rm、mv、cp、重定向写入等命令被识别为非安全。这通过 shell-quote 库解析命令字符串实现。这是第 5.2 节所述”动态计算模式”的典型应用。
任务管理工具数量最多。TaskCreate / TaskUpdate / TaskList / TaskStop / TaskOutput / TaskGet——6 个工具覆盖了后台任务的完整生命周期。这说明多任务并行是 Claude Code 的核心使用场景之一。
工具来源现在有四个层级:内置工具(随 Claude Code 发布,上述 9 大类)、MCP 工具(用户配置的外部服务集成)、Plugin 工具(通过 /plugin 命令从市场安装,打包 Skills + Commands + Agents + Hooks)、组织托管 MCP 工具(v2.1.259,2026-09-02 新增)。Plugin 系统改变了工具注册架构——它引入了新的工具来源和注册路径,使得工具池不再局限于预定义和用户配置的范围。
⚡ 第四层:组织托管 MCP(v2.1.259,2026-09-02)
Added
managedMcpServersmanaged setting: organizations can provide HTTP/SSE MCP servers to every user (same entry shape as.mcp.json); entries that name a command to run are skipped组织通过
managedMcpServers托管设置向每位用户下发 HTTP/SSE MCP 服务器,条目形状与.mcp.json相同,但命名了要运行命令的条目会被跳过(即这一层只接受远程传输,不接受本地起进程)。这一层真正的差别不在技术形态,而在信任模型:前三层都由用户主动引入——安装客户端、配置 MCP、装插件;第四层是自上而下下发的,用户从未做过引入决策,甚至不一定知道工具池里多了什么。5.4 的工具供应链风险因此要从”用户会不会装到坏东西”扩展到”组织下发了什么”——后者没有用户侧的审查环节可以兜底。
注:2026-06-18 Claude Code 上线 Artifacts(Team/Enterprise Beta),将终端会话转化为交互式 HTML 网页(私有 claude.ai URL)。从工具系统视角,Artifacts 代表了一个此前不存在的工具类别——内容发布与协作(Publish)。它不像 Bash/FileWrite 那样修改本地文件,也不像 WebFetch 那样获取外部内容,而是将 Claude Code 的会话产出转化为 web 可访问的持久化产物。尚不确定 Artifacts 在源码层面是否作为独立 Tool 实现,但它对工具分类的扩展意义是明确的。
5.4 工具注册与发现
工具的加载不是”全部加载”,而是一个精心设计的发现过程:
getAllBaseTools()
├── 加载核心工具(始终可用)
├── 条件加载(Feature Flag 控制)
│ ├── feature('VERIFICATION_AGENT') → VerificationAgent
│ ├── feature('MONITOR_TOOL') → MonitorMcpTask
│ └── process.env.USER_TYPE === 'ant' → 内部工具
└── 环境适配
├── Windows → PowerShell 替代 Bash
└── hasEmbeddedSearchTools() → Bash 替代 Glob/Grep
└── --restricted → 移除命令/代码工具与 WebFetch(除非 --tools 命名)
第四种裁剪维度:--restricted(v2.1.248,2026-08-27)。 上面三种裁剪机制做的都是”加法或替换”——按条件添加工具,或按平台替换实现。--restricted 是唯一的减法:
Added
--restricted(orCLAUDE_CODE_RESTRICTED=1): removes the built-in tools that run commands or code andWebFetch(unless named in--tools), keeps file tools inside the working directory, refusesbypassPermissions, and ignores user, project and local settings files
它做了四件事,其中只有第一件属于”工具池裁剪”:移除运行命令或代码的内置工具与 WebFetch(除非在 --tools 中显式命名);把文件工具限制在工作目录内;拒绝 bypassPermissions;忽略用户、项目与本地三层设置文件。
最后一点是关键。前三种裁剪机制的结果都依赖本地状态——装了哪些插件、配置了哪些 MCP、settings.json 里写了什么。--restricted 让工具池不再依赖任何本地状态,这正是平台方以最小权限嵌入 Claude Code 所需要的形式:调用方能预先钉死能力边界,而不必信任被嵌入方的配置。
加载完成后,通过 filterToolsByDenyRules 应用权限过滤——被 deny 规则匹配的工具直接从列表中移除,模型根本看不到它们。
2026-10 又出现三个维度,而且它们不属于加减工具,是”改工具的输入输出形态”:
| 版本 | 维度 | 官方 changelog 要点 |
|---|---|---|
| v2.1.285 | WebFetch 整体消失 | CLAUDE_CODE_DISABLE_WEB_FETCH:把 WebFetch 工具关掉 |
| v2.1.296 | Read 解除体积限制 | 新增 allow_large 选项,需要全文且上下文放得下时一次读完超限文件 |
| v2.1.299 | @ 提及只给路径 | CLAUDE_CODE_DISABLE_FILE_MENTIONS:Claude 拿到 @ 提及文件的路径去自己读,而不是文件内容 |
最后一个与 5.8 第 6 条「工具配置本身也是上下文成本」直接对应:把 @ 提及从”注入内容”改成”给路径”,是官方第一个把上下文成本直接做成开关的例子——用一次工具调用换掉一段上下文。这说明”上下文成本”在本章不再只是一个需要被测量的量,它已经变成了一个有开关的产品行为。
因此上面那句要更新:--restricted 仍是唯一的”整层减法”,但不再是唯一的裁剪方式——裁剪的对象从”工具列表”扩展到了”单个工具的输入输出形态”,而且方向不再只是减法(allow_large 是加法)。
权限粒度演进:v2.1.166 引入 Glob deny rules(工具名位置支持通配符,如 Bash(git push*))。v2.1.178 引入参数级权限规则 Tool(param:value)(如 Agent(model:opus) 仅允许特定条件下使用)。这两项增强将权限控制从”工具级”升级为”参数级 + 模式级”——permission rules 不仅可以 deny 整个工具,还可以 deny Bash 的特定命令或 Agent 的特定模型配置。
第五个维度:网络访问下沉为命令级参数(v2.1.271)。 上面四类裁剪动的是工具集与参数值,网络维度长期不在工具参数里——它是一个整会话开关。v2.1.271 补上了逐命令的白名单:
Added per-command
allowed_domainsto Bash, PowerShell and Monitor in auto mode with sandboxing: the hosts a command needs are reviewed with it and opened for it alone; other hosts are refused
机制上是三条:命令所需的 host 与命令本身一起被审查,只为这一条命令开放,其余 host 一律拒绝。由此网络策略从”会话级开关”下沉为”命令级参数”,工具 schema 随权限态动态变化又多了一个维度——同一条 Bash 命令,在不同 allowed_domains 下允许的出口范围不同。第 7 章 7.9.2 的沙箱能力矩阵中”网络 ❌ 未覆盖”一格据此改为部分覆盖。
这套语法的静默失效方式(v2.1.260,2026-09-03 的一组官方反例)。 上面给了两个正例,但权限语法最危险的失效模式是写错了不报错、只是静默不生效。官方在 v2.1.260 一次性修掉了好几种:
| 失效形态 | 后果 | 处置 |
|---|---|---|
右括号后有文本,如 Bash(ls) x | 从未匹配任何内容 | 现在报告为无效设置,不再静默忽略 |
Edit/Write/Read 规则路径含括号 | 规则被丢弃或被沙箱忽略,“只读”文件夹实际可写 | 修复;并改进 Edit(C:\dir\(name)\**) 这类转义括号的报错提示 |
含不可编译模式(如未闭合的 [) | 所有文件编辑失败,报 Invalid regular expression | 现在该 deny 规则守护其拼写的字面路径 |
| Glob/Grep 在权限检查前探测磁盘搜索路径 | 权限决定前就暴露路径是否存在 | 改为权限决定后再报告缺失路径(与 Read 一致) |
v2.1.259 曾把 Read() deny 规则应用到 Bash 参数 | npm run build 在 Read(./**/build/**) 下被拒绝;cd … && grep 在 auto 模式也提示 | v2.1.260 回滚 |
原文的几条:
Fixed
Edit/Write/Readpermission rules whose path contains parentheses being dropped as invalid or ignored by the Bash sandbox, which left “read-only” folders writable
Fixed one file permission rule with an uncompilable pattern (e.g. an unclosed
[) making every file edit fail withInvalid regular expression; such a deny rule now guards the literal path it spells
Reverted the 2.1.259 change applying
Read()deny rules to Bash arguments; it deniednpm run buildunder aRead(./**/build/**)rule in every mode and madecd … && grepprompt even in auto mode
两个值得记住的点。第一,“只读文件夹实际可写”是安全方向的失效——规则看起来生效了,实际没有,而用户会以为自己受保护。第二,最后一条是一条”过度收紧又被撤回”的案例,说明权限语法的适用边界在实践中仍在收敛:Read() 规则的语义到底是管文件读取还是管 Bash 参数,官方自己也在试。
实践提示:v2.1.246 起,Bash allow 规则在子命令前使用通配符(如 Bash(git * main))会在启动时被警告,因为它们也匹配插在子命令前的选项。⚠️ 提案把这条系于 v2.1.257,实际为 v2.1.246。
AI 分类器不是最终裁决者:auto 模式的红线(v2.1.257,2026-09-01)。 5.5 步骤 4 的 checkPermissions 会走到 AI 分类器决策,但分类器有它无权放行的类别:
Added a Containment Escape rule to auto mode so cloud metadata-credential fetches, egress evasion, and cross-tenant reach are no longer auto-approved unless your environment marks them expected
云元数据凭据获取、出口规避与跨租户访问不再被自动批准,除非环境将它们标记为预期。这对第 7 章”AI 分类器的边界”是一个此前缺失的事实:分类器之上还有一条硬编码红线。
同一版本还加了工作目录外读取的一次性提示,可用 permissions.blockReadsOutsideWorkingDirectories 直接阻断。另外 v2.1.246 起 /permissions 有 Auto mode 标签页,可查看与编辑 auto 模式分类器规则——分类器规则从黑箱变成了可审计、可编辑的对象。
最后,assembleToolPool 将内置工具与 MCP 工具合并:
// tools.ts 中的工具池装配(简化)
function assembleToolPool(baseTools, mcpTools) {
const sorted = [
...baseTools.sort(byName),
...mcpTools.sort(byName),
];
// 按名称去重,内置工具优先级更高
return uniqBy(sorted, tool => tool.name);
}
排序是有意义的——排序后的工具列表在 System Prompt 中的顺序是稳定的,这提升了 Anthropic API 的 Prompt Caching 命中率。如果工具列表的顺序每次都变,缓存就失效了。
源码引用:工具注册与发现参见 tools.ts#L193-367。
工具供应链安全
上文的 assembleToolPool 将 MCP 工具无条件合并到工具池,filterToolsByDenyRules 仅关注权限过滤和去重,但两者都未涉及工具来源的可信度验证。随着 Plugin 系统上线,Claude Code 的工具来源从”预定义 + 用户配置”扩展到”可安装第三方包”,供应链安全成为一个不可回避的问题。
OWASP 于 2026 年发布的 Agentic Skills Top 10 和 Agentic AI Top 10 直接关联工具系统的安全设计。两个框架互相补充——AST 聚焦可安装组件级风险,ASI 聚焦运行时系统级风险。
Agentic Skills Top 10(AST01-AST10)——技能/插件级风险:
| OWASP 编号 | 风险 | 严重度 | 真实案例 | 与 Claude Code 工具系统的关系 |
|---|---|---|---|---|
| AST01 | 恶意技能 | Critical | ClawHavoc:1,184 恶意技能 | Plugin 打包 Skills + Commands + Agents + Hooks,任一层被污染即构成威胁 |
| AST02 | 技能市场投毒 | Critical | ClawHub collapse | /plugin 命令从市场安装,缺乏安全审核时投毒可大规模扩散 |
| AST03 | 权限提升 | High | 280+ 凭证泄露技能 | 恶意工具可能利用 tool.call() 绕过 checkPermissions |
| AST04 | 不安全元数据 | High | Fake “Google” skill 伪装 | SKILL.md 中的误导性描述可能欺骗用户和分类器 |
| AST05 | 不安全反序列化 | High | YAML 载荷注入 SKILL.md | 配置/元数据解析缺乏沙箱化 |
| AST06 | 弱隔离 | High | OpenClaw host-mode 135K 暴露实例 | 容器逃逸或权限边界不足 |
| AST07-10 | 凭证泄露/不安全通信/日志注入/资源耗尽 | Medium-High | 各类实际事件 | 影响整体安全态势 |
Agentic AI Top 10(ASI01-ASI10)——系统级风险:
| OWASP 编号 | 风险 | 严重度 | 与工具系统的关系 |
|---|---|---|---|
| ASI01 | Agent 目标劫持 | Critical | 规则文件后门、.cursorrules 隐藏 Unicode 注入可操纵工具调用目标 |
| ASI02 | 工具滥用 | Critical | MCP 服务器参数操纵可读取 /etc/passwd 等敏感文件 |
| ASI06 | 供应链漏洞 | High | 工具依赖链中的恶意代码注入 |
Georgia Tech 数据显示 AI 编码工具相关 CVE 从 1 月的 6 个增至 3 月的 35 个,增速显著。
当前防护覆盖度:Claude Code 的权限系统(第 7 章)和沙箱(Seatbelt/bubblewrap)在工具执行阶段提供了防御,但对供应链攻击的覆盖是间接的——它们防御的是”工具做了坏事”,而非”工具本身就是坏的”。2026-05-27 Anthropic 发布的 Claude Code 安全插件(v2.1.144+)在代码编写、会话结束与提交三阶段实时拦截并修复漏洞,部分缓解了”工具本身就是坏的”风险。基准测试显示安全修复意见减少 30%-40%。
真实案例:Claude Code GitHub Actions 供应链漏洞(2026-06):GMO Flatt Security 研究员 RyotaK 发现 claude-code-action 存在间接提示注入 + 权限检查绕过漏洞(CVSS 7.8)。攻击链:注册 GitHub App → 在任何公开仓库创建 Issue → 利用 checkWritePermissions 无条件信任 [bot] 实体的缺陷绕过权限检查 → 在 Issue body 中植入间接提示注入载荷 → Claude Code 读取 /proc/self/environ 窃取 CI/CD secrets → 重放 OIDC token 交换获取仓库写入权限。此前 Cline 已在 2026-02 被类似手法攻击(npm publish token 被盗)。Anthropic 在 v1.0.94 修复,但该案例揭示了 AI 工具系统供应链安全的一个根本矛盾:Agent 读取的内容继承了内容作者的信任级别——任何能向 Agent 提供输入的攻击者都可以尝试重定向其行为。
AST10 v1.0 正式发布(2026-07):OWASP AST10 已完成公共审查并发布 v1.0。Snyk ToxicSkills 扫描 3,984 技能中发现 1,467(36.82%)含安全缺陷、534(13.4%)有严重问题、76+ 确认恶意载荷。ClawHavoc 攻击战役累计 1,184 恶意技能(Antiy CERT,2026-02)。
Claude Code 特定 CVE(Check Point Research):两个 CVE 揭示了 Agent 工具系统的一个深层攻击面——仓库级配置文件成为执行层:
- CVE-2025-59536(CVSS 8.7):仓库级配置文件通过 Hooks 机制触发任意 Shell 命令执行——在用户同意对话框出现前即可执行。仅克隆并打开不受信任项目即可触发。
- CVE-2026-21852(CVSS 5.3):恶意仓库配置文件覆盖
ANTHROPIC_BASE_URL以重定向 API 凭证。 - 这两个 CVE 的共同模式是:信任了仓库内容——
filterToolsByDenyRules和assembleToolPool均发生在仓库配置已加载之后,对此类攻击无防护。
2026-09 ~ 10 的三条官方证据,把上面的”真实案例”从第三方披露补到了官方自修:
| 版本 | 形态 | 官方 changelog 要点 |
|---|---|---|
| v2.1.286 | 供应链(来源形态收窄) | 插件安装拒绝 git 仓库或文件夹形式的 npm 源,插件依赖只能从 registry 包安装 |
| v2.1.289 | 工具描述完整性 | 修掉”用户安装的插件可以改写组织托管 MCP server 的登录工具描述” |
| v2.1.293 | 排序规则变更 | 含非 ASCII 字符的 agent 名与 MCP server 名排到 ASCII 名之后 |
- 第一条是”市场审核机制仍在建设中”这条的进展:从 v2.1.275 的”拿到了不执行脚本”(
npm pack --ignore-scripts)前移到”根本不允许从 git/文件夹拿”。 - 🔴 第二条是本章全部引用里唯一一个由 Anthropic 在自己代码里修掉的真实 tool poisoning 实例。 攻击发生在两个都受信的实体之间——用户侧插件顶替了组织托管 MCP server 的登录工具描述,托管的信任等级被用户侧内容覆盖。它比第三方研究报告更硬:研究报告只能证明攻击可行,官方的一条 Fixed 证明它在生产代码里真实存在过。⚠️ 官方只给一句 Fix,未说明插件通过什么机制改写描述(hook?工具注册?),本书不推测。
- 第三条与 5.4「排序稳定 = 保缓存」是一对:排序规则本身的变更是一次全量缓存前缀失效事件(该推论见 Ch02)。
MCP 协议的信任模型反转(CSA “Poisoned Foundations”,2026-07):CSA 报告指出 MCP 的 auto-discovery + auto-invocation 特性创造了反常的信任模型——传统 client-server 架构中 client 请求数据、信任流向 server;MCP 环境中,server 定义工具和指令,AI client 基于自然语言上下文执行。恶意 MCP server 不需要传统代码执行漏洞即可操控 agent 行为——仅通过修改工具描述、metadata 和响应内容。Claude Code 的 assembleToolPool 将 MCP 工具无条件合并,信任了工具的自我描述(tool descriptions 进入 System Prompt 即成为模型”信任的指令”)。报告引用 NSA 咨询、OX Security 发现的跨 SDK 命令注入漏洞(影响 1.5 亿下载的 MCP SDK)、以及 CVE-2026-33032(CVSS 9.8,nginx-ui MCP 未认证命令执行)。“Tool Poisoning”和”Rug Pull”被识别为 MCP 生态特有的攻击类别。
COPEX(arXiv:2610.04378《COPEX: Benchmarking LLM Robustness to Adversarial Context Across Model Context Protocol Layers》,2026-10-03):上面 CSA 的论述是定性 + 生态侧统计,COPEX 是把模型隔离出来测的第一个基准——固定外围 agent 栈、只换”选工具的那个模型”,从而把模型易感性与系统暴露面分开。abs 页逐字:
COPEX covers 25 attack types instantiated as 125 scenarios across four entry surfaces: model/agent, client, server/tool, and transport. Across nine models and 3,375 trials, the mean attack success rate is 64.4%, with surface-level means ranging from 58.3% to 71.4%. Some client- and transport-level attacks succeed partly outside the model’s observation or control, separating system exposure from model susceptibility. Combined input and context scanning reduces mean attack success by 49.6% on an eight-attack defense subset relative to the undefended setting.
三条解读:
- 本章此前引用的安全数字全是生态侧统计(恶意技能数、含缺陷技能比例、CVE)。COPEX 给的是模型/协议层抗攻击能力的实测——平均攻击成功率 64.4%,分面均值 58.3%–71.4%。⚠️ 摘要未指明哪个面对应哪个均值,本书不猜;“9 个模型”摘要未列名单,引用时只写数量。
- 第四个入口面 transport 是本章完全没有的一格。 CSA 条目列的工具描述 / metadata / 响应内容,全部落在 server/tool 面——本章此前的攻击面清单缺了四类里的三类。
- 🔴 最关键的一句:官方摘要明说部分 client 层与 transport 层攻击在模型的观测与控制范围之外成功。这与本章”工具描述进入 System Prompt 即成为模型信任的指令”合起来,指向一个结论——防护不能只做在”让模型更谨慎”这一侧:有一整类攻击模型根本看不到,“系统暴露”与”模型易感性”必须分开度量。
防护缺口:MCP 工具来源验证目前依赖用户自行审查 MCP 服务器配置;Plugin 系统的市场审核机制仍在建设中;assembleToolPool 合并工具池时未对 MCP/Plugin 工具进行来源签名验证。AST04(不安全元数据)和 ASI01(目标劫持)是当前防护体系最薄弱的环节。
🔴 2026-10-17 改写:缺口的形状比上面这句更具体,而且不止一处:
- 缺口不只在模型侧。 COPEX 证明有一类攻击在模型可观测范围之外成功——传输层是不可见的那一段,只靠”让模型更谨慎”覆盖不到。
- 缺口也不只在”工具来源没验签”,还在”工具描述可以被另一个已安装的组件改写”。 v2.1.289 官方修掉的正是这一条(详见下文真实案例段):用户安装的插件可以改写组织托管 MCP server 的登录工具描述。
assembleToolPool未做签名验证这一点,在 v2.1.289 之后有了具体的失效形态——不是”万一有人塞了个坏 server”,而是两个都受信的实体之间,信任等级被顶替。
5.5 工具执行生命周期
一个工具从被模型调用到返回结果,经历以下步骤:
1. Schema 验证 → Zod.safeParse(input)
2. 业务规则验证 → tool.validateInput(input)
3. PreToolUse Hook → 用户自定义前置钩子
<!-- edit: 2026-10-17 5.5 补 hook 的失败语义(v2.1.295 / 294)与并发语义(v2.1.302);三条 changelog 原文已核 -->
⚠️ **Hook 自身的三种语义(2026-10-17 补)**。上表第 3、7 步只说"可定义自定义脚本",2026-10 的三条把 hook 自身的行为也变成了可配置项:
1. **失败语义(v2.1.295)**:`onFailure: "block"`——hook 起不来、超时、退出码异常时**阻断动作**而不是放过去。官方 changelog 逐字:"a hook that can't start, times out, or exits with an unexpected code blocks the action instead of letting it through"。**fail-closed 从"工具行为标记的默认值"延伸到了"hook 自身的故障"**——5.2 讲的 fail-closed 此前只覆盖前者。
2. **自然语言规则的失败(v2.1.294)**:官方修掉"`prompt` 与 `agent` hooks 写成指令形式(如 "Block commands that...")时放行了本该阻止的内容"。这对 5.6「描述是给模型的指令」是一条**反向提醒**:**用自然语言写的规则,在 hook 这一层同样会被执行错**。5.6 讲"怎么写描述模型才听得懂",这条讲的是"**写了它也不一定照做**"——同一条规则在模型侧与 hook 侧有各自的失效模式。
3. **并发语义(v2.1.302)**:同一事件的 prompt / agent hooks **最多同时跑 12 个**,其余排队。**hook 有自己的容量,不是零成本旁路**——事件密集时它会成为等待点。
4. 权限检查 → checkPermissionsAndCallTool()
5. 工具执行 → tool.call(input)
6. 结果格式化 → mapToolResultToToolResultBlockParam()(三级容量策略,见下)
7. PostToolUse Hook → 用户自定义后置钩子
8. 结果返回 → yield 给 QueryEngine
步骤 3 和 7(Hooks)是用户可扩展的点——你可以在 .claude/settings.json 中定义在某个工具执行前后运行自定义脚本。这在第 11 章会详细介绍。
步骤 4(权限检查)是整个流程中最复杂的环节,涉及六层规则来源、AI 分类器、路径安全验证等。这在第 7 章会完整剖析。
v2.1.200 默认策略变更:v2.1.200 将默认权限模式从 Default/Auto 改为 Manual(手动确认),覆盖 CLI、VS Code、JetBrains 全平台。这意味着在不匹配任何显式权限规则时,工具调用的默认行为从”invoke AI 分类器决策”变为”等待用户手动确认”。AskUserQuestion 对话框也取消了自动延顺。这是自 Claude Code 发布以来最重大的安全默认值调整——从偏向”便利性”转向偏向”安全性”。对工具执行生命周期的影响:步骤 4 的 canUseTool() 在不匹配规则时的默认路径已从 ask → AI 分类器 变为 ask → 用户手动确认。
Dynamic Workflows 子Agent 的简化执行路径
Dynamic Workflows 于 v2.1.197 正式 GA(此前为 Research Preview),面向 Pro 计划用户开放。支持多达 1,000 个并行子代理——这在 v2.1.172 的 5 层嵌套基础上进一步扩展了横向扩展能力。一个典型锚点:Bun 项目使用 Dynamic Workflows 将 75 万行 Zig→Rust 的代码迁移在 11 天内完成,最终测试兼容性达到 99.8%。
Dynamic Workflows 中子Agent 的工具执行路径与上述 8 步流程有关键差异:子Agent 在 acceptEdits 模式下运行,文件修改自动审批,跳过了步骤 4(checkPermissionsAndCallTool)中的用户确认环节。编排脚本自身的安全约束是:不能访问文件系统或 Shell,只能通过派生子Agent 间接执行——形成”脚本只调度、Agent 只执行”的职责分离。
风险分析:acceptEdits 模式下,恶意任务描述可能通过编排脚本驱动子Agent 执行非预期操作而绕过人工审核。这与 5.4 节的 ASI01(Agent 目标劫持)风险直接相关——脚本可信但任务描述不可信时的安全策略需要在第 7 章中明确。信任边界的定义是 Dynamic Workflows 安全设计的关键问题。
v2.1.212:/fork 行为变更与 /subtask 新命令。v2.1.212 对子 Agent 的触发方式做了根本性区分:/fork 从”启动会话内子 Agent”变为”将对话复制到新背景会话(独立的 claude agents 行)“,而原来的”会话内子 Agent”角色迁移到了新命令 /subtask。Dynamic Workflows 场景下,子 Agent 通常在 /subtask 模式下运行(共享父会话的某些状态),而 /fork 则适用于需要完全独立会话上下文的工作流。这一变更对第 8 章多代理架构的影响更为深远——参见 8.5 节的详细分析。
v2.1.216:沙箱文件系统隔离的可参数化。sandbox.filesystem.disabled 设置允许跳过文件系统隔离但保留网络出口控制——这打破了此前的”沙箱 = 文件系统 + 网络”的耦合假设。对工具执行的影响:当文件系统隔离被跳过时,工具(Bash/FileRead/FileWrite 等)可以访问完整文件系统而非仅限沙箱目录,但网络出口仍受控制(结合 v2.1.219 的 sandbox.network.strictAllowlist)。这为需要在完整文件系统上操作但严格限制网络访问的工具场景提供了新的灵活性。
5.6 工具描述的艺术
工具的 description 不是给人看的——它是给模型看的指令。模型通过阅读工具描述来决定何时使用哪个工具。写好工具描述是 Context Engineering 的一部分。
以 Grep 工具为例(概念性描述):
Search for a pattern in files using ripgrep.
- Returns matching lines with file paths and line numbers.
- Supports regex patterns.
- Use this when you need to find specific code patterns across many files.
- Prefer this over FileRead when you don't know which file contains the pattern.
注意最后一句——它不只是描述功能,还告诉模型在什么场景下选择这个工具而不是另一个工具。这种”选择指导”在复杂工具集中特别重要,否则模型可能会在应该用 Grep 的时候用 FileRead 逐个文件搜索。
第一个带数字的”工具该怎么设计”实证(arXiv:2610.05009《Rethinking Tool Design for Agentic RCA: A Controlled Empirical Study》,2026-10-04)。 5.6 此前只有原则与概念性示例,5.8 第 5 条也只有 JSON vs code execution 的定性权衡。这项研究把工具抽象层级做成了可控变量:24 个结构化工具分指标访问(L1)/ 证据分析(L2)/ 诊断(L3),外加 Python 参考设置(L0);375 个故障用例、三个微服务系统、8 种配置。abs 页逐字给出三条反直觉结论:
- 更高抽象不是更准,是更快。 L3 的 top-1 定位准确率 82.8% vs L0 的 85.4%,但每例耗时不到一半。(主模型为 Qwen3.7-Plus;另在四个 Qwen 模型上比较了四种共享配置。)
- 加工具层级会在一个指标上变好、另一个上变差。 提升故障类型识别,却降低定位准确率;轨迹分析还发现 agent 在咨询额外证据后会推翻自己原本正确的诊断建议。
- 🔴 模型选择改变工具收益。 在 L1+L2 之上加 L3 对三个模型有提升,对 Qwen3-8B 反而变差——因为它很少调用 L3;且工具配置的排序随目标系统而变。
对本章的意义:本章把工具描述当作模型的固定输入,这篇说明工具粒度与描述的有效性是与读它的模型共同决定的。Ch02 记录的 claude plugin eval 那个”Δ≈0 且 tool_used: Skill grader 失败”的高频首次发现,其实是同一个现象的轻量版——模型根本没选这个工具,于是描述写得再好也不产生差别。因此 5.6 那句”描述要给模型看选择指导”应当补一个前提:先确认这个模型会不会调用它。
⚠️ 置信度:中。数值本身可靠(abs 页逐字),但实验在单一任务域(微服务 RCA)与单一模型族(Qwen)内完成,不可直接外推到 Claude Code 的通用编码场景。另:82.8% / 85.4% 的口径为 top-1 localization accuracy,其具体定义与测试集划分未回正文确认;“耗时不到一半”未给绝对秒数。
5.7 延迟加载与 ToolSearch
一个带真实数字的官方案例(v2.1.248,2026-08-27)。 5.6 与本节此前只有原则性论述和一个 Grep 的概念性示例,内置工具侧没有任何量化数据。Workflow 工具补上了这个缺口:
Improved the Workflow tool’s prompt footprint: its description is now about 1k tokens instead of 5.7k, with the script-writing reference moved into a bundled
workflow-authoringskill
一个工具把自己的描述从约 5.7k token 压到约 1k token,降幅约 82%,做法是把脚本编写参考外移到捆绑的 workflow-authoring 技能,按需加载。
可以提炼成一条可复用的模式:工具描述只放”什么时候用我”,详细参考放按需加载的技能。这与 5.6 结尾”告诉模型在什么场景下选择这个工具而不是另一个工具”的选择指导原则并不冲突——Workflow 保留在描述里的正是选择指导,被外移的是编写参考;前者决定模型会不会选错工具,后者只在选对之后才需要。
42+ 个工具全部塞进 System Prompt 会消耗大量 Token。Claude Code 的解决方案是延迟加载——通过 shouldDefer 标记,部分工具在初始加载时不包含在提示词中,只有当模型调用 ToolSearch 工具查询后才动态加入。
这本质上是一个两阶段工具发现机制:
- 常用工具始终可用(
alwaysLoad: true) - 非常用工具通过搜索发现(
shouldDefer: true)
REPL 模式下的原始工具还采用单例模式(singleton),避免每次会话重复实例化。
延迟加载有两种成因,不要混为一谈。 上面讲的是第一种:主动延迟——为了控制工具集规模与按需供给,把非常用工具从初始提示里拿掉。第二种成因是被动的,且发生在没有 ToolSearch 的会话里:
Fixed mid-session MCP and plugin tools being added to the tool list in sessions without ToolSearch, which broke prompt-cache reuse; supported models now receive them as deferred definitions
这里 deferred 的目的不是缩小工具集,而是保住 prompt cache 的前缀稳定性——中途加入的 MCP 与 plugin 工具若直接插进工具列表,会把已缓存的前缀整个重写一遍。v2.1.268 把同一思路推广到第三方云平台:
Changed Bedrock, Vertex and Foundry sessions to keep the tool list byte-stable across a conversation (late-connecting tools load deferred instead of rewriting it), matching first-party sessions
同一个 deferred 机制服务于两个目标,读者若只知道第一种,遇到”我没开延迟加载,为什么新装的 MCP 工具没立刻进工具列表”就会误判为故障。判据:看会话有没有 ToolSearch——有,deferred 是为了按需;没有,deferred 是为了缓存。
MCP Tool Search:三阶段延迟加载
2026 年初,Claude Code 推出 MCP Tool Search 功能,将延迟加载从内置工具扩展到 MCP 工具——解决了”启动税”问题。之前所有 MCP 工具 Schema 在会话启动时全部加载到上下文,有时消耗 50+ 工具的定义。新机制改为按需搜索加载,节省约 90% 的上下文窗口占用。
工具发现机制从两阶段演进为三阶段:
| 阶段 | 加载策略 | 典型工具 | 上下文占用 |
|---|---|---|---|
| 1. 常驻工具 | alwaysLoad: true,始终在 System Prompt | FileRead / Bash / Grep 等 | 固定 |
| 2. 内置延迟加载 | shouldDefer: true,通过 ToolSearch 发现 | NotebookEdit / TaskStop 等 | 按需 |
| 3. MCP Tool Search | 按需搜索 MCP 服务器上的工具 | 全部 MCP 工具 | 按需(节省 ~90%) |
MCP Tool Search 与内置 ToolSearch 的关键区别:内置 ToolSearch 在本地工具池中搜索(工具已注册但描述未加载),MCP Tool Search 则需要向 MCP 服务器发起查询请求(工具在远程,描述和 Schema 按需获取)。这意味着 MCP Tool Search 引入了网络延迟,但换来了上下文窗口的大幅节省——对于连接了多个 MCP 服务器的用户,效果尤为显著。
2026-10-17:粒度反转——工具级可以覆盖 server 级。 v2.1.285 官方 changelog 逐字:
Changed MCP tools so a tool that sets its own
_meta['anthropic/alwaysLoad']to false stays deferred when its--mcp-config, Agent SDK or plugin server is set toalwaysLoad
即一个工具可以用自己的 _meta['anthropic/alwaysLoad']: false 声明保持 deferred,即使它所属的 server 被设为 alwaysLoad。⚠️ 本轮只见到这一条,_meta 是否还有其他取值、以及后续有无修订未确认。
与上面 5.7 的四轨化合并起来看:MCP 侧”工具如何进入上下文”已从单一开关变成多级协商——server 级(alwaysLoad)→ 工具级(_meta['anthropic/alwaysLoad'])→ 加载路径级(up-front 4,096 / tool search 16,384 / 第一方不截断)。本章三阶段表描述的是”阶段”,现在还要加一列”每个阶段内谁说了算”。
MCP 2026-07-28 正式发布对工具发现和缓存的影响
MCP 于 2026-07-28 正式发布——这是史上最重要的 spec 发布之一。SDK 月下载超 4 亿(4× 年增长),FigJam、Intuit、Netlify、Zoom 等企业已公开背书。核心结构性变更直接影响 Claude Code 的工具发现:
- 从双向有状态到 Request/Response 无状态模型:Server 可部署在 serverless/edge 基础设施,不再依赖持久连接
ttlMs/cacheScope字段:tools/list响应新增缓存控制,客户端可按 TTL 指引轮询而非保持持久 SSE 连接,降低工具列表变更检测的连接开销Mcp-Method/Mcp-Name必需头:API 网关可在头级别区分tools/list和tools/call,无需解析请求体- Session 消除:远程 MCP 工具的发现请求可路由到任意服务器实例,简化负载均衡
- JSON Schema 2020-12 强制(已生效):工具 inputSchema 必须符合完整 JSON Schema 2020-12 规范。Claude Code 内部使用 Zod(见 5.2 节),而 Zod 与 JSON Schema 2020-12 之间存在格式差异(如 Zod 的
.default()/.refine()/.transform()无直接 JSON Schema 等价物)。MCP 工具的 Schema 验证链路需要引入 Zod → JSON Schema 2020-12 的双向转换层——这一要求在 7/28 正式发布后已变为强制性约束
对 Claude Code MCP Tool Search 的影响:本地 STDIO 基本不变,但远程 MCP Server 的工具发现将更轻量,缓存策略需适配 TTL 语义。三阶段延迟加载架构无需根本性调整,但阶段 3 的 MCP 服务器交互逻辑已正式适配无状态协议。
MCP 超时配置与常见陷阱
MCP 工具的另一个容易被忽视的工程细节是超时配置。request_timeout_ms 字段允许为每个 MCP 服务器单独配置工具调用超时:
{
"mcpServers": {
"db-query": {
"command": "npx",
"args": ["-y", "@company/db-mcp"],
"request_timeout_ms": 30000
}
}
}
v2.1.206 之前的关键 bug:request_timeout_ms 字段在所有 MCP 服务器配置中被静默忽略,所有 MCP 工具调用强制回退到 60 秒默认值。这意味着无论你设了 5 秒还是 300 秒,实际表现都一样——长操作(数据库查询、代码执行沙箱、图片生成)被过早中断,而需要快速失败的调用又等待过久。v2.1.206(2026-07-09)修复了这一问题,per-server 超时配置现在真正生效。
⚠️ “per-server timeout 真正生效”这句话要分层读(v2.1.274)。 上面这句在 v2.1.206 的语境下正确,但官方后来修掉了一条不受它控制的硬上限:
Fixed Streamable HTTP MCP tool calls timing out after about 5 minutes even when a longer per-server
timeoutwas set在 Streamable HTTP 传输上,即使把
request_timeout_ms配成 10 分钟,调用仍会在约 5 分钟被切断。v2.1.206 修的是”配置被静默忽略”,v2.1.274 修的是”配了也有一层更硬的盖子”——两者不是同一件事。准确表述是:per-servertimeout对一般情况生效,但 Streamable HTTP 上曾存在约 5 分钟硬上限。
配置注意:低于 1000ms 的值被截断到 1 秒看门狗——不要期望毫秒级超时。建议为长时操作 MCP 服务器(数据库、沙箱、图片/视频生成)显式配置超时,避免被 60 秒默认值截断。
MCP 工具描述与 server instructions 的 2,048 字符上限是可调的(v2.1.280)。 默认对 MCP 工具描述与 server instructions 截断到 2,048 字符;v2.1.280 起可用 CLAUDE_CODE_MAX_MCP_DESCRIPTION_LENGTH 调整:
Added
CLAUDE_CODE_MAX_MCP_DESCRIPTION_LENGTHto change the 2,048-character cap on MCP tool descriptions and server instructions for every MCP server in the session
⚠️ 2026-10-17 重写:这个上限自 v2.1.295 起不再是一个数,而是按加载路径分叉的四条轨道。
| 轨道 | 上限 | 版本 | 官方 changelog 原文要点 |
|---|---|---|---|
会话级总开关 CLAUDE_CODE_MAX_MCP_DESCRIPTION_LENGTH | 可调(原默认 2,048) | v2.1.280 | ”for every MCP server in the session”——对该会话所有 server 统一生效 |
| 经 tool search 加载的工具描述 | 16,384(原 2,048) | v2.1.295 | ”descriptions the model loads through tool search” |
| sent up front 的工具描述 + server instructions | 4,096(原 2,048) | v2.1.296 | ”the default limit on MCP tool descriptions sent up front and on MCP server instructions” |
Anthropic 第一方 server(api.anthropic.com) | 不截断 | v2.1.299 | ”to reach Claude in full instead of being cut at 4,096 characters” |
结论因此要改写三处:
- 不是 per-server,但也不再是单一会话级上限——它是”会话级总开关 + 三条按加载路径分叉的轨道”。写工具描述时不能假设”给这个服务器单独开个例外”,但 v2.1.299 之后这个例外对第一方 server 部分成立。
- “超了会怎样”在三条有上限的轨道上各不相同:up-front 超限 = 描述被裁短(模型看到的信息变少);tool search 超限 = 搜到的工具描述不完整(命中但内容残缺);第一方轨道压根没有上限。
- 与 5.6 的联动:5.6 说”工具描述是给模型的指令”,现在要知道——这段指令的可用长度取决于它走哪条加载路径。同一个工具写在 up-front 轨道上只有 4,096 字符,被 tool search 命中时却有 16,384 字符。“工具描述能写多长”这道题的答案不是一个数,而是四个。(本章 5.7 与 Ch02 的「体积三面」表已同步改。)
v2.1.212 超时后的三元路径:此前 MCP 工具调用是”超时→失败”的二元路径——一旦超过 request_timeout_ms,调用立即失败,整个会话被冻结等待。v2.1.212 引入了”超时→转入后台→异步通知”的三元路径:超过 2 分钟的 MCP 工具调用自动转入后台执行,不再阻塞主会话。这对长时间操作(如数据库查询、代码执行沙箱、图片生成)的影响尤其显著——用户无需等待长操作完成即可继续与 Agent 交互。
服务器不可达或配置不匹配时的降级路径。 上面讨论的超时与三阶段延迟加载都假定 MCP 服务器可达、配置正确。工程实践里最高频的一类问题恰恰是”连不上”,2026-08 至 09 之间官方补了三条:
| 场景 | 行为 | 版本 |
|---|---|---|
服务器配置为 http 但只支持旧版 HTTP+SSE 传输 | 按 MCP 规范回退到 SSE,而非永不连接 | v2.1.265(2026-09-08) |
| 远程服务器需要登录 | 真正认证前不再向其注册 OAuth 客户端 | v2.1.265 |
非交互(-p)与 SDK 会话中远程服务器掉线 | 自动重连或报告失败,此前的表现是永不恢复 | v2.1.243(2026-08-25) |
原文:
Fixed MCP servers configured as
httpthat only speak the legacy HTTP+SSE transport never connecting; Claude Code now falls back to SSE as the MCP spec describes
Improved remote MCP servers that need sign-in: Claude Code no longer registers an OAuth client with them until you actually authenticate
Fixed remote MCP servers in non-interactive (
-p) and SDK sessions never recovering after a dropped connection; they now reconnect automatically or report as failed
第二条容易被忽略但值得注意:在认证之前就注册 OAuth 客户端,等于向一个尚未验证身份的第三方暴露了 client 信息。把它挪到认证之后,收紧的是一个此前无谓的暴露面。
第三条对 SDK 用户尤其关键——“永不恢复”意味着长跑会话里一次网络抖动就会静默地永久失去该服务器的工具,而调用方可能毫无察觉。
MCP 长任务需要显式的存活信号(v2.1.271)。 上面那条 5 分钟硬超时说明”把 timeout 调大”不是长任务的可靠解法。官方给 claude mcp serve 补的心跳是另一个方向的措施:
Improved
claude mcp serve: a running tool call now sends a progress update every 30 seconds, so clients show it is still running and idle timeouts don’t abort a long command that prints nothing
进度通知在这里不只是 UX——它本身就是对抗空闲超时的功能性机制:一条长时间不打印任何输出的长命令,在没有心跳时会被空闲超时当成死任务杀掉。v2.1.283 又补了后台化之后的通知可见性:
Fixed MCP progress notifications being discarded once a long-running tool call moved to the background; the background task now shows the latest progress
把这两条与 5 分钟硬超时并读,可得一条工程结论:MCP 长任务需要”显式心跳 + 后台化”双保险,单靠调大 request_timeout_ms 不够。
两条可直接引用的数值/开关。 v2.1.268 起 WebFetch 有 300 秒截止(可用 CLAUDE_CODE_WEBFETCH_DEADLINE_MS 覆盖,0 表示关掉),修的是”服务器一直开着响应不结束导致 WebFetch 无限挂起”;v2.1.269 起 Bash 工具处理文件编辑时会在结果里返回所改文件的 diff(开关 bashEditDiffEnabled),让调用方不必再单独跑一次 git diff 才知道改了什么。
5.8 设计启示
-
Schema 是契约,不是装饰。Zod Schema 在运行时强制执行,模型返回的参数必须通过验证。这消除了一大类”模型生成了错误参数导致工具崩溃”的问题。
-
行为标记驱动调度。工具不需要知道自己会被如何调度——它只需要诚实地声明自己是否并发安全、是否只读。调度策略是编排层的责任。
-
工具列表顺序影响缓存。这是一个容易忽略但影响成本的细节。
-
描述是给模型的指令。工具描述不是文档,而是影响模型行为的 prompt engineering。
-
JSON function calling vs Code execution 的权衡。Claude Code 选择 JSON Schema 模式调用工具,而非 SmolAgents 等框架采用的”模型生成代码直接执行”模式。核心原因是安全性和可审计性:JSON Schema 允许对工具名 + 参数做精确的权限匹配(第 7 章的六层规则正是建立在此基础上),而代码执行模式的攻击面更大——一段恶意 Python 代码的危害远超一个格式化的 JSON 工具调用。这个选择牺牲了灵活性(模型不能自由组合工具),换来了可控性。
-
工具配置本身也是上下文成本。未使用的 MCP 服务器、过时插件、冗余技能——每个都在悄悄消耗 System Prompt 的上下文窗口。v2.1.205 引入的
/doctor(别名/checkup)命令标志着工具系统从”手动清理”向”自动化配置优化”的演进:自动识别未使用 MCP(减少 St3 加载)、拆分臃肿 CLAUDE.md(减少 L4 体积)、关闭慢速 hooks、预批只读命令。这一趋势的启示是:工具系统的稳定性不仅取决于设计质量,还取决于运行时维护成本的自动化程度。 -
自主性降级是可逆能力(v2.1.215,2026-07-19)。v2.1.215 将 verify 和 code-review 技能从”内置自动运行”改为”用户显式调用”。这不是功能退步——而是工具系统成熟度的信号:当自动化执行被发现不可靠时,应有明确的降级路径从”自动”回退到”建议/显式调用”。工具系统的设计不应假设自动化永远是正确方向——自主性降级和自主性升级一样重要。社区将此解读为”验证闭环归属操作者而非自动驾驶仪”。