Tool Search 是 MCP 客户端在工具定义超过 context 阈值时启动的一种延迟加载机制,它要延迟加载的对象是”工具的完整定义”,也就是每个工具的名字、描述、输入输出 JSON Schema——而不是工具的执行结果,也不是服务器端的数据。一个工具被标记成 deferred 后,客户端只在模型主动搜索时才把它的完整 Schema 拉进 context,模型因此能保留更多预算用于真正的任务。
一、为什么要做 Tool Search
MCP 客户端连接到 N 个服务器后,会在每轮请求里把它们 tools/list 响应里的全部工具定义塞进 context。一份完整的工具定义包含 name、description、inputSchema 三部分,每个工具在 tokenize 后通常要消耗 100 到 300 个 token。
| 服务器 | 工具数 | 工具定义 token 成本(参考) |
|---|---|---|
| GitHub 官方服务器 | 27 | 约 7,400 |
| Playwright(Microsoft) | 32 | 约 6,800 |
| Stripe | 24 | 约 5,100 |
| Slack | 18 | 约 3,800 |
| Filesystem | 14 | 约 3,500 |
把 GitHub、Linear、Supabase、Filesystem 四个服务器一起挂上,工具定义就吃掉 2 万多个 token,而模型总共的 context window 可能只有 20 万。这部分预算还没开始对话就花掉了。Tool Search 的目标就是把”按对话必付”改成”按需检索才付”。
二、Tool Search 延迟加载的到底是什么
很多读者会把 Tool Search 理解成”懒加载工具执行”,这是错的。它延迟加载的对象具体是:
- 工具的完整 JSON Schema(inputSchema 里嵌套的属性、required、description 等);
- 工具的完整 description(不仅是名字,而是写给模型看的几十到上百 token 的描述);
- 工具的输出 Schema(如果有标注)。
模型在搜索阶段拿到的不是完整定义,而是一行行精简过的索引项,结构形如:
[
{ "name": "salesforce_updateRecord", "description": "Update fields on a Salesforce object" },
{ "name": "salesforce_upsertRecord", "description": "Insert or update based on external ID" }
]
只有当模型调用 get_tool_details 指定某个工具后,那一个工具的完整 Schema 才会被注入 context。这种三段式(catalog / inspect / execute)让模型在选择阶段只看名字和一行描述,真正要用时再展开。
三、Tool Search 的触发阈值与开关
MCP 规范里只规定了”客户端应该做 progressive discovery”,但具体阈值由各客户端实现决定。Claude Code 默认在 MCP 工具定义总 token 超过上下文 10% 时自动启用,可通过环境变量调整。
# 默认:超过 10% 启用 Tool Search
ENABLE_TOOL_SEARCH=auto claude
# 5% 阈值:更早启用
ENABLE_TOOL_SEARCH=auto:5 claude
# 强制启用,不看阈值
ENABLE_TOOL_SEARCH=true claude
# 关闭:所有工具定义照旧全量加载
ENABLE_TOOL_SEARCH=false claude
阈值背后的权衡是:阈值越低,能省的 context 越多,但每次启动任务都要先做一次检索,多一跳延迟;阈值越高,省得少但冷启动快。生产环境里 5% 到 10% 是常用区间。
四、Tool Search 内部的三层加载模型
Tool Search 不是单一动作,它把工具的可用性拆成三层,让模型按需索取。
| 层级 | 内容 | 何时加载 | 单条成本 |
|---|---|---|---|
| L1 目录 | 工具名 + 一行描述 | 始终在 context | 约 20-40 token |
| L2 概览 | 完整 description + 简化参数列表 | 搜索命中后 | 约 100-200 token |
| L3 完整 Schema | 全部 inputSchema 嵌套与约束 | 模型调用 gettooldetails 后 | 约 200-600 token |
L1 是”我知道你能做什么”,L2 是”我知道你大概怎么做”,L3 是”我准备好调用了”。每一层都只为被命中的少数工具付出 token,其他工具继续留在 deferred 状态。
五、检索策略选型
Tool Search 把”如何从 N 个工具里挑出最相关的 K 个”这件事交给检索策略,常见的有四类:
- 关键词(BM25 / 正则):实现简单,对名字描述规范的工具效果就够用;
- 向量相似度:把工具描述 embedding 后做最近邻搜索,能命中同义改写;
- 子 Agent:用一个轻量模型(如 Claude Haiku、Gemini Flash)直接挑工具,效果通常好但成本偏高;
- 混合:对关键词与向量结果做加权,或在不同场景下切换策略。
OpenAI 与 Anthropic 在各自平台层都提供了官方 tool search 实现,开发者可以直接用而不用自建。官方实现的检索质量与延迟都已经过调优,第三方客户端优先复用比从头写更划算。
六、给 MCP 服务端作者的注意点
Tool Search 改变的不只是客户端,工具的描述质量与分组方式直接决定它能否被搜到。服务端做下面三件事能让搜索效果更好:
- 写简洁且具体的 description,避免”用于销售管理”这种通用描述,模型无法匹配;
- 给工具加上语义标签或分类标识,便于子 Agent 策略按类别筛选;
- 把相关工具归到同一个 server,配合 server instructions 告诉客户端”这些工具适用于何种任务”。
// 工具描述对比:差 / 好
// 差:模型无法判断何时调用
{ "name": "do_thing", "description": "Sales management" }
// 好:包含触发场景与输入对象
{
"name": "salesforce_updateRecord",
"description": "Update fields on a Salesforce object. Use when the user wants to change a record that already exists by ID.",
"input_schema": { /* ... */ }
}
代码块前后注意:前一句在说明服务端 description 是”被检索”还是”被忽略”的分水岭;后一句在强调——description 不只是给人看的文档,更是检索阶段的语料,模糊的描述会直接降低工具的曝光率。
七、Tool Search 不是银弹
Tool Search 把成本从”按请求必付”转成”按使用必付”,但使用本身也要付钱。搜索结果要进 context、Schema 要重新加载、检索策略可能要调一个子模型,这些都会消耗 token。当工具总数已经很少(不到 20 个)时,强制启用 Tool Search 反而会因为多一跳检索而变慢。只有工具多到能吃掉 10% 以上 context 时,Tool Search 才值得开。
常见问题(FAQ)
Q1:Tool Search 延迟加载的是工具执行结果吗?
不是。它延迟加载的是工具定义(名字、描述、Schema),执行结果该传还是要传。
Q2:怎么判断该不该启用 Tool Search?
工具定义总 token 超过 context 10% 时启用,低于时保持全量加载;可用 /context 命令看实际占比。
Q3:服务端需要做改动才能配合 Tool Search 吗?
不需要协议级改动,但需要重写工具 description、补充分类标签,否则搜不到。