From cda7703ec4bef30e9122bbe7d8b11e74df98f9c8 Mon Sep 17 00:00:00 2001 From: claude-code-best Date: Mon, 20 Apr 2026 11:02:51 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E9=87=8D=E5=86=99=E6=89=A9=E5=B1=95?= =?UTF-8?q?=E6=80=A7=E4=B8=89=E7=AB=A0=EF=BC=88MCP=E9=85=8D=E7=BD=AE/?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE/=E8=87=AA=E5=AE=9A=E4=B9=89Agent=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除 TypeScript 代码、源码路径和传输层实现细节, 聚焦多来源合并的企业管控设计、内置vs外部的架构一致性、 工具描述截断的防护和 disallowedTools 黑名单优先的安全思维。 Co-Authored-By: Claude Opus 4.6 --- docs/extensibility/custom-agents.mdx | 228 ++++-------- docs/extensibility/mcp-configuration.mdx | 358 +++---------------- docs/extensibility/mcp-protocol.mdx | 432 ++++------------------- 3 files changed, 177 insertions(+), 841 deletions(-) diff --git a/docs/extensibility/custom-agents.mdx b/docs/extensibility/custom-agents.mdx index 2846f5d48..37dca58e0 100644 --- a/docs/extensibility/custom-agents.mdx +++ b/docs/extensibility/custom-agents.mdx @@ -1,211 +1,107 @@ --- -title: "自定义 Agent - 从 Markdown 到运行时的完整链路" -description: "揭秘 Claude Code 自定义 Agent 完整链路:Agent 定义的 Markdown 数据模型、三种加载来源、工具过滤策略和与 AgentTool 的联动机制。" -keywords: ["自定义 Agent", "Agent 定义", "Markdown Agent", "Agent 配置", "角色定制"] +title: "自定义 Agent" +description: "用 Markdown 文件定义自己的 Agent。理解 Agent 定义的数据模型、工具过滤策略和与 AgentTool 的联动。" +keywords: ["自定义 Agent", "Agent 定义", "Markdown Agent", "角色定制"] --- -{/* 本章目标:揭示 Agent 定义的完整数据模型、加载发现机制、工具过滤和与 AgentTool 的联动 */} +## 核心概念 -## Agent 定义的三种来源 - -Claude Code 的 Agent 不仅仅来自用户自定义——系统有三类来源,按优先级合并: - -| 来源 | 位置 | 优先级 | -|------|------|--------| -| **Built-in** | `packages/builtin-tools/src/tools/AgentTool/built-in/` 硬编码 | 最低(可被覆盖) | -| **Plugin** | 通过插件系统注册 | 中 | -| **User/Project/Policy** | `.claude/agents/*.md` 或 settings.json | 最高 | - -合并逻辑在 `getActiveAgentsFromList()` 中:按 `agentType` 去重,后者覆盖前者。这意味着你可以在 `.claude/agents/` 中放一个 `Explore.md` 来完全替换内置的 Explore Agent。 - -## Markdown Agent 文件的完整格式 +自定义 Agent 是一个 Markdown 文件——frontmatter 定义配置,正文是 system prompt。不需要写代码。 ```markdown --- -# === 必需字段 === -name: "reviewer" # Agent 标识(agentType) -description: "Code review specialist, read-only analysis" - -# === 工具控制 === -tools: "Read,Glob,Grep,Bash" # 允许的工具列表(逗号分隔) -disallowedTools: "Write,Edit" # 显式禁止的工具 - -# === 模型配置 === -model: "haiku" # 指定模型(或 "inherit" 继承主线程) -effort: "high" # 推理努力程度:low/medium/high 或整数 - -# === 行为控制 === -maxTurns: 10 # 最大 agentic 轮次 -permissionMode: "plan" # 权限模式:plan/bypassPermissions 等 -background: true # 始终作为后台任务运行 -initialPrompt: "/search TODO" # 首轮用户消息前缀(支持斜杠命令) - -# === 隔离与持久化 === -isolation: "worktree" # 在独立 git worktree 中运行 -memory: "project" # 持久记忆范围:user/project/local - -# === MCP 服务器 === -mcpServers: - - "slack" # 引用已配置的 MCP 服务器 - - database: # 内联定义 - command: "npx" - args: ["mcp-db"] - -# === Hooks === -hooks: - PreToolUse: - - command: "audit-log.sh" - timeout: 5000 - -# === Skills === -skills: "code-review,security-review" # 预加载的 skills(逗号分隔) - -# === 显示 === -color: "blue" # 终端中的 Agent 颜色标识 +name: "reviewer" +description: "Code review specialist" +tools: "Read,Glob,Grep" +model: "haiku" --- 你是代码审查专家。你的职责是... - -(正文内容 = system prompt) ``` -### 字段解析细节 +这个 Markdown 文件就定义了一个完整的 Agent:它使用什么工具、什么模型、什么行为规则。 -- **`tools`**:通过 `parseAgentToolsFromFrontmatter()` 解析,支持逗号分隔字符串或数组 -- **`model: "inherit"`**:使用主线程的模型(区分大小写,只有小写 "inherit" 有效) -- **`memory`**:启用后自动注入 `Write`/`Edit`/`Read` 工具(即使 `tools` 未包含),并在 system prompt 末尾追加 memory 指令 -- **`isolation: "remote"`**:仅在 Anthropic 内部可用(`USER_TYPE === 'ant'`),外部构建只支持 `worktree` -- **`background`**:`true` 使 Agent 始终在后台运行,主线程不等待结果 +## Agent 的三种来源 -## 加载与发现机制 +| 来源 | 位置 | 优先级 | +|------|------|--------| +| **Built-in** | 硬编码 | 最低(可被覆盖) | +| **Plugin** | 插件系统注册 | 中 | +| **User/Project** | `.claude/agents/*.md` | 最高 | -`getAgentDefinitionsWithOverrides()`(被 `memoize` 缓存)执行完整的发现流程: +合并时按 `agentType` 去重,后者覆盖前者。这意味着你可以在 `.claude/agents/` 中放一个 `Explore.md` 来完全替换内置的 Explore Agent。 -``` -1. 加载 Markdown 文件 - ├── loadMarkdownFilesForSubdir('agents', cwd) - │ ├── ~/.claude/agents/*.md (用户级,source = 'userSettings') - │ ├── .claude/agents/*.md (项目级,source = 'projectSettings') - │ └── managed/policy sources (策略级,source = 'policySettings') - │ - └── 每个 .md 文件: - ├── 解析 YAML frontmatter - ├── 正文作为 system prompt - ├── 校验必需字段(name, description) - ├── 静默跳过无 frontmatter 的 .md 文件(可能是参考文档) - └── 解析失败 → 记录到 failedFiles,不阻塞其他 Agent +## 配置字段 -2. 并行加载 Plugin Agents - └── loadPluginAgents() → memoized +### 工具控制 -3. 初始化 Memory Snapshots(如果 AGENT_MEMORY_SNAPSHOT 启用) - └── initializeAgentMemorySnapshots() +- `tools` — 允许的工具白名单(未指定 = 全部工具) +- `disallowedTools` — 显式禁止的工具(即使 `tools` 未指定也生效) -4. 合并 Built-in + Plugin + Custom - └── getActiveAgentsFromList() → 按 agentType 去重,后者覆盖前者 +**设计考量**:`disallowedTools` 是比 `tools` 更安全的控制方式。如果只指定 `tools` 白名单,新增工具时需要更新白名单。`disallowedTools` 是黑名单思维——默认允许,只禁止危险的。 -5. 分配颜色 - └── setAgentColor(agentType, color) → 终端 UI 中区分不同 Agent -``` +### 模型配置 -## 工具过滤的实现 +- `model` — 指定模型(`haiku`/`sonnet`/`opus`/`inherit`) +- `effort` — 推理努力程度(`low`/`medium`/`high`) -当 Agent 被派生时,`AgentTool` 根据定义中的 `tools` / `disallowedTools` 过滤可用工具列表: +`inherit` 使用主线程的模型——适合需要完整推理能力的任务。`haiku` 适合轻量任务(如搜索),更便宜更快。 -``` -全部工具 - ↓ disallowedTools 移除 - ↓ tools 白名单过滤(如果指定) -可用工具 -``` +### 行为控制 -- **`tools` 未指定**:Agent 可以使用所有工具(默认全能) -- **`tools` 指定**:只能使用列出的工具 -- **`disallowedTools`**:即使 `tools` 未指定,这些工具也被禁止 -- **自动注入**:`memory` 启用时自动添加 `Write`/`Edit`/`Read` +- `maxTurns` — 最大 agentic 轮次(防止无限循环) +- `permissionMode` — 权限模式(`plan`/`bypassPermissions` 等) +- `background` — 始终作为后台任务运行 +- `isolation` — 在独立 worktree 中运行 -以内置 Explore Agent 为例: +### 持久化 -```typescript -// packages/builtin-tools/src/tools/AgentTool/built-in/exploreAgent.ts -disallowedTools: [ - 'Agent', // 不能嵌套调用 Agent - 'ExitPlanMode', // 不需要 plan mode - 'FileEdit', // 只读 - 'FileWrite', // 只读 - 'NotebookEdit', // 只读 -] -``` +- `memory` — 启用跨会话的持久记忆(`user`/`project`/`local`) +- `mcpServers` — 引用或内联定义 MCP 服务器 +- `hooks` — Agent 专属的 Hook 配置 +- `skills` — 预加载的技能 ## System Prompt 的注入方式 -Agent 的 system prompt 通过 `getSystemPrompt()` 闭包延迟生成: +Agent 的 Markdown 正文**完全替换**默认的 system prompt,而非追加。这意味着自定义 Agent 的行为完全由你定义——不受默认 prompt 的约束。 -```typescript -// Markdown Agent -getSystemPrompt: () => { - if (isAutoMemoryEnabled() && memory) { - return systemPrompt + '\n\n' + loadAgentMemoryPrompt(agentType, memory) - } - return systemPrompt -} +如果启用了 `memory`,记忆指令自动追加到 system prompt 末尾。 + +## 工具过滤流程 + +``` +全部工具 → disallowedTools 移除 → tools 白名单过滤 → 可用工具 ``` -这意味着: -1. **Markdown 正文 = 完整的 system prompt**——不是追加,而是替换默认 prompt -2. **Memory 指令**在 memory 启用时自动追加到末尾 -3. **闭包延迟计算**——memory 状态可能在文件加载后才变化 +内置 Explore Agent 的工具限制是很好的例子: +- 禁止 `Agent`(不能嵌套调用子 Agent) +- 禁止 `FileEdit`/`FileWrite`(只读) +- 禁止 `ExitPlanMode`(不需要 plan mode) -对于 Built-in Agent,`getSystemPrompt` 接受 `toolUseContext` 参数,可以根据运行时状态(如是否使用嵌入式搜索工具)动态调整 prompt 内容。 +这些限制让 Explore Agent 成为一个纯粹的搜索工具——它只能看,不能改。 ## 与 AgentTool 的联动 当主 Agent 需要派生子 Agent 时: ``` -AgentTool.call({ subagent_type: "reviewer", ... }) - ↓ -1. 从 agentDefinitions.activeAgents 查找 agentType === "reviewer" - ↓ -2. 检查 requiredMcpServers(如果 Agent 要求特定 MCP 服务器) - ↓ -3. 过滤工具列表(tools / disallowedTools) - ↓ -4. 解析模型: - - "inherit" → 使用主线程模型 - - 具体模型名 → 直接使用 - - 未指定 → 主线程模型 - ↓ -5. 解析权限模式(permissionMode) - ↓ -6. 构建隔离环境(如果 isolation === "worktree") - ↓ -7. 注入 system prompt(getSystemPrompt()) - ↓ -8. 注入 initialPrompt(如果定义了) - ↓ -9. 启动子 Agent 循环(forkSubagent / runAgent) +查找 Agent 定义 → 检查 MCP 依赖 → 过滤工具 → 解析模型 → 构建隔离环境 → 注入 system prompt → 启动子 Agent ``` +每一步都使用 Agent 定义中的配置——工具列表、模型选择、权限模式、隔离环境等。 + ## 内置 Agent 参考 -| Agent | agentType | 角色 | 工具限制 | 模型 | -|-------|-----------|------|---------|------| -| **General Purpose** | `general-purpose` | 默认子 Agent | 全部工具 | 主线程模型 | -| **Explore** | `Explore` | 代码搜索专家 | 只读(无 Write/Edit) | haiku(外部) | -| **Plan** | `Plan` | 规划专家 | 只读 + ExitPlanMode | inherit | -| **Verification** | `verification` | 结果验证 | 由 feature flag 控制 | — | -| **Code Guide** | `claude-code-guide` | Claude Code 使用指南 | 只读 | — | -| **Statusline Setup** | `statusline-setup` | 终端状态栏配置 | 有限 | — | +| Agent | 角色 | 工具限制 | 模型 | +|-------|------|---------|------| +| General Purpose | 通用任务 | 全部工具 | 继承主线程 | +| Explore | 代码搜索 | 只读 | haiku | +| Plan | 规划研究 | 只读 + ExitPlanMode | 继承主线程 | +| Verification | 结果验证 | 由 feature flag 控制 | — | +| Code Guide | 使用指南 | 只读 | — | -SDK 入口(`sdk-ts`/`sdk-py`/`sdk-cli`)不加载 Code Guide Agent。环境变量 `CLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS` 可以完全禁用内置 Agent,给 SDK 用户提供空白画布。 +## 接下来 -## Agent Memory:持久化的 Agent 状态 - -当 `memory` 字段启用时,Agent 获得跨会话的持久记忆: - -- **`local`**:当前项目、当前用户有效 -- **`project`**:当前项目所有用户共享 -- **`user`**:所有项目共享 - -Memory 通过 `loadAgentMemoryPrompt()` 注入到 system prompt 末尾,包含读写记忆的指令。Agent Memory Snapshot 机制在项目间同步 `user` 级记忆。 +- **子 Agent** — 理解子 Agent 的完整执行链路 +- **Skills** — 理解 Skill 中指定 Agent 定义 +- **MCP 配置** — 理解 Agent 中引用 MCP 服务器 diff --git a/docs/extensibility/mcp-configuration.mdx b/docs/extensibility/mcp-configuration.mdx index c696096f9..446f3dc35 100644 --- a/docs/extensibility/mcp-configuration.mdx +++ b/docs/extensibility/mcp-configuration.mdx @@ -1,346 +1,84 @@ --- -title: "MCP 配置 - 多来源合并、作用域与策略管控" -description: "详细说明 Claude Code MCP 配置的来源层次、合并优先级、传输类型、企业策略管控、插件集成和保留名称机制。" -keywords: ["MCP", "配置", "settings.json", ".mcp.json", "企业策略", "插件"] +title: "MCP 配置" +description: "MCP 服务器从多个来源汇聚配置。理解多来源合并、企业排他模式、项目配置审批和保留名称机制。" +keywords: ["MCP", "配置", "settings.json", ".mcp.json", "企业策略"] --- -## 配置来源与作用域 +## 核心问题 -Claude Code 的 MCP 配置来自多个来源,每个来源对应一个 `scope`(作用域)。配置按优先级合并,高优先级来源的同名配置覆盖低优先级。 +MCP(Model Context Protocol)让 Claude Code 可以使用外部工具——数据库查询、浏览器控制、API 调用等。但 MCP 配置来自多个来源:用户全局、项目级、插件、企业策略。如何合并?谁优先? -### 来源列表 +## 配置来源与合并优先级 -| 来源 | Scope | 文件/接口 | 说明 | -|------|-------|----------|------| -| 企业管控 | `enterprise` | 系统管理路径 `managed-mcp.json` | **排他模式**:存在时忽略所有其他来源 | -| 本地项目 | `local` | `/.claude/settings.local.json` | 项目级私有配置(不提交到 VCS) | -| 项目配置 | `project` | `/.mcp.json` | 项目级共享配置(可提交到 VCS) | -| 用户全局 | `user` | `~/.claude/settings.json` | 用户级配置,所有项目共享 | -| 插件 | `dynamic` | 插件 manifest 中 `.mcp.json` / `.mcpb` | 插件提供的 MCP 服务器 | -| claude.ai | `claudeai` | 通过 API 获取 | claude.ai 网页端配置的连接器 | -| 内置动态 | `dynamic` | 代码中注册 | Computer Use / Chrome 等内置服务器 | -| IDE SDK | `sdk` | IDE 传入 | VS Code / JetBrains 嵌入模式 | - -### 合并优先级(从低到高) +配置按优先级从低到高合并,高优先级覆盖低优先级: ``` -claude.ai 连接器 ← 最低优先级 - ↓ 去重 -插件服务器 - ↓ 去重 -用户全局配置 - ↓ -项目配置(.mcp.json) ← 需要用户审批 - ↓ -本地项目配置 - ↓ -动态配置(内置 MCP) ← 最高优先级 +claude.ai 连接器(最低) → 插件 → 用户全局 → 项目配置 → 本地项目 → 内置动态(最高) ``` -`Object.assign({}, dedupedPluginServers, userServers, approvedProjectServers, localServers)` 实现合并——后出现的同名键覆盖前者。 +| 来源 | Scope | 说明 | +|------|-------|------| +| claude.ai 连接器 | `claudeai` | 网页端配置的远程连接器 | +| 插件 | `dynamic` | 插件 manifest 中声明 | +| 用户全局 | `user` | `~/.claude/settings.json` | +| 项目配置 | `project` | `.mcp.json`(需审批) | +| 本地项目 | `local` | `settings.local.json`(不提交 VCS) | +| 内置动态 | `dynamic` | Computer Use 等内置服务器 | -## 企业管控模式 +## 企业排他模式 -当 `managed-mcp.json` 文件存在时,进入 **排他模式**: +当企业管理员部署 `managed-mcp.json` 时,进入**排他模式**:只使用企业配置,忽略所有用户、项目、插件和 claude.ai 配置。 -```typescript -// config.ts:1084 -if (doesEnterpriseMcpConfigExist()) { - // 只返回企业配置,忽略所有用户/项目/插件/claude.ai 配置 - return { servers: filtered, errors: [] } -} -``` - -特性: -- 路径由系统管理决定(`getManagedFilePath()` + `managed-mcp.json`) -- 覆盖所有用户级、项目级、插件和 claude.ai 配置 -- 仍然应用策略过滤(allowlist/denylist) -- 无法通过 CLI 添加新服务器(`addMcpConfig` 会拒绝) - -## 传输类型与配置 Schema - -### stdio(默认) - -启动子进程,通过 stdin/stdout JSON-RPC 通信。 - -```json -{ - "my-server": { - "command": "npx", - "args": ["-y", "@my-org/mcp-server"], - "env": { "API_KEY": "..." } - } -} -``` - -`type` 字段可省略(默认为 `stdio`)。环境变量通过 `env` 传递给子进程,会与当前进程环境合并。 - -**Windows 注意**:使用 `npx` 需要包装为 `cmd /c npx`,否则会报错。 - -### SSE(Server-Sent Events) - -通过 HTTP SSE 连接远程 MCP 服务器。 - -```json -{ - "my-remote": { - "type": "sse", - "url": "https://mcp.example.com/sse", - "headers": { "Authorization": "Bearer ..." }, - "oauth": { - "clientId": "...", - "authServerMetadataUrl": "https://auth.example.com/.well-known/oauth-authorization-server" - } - } -} -``` - -支持 OAuth 认证流程。认证失败时进入 `needs-auth` 状态,15 分钟 TTL 缓存避免重复提示。 - -### HTTP(Streamable HTTP) - -HTTP 流式传输。 - -```json -{ - "my-http": { - "type": "http", - "url": "https://mcp.example.com/mcp", - "headers": { "X-API-Key": "..." } - } -} -``` - -支持与 SSE 相同的 OAuth 配置。 - -### WebSocket - -```json -{ - "my-ws": { - "type": "ws", - "url": "wss://mcp.example.com/ws" - } -} -``` - -### IDE 专用类型(内部) - -`sse-ide` 和 `ws-ide` 是 IDE 扩展专用类型,不由用户直接配置。 - -- `sse-ide`:使用 lockfile token 认证 -- `ws-ide`:使用 `X-Claude-Code-Ide-Authorization` header - -### SDK 类型(内部) - -`type: "sdk"` 由 IDE 嵌入模式传入,不经过保留名称检查和企业管控排他限制。 - -### claude.ai 代理类型(内部) - -`type: "claudeai-proxy"` 由 claude.ai 网页端配置的连接器使用,通过 OAuth bearer token 认证并支持 401 重试。 - -## 配置操作 - -### 添加 MCP 服务器 - -通过 CLI 命令 `claude mcp add` 或 API 调用 `addMcpConfig()`: - -```bash -# 添加到用户配置 -claude mcp add my-server -s user -- npx @my-org/mcp-server - -# 添加到项目配置 -claude mcp add my-server -s project -- npx @my-org/mcp-server - -# 添加 HTTP 类型 -claude mcp add my-remote -s user -t http -u https://mcp.example.com/mcp -``` - -添加时的验证流程: - -1. **名称校验**:只允许字母、数字、连字符和下划线 -2. **保留名检查**:`claude-in-chrome` 和 `computer-use` 被保留 -3. **企业管控检查**:企业模式下拒绝添加 -4. **Schema 验证**:Zod 校验配置格式 -5. **策略检查**:denylist 拒绝、allowlist 验证 - -### 移除 MCP 服务器 - -```bash -claude mcp remove my-server -s user -``` - -### 列出 MCP 服务器 - -```bash -claude mcp list -``` +**设计考量**:企业环境需要严格控制 AI 可以访问哪些外部工具。排他模式确保用户不能绕过企业策略添加自己的 MCP 服务器。 ## 项目配置审批 -`.mcp.json` 中的项目配置需要用户显式审批才能生效: +`.mcp.json` 是项目级共享配置(可提交到 git),但需要用户显式审批才能生效。 -```typescript -// config.ts:1166 -const approvedProjectServers: Record = {} -for (const [name, config] of Object.entries(projectServers)) { - if (getProjectMcpServerStatus(name) === 'approved') { - approvedProjectServers[name] = config - } -} -``` +**为什么需要审批**?项目配置可能由任何人修改(包括恶意贡献者)。审批机制确保用户知情并同意项目提供的 MCP 服务器连接到他们的环境。 -首次打开项目时,Claude Code 会提示用户审批 `.mcp.json` 中的每个服务器。审批状态持久化在本地配置中。 +审批状态持久化在本地配置中,不需要每次重新审批。 -## 插件 MCP 集成 +## 传输类型 -插件通过 manifest 中的 `.mcp.json` 或 `.mcpb` 文件声明 MCP 服务器: +MCP 服务器通过不同的传输方式连接: -```typescript -// 插件 MCP 加载流程 -const pluginResult = await loadAllPluginsCacheOnly() -const pluginServerResults = await Promise.all( - pluginResult.enabled.map(plugin => getPluginMcpServers(plugin, mcpErrors)) -) -``` +| 类型 | 适用场景 | 配置方式 | +|------|---------|---------| +| **stdio** | 本地工具(启动子进程) | `command` + `args` | +| **SSE** | 远程 Server-Sent Events | `url` + 可选 `headers` | +| **HTTP** | HTTP 流式传输 | `url` + 可选 `headers` | +| **WebSocket** | 双向实时通信 | `wss://` URL | -### 插件命名空间 +stdio 类型最常见——它启动一个本地子进程,通过 stdin/stdout 通信。远程类型(SSE/HTTP/WS)用于连接远程服务。 -插件 MCP 服务器名格式为 `plugin::`,不会与手动配置的名称冲突。 +### 认证 -### 去重机制 +远程 MCP 服务器支持 OAuth 认证。认证失败时进入 `needs-auth` 状态,15 分钟内不重复提示。 -插件服务器通过内容签名去重(`dedupPluginMcpServers`): +## 插件集成 -- **stdio 类型**:签名 = `stdio:` + JSON.stringify([command, ...args]) -- **URL 类型**:签名 = `url:` + 原始 URL(unwrap CCR proxy URL) -- **sdk 类型**:签名为 null,不去重 +插件通过 manifest 声明 MCP 服务器,命名空间为 `plugin::`,不会与手动配置冲突。 -去重规则: -1. 手动配置优先于插件配置 -2. 先加载的插件优先于后加载的 -3. 被抑制的插件服务器在 `/plugin` UI 中显示提示 - -### claude.ai 连接器去重 - -claude.ai 连接器使用相同的内容签名机制去重(`dedupClaudeAiMcpServers`): -- 仅启用的手动配置参与去重(禁用的手动配置不应抑制连接器) -- 连接器名格式为 `claude.ai ` +插件服务器通过内容签名去重: +- stdio 类型:基于 command + args +- URL 类型:基于 URL +- 手动配置优先于插件配置 ## 策略管控 -### Allowlist / Denylist - -企业策略通过 allowlist 和 denylist 控制可用的 MCP 服务器: - -```typescript -// config.ts:1243 - 最终策略过滤 -for (const [name, serverConfig] of Object.entries(configs)) { - if (!isMcpServerAllowedByPolicy(name, serverConfig)) { - continue // 跳过策略禁止的服务器 - } - filtered[name] = serverConfig -} -``` - -策略检查考虑: -- 服务器名称匹配 -- stdio 类型的 command + args 匹配 -- URL 类型的 URL 模式匹配(支持通配符) - -### 插件专用模式 - -`isRestrictedToPluginOnly('mcp')` 启用时,只允许插件提供的 MCP 服务器——用户/项目级配置被忽略。 - -## 环境变量展开 - -MCP 配置中的环境变量支持 `$VAR` 和 `${VAR}` 语法展开: - -```json -{ - "my-server": { - "command": "npx", - "args": ["@my-org/mcp-server"], - "env": { - "API_KEY": "$MY_API_KEY", - "DB_URL": "${DATABASE_URL}" - } - } -} -``` - -展开时缺失的变量会生成警告信息,但不阻止配置加载。 - -## 内置 MCP 动态注册 - -内置 MCP 服务器在 `main.tsx` 启动流程中动态注入配置: - -### Computer Use MCP - -```typescript -// src/utils/computerUse/setup.ts -export function setupComputerUseMCP(): { - mcpConfig: Record - allowedTools: string[] -} { - return { - mcpConfig: { - "computer-use": { - type: "stdio", - command: process.execPath, - args: ["--computer-use-mcp"], - scope: "dynamic", - } - }, - allowedTools: ["mcp__computer-use__screenshot", ...] - } -} -``` - -启用条件: -- Feature flag `CHICAGO_MCP` 开启 -- `getPlatform() !== "unknown"`(macOS/Windows/Linux) -- 非非交互式会话 -- GrowthBook gate `getChicagoEnabled()` 返回 true - -### Claude in Chrome MCP - -```typescript -// 类似 Computer Use,在 main.tsx 中注册 -const { mcpConfig, allowedTools, systemPrompt } = setupClaudeInChrome() -dynamicMcpConfig = { ...dynamicMcpConfig, ...mcpConfig } -``` - -启用条件: -- `--chrome` 参数或 `claudeInChromeDefaultEnabled` 配置 -- Chrome 扩展已安装 - -### VSCode SDK MCP - -IDE 嵌入模式通过初始化消息传入 `type:'sdk'` 的配置,由 `setupVscodeSdkMcp()` 设置双向通知。 +企业策略通过 allowlist 和 denylist 控制可用的 MCP 服务器。策略检查不仅匹配服务器名称,还匹配 command/args(stdio)和 URL 模式(远程)。 ## 保留名称 -以下 MCP 服务器名称被保留,用户无法手动配置同名服务器: +以下名称被保留,用户无法手动配置: +- `claude-in-chrome` — Chrome 浏览器控制 +- `computer-use` — 桌面自动化 -| 名称 | 用途 | 检查条件 | -|------|------|---------| -| `claude-in-chrome` | Chrome 浏览器控制 | 始终检查 | -| `computer-use` | 桌面自动化 | `CHICAGO_MCP` feature flag 开启时检查 | -| `claude-vscode` | VSCode IDE 集成 | 由 SDK 传入,不经过名称检查 | +这防止用户意外覆盖内置服务器的配置。 -保留名检查在两个位置: -1. `addMcpConfig()`(`config.ts:636-648`)— 运行时拒绝 -2. `main.tsx` 启动检查(`main.tsx:2351-2368`)— 启动时退出 +## 接下来 -## 关键源文件索引 - -| 文件 | 职责 | -|------|------| -| `src/services/mcp/config.ts` | 配置管理核心:合并、去重、策略、添加/删除 | -| `src/services/mcp/types.ts` | Zod Schema 定义、类型声明 | -| `src/services/mcp/client.ts` | 连接管理、传输层选择 | -| `src/utils/plugins/mcpPluginIntegration.ts` | 插件 MCP 配置加载 | -| `src/utils/computerUse/setup.ts` | Computer Use 动态注册 | -| `src/utils/claudeInChrome/common.ts` | Chrome MCP 保留名与工具名 | -| `src/services/mcp/vscodeSdkMcp.ts` | VSCode SDK 双向通知 | +- **MCP 协议** — 理解连接管理、工具发现和执行链路 +- **Hooks** — 理解 MCP 生命周期中的 Hook 集成 +- **自定义 Agent** — 理解 Agent 中引用 MCP 服务器 diff --git a/docs/extensibility/mcp-protocol.mdx b/docs/extensibility/mcp-protocol.mdx index 5498813f1..000166204 100644 --- a/docs/extensibility/mcp-protocol.mdx +++ b/docs/extensibility/mcp-protocol.mdx @@ -1,407 +1,109 @@ --- -title: "MCP 协议 - 连接管理、工具发现与执行链路" -description: "从源码角度解析 Claude Code 的 MCP 集成:内置 MCP 与外部 MCP 的区别、7 种传输层实现、connectToServer 的 memoize 缓存、工具发现的 LRU 策略、认证状态机、以及 MCP 工具如何进入权限检查链路。" -keywords: ["MCP", "Model Context Protocol", "工具扩展", "MCP 客户端", "工具发现", "内置 MCP", "外部 MCP"] +title: "MCP 协议" +description: "从配置到可用工具:MCP 连接管理、内置 vs 外部两种模式、工具发现和执行链路的设计。" +keywords: ["MCP", "Model Context Protocol", "工具扩展", "MCP 客户端", "工具发现"] --- -{/* 本章目标:从源码角度揭示 MCP 客户端的两种运行模式(内置/外部)、连接管理、工具发现协议和执行链路 */} +## 核心问题 -## 架构总览:从配置到可用工具 +MCP 让 Claude Code 使用外部工具。但连接外部服务有延迟、可能失败、工具列表可能变化。如何管理这些不确定性? + +## 架构总览 ``` -配置层(多来源合并) - ├── 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 - ↓ -useManageMCPConnections() ← React Hook 管理连接生命周期 - ↓ -connectToServer(name, config) ← memoize 缓存(lodash memoize) - ├── 判断:内置 MCP → InProcessTransport(同进程) - ├── 判断:外部 stdio → StdioClientTransport(子进程) - ├── 判断:远程 SSE/HTTP/WS → 网络传输 - └── 返回 MCPServerConnection ← { connected | failed | needs-auth | pending | disabled } - ↓ -fetchToolsForClient(client) ← LRU(20) 缓存 - ├── client.request({ method: 'tools/list' }) - └── 每个工具包装为 MCPTool ← 统一 Tool 接口 - ↓ -assembleToolPool() ← 合并内置工具 + MCP 工具 - ↓ -工具名格式: mcp____ ← buildMcpToolName() +配置(多来源合并) → 连接管理(缓存) → 工具发现(MCP 协议) → 工具执行 ``` -## 两种 MCP 模式:内置 vs 外部 - -Claude Code 的 MCP 实现区分 **内置 MCP 服务器** 和 **外部 MCP 服务器**。两者使用相同的客户端协议和工具发现机制,但在连接方式、生命周期管理和配置来源上完全不同。 +## 两种 MCP 模式 ### 内置 MCP 服务器 -内置 MCP 服务器由 Claude Code 自身提供,无需用户手动配置。它们在启动时自动注册为 `dynamic` scope 的配置,并在同进程内运行。 +Computer Use、Chrome 控制等内置功能通过 MCP 协议暴露,但运行在**同进程内**——不启动子进程,无网络开销,无 IPC 序列化。 -| 服务器 | 名称 | 包路径 | 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 协议(`tools/list`、`tools/call`),但通过 `InProcessTransport` 在进程内通信。这意味着所有工具发现、权限检查、执行逻辑都是同一套代码——内置和外部工具的唯一区别是传输方式。 ### 外部 MCP 服务器 -外部 MCP 服务器由用户在配置文件中声明,通过子进程或网络连接运行。 +用户配置的外部工具,通过子进程(stdio)或网络连接(SSE/HTTP/WS)运行。 -#### 配置来源 +| 维度 | 内置 | 外部 | +|------|------|------| +| 进程模型 | 同进程 | 子进程或网络 | +| 启动开销 | 零 | 子进程启动或网络握手 | +| 权限 | 自动授权 | 需要用户确认 | +| 配置来源 | 动态注册 | settings.json / .mcp.json | +| 名称保护 | 保留名,不可覆盖 | 自由命名 | -| 来源 | Scope | 文件位置 | 优先级 | -|------|-------|---------|--------| -| 项目配置 | `project` | `/.mcp.json` | 最高(同名覆盖) | -| 本地配置 | `local` | `/.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://..." } - }, +连接使用 memoize 缓存——相同的配置不会重复建立连接。缓存 key 包含服务器名和配置内容,配置变化时自动失效。 - // 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" - }, +远程连接有连续错误计数器。遇到网络错误(ECONNRESET、ETIMEDOUT 等)连续 3 次后,主动关闭连接触发重连。 - // WebSocket 类型 - "ws-service": { - "type": "ws", - "url": "wss://ws.example.com/mcp" - } - } -} -``` +**设计考量**:网络连接是脆弱的——临时故障不应该永久禁用一个 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` | 外部本地子进程 | 无 | -| `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`): +stdio 类型的子进程清理使用信号升级策略: ``` SIGINT (100ms) → SIGTERM (400ms) → SIGKILL ``` -总清理时间上限 600ms,防止 MCP 服务器关闭阻塞 CLI 退出。 +总清理时间上限 600ms。防止 MCP 服务器关闭阻塞 CLI 退出。 -### 远程传输的认证状态机 +### 并发控制 -SSE/HTTP 类型使用 `ClaudeAuthProvider` 实现 OAuth 认证流程。认证失败时进入 `needs-auth` 状态,并写入 15 分钟 TTL 的缓存文件(`mcp-needs-auth-cache.json`),避免重复弹出认证提示。 +| 类型 | 并发上限 | 原因 | +|------|---------|------| +| 本地(stdio) | 3 | 每个子进程是重量级资源 | +| 远程(HTTP) | 20 | 轻量级 HTTP 请求 | -``` -连接尝试 → 401 Unauthorized - ↓ -handleRemoteAuthFailure() - ├── logEvent('tengu_mcp_server_needs_auth') - ├── setMcpAuthCacheEntry(name) ← 写入 15min TTL 缓存 - └── return { type: 'needs-auth' } ← UI 显示认证提示 -``` +## 工具发现 -## 连接缓存与重连机制 +### 从 MCP 到 Tool 接口 -`connectToServer` 使用 lodash `memoize` 缓存连接对象,缓存 key 为 `${name}-${JSON.stringify(config)}`。 - -### 缓存失效触发 - -当连接关闭时(`client.onclose`),清除所有相关缓存(`client.ts:1376-1404`): - -```typescript -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`): - -```typescript -let consecutiveConnectionErrors = 0 -const MAX_ERRORS_BEFORE_RECONNECT = 3 -``` - -遇到终端错误(ECONNRESET、ETIMEDOUT、EPIPE 等)连续 3 次后,主动关闭 transport 触发重连。对于 HTTP 传输,还检测 session 过期(404 + JSON-RPC code -32001)。 - -### 请求级超时保护 - -每个 HTTP 请求使用独立的 `setTimeout` 超时(`wrapFetchWithTimeout`,`client.ts:493`),而非共享 `AbortSignal.timeout()`。原因是 Bun 对 AbortSignal.timeout 的 GC 是惰性的——每个请求约 2.4KB 原生内存,即使请求毫秒级完成也要等 60s 才回收。 - -```typescript -const controller = new AbortController() -const timer = setTimeout(c => c.abort(...), MCP_REQUEST_TIMEOUT_MS, controller) -timer.unref?.() // 不阻止进程退出 -``` - -## 工具发现:从 MCP 到 Tool 接口 - -`fetchToolsForClient()`(`client.ts:1744-2000`)使用 `memoizeWithLRU` 缓存(上限 100),将 MCP 工具转换为 Claude Code 的统一 Tool 接口: - -```typescript -const fullyQualifiedName = buildMcpToolName(client.name, tool.name) -// 结果: "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 服务器通过 `tools/list` 方法暴露工具列表。每个工具被包装为 Claude Code 统一的 Tool 接口,工具名格式为 `mcp____`。 ### 工具描述截断 -MCP 工具描述上限 2048 字符(`MAX_MCP_DESCRIPTION_LENGTH`)。OpenAPI 生成的 MCP 服务器曾观察到 15-60KB 的描述文档。 +MCP 工具描述上限 2048 字符。OpenAPI 生成的 MCP 服务器曾观察到 15-60KB 的描述文档——截断防止这些巨大描述占满 System Prompt。 ### 工具能力标注 -每个 MCP 工具根据 `tool.annotations` 自动标注: +MCP 工具通过 `annotations` 声明自身特性: -| 注解 | 映射到 | 含义 | -|------|--------|------| -| `readOnlyHint` | `isReadOnly()` + `isConcurrencySafe()` | 只读,可并行 | -| `destructiveHint` | `isDestructive()` | 破坏性操作 | -| `openWorldHint` | `isOpenWorld()` | 开放世界(不可枚举) | -| `title` | `userFacingName()` | 显示名称 | - -### MCP 工具的权限检查 - -MCP 工具默认返回 `{ behavior: 'passthrough' }`(`client.ts:1816-1834`),意味着它们始终进入权限确认流程。工具名使用 `mcp__` 前缀精确匹配权限规则。 - -内置 MCP 服务器的工具通过 `allowedTools` 列表自动授权——在 `main.tsx` 启动时加入,绕过普通权限提示。例如 Computer Use 工具的 `request_access` 自行处理会话级审批。 - -## 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 } -``` - -### Session 过期自动重试 - -HTTP 传输的 MCP session 可能过期。检测到 `McpSessionExpiredError` 后自动重试一次(`client.ts:1862`),因为 `ensureConnectedClient()` 已经清除了缓存并建立了新连接。 - -### 内容截断与持久化 - -大型 MCP 工具输出通过 `truncateMcpContentIfNeeded` 截断,二进制内容(图片)通过 `persistBinaryContent` 写入文件并返回文件路径。图片自动 resize(`maybeResizeAndDownsampleImageBuffer`)。 - -## MCP 连接的并发控制 - -```typescript -// 本地服务器并发连接数 -getMcpServerConnectionBatchSize() // 默认 3 - -// 远程服务器并发连接数 -getRemoteMcpServerConnectionBatchSize() // 默认 20 -``` - -本地 MCP 服务器(stdio)是重量级的子进程,默认限制 3 个并发连接。远程服务器是轻量级 HTTP 请求,允许 20 个并发。 - -## 内置 vs 外部 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 | - -## 关键源文件索引 - -| 文件 | 职责 | +| 注解 | 含义 | |------|------| -| `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) | +| `readOnlyHint` | 只读操作,可并行 | +| `destructiveHint` | 破坏性操作 | +| `openWorldHint` | 开放世界(不可枚举所有行为) | + +这些标注影响权限检查——只读工具在更多权限模式下被自动放行。 + +### 权限检查 + +MCP 工具默认进入权限确认流程(`passthrough`),通过 `mcp__` 前缀匹配权限规则。用户可以为特定 MCP 服务器配置 allow/deny 规则。 + +内置 MCP 工具通过白名单自动授权,不触发权限提示。 + +## 执行链路 + +``` +AI 调用 MCP 工具 → 确保连接有效 → 通过 MCP 协议执行 → 处理结果 + → Session 过期?自动重试一次 + → 结果过大?截断 + 持久化到磁盘 + → 包含图片?自动 resize + 持久化 +``` + +**设计考量**:MCP 工具的执行结果可能很大(数据库查询结果、API 响应)。截断 + 持久化确保大型结果不会耗尽上下文窗口,同时 AI 仍然知道结果在哪里可以找到。 + +## 接下来 + +- **MCP 配置** — 理解多来源合并和企业管控 +- **工具系统** — 理解所有工具的统一接口 +- **权限模型** — 理解 MCP 工具的权限检查