Files

13 KiB
Raw Permalink Blame History

tags, create time
tags create time
MCP
Model-Context-Protocol
工具扩展
MCP-客户端
工具发现
Claude-Code
2026-06-09 22:30

MCP 协议 - 连接管理、工具发现与执行链路

概述

Claude Code 的 MCP 集成区分内置 MCP 服务器(同进程 InProcessTransport)和外部 MCP 服务器(子进程/网络连接),通过 7 种传输层实现统一的工具发现和执行。本文揭示从配置到可用工具的完整链路,包括 memoize 连接缓存、LRU 工具缓存、认证状态机和权限检查集成。

正文

架构总览:从配置到可用工具

flowchart TD
    A["配置层\n多来源合并"] --> B["getAllMcpConfigs()\nenterprise 独占 或 合并"]
    B --> C["useManageMCPConnections()\nReact Hook 管理连接生命周期"]
    C --> D["connectToServer(name, config)\nmemoize 缓存"]
    D --> D1["内置 MCP -> InProcessTransport"]
    D --> D2["外部 stdio -> StdioClientTransport"]
    D --> D3["远程 SSE/HTTP/WS -> 网络传输"]
    D --> E["MCPServerConnection\nconnected/failed/needs-auth/pending/disabled"]
    E --> F["fetchToolsForClient(client)\nLRU 20 缓存"]
    F --> G["每个工具包装为 MCPTool\n统一 Tool 接口"]
    G --> H["assembleToolPool()\n合并内置工具 + MCP 工具"]

工具名格式:mcp__<serverName>__<toolName>(由 buildMcpToolName() 生成)。

两种 MCP 模式:内置 vs 外部

Claude Code 的 MCP 实现区分内置 MCP 服务器和外部 MCP 服务器。两者使用相同的客户端协议和工具发现机制,但在连接方式、生命周期管理和配置来源上完全不同。

内置 MCP 服务器

内置 MCP 服务器由 Claude Code 自身提供,无需用户手动配置。它们在启动时自动注册为 dynamic scope 的配置,并在同进程内运行。

服务器 名称 包路径 Feature Flag 启用方式
Computer Use computer-use @ant/computer-use-mcp CHICAGO_MCP GrowthBook gate + macOS + interactive
Claude in Chrome claude-in-chrome @ant/claude-for-chrome-mcp — --chrome 参数或配置
VSCode SDK claude-vscode — — IDE 嵌入模式 (type:sdk)
InProcessTransport:零开销同进程通信

内置服务器通过 InProcessTransport(src/services/mcp/InProcessTransport.ts)运行,不启动子进程:

// 创建一对 linked transport —— 消息在两端之间直接传递
const [clientTransport, serverTransport] = createLinkedTransportPair()

// server 端连接到 serverTransport
inProcessServer = createComputerUseMcpServerForCli()
await inProcessServer.connect(serverTransport)

// client 端使用 clientTransport(与外部 MCP 的 Client 相同接口)
transport = clientTransport

InProcessTransport 的核心设计:

  • send() 通过 queueMicrotask() 异步投递消息到对端,避免同步请求/响应的栈深度问题
  • close() 双向关闭,任一端关闭都会触发两端的 onclose 回调
  • 无网络开销、无 IPC 序列化、无进程启动时间
连接时拦截

connectToServer() 在 client.ts:906-944 中根据服务器名拦截内置服务器,创建 InProcessTransport 而非启动子进程。这样避免了约 325MB 的子进程开销(Chrome MCP)。

保留名称保护

内置服务器的名称被保留,用户无法手动添加同名配置。启动时也有全局检查:如果用户配置中包含保留名(非 type:'sdk'),直接 process.exit(1)。

VSCode SDK MCP

VSCode SDK MCP 是特殊的内置模式。IDE 通过嵌入方式启动 Claude Code,并传入 type:'sdk' 的 MCP 配置。这类配置:

  • 不经过保留名称检查(IDE 可以使用任意名称)
  • 不参与 enterprise MCP 的排他控制
  • 支持双向通知(如 file_updated、experiment_gates)

外部 MCP 服务器

外部 MCP 服务器由用户在配置文件中声明,通过子进程或网络连接运行。

配置来源
来源 Scope 文件位置 优先级
项目配置 project <project>/.mcp.json 最高(同名覆盖)
本地配置 local <project>/.claude/settings.local.json 高
用户配置 user ~/.claude/settings.json 中
插件 dynamic 插件 manifest 中 .mcp.json 中
claude.ai claudeai 通过 API 获取 低
企业管控 enterprise 系统管理路径 managed-mcp.json 排他(存在时覆盖全部)
配置合并与去重

getAllMcpConfigs()(config.ts)按优先级合并多个来源的配置:

  1. 企业管控配置存在时,独占返回(忽略所有其他来源)
  2. 否则合并:user -> project -> local -> plugin -> claude.ai
  3. 插件与手动配置去重:通过 getMcpServerSignature() 生成内容签名(基于 command/args/url),插件配置被同名手动配置抑制
  4. addScopeToServers() 为每个配置项标注来源 scope

7 种传输层实现

connectToServer()(client.ts:596-1643)根据 config.type 分发到不同的 Transport 实现:

传输类型 Transport 类 适用场景 认证方式
stdio(默认) StdioClientTransport 外部本地子进程 无
sse SSEClientTransport 远程 SSE 服务 ClaudeAuthProvider + OAuth
http StreamableHTTPClientTransport HTTP 流 ClaudeAuthProvider + OAuth
sse-ide SSEClientTransport IDE 集成 lockfile token
ws-ide WebSocketTransport IDE WebSocket X-Claude-Code-Ide-Authorization
ws WebSocketTransport WebSocket 服务 session ingress token
claudeai-proxy StreamableHTTPClientTransport claude.ai 代理 OAuth bearer + 401 重试
InProcess(内置) InProcessTransport Computer Use / Chrome 无(同进程)

stdio 传输的进程管理

stdio 类型的 MCP 服务器作为子进程运行,cleanup 时采用信号升级策略(client.ts:1431-1564):

SIGINT (100ms) -> SIGTERM (400ms) -> SIGKILL

总清理时间上限 600ms,防止 MCP 服务器关闭阻塞 CLI 退出。

远程传输的认证状态机

SSE/HTTP 类型使用 ClaudeAuthProvider 实现 OAuth 认证流程:

flowchart TD
    A["连接尝试"] -->|"401 Unauthorized"| B["handleRemoteAuthFailure()"]
    B --> C["logEvent(tengu_mcp_server_needs_auth)"]
    B --> D["setMcpAuthCacheEntry(name)\n写入 15min TTL 缓存"]
    B --> E["return needs-auth\nUI 显示认证提示"]

连接缓存与重连机制

connectToServer 使用 lodash memoize 缓存连接对象,缓存 key 为 ${name}-${JSON.stringify(config)}。

缓存失效触发

当连接关闭时(client.onclose),清除所有相关缓存:

client.onclose = () => {
  const key = getServerCacheKey(name, serverRef)
  fetchToolsForClient.cache.delete(name)      // 工具缓存
  fetchResourcesForClient.cache.delete(name)  // 资源缓存
  fetchCommandsForClient.cache.delete(name)   // 命令缓存
  connectToServer.cache.delete(key)           // 连接缓存
}

连接降级检测

远程传输有连续错误计数器:

let consecutiveConnectionErrors = 0
const MAX_ERRORS_BEFORE_RECONNECT = 3

遇到终端错误(ECONNRESET、ETIMEDOUT、EPIPE 等)连续 3 次后,主动关闭 transport 触发重连。对于 HTTP 传输,还检测 session 过期(404 + JSON-RPC code -32001)。

请求级超时保护

每个 HTTP 请求使用独立的 setTimeout 超时(wrapFetchWithTimeout),而非共享 AbortSignal.timeout()。原因是 Bun 对 AbortSignal.timeout 的 GC 是惰性的——每个请求约 2.4KB 原生内存,即使请求毫秒级完成也要等 60s 才回收。

工具发现:从 MCP 到 Tool 接口

fetchToolsForClient()(client.ts:1744-2000)使用 memoizeWithLRU 缓存(上限 100),将 MCP 工具转换为 Claude Code 的统一 Tool 接口:

const fullyQualifiedName = buildMcpToolName(client.name, tool.name)
// 结果: "mcp__my-database__query"

内置 MCP 的工具发现

内置 MCP 服务器虽然使用 InProcessTransport,但工具发现流程与外部服务器完全一致:

  • Computer Use:构建 MCP Server 对象,注册 ListToolsRequestSchema handler,工具描述包含平台特定的已安装应用列表
  • Claude in Chrome:提供 17+ 个浏览器控制工具
  • VSCode SDK:由 IDE 端提供工具列表,通过 SDK transport 传递

工具描述截断

MCP 工具描述上限 2048 字符(MAX_MCP_DESCRIPTION_LENGTH)。OpenAPI 生成的 MCP 服务器曾观察到 15-60KB 的描述文档。

工具能力标注

每个 MCP 工具根据 tool.annotations 自动标注:

注解 映射到 含义
readOnlyHint isReadOnly() + isConcurrencySafe() 只读,可并行
destructiveHint isDestructive() 破坏性操作
openWorldHint isOpenWorld() 开放世界(不可枚举)
title userFacingName() 显示名称

MCP 工具的权限检查

MCP 工具默认返回 { behavior: 'passthrough' },意味着它们始终进入权限确认流程。工具名使用 mcp__ 前缀精确匹配权限规则。

[!tip] 内置 MCP 的自动授权 内置 MCP 服务器的工具通过 allowedTools 列表自动授权——在 main.tsx 启动时加入,绕过普通权限提示。

MCP 工具的执行链路

flowchart TD
    A["AI 生成 tool_use\nmcp__my-db__query"] --> B["MCPTool.call()"]
    B --> C["ensureConnectedClient()\n确保连接有效"]
    C --> D["callMCPToolWithUrlElicitationRetry()\nclient.request tools/call"]
    D --> E["处理图片结果\nresize + persist"]
    E --> F["内容截断\nmcpContentNeedsTruncation"]
    F --> G{"McpSessionExpiredError?"}
    G -->|"是"| C
    G -->|"否"| H["返回 data + mcpMeta"]

Session 过期自动重试

HTTP 传输的 MCP session 可能过期。检测到 McpSessionExpiredError 后自动重试一次,因为 ensureConnectedClient() 已经清除了缓存并建立了新连接。

内容截断与持久化

大型 MCP 工具输出通过 truncateMcpContentIfNeeded 截断,二进制内容(图片)通过 persistBinaryContent 写入文件并返回文件路径。图片自动 resize(maybeResizeAndDownsampleImageBuffer)。

MCP 连接的并发控制

// 本地服务器并发连接数
getMcpServerConnectionBatchSize()    // 默认 3

// 远程服务器并发连接数
getRemoteMcpServerConnectionBatchSize()  // 默认 20

本地 MCP 服务器(stdio)是重量级的子进程,默认限制 3 个并发连接。远程服务器是轻量级 HTTP 请求,允许 20 个并发。

内置 vs 外部 MCP 对比总结

维度 内置 MCP 外部 MCP
Transport InProcessTransport(同进程) stdio / SSE / HTTP / WebSocket
配置来源 动态注册 settings.json / .mcp.json / 插件 / claude.ai
Scope dynamic user / project / local / enterprise / claudeai
进程模型 同进程,零开销 子进程(stdio)或网络连接
名称保护 保留名,用户不可添加同名 自由命名(字母数字 + -_)
生命周期 随 CLI 启停 连接缓存 + 按需重连
权限 allowedTools 自动授权 passthrough 进入权限确认
Feature Flag CHICAGO_MCP(Computer Use)等 无(始终可用)
工具发现 与外部相同(MCP 协议) 标准 MCP tools/list
清理 inProcessServer.close() 信号升级策略 SIGINT->SIGTERM->SIGKILL

关键源文件索引

文件 职责
src/services/mcp/client.ts 核心客户端:connectToServer、fetchToolsForClient、MCPTool.call
src/services/mcp/config.ts 配置管理:getAllMcpConfigs、addMcpConfig、removeMcpConfig
src/services/mcp/types.ts 类型定义:配置 Schema、连接状态类型
src/services/mcp/InProcessTransport.ts 内置 MCP 传输层:linked transport pair
src/services/mcp/vscodeSdkMcp.ts VSCode SDK MCP:双向通知、实验门控
src/services/mcp/useManageMCPConnections.ts React Hook:连接生命周期、重连
src/utils/computerUse/mcpServer.ts Computer Use MCP Server 构建
src/utils/computerUse/setup.ts Computer Use 动态注册
src/utils/claudeInChrome/mcpServer.ts Chrome MCP Server 构建 + Bridge 配置
src/tools/MCPTool/MCPTool.ts MCP 工具包装:统一 Tool 接口
src/entrypoints/mcp.ts MCP server 入口(Claude Code 作为 MCP server)

关联笔记