跳转到内容

MCP 协议

settings.json: { mcpServers: { "my-db": { command: "npx", args: [...] } } }
getAllMcpConfigs() ← 合并 user/project/local 三级配置
useManageMCPConnections() ← React Hook 管理连接生命周期
connectToServer(name, config) ← memoize 缓存(lodash memoize)
├── 创建 Transport(stdio/sse/http/...)
├── new Client() ← @modelcontextprotocol/sdk
├── client.connect(transport) ← 超时控制(MCP_TIMEOUT, 默认 30s)
└── 返回 MCPServerConnection ← { connected | failed | needs-auth | pending }
fetchToolsForClient(client) ← LRU(20) 缓存
├── client.request({ method: 'tools/list' })
└── 每个工具包装为 MCPTool ← 统一 Tool 接口
assembleToolPool() ← 合并内置工具 + MCP 工具
工具名格式: mcp__<serverName>__<toolName> ← buildMcpToolName()

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

传输类型Transport 类适用场景认证方式
stdio(默认)StdioClientTransport本地子进程
sseSSEClientTransport远程 SSE 服务ClaudeAuthProvider + OAuth
httpStreamableHTTPClientTransportHTTP 流ClaudeAuthProvider + OAuth
sse-ideSSEClientTransportIDE 集成lockfile token
ws-ideWebSocketTransportIDE WebSocketX-Claude-Code-Ide-Authorization
wsWebSocketTransportWebSocket 服务session ingress token
claudeai-proxyStreamableHTTPClientTransportclaude.ai 代理OAuth bearer + 401 重试

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

SIGINT (100ms) → SIGTERM (400ms) → SIGKILL

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

SSE/HTTP 类型使用 ClaudeAuthProvider 实现 OAuth 认证流程。认证失败时进入 needs-auth 状态,并写入 15 分钟 TTL 的缓存文件(mcp-needs-auth-cache.json),避免重复弹出认证提示。

连接尝试 → 401 Unauthorized
handleRemoteAuthFailure()
├── logEvent('tengu_mcp_server_needs_auth')
├── setMcpAuthCacheEntry(name) ← 写入 15min TTL 缓存
└── return { type: 'needs-auth' } ← UI 显示认证提示

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

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

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

远程传输有 连续错误计数器client.ts:1229):

let consecutiveConnectionErrors = 0
const MAX_ERRORS_BEFORE_RECONNECT = 3

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

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

const controller = new AbortController()
const timer = setTimeout(c => c.abort(...), MCP_REQUEST_TIMEOUT_MS, controller)
timer.unref?.() // 不阻止进程退出

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

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

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

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

注解映射到含义
readOnlyHintisReadOnly() + isConcurrencySafe()只读,可并行
destructiveHintisDestructive()破坏性操作
openWorldHintisOpenWorld()开放世界(不可枚举)
titleuserFacingName()显示名称

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

AI 生成 tool_use: { name: "mcp__my-db__query", input: { sql: "..." } }
MCPTool.call() ← client.ts:1835
├── ensureConnectedClient() ← 确保连接有效(重连)
├── callMCPToolWithUrlElicitationRetry() ← 带 Elicitation 重试
│ ├── client.request({ method: 'tools/call' })
│ ├── 处理图片结果(resize + persist)
│ └── 内容截断(mcpContentNeedsTruncation)
├── McpSessionExpiredError → 重试一次
└── 返回 { data: content, mcpMeta }

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

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

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

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

// settings.json 中的 MCP 配置
{
"mcpServers": {
"my-database": {
"command": "npx",
"args": ["@my-org/db-mcp-server"],
"env": { "DB_URL": "postgres://..." }
},
"remote-api": {
"type": "http",
"url": "https://api.example.com/mcp"
}
}
}

配置后,AI 的工具列表中会出现 mcp__my-database__querymcp__remote-api__* 工具——与内置工具使用相同的权限检查链路和 UI 渲染。