# MCP 工具进注册表 · 远程工具怎么变成本地工具

> 连上 MCP 服务器后，它的远程工具怎么进 grok 的工具注册表、怎么和内置工具并存、又怎么在成千上万个工具面前不撑爆上下文。这一章看工具限定名、动态注册，以及 search_tool/use_tool 的规模化解法。

- 作者：David（道雾轩）
- 专栏：Grok Build 源码解读（https://daiw.org/manual/grok-build.md）
- 最后更新：2026-09-15
- 原文：https://daiw.org/manual/grok-build/mcp-registry
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# MCP 工具进注册表 · 远程工具怎么变成本地工具

[上一章](https://daiw.org/manual/grok-build/mcp-client)连上了 MCP 服务器。这一章看关键的下一步：它暴露的远程工具，怎么变成模型能调的工具——还要和[内置工具系统](https://daiw.org/manual/grok-build/tool-registry)无缝并存。

## 限定名：`server__tool`

多个 MCP 服务器可能有同名工具(都叫 `search`)，所以要**限定名**：`MCP_TOOL_NAME_DELIMITER = "__"`，工具叫 `<服务器>__<工具>`。`into_registration` 有个严格守卫：**分隔符数量必须恰好是 1**——`foo__bar` 服务器、`my__thing` 工具、边界歧义(连 `___` 都按两个边界算)，统统跳过并打一条 error 日志。工具名还过一道 `validate_tool_name` 正则(取各家模型 API 命名规则的最严交集)，保证在哪家模型下都合法；校验的是整个限定名，所以服务器名加工具名合计不能超过 64 个字符。

## 动态注册进 ToolBridge

每个远程工具包成 `McpErasedTool`，它实现了和[内置工具一样的 `Tool` trait](https://daiw.org/manual/grok-build/tool-contract)(`Args = Value`、`Output = ToolOutput`),`id()` 就是限定名——**同一个原始工具挂在两个服务器上，自然成为注册表里两个不同条目**。然后 `ToolBridge::register_mcp_tools` **运行时动态**把它塞进 `FinalizedToolset`。所以 MCP 工具和内置工具在注册、派发层**完全一视同仁**——这正是[工具协议那章](https://daiw.org/manual/grok-build/tool-protocol)「一套契约」抽象的回报。只是模型并不直接调它们：调用由元工具 `use_tool` 经 `InnerDispatch::call_terminal` 转发，绕开外层 `ToolBridge` 的锁，也不重复跑提醒与持久化。

还有个 `model_visible`(读 `meta.ui.visibility`)：有些 MCP 工具是**纯 UI 的**(只给前端界面用，走 `x.ai/mcp/call` 调)，不注册给模型。

## 规模化：成千上万个工具怎么办

一个真实困境：接几个 MCP 服务器，可能冒出**几千个工具**。全塞进模型的工具列表，上下文直接爆，还严重干扰选择。grok 的解法效果上就是[工具协议那章](https://daiw.org/manual/grok-build/tool-protocol)提过的 **Concise 模式**，但 CLI 并没有构造那个枚举，而是一套常开的机制：

- 每轮组装工具定义时只取内置工具(`tool_definitions_builtins_only`，调用处注释写着「本地模式下工具搜索总是开启」)，名字里带 `__` 的 MCP 工具一律滤掉；
- 模型面对 MCP 只看到两个内置元工具 `search_tool` / `use_tool`：前者在内存里的 BM25 索引上搜(默认返回 5 条)，直接给出输入 schema，后者按限定名转发调用；
- 提醒明确要求：「调 `use_tool` 前**必须先 `search_tool`** 拿到该工具的输入 schema」，绝不许猜参数名。

于是无论挂多少 MCP 工具，模型看到的工具集**始终稳定**(`search_tool` 的描述本身是静态的，不破 KV 缓存；服务器清单以 `<system-reminder>` 增量注入)。服务器连不上或还在连接时也各有提醒，免得模型把空的搜索结果误读成「没有这个工具」。这和[读与搜那章](https://daiw.org/manual/grok-build/read-and-search)澄清的「`search_tool` 搜的是工具」正好闭环。

<Callout type="info">
  源码里还有一个 `materialize_descriptors`，能把每个工具的描述落盘成 `<server>/tools/<name>.json`。但它服务的是 Cursor 风格的另一套 prompt 模板，开源构建里写死关闭(`cursor_mcps_root()` 恒为 `None`)，和 search/use 这条路无关。
</Callout>

## 和《从 LLM 到 Coding Agent》对照

对照 [mcp](https://daiw.org/manual/llm-to-agent/mcp)：最小实现里，MCP 工具列出来直接加进工具数组就完事。grok 的增量在两件事：**无缝并入**(远程工具走和内置工具一样的 `ToolHandle` 契约)和**规模化**(几千工具靠常开的过滤 + search/use 元工具收敛)。下一章看另一类扩展点：hooks。

---

上一篇：[MCP 客户端](https://daiw.org/manual/grok-build/mcp-client) · 下一篇：[Hooks](https://daiw.org/manual/grok-build/hooks)
