Skip to content
源码分析手册

MCP 注册表与服务发现:能力的分层与按需装载

Claude Code 通过分层的注册机制 (Registry) 管理所有的 MCP 服务,并通过创新的 Tool Search 机制解决了工具池过大导致的上下文溢出问题。

一句话讲清楚

MCP 注册表本质上是 能力的路由字典(Capability Routing Dictionary)

它定义了“哪个工具名对应哪个可执行文件”以及“运行它需要哪些环境变量”。为了保证性能,Claude Code 并不会在启动时将所有工具的完整定义(JSON Schema)都塞进主 Prompt,而是只暴露工具名。当模型需要调用特定工具时,才会通过 ToolSearch 这个特殊的“分页装载器”将具体定义按需注入。

实现机制

这套机制由配置聚合与延迟装载两部分组成:

  1. 分层配置聚合 (src/services/mcp/config.ts)

    • 官方注册表 (officialRegistry.ts):包含 Google Search、Memory 等内建的优质服务。
    • 全局配置 (Global Config)~/.claude/mcp.json,存储跨项目通用的服务(如 GitHub)。
    • 项目配置 (Local Config):当前项目目录下的 .claude/mcp.json,仅在工作区生效。
    • 源码逻辑确保了项目级配置具有最高优先级,可以覆盖同名的全局配置。
  2. 延迟装载与 Tool Search (src/utils/toolSearch.ts)

    • Deferred Toolssrc/tools/ToolSearchTool/prompt.ts 默认将所有 MCP 工具标为 defer_loading,除非它们明确设置了 alwaysLoad
    • 按需加载 (On-demand Ingestion):当模型发现现有工具无法解决问题时,会调用 ToolSearchTool
    • 工具引用 (Tool Reference)ToolSearchTool 并不直接返回结果,而是返回 tool_reference block。Claude Code 会在随后的 API 请求中,将这些被引用的工具 Schema 补进上下文。
  3. 动态环境变量扩展 (src/services/mcp/envExpansion.ts)

    • 配置文件支持 ${VAR} 语法,这允许在不泄露 API Key 的前提下,通过 Shell 注入敏感凭证。
  4. 发现与热重载

    • 当检测到 .mcp.json 变动时,src/services/mcp/useManageMCPConnections.ts 会触发平滑重连。系统会对比新旧配置,仅重启受影响的服务,从而保证会话不中断。

限制与陷阱

  • 名字与定义分离:模型最初只能看到 deferred 工具的名字。如果你禁用了 ToolSearch 工具,模型将永远无法获得 MCP 工具的完整参数定义,导致调用失败。
  • Auto 模式逻辑ENABLE_TOOL_SEARCH=auto 是根据工具总描述量(Token 计数,兜底字符数)与上下文窗口的比例来决定是否激活延迟装载。
  • Proxy 限制:如果你使用非 Anthropic 官方的 API Proxy,ToolSearch 可能因为不支持 tool_reference beta 特性而失效。源码中对此有专门的防错分支逻辑。
  • 工作区信任 (Workspace Trust):项目级(Local)的配置只有在工作区被显式信任后才会被加载,这是防止恶意代码在进入目录时自动执行的第一道防线。

延伸阅读

源码锚点

  • claude-code-opensource/src/services/mcp/config.ts — 配置加载与层级合并逻辑。
📄 src/services/mcp/config.ts — 配置加载与层级合并逻辑。L35-37 of 1579
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 模式裁决逻辑。L2-10 of 757
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` 的具体工具实现。L440-469 of 472
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 ToolResultBlockParam
  • claude-code-opensource/src/services/mcp/officialRegistry.ts — 官方默认插件清单。
📄 src/services/mcp/officialRegistry.ts — 官方默认插件清单。L5-9 of 73
typescript
type RegistryServer = {
  server: {
    remotes?: Array<{ url: string }>
  }
}
  • claude-code-opensource/src/utils/mcpInstructionsDelta.ts — 向模型同步可用工具池变动的 Attachment 逻辑。
📄 src/utils/mcpInstructionsDelta.ts — 向模型同步可用工具池变动的 Attachment 逻辑。L10-16 of 131
typescript
export type McpInstructionsDelta = {
  /** Server names — for stateless-scan reconstruction. */
  addedNames: string[]
  /** Rendered "## {name}\n{instructions}" blocks for addedNames. */
  addedBlocks: string[]
  removedNames: string[]
}

基于 Claude Code v2.1.88 开源快照的深度分析