feat: 工具层及 mcp 大重构 (#252)

* feat: 第一版大重构

* fix: 修复类型问题

* chore: 更新版本到 1.3.2

* Add brave as alternative WebSearchTool

* fix: 修正顺序

* fix: 修复对穷鬼模式的 auto dream 和 session memory 越过

* feat: 穷鬼模式去除 session-summary

* feat: 创建 builtin-tools 包,搬运所有工具实现

将 src/tools/ 下的全部 60 个工具目录迁移至 packages/builtin-tools/src/tools/,
内部导入路径已更新为 src/ alias 模式。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: 更新 src/ 中所有工具引用至 builtin-tools 包,删除 src/tools/

- src/tools.ts 及 178 个 src/ 文件的 import 路径从 ./tools/ 改为 builtin-tools/tools/
- 删除 src/tools/ 整个目录(已迁移至 packages/builtin-tools/)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: 添加 builtin-tools 路径别名至 tsconfig,更新 bun.lock

- tsconfig.json 新增 builtin-tools/* 和 builtin-tools 路径映射
- 新增 packages/builtin-tools/src 至 include

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: 为 builtin-tools、mcp-client、agent-tools 添加 @claude-code-best 作用域前缀

所有包名及 import 路径统一添加 @claude-code-best/ 前缀:
- builtin-tools → @claude-code-best/builtin-tools
- mcp-client → @claude-code-best/mcp-client
- agent-tools → @claude-code-best/agent-tools

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: 修复 node 环境没有 bun 的问题

---------

Co-authored-by: Eric-Guo <eric.guocz@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
claude-code-best
2026-04-13 09:52:05 +08:00
committed by GitHub
parent bbb8b613a9
commit 2fb1c9dcd8
559 changed files with 9346 additions and 1837 deletions

View File

@@ -1,25 +1,32 @@
---
title: "MCP 协议 - 连接管理、工具发现与执行链路"
description: "从源码角度解析 Claude Code 的 MCP 集成7 种传输层实现、connectToServer 的 memoize 缓存、工具发现的 LRU 策略、认证状态机、以及 MCP 工具如何进入权限检查链路。"
keywords: ["MCP", "Model Context Protocol", "工具扩展", "MCP 客户端", "工具发现"]
description: "从源码角度解析 Claude Code 的 MCP 集成:内置 MCP 与外部 MCP 的区别、7 种传输层实现、connectToServer 的 memoize 缓存、工具发现的 LRU 策略、认证状态机、以及 MCP 工具如何进入权限检查链路。"
keywords: ["MCP", "Model Context Protocol", "工具扩展", "MCP 客户端", "工具发现", "内置 MCP", "外部 MCP"]
---
{/* 本章目标:从源码角度揭示 MCP 客户端的连接管理、工具发现协议和执行链路 */}
{/* 本章目标:从源码角度揭示 MCP 客户端的两种运行模式(内置/外部)、连接管理、工具发现协议和执行链路 */}
## 架构总览:从配置到可用工具
```
settings.json: { mcpServers: { "my-db": { command: "npx", args: [...] } } }
配置层(多来源合并)
├── settings.json: { mcpServers: { "my-db": { command: "npx", args: [...] } } } ← 外部
├── .mcp.json: 项目级 MCP 配置 ← 外部
├── 插件 manifest (.mcp.json / .mcpb) ← 外部(插件)
├── claude.ai connectors ← 外部(远程)
├── enterprise managed-mcp.json ← 外部(企业管控)
├── setupComputerUseMCP() / setupClaudeInChrome() ← 内置(动态注册)
└── SDK 传入 (type:'sdk') ← 内置IDE 嵌入)
getAllMcpConfigs() ← enterprise 独占合并 user/project/local + plugin + claude.ai
getAllMcpConfigs() ← enterprise 独占合并 user/project/local + plugin + claude.ai
useManageMCPConnections() ← React Hook 管理连接生命周期
connectToServer(name, config) ← memoize 缓存lodash memoize
├── 创建 Transportstdio/sse/http/...
├── new Client() ← @modelcontextprotocol/sdk
├── client.connect(transport) ← 超时控制MCP_TIMEOUT, 默认 30s
└── 返回 MCPServerConnection ← { connected | failed | needs-auth | pending }
├── 判断:内置 MCP → InProcessTransport同进程
├── 判断:外部 stdio → StdioClientTransport子进程
├── 判断:远程 SSE/HTTP/WS → 网络传输
└── 返回 MCPServerConnection ← { connected | failed | needs-auth | pending | disabled }
fetchToolsForClient(client) ← LRU(20) 缓存
├── client.request({ method: 'tools/list' })
@@ -30,19 +37,208 @@ assembleToolPool() ← 合并内置工具 + 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` 参数或 `claudeInChromeDefaultEnabled` 配置 |
| VSCode SDK | `claude-vscode` | — | — | IDE 嵌入模式 (type:`sdk`) |
#### InProcessTransport零开销同进程通信
内置服务器通过 `InProcessTransport``src/services/mcp/InProcessTransport.ts`)运行,**不启动子进程**
```typescript
// 创建一对 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 序列化、无进程启动时间
#### 动态注册流程
内置服务器在 `main.tsx` 的启动流程中注册,注入 `dynamicMcpConfig`
```typescript
// main.tsx: Computer Use MCP 动态注册
if (feature("CHICAGO_MCP") && getPlatform() !== "unknown" && !getIsNonInteractiveSession()) {
const { getChicagoEnabled } = await import("src/utils/computerUse/gates.js")
if (getChicagoEnabled()) {
const { setupComputerUseMCP } = await import("src/utils/computerUse/setup.js")
const { mcpConfig, allowedTools } = setupComputerUseMCP()
dynamicMcpConfig = { ...dynamicMcpConfig, ...mcpConfig }
allowedTools.push(...cuTools)
}
}
```
`setupComputerUseMCP()` 返回的配置(`src/utils/computerUse/setup.ts`
```typescript
{
"computer-use": {
type: "stdio", // 类型标记为 stdio但 client.ts 会拦截为 InProcessTransport
command: process.execPath,
args: ["--computer-use-mcp"],
scope: "dynamic", // 动态作用域,不持久化
}
}
```
#### 连接时拦截
`connectToServer()` 在 `client.ts:906-944` 中根据服务器名拦截内置服务器:
```typescript
// Chrome MCP — 在 process 内运行,避免 ~325MB 子进程
if (isClaudeInChromeMCPServer(name)) {
const { createChromeContext } = await import('../../utils/claudeInChrome/mcpServer.js')
const { createClaudeForChromeMcpServer } = await import('@ant/claude-for-chrome-mcp')
const { createLinkedTransportPair } = await import('./InProcessTransport.js')
const context = createChromeContext(config.env)
inProcessServer = createClaudeForChromeMcpServer(context)
const [clientTransport, serverTransport] = createLinkedTransportPair()
await inProcessServer.connect(serverTransport)
transport = clientTransport
}
// Computer Use MCP — 同理
if (feature('CHICAGO_MCP') && isComputerUseMCPServer(name)) {
const { createComputerUseMcpServerForCli } = await import('../../utils/computerUse/mcpServer.js')
const { createLinkedTransportPair } = await import('./InProcessTransport.js')
inProcessServer = await createComputerUseMcpServerForCli()
const [clientTransport, serverTransport] = createLinkedTransportPair()
await inProcessServer.connect(serverTransport)
transport = clientTransport
}
```
#### 保留名称保护
内置服务器的名称被保留,用户无法手动添加同名配置(`config.ts:636-648`
```typescript
// 添加 MCP 配置时检查保留名
if (isClaudeInChromeMCPServer(name)) {
throw new Error(`Cannot add MCP server "${name}": this name is reserved.`)
}
if (feature('CHICAGO_MCP') && isComputerUseMCPServer(name)) {
throw new Error(`Cannot add MCP server "${name}": this name is reserved.`)
}
```
启动时也有全局检查(`main.tsx:2351-2368`):如果用户配置中包含保留名(非 `type:'sdk'`),直接 `process.exit(1)`。
#### VSCode SDK MCP
VSCode SDK MCP 是特殊的内置模式。IDE如 VS Code、JetBrains通过嵌入方式启动 Claude Code并传入 `type:'sdk'` 的 MCP 配置。这类配置:
- 不经过保留名称检查IDE 可以使用任意名称)
- 不参与 enterprise MCP 的排他控制
- 通过 VSCode SDK transport 连接
- 支持双向通知(如 `file_updated`、`experiment_gates`
```typescript
// src/services/mcp/vscodeSdkMcp.ts
export function setupVscodeSdkMcp(sdkClients: MCPServerConnection[]): void {
const client = sdkClients.find(client => client.name === 'claude-vscode')
if (client && client.type === 'connected') {
// 注册 log_event 通知处理器
client.client.setNotificationHandler(LogEventNotificationSchema(), ...)
// 发送实验门控到 VSCode
client.client.notification({ method: 'experiment_gates', params: { 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` | 排他(存在时覆盖全部) |
#### 配置示例
```json
// settings.json / .mcp.json 中的 MCP 配置
{
"mcpServers": {
// stdio 类型 — 启动子进程
"my-database": {
"command": "npx",
"args": ["@my-org/db-mcp-server"],
"env": { "DB_URL": "postgres://..." }
},
// HTTP 流类型 — 远程服务器
"remote-api": {
"type": "http",
"url": "https://api.example.com/mcp"
},
// SSE 类型 — Server-Sent Events
"realtime-feed": {
"type": "sse",
"url": "https://feed.example.com/sse"
},
// WebSocket 类型
"ws-service": {
"type": "ws",
"url": "wss://ws.example.com/mcp"
}
}
}
```
#### 配置合并与去重
`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` | 本地子进程 | 无 |
| `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 传输的进程管理
@@ -112,9 +308,17 @@ timer.unref?.() // 不阻止进程退出
```typescript
const fullyQualifiedName = buildMcpToolName(client.name, tool.name)
// 结果: "mcp__my-db__query"
// 结果: "mcp__my-database__query"
```
### 内置 MCP 的工具发现
内置 MCP 服务器虽然使用 InProcessTransport但工具发现流程与外部服务器完全一致
- **Computer Use**`createComputerUseMcpServerForCli()` 在 `src/utils/computerUse/mcpServer.ts` 中构建 MCP Server 对象,注册 `ListToolsRequestSchema` handler。工具描述包含平台特定的已安装应用列表1s 超时枚举)。
- **Claude in Chrome**`createClaudeForChromeMcpServer()` 在 `@ant/claude-for-chrome-mcp` 包中构建 Server提供 17+ 个浏览器控制工具。
- **VSCode SDK**:由 IDE 端提供工具列表,通过 SDK transport 传递。
### 工具描述截断
MCP 工具描述上限 2048 字符(`MAX_MCP_DESCRIPTION_LENGTH`。OpenAPI 生成的 MCP 服务器曾观察到 15-60KB 的描述文档。
@@ -134,6 +338,8 @@ MCP 工具描述上限 2048 字符(`MAX_MCP_DESCRIPTION_LENGTH`。OpenAPI
MCP 工具默认返回 `{ behavior: 'passthrough' }``client.ts:1816-1834`),意味着它们始终进入权限确认流程。工具名使用 `mcp__` 前缀精确匹配权限规则。
内置 MCP 服务器的工具通过 `allowedTools` 列表自动授权——在 `main.tsx` 启动时加入,绕过普通权限提示。例如 Computer Use 工具的 `request_access` 自行处理会话级审批。
## MCP 工具的执行链路
```
@@ -169,23 +375,33 @@ getRemoteMcpServerConnectionBatchSize() // 默认 20
本地 MCP 服务器stdio是重量级的子进程默认限制 3 个并发连接。远程服务器是轻量级 HTTP 请求,允许 20 个并发。
## 实际配置示例
## 内置 vs 外部 MCP 对比总结
```json
// 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"
}
}
}
```
| 维度 | 内置 MCP | 外部 MCP |
|------|---------|---------|
| **Transport** | `InProcessTransport`(同进程) | stdio / SSE / HTTP / WebSocket |
| **配置来源** | `setupComputerUseMCP()` / `setupClaudeInChrome()` 等动态注册 | 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 |
配置后AI 的工具列表中会出现 `mcp__my-database__query` 和 `mcp__remote-api__*` 工具——与内置工具使用相同的权限检查链路和 UI 渲染。
## 关键源文件索引
| 文件 | 职责 |
|------|------|
| `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 |