docs: 重写扩展性三章(MCP配置/协议/自定义Agent)

移除 TypeScript 代码、源码路径和传输层实现细节,
聚焦多来源合并的企业管控设计、内置vs外部的架构一致性、
工具描述截断的防护和 disallowedTools 黑名单优先的安全思维。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
claude-code-best
2026-04-20 11:02:51 +08:00
parent 2b1953ce8a
commit cda7703ec4
3 changed files with 177 additions and 841 deletions

View File

@@ -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 promptgetSystemPrompt()
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 服务器

View File

@@ -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`(作用域)。配置按优先级合并,高优先级来源的同名配置覆盖低优先级。
MCPModel Context Protocol让 Claude Code 可以使用外部工具——数据库查询、浏览器控制、API 调用等。但 MCP 配置来自多个来源:用户全局、项目级、插件、企业策略。如何合并?谁优先?
### 来源列表
## 配置来源与合并优先级
| 来源 | Scope | 文件/接口 | 说明 |
|------|-------|----------|------|
| 企业管控 | `enterprise` | 系统管理路径 `managed-mcp.json` | **排他模式**:存在时忽略所有其他来源 |
| 本地项目 | `local` | `<project>/.claude/settings.local.json` | 项目级私有配置(不提交到 VCS |
| 项目配置 | `project` | `<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`,否则会报错。
### SSEServer-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 缓存避免重复提示。
### HTTPStreamable 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<string, ScopedMcpServerConfig> = {}
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:<pluginName>:<serverName>`,不会与手动配置的名称冲突。
### 认证
### 去重机制
远程 MCP 服务器支持 OAuth 认证。认证失败时进入 `needs-auth` 状态15 分钟内不重复提示。
插件服务器通过内容签名去重(`dedupPluginMcpServers`
## 插件集成
- **stdio 类型**:签名 = `stdio:` + JSON.stringify([command, ...args])
- **URL 类型**:签名 = `url:` + 原始 URLunwrap CCR proxy URL
- **sdk 类型**:签名为 null不去重
插件通过 manifest 声明 MCP 服务器,命名空间为 `plugin:<pluginName>:<serverName>`,不会与手动配置冲突。
去重规则
1. 手动配置优先于插件配置
2. 先加载的插件优先于后加载的
3. 被抑制的插件服务器在 `/plugin` UI 中显示提示
### claude.ai 连接器去重
claude.ai 连接器使用相同的内容签名机制去重(`dedupClaudeAiMcpServers`
- 仅启用的手动配置参与去重(禁用的手动配置不应抑制连接器)
- 连接器名格式为 `claude.ai <DisplayName>`
插件服务器通过内容签名去重:
- 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<string, ScopedMcpServerConfig>
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/argsstdio和 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 服务器

View File

@@ -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__<serverName>__<toolName> ← 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` | `<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://..." }
},
连接使用 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__<serverName>__<toolName>`。
### 工具描述截断
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 工具的权限检查