MCP 注册表与服务发现:能力的分层与按需装载
Claude Code 通过分层的注册机制 (Registry) 管理所有的 MCP 服务,并通过创新的 Tool Search 机制解决了工具池过大导致的上下文溢出问题。
一句话讲清楚
MCP 注册表本质上是 能力的路由字典(Capability Routing Dictionary)。
它定义了“哪个工具名对应哪个可执行文件”以及“运行它需要哪些环境变量”。为了保证性能,Claude Code 并不会在启动时将所有工具的完整定义(JSON Schema)都塞进主 Prompt,而是只暴露工具名。当模型需要调用特定工具时,才会通过 ToolSearch 这个特殊的“分页装载器”将具体定义按需注入。
实现机制
这套机制由配置聚合与延迟装载两部分组成:
分层配置聚合 (src/services/mcp/config.ts):
- 官方注册表 (officialRegistry.ts):包含 Google Search、Memory 等内建的优质服务。
- 全局配置 (Global Config):
~/.claude/mcp.json,存储跨项目通用的服务(如 GitHub)。 - 项目配置 (Local Config):当前项目目录下的
.claude/mcp.json,仅在工作区生效。 - 源码逻辑确保了项目级配置具有最高优先级,可以覆盖同名的全局配置。
延迟装载与 Tool Search (src/utils/toolSearch.ts):
- Deferred Tools:
src/tools/ToolSearchTool/prompt.ts默认将所有 MCP 工具标为defer_loading,除非它们明确设置了alwaysLoad。 - 按需加载 (On-demand Ingestion):当模型发现现有工具无法解决问题时,会调用
ToolSearchTool。 - 工具引用 (Tool Reference):
ToolSearchTool并不直接返回结果,而是返回tool_referenceblock。Claude Code 会在随后的 API 请求中,将这些被引用的工具 Schema 补进上下文。
- Deferred Tools:
动态环境变量扩展 (src/services/mcp/envExpansion.ts):
- 配置文件支持
${VAR}语法,这允许在不泄露 API Key 的前提下,通过 Shell 注入敏感凭证。
- 配置文件支持
发现与热重载:
- 当检测到
.mcp.json变动时,src/services/mcp/useManageMCPConnections.ts会触发平滑重连。系统会对比新旧配置,仅重启受影响的服务,从而保证会话不中断。
- 当检测到
限制与陷阱
- 名字与定义分离:模型最初只能看到 deferred 工具的名字。如果你禁用了
ToolSearch工具,模型将永远无法获得 MCP 工具的完整参数定义,导致调用失败。 - Auto 模式逻辑:
ENABLE_TOOL_SEARCH=auto是根据工具总描述量(Token 计数,兜底字符数)与上下文窗口的比例来决定是否激活延迟装载。 - Proxy 限制:如果你使用非 Anthropic 官方的 API Proxy,
ToolSearch可能因为不支持tool_referencebeta 特性而失效。源码中对此有专门的防错分支逻辑。 - 工作区信任 (Workspace Trust):项目级(Local)的配置只有在工作区被显式信任后才会被加载,这是防止恶意代码在进入目录时自动执行的第一道防线。
延伸阅读
- 如果你想了解工具调用的底层权限流,请看 MCP 安全与信任模型。
- 如果你关插件的登录授权流程,请看 MCP 认证与 OAuth 流程。
- 如果你关基础的协议架构,请看 MCP 协议与客户端架构。
源码锚点
claude-code-opensource/src/services/mcp/config.ts— 配置加载与层级合并逻辑。
📄 src/services/mcp/config.ts — 配置加载与层级合并逻辑。
typescript
type SettingsJson,
} from '../../utils/settings/types.js'
import type { ValidationError } from '../../utils/settings/validation.js'claude-code-opensource/src/utils/toolSearch.ts— Tool Search 总开关与 auto 模式裁决逻辑。
📄 src/utils/toolSearch.ts — Tool Search 总开关与 auto 模式裁决逻辑。
typescript
* Tool Search utilities for dynamically discovering deferred tools.
*
* When enabled, deferred tools (MCP and shouldDefer tools) are sent with
* defer_loading: true and discovered via ToolSearchTool rather than being
* loaded upfront.
*/
import memoize from 'lodash-es/memoize.js'
import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js'claude-code-opensource/src/tools/ToolSearchTool/ToolSearchTool.ts— 搜索并返回tool_reference的具体工具实现。
📄 src/tools/ToolSearchTool/ToolSearchTool.ts — 搜索并返回 `tool_reference` 的具体工具实现。
typescript
* Returns a tool_result with tool_reference blocks.
* This format works on 1P/Foundry. Bedrock/Vertex may not support
* client-side tool_reference expansion yet.
*/
mapToolResultToToolResultBlockParam(
content: Output,
toolUseID: string,
): ToolResultBlockParam {
if (content.matches.length === 0) {
let text = 'No matching deferred tools found'
if (
content.pending_mcp_servers &&
content.pending_mcp_servers.length > 0
) {
text += `. Some MCP servers are still connecting: ${content.pending_mcp_servers.join(', ')}. Their tools will become available shortly — try searching again.`
}
return {
type: 'tool_result',
tool_use_id: toolUseID,
content: text,
}
}
return {
type: 'tool_result',
tool_use_id: toolUseID,
content: content.matches.map(name => ({
type: 'tool_reference' as const,
tool_name: name,
})),
} as unknown as ToolResultBlockParamclaude-code-opensource/src/services/mcp/officialRegistry.ts— 官方默认插件清单。
📄 src/services/mcp/officialRegistry.ts — 官方默认插件清单。
typescript
type RegistryServer = {
server: {
remotes?: Array<{ url: string }>
}
}claude-code-opensource/src/utils/mcpInstructionsDelta.ts— 向模型同步可用工具池变动的 Attachment 逻辑。
📄 src/utils/mcpInstructionsDelta.ts — 向模型同步可用工具池变动的 Attachment 逻辑。
typescript
export type McpInstructionsDelta = {
/** Server names — for stateless-scan reconstruction. */
addedNames: string[]
/** Rendered "## {name}\n{instructions}" blocks for addedNames. */
addedBlocks: string[]
removedNames: string[]
}