Claude Code 源码深度讲解 · 必修篇 1/5

MCP (Model Context Protocol) 系统详解

MCP 是 Anthropic 2024 年推出、现已被 Cursor / Cline / Continue / Zed / Goose 等多家产品采纳的 agent 工具生态事实标准。 理解它既是做 AI agent 开发必过的一关,也是简历上最硬的 20% 差异化知识点。本文从 JSON-RPC 底层一路讲到实战写一个 MCP server。

01MCP 是什么,为何是行业标准

MCP = Model Context Protocol。它解决一个核心问题:LLM 客户端(Claude Code、Cursor、ChatGPT Desktop...)如何统一地连接到任意工具服务(GitHub、Slack、数据库、搜索引擎...)。

1.1 MCP 要解决的"N×M 问题"

没有 MCP:N × M 对连接 Claude Cursor Cline GitHub Slack DB Files 12 条定制集成
引入 MCP 前:3 个客户端 × 4 个工具 = 12 个特定集成;引入后:3+4=7 个协议实现即可

1.2 类比:MCP 之于 LLM 工具 = LSP 之于编辑器语言

LSP(Language Server Protocol)让 VSCode / Vim / Emacs / IntelliJ 能共用一套"语言服务器"。 MCP 用同样的思路让任意 LLM 客户端能共用一套"工具服务器"。

1.3 为什么简历要写它

  • 标准化:Anthropic、Google、Microsoft 都在支持
  • 可写可调:官方 SDK 有 TypeScript / Python / Go
  • 面试热点:涉及 JSON-RPC、OAuth、异步消息、schema 设计
  • 工程密度高:auth.ts 单文件 88KB,有大量生产级决策

02协议基础 — JSON-RPC 2.0

MCP 底层是 JSON-RPC 2.0,一个极其轻量的 RPC 协议。三种消息类型、一个 id 字段,就把所有交互都统一了。

2.1 三种消息类型

// Request(请求) — 必须带 id
{ "jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {} }

// Response(响应) — 必须带对应 id
{ "jsonrpc": "2.0", "id": 1, "result": { "tools": [...] } }
// 或错误响应
{ "jsonrpc": "2.0", "id": 1, "error": { "code": -32601, "message": "Method not found" } }

// Notification(通知) — 无 id,无需响应
{ "jsonrpc": "2.0", "method": "notifications/tools/list_changed" }

2.2 MCP 定义的方法清单

方法方向用途
initializeclient → server握手,交换 capabilities
notifications/initializedclient → server握手完成通知
tools/listclient → server列出工具
tools/callclient → server调用工具
resources/listclient → server列出资源
resources/readclient → server读取资源内容
prompts/listclient → server列出 prompt 模板
prompts/getclient → server实例化 prompt
sampling/createMessageserver → clientserver 请求 client 调 LLM
elicitation/createserver → clientserver 向 client 索要缺失参数
notifications/tools/list_changedserver → client工具列表变化
notifications/resources/updatedserver → client资源内容更新

2.3 握手序列

Client Server initialize(clientCapabilities) serverCapabilities + serverInfo notifications/initialized(无响应) tools/list (request) { tools: [...] }
建立连接后的四次交互:握手 + 能力交换 + 工具发现

03三种传输层对比

JSON-RPC 消息通过什么物理通道传输?MCP 支持 3+ 种,Claude Code 都实现了(src/services/mcp/types.ts:24)。

3.1 传输类型对照(types.ts)

Transport进程模型网络适合场景
stdio子进程无(管道)本地工具、CLI 集成、最安全
sse远端服务HTTP + Server-Sent Events旧版远程 server(向后兼容)
http远端服务HTTP(streamable)现代远程 server(推荐)
ws远端服务WebSocket低延迟双向
sse-ideIDE 插件localhost内部:IDE 扩展暴露工具
ws-ideIDE 插件localhost内部:WebSocket IDE
sdk同进程in-process MCP(SDK 宿主)
claudeai-proxyClaude.ai 转发HTTP内部:订阅用户用

3.2 stdio 详解(最常见)

// ~/.claude/mcp.json 配置
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
      "env": { "LOG_LEVEL": "debug" }
    }
  }
}

Claude Code 启动时会 spawn('npx', args, { env }) 起子进程,通过 stdin/stdout 交换 JSON-RPC 消息,以换行 \n 分隔。

为什么 stdio 最安全
子进程只能访问被显式授予的资源(通过 args 和 env)。没有监听端口 → 没有网络攻击面。 父进程退出子进程自动终止。适合文件、数据库、本地 API 等场景。

3.3 SSE vs streamable HTTP 区别

方面SSE(旧)streamable HTTP(新)
连接数2 个(GET 长连接 + POST 短连接)1 个(可复用)
请求-响应分两条路径同一个 HTTP 请求内完成
代理友好度差(SSE 易被代理超时关)好(普通 HTTP)
推荐程度legacy新项目

3.4 Claude Code 的长连接特殊处理

// client.ts:648 — SSE EventSource 的 fetch 不能用 timeout wrapper
// 因为 EventSource 是长连接,60 秒 timeout 会杀掉它
transportOptions.eventSourceInit = {
  fetch: async (url, init) => {
    const tokens = await authProvider.tokens()
    if (tokens) authHeaders.Authorization = `Bearer ${tokens.access_token}`
    return fetch(url, { ...init, headers: { ...init.headers, ...authHeaders } })
  },
}
// POST 则使用带 timeout 的 wrapFetchWithTimeout

04连接生命周期

每个 MCP server 都是一个有限状态机,Claude Code 在 src/services/mcp/types.ts:180-226 定义了 5 种状态。

4.1 五种状态

pending 正在连接 connected 已连接,工具可用 failed 永久失败 needs-auth 需要 OAuth disabled 被策略禁用 握手成功 异常 401/403 OAuth 完成后重试 不会自动转换到其他状态 状态字段差异 • connected: 带 client / capabilities / cleanup 函数 • failed: 带 error 原因字符串 • pending: 带 reconnectAttempt / maxReconnectAttempts
MCPServerConnection 五种状态,disabled 是特殊的"用户禁用",不参与自动转换

4.2 ConnectedMCPServer 完整结构

// types.ts:180
type ConnectedMCPServer = {
  client: Client                      // MCP SDK Client 实例
  name: string                        // server 配置里的 key
  type: 'connected'
  capabilities: ServerCapabilities    // server 声明的能力
  serverInfo?: { name: string, version: string }
  instructions?: string              // server 的 system 级提示
  config: ScopedMcpServerConfig       // 原始配置 + 来源 scope
  cleanup: () => Promise<void>         // 关闭连接
}

4.3 状态转换触发点

转换触发源代码位置
pending → connectedconnectToServer 握手成功client.ts:595
pending → failed任意非 401 异常catch 分支
pending → needs-auth401 + OAuth 配置auth.ts:340
connected → needs-auth工具调用遇 401McpAuthError 抛出后
needs-auth → connected用户完成 OAuth 后重连reconnectMcpServerImpl

05配置 7 层来源

MCP server 配置可以来自 7 个不同地方,按优先级合并(src/services/mcp/types.ts:10)。

5.1 ConfigScope 7 层

// types.ts:10
ConfigScope = 'local'         // ~/.claude/mcp.json 用户全局
            | 'user'          // 用户级 settings
            | 'project'       // .claude/mcp.json 项目级
            | 'dynamic'       // agent 运行时 inline 定义
            | 'enterprise'    // 企业级(MDM)
            | 'claudeai'      // Claude.ai 集成
            | 'managed'       // 托管策略

5.2 合并优先级(由低到高)

  1. local:用户个人的 ~/.claude/mcp.json
  2. user:~/.claude/settings.json 里的 mcpServers
  3. project:项目目录 .claude/mcp.json(团队共享)
  4. dynamic:agent 在 frontmatter 里 inline 定义的 MCP server,用完即销毁
  5. claudeai:Claude.ai 订阅用户的托管 MCP 集成
  6. managed:managed-settings.json 的托管配置
  7. enterprise(最高):MDM 推送的策略,用户无法覆盖

5.3 策略过滤 — allowlist / denylist

// config.ts:364 — 企业可以全局禁用某些 server
function isMcpServerDenied(name, config, denylist) {
  // 按名字匹配 / 按 URL pattern 匹配
  if (denylist.names?.includes(name)) return true
  const url = getServerUrl(config)
  if (url && denylist.urlPatterns?.some(p => urlMatchesPattern(url, p))) return true
  return false
}

5.4 env 变量展开

// config.ts:556 — env 值里支持 ${VAR_NAME} 引用
{
  "command": "node",
  "args": ["server.js"],
  "env": {
    "API_KEY": "${MY_API_KEY}",    // ← 从 process.env 展开
    "DB_URL": "postgres://..."
  }
}

06connectToServer 的 memoize

connectToServer 被 lodash memoize 包装,cache key 是 name + JSON.stringify(config)。这个看似简单的优化解决了一个关键工程问题。

6.1 为什么要 memoize

// client.ts:581
export function getServerCacheKey(name, serverRef) {
  return `${name}-${jsonStringify(serverRef)}`
}

export const connectToServer = memoize(
  async (name, serverRef, serverStats) => { ... },
  getServerCacheKey
)
设计考量
同一个 MCP server 可能被多个 agent 引用:主线程、子 agent 1、子 agent 2 都可能用 github server。 如果每个 agent 各自 spawn 一次 npx,会有:
  • 同一 OAuth token 的三次并行 refresh
  • 子进程开销(npx 冷启动 ~2s)×3
  • 文件锁 / 端口冲突
memoize 保证同名 + 同配置的 server 只连一次,多个 agent 共享 client 实例。

6.2 反面:什么时候必须新建连接

agent frontmatter 里写的 inline server(scope='dynamic')会带上 agent 特定的配置。这种情况 cacheKey 不同,会新建一个连接,跟着 agent 生命周期走 —— 这在 multi-agent 篇的 initializeAgentMcpServers 里已经讲过。

6.3 clearServerCache 的用法

// client.ts:1650
export async function clearServerCache(name: string, config?: ScopedMcpServerConfig) {
  // 先关闭该连接的所有资源
  // 然后 connectToServer.cache.delete(key)
  // 下一次 connectToServer 调用会重新建立连接
}

用途:OAuth token 更新后、用户改动配置后、reconnect 命令等。

07OAuth + DCR 完整流程

远端 MCP server 要做 OAuth 认证。Claude Code 用的是 OAuth 2.1 + PKCE + Dynamic Client Registration 组合。整个 auth.ts 有 88KB,处理各种边缘情况。

7.1 完整流程

Client (Claude Code) MCP Server Auth Server ① GET /sse 无 token ② 401 + WWW-Authenticate resource_metadata_url ③ 获取 resource metadata 拿到 auth server URL ④ 获取 /.well-known/oauth-authorization-server ⑤ POST /register(DCR,动态注册 client) ⑥ 返回 client_id + client_secret ⑦ 浏览器打开 /authorize + PKCE code_challenge ⑧ 用户授权,callback + code 回到本地端口 ⑨ POST /token + code_verifier(PKCE) ⑩ access_token + refresh_token 存入 keychain
完整 OAuth + DCR 流程,10 步内完成从"未认证"到"拿到 token"

7.2 PKCE 为什么重要

没有 PKCE(Proof Key for Code Exchange),攻击者可以拦截 authorization code 换 token。PKCE 让 client 在 /authorize 时发送 code_challenge = SHA256(random_verifier),在 /token 时发送原始 code_verifier。服务端校验后才发 token。

7.3 DCR(动态客户端注册)

传统 OAuth 要求 client 预先在 provider 那注册拿 client_id。但 MCP 是动态发现的 —— 用户今天才知道要用这个 server。DCR(RFC 7591)允许 client 自动 POST 到 /register 端点动态拿 client_id,解决这个 chicken-and-egg 问题。

7.4 Token 刷新

// ClaudeAuthProvider(auth.ts:1376)是 MCP SDK 的 OAuthClientProvider 实现
class ClaudeAuthProvider implements OAuthClientProvider {
  async tokens(): Promise<OAuthTokens | undefined>
  async saveTokens(tokens): Promise<void>
  async redirectToAuthorization(url): Promise<void>  // 打开浏览器
  async saveCodeVerifier(verifier): Promise<void>
  async codeVerifier(): Promise<string>
}

Token 用本地系统 keychain 保存(macOS Keychain / Windows Credential / libsecret),不写明文文件。

7.5 Step-Up Authentication

某些 API 需要"加强认证"。server 返回 403 + insufficient_scope,client 需要重新走一次 OAuth 但带上 scope=higherwrapFetchWithStepUpDetection(auth.ts:1354) 在 fetch 层做拦截。

08原语 1 — tool

Tool 是最常用的原语,90% 的 MCP server 只提供 tool。

8.1 tool 的生命周期

  1. discover:tools/list 返回 [{name, description, inputSchema}]
  2. normalize:服务端工具名可能含非法字符,client 做规范化(normalization.ts)。规则:name → mcp__<server>__<normalizedName>
  3. register:包装成 Claude Code 的 Tool 接口,加入 tool pool
  4. call:tools/call 传入 {name, arguments},返回 {content, isError}

8.2 名字规范化

// 服务端原始名
"create-issue"  (含连字符)
"github.search" (含点)

// 经过规范化
"mcp__github__create_issue"
"mcp__github__github_search"

// 规则:下划线化,加前缀,保留原名供反查

8.3 Content 类型

// tool 结果可以是多种内容类型混合
MCPToolResult = {
  content: Array<
    | { type: 'text', text: string }
    | { type: 'image', data: string, mimeType: string }    // base64
    | { type: 'resource', resource: { uri, mimeType, text? / blob? } }
  >
  isError?: boolean
  _meta?: Record<string, unknown>    // 非模型可见元数据
}

8.4 client 侧 transformResultContent

// client.ts:2480 — 处理 resource 嵌套、blob 持久化
export async function transformResultContent(content, toolName, serverName) {
  // 1. text 直接透传
  // 2. image 检查 size < 限制,否则截断
  // 3. resource(blob) 先落盘到 /tmp,content 替换为 "[saved to /tmp/xxx]"
  // 4. resource(text) 内嵌
}

09原语 2 — resource

Resource 是 server 暴露给 client 的"可读内容",用 URI 标识。像 REST 的 GET,只是没有 body。

9.1 例子

// filesystem MCP server 暴露文件为 resource
{ uri: "file:///Users/me/project/README.md", name: "README", mimeType: "text/markdown" }
{ uri: "file:///Users/me/project/src/index.ts", ... }

// PostgreSQL MCP 暴露表
{ uri: "postgres://users", name: "users table", mimeType: "application/json" }

// Git MCP 暴露 branch
{ uri: "git://branch/main", name: "main branch", ... }

9.2 URI template(RFC 6570)

server 可以返回 URI template,client 按模板填参:

{ uriTemplate: "file:///{path}" }
// client 填参数:file:///Users/me/specific.txt

9.3 订阅变化

// client 订阅
{ method: "resources/subscribe", params: { uri: "file:///watched/log.txt" } }

// server 内容变化时推送 notification
{ method: "notifications/resources/updated", params: { uri: "file:///watched/log.txt" } }

// client 收到通知后重新 resources/read

9.4 Resource 在 Claude Code 的使用

Claude Code 通过 prefetchAllMcpResources(client.ts:2410)在连接建立后预取资源列表,展示在 UI 里供用户 @ 引用。

10原语 3 — prompt

Prompt 是 server 提供的"prompt 模板",类似 slash command。用户/agent 用参数实例化,得到完整消息。

10.1 例子

// Git MCP server 提供 prompt
{
  name: "review_pr",
  description: "Review a pull request for issues",
  arguments: [
    { name: "pr_number", required: true },
    { name: "focus", required: false }
  ]
}

// client 调用:
{ method: "prompts/get", params: { name: "review_pr", arguments: { pr_number: "123" } } }

// server 返回实例化后的消息:
{
  messages: [
    { role: "user", content: { type: "text", text: "Review PR #123 carefully..." } }
  ]
}

10.2 为什么单独的原语

把"怎么问"也变成 server 的能力
常规模式是 client 写 prompt → 调 server 的工具。但很多 server 希望自己控制"使用说明"。 prompts/get 让 server 能把"查询专家提示词"、"代码 review 清单"等模板化提示随工具一起分发, 不需要用户自己背记。

11原语 4 — sampling(反向调用)

Sampling 是 MCP 最反直觉的原语:server 请求 client 用 LLM。方向反了。

11.1 场景

// 一个"代码审查" MCP server 的内部逻辑:
// 1. 用户调工具 reviewCode(code)
// 2. server 自己需要调 LLM 分析(但 server 没有 API key!)
// 3. server 发 sampling/createMessage 给 client:
{
  method: "sampling/createMessage",
  params: {
    messages: [{ role: "user", content: "Analyze: ..." }],
    maxTokens: 2000,
    systemPrompt: "You are a code reviewer..."
  }
}
// 4. client 用自己的 LLM 跑(消耗用户 API 配额)
// 5. 结果返回给 server
// 6. server 综合后返回给调用工具的那一方

11.2 为什么有这个

  • server 不需要自己持有 LLM 凭证
  • 用户可以控制模型选择、成本、cache
  • 隐私:敏感数据不需要发给 server 再发给 LLM
安全风险
恶意 server 可能用 sampling 偷你的 LLM 配额做任意事。MCP 规范要求 client 在 sampling 请求前显示预览给用户 approve。Claude Code 目前不默认支持 sampling,只在白名单 server 上启用。

12Elicitation 动态参数

工具调用时 server 发现参数不全,可以向 client 反问要缺失的参数。

12.1 流程

// 模型调:createFile({ path: "foo.ts" })  — 少了 content 参数
// server 不是返回 error,而是:
{
  method: "elicitation/create",
  params: {
    message: "What should be the file content?",
    requestedSchema: { type: "object", properties: { content: { type: "string" } } }
  }
}
// client 收到后:
//   - 打开一个对话框让用户填(UI mode)
//   - 或传给模型让它填(auto mode)
// 然后发回 server,server 继续执行工具

12.2 URL Elicitation 特例

// client.ts:2815 — 专门处理"打开 URL 让用户点击"型 elicitation
async function callMCPToolWithUrlElicitationRetry({ ..., handleElicitation }) {
  for (let attempt = 0; ; attempt++) {
    try { return await callToolFn(...) }
    catch (error) {
      if (!(error instanceof McpError) ||
          error.code !== ErrorCode.UrlElicitationRequired) throw error
      if (attempt >= MAX_URL_ELICITATION_RETRIES) throw error
      await handleElicitation(serverName, params, signal)
      // 然后循环重试,希望这次 server 不再需要
    }
  }
}

13通知系统

server 可以主动通知 client 状态变化,无需 client 轮询。

13.1 主要通知类型

通知触发Claude Code 响应
tools/list_changedserver 动态加减工具重新 tools/list,更新 tool pool
resources/list_changed资源列表变重新 resources/list
resources/updated订阅的资源内容变通知订阅方,重新 resources/read
prompts/list_changedprompt 变化刷新 prompts 列表
progress长任务进度onProgress 回调更新 UI
log/messageserver 打日志记录到 client 调试日志

13.2 progress 通知的特殊处理

// server 在执行 reviewCode 时每完成一个文件发一次 progress
{ method: "notifications/progress", params: {
  progressToken: "req-42",       // 关联到哪个 request
  progress: 30, total: 100,
  message: "Reviewed src/foo.ts"
}}
// client 通过 onProgress 回调显示进度条

14错误恢复

MCP 的错误有三个层级:传输层、协议层、应用层。每种都有不同恢复策略。

14.1 传输层错误

错误原因恢复
ECONNRESET连接断reconnectMcpServerImpl 重连
ETIMEDOUT超时指数退避重试
stdio pipe closed子进程死了重新 spawn,重新握手

14.2 协议层错误(JSON-RPC)

// 标准错误码
-32700 Parse error
-32600 Invalid Request
-32601 Method not found
-32602 Invalid params
-32603 Internal error

// MCP 扩展码
ErrorCode.UrlElicitationRequired   // 需要 URL elicitation
ErrorCode.AuthorizationRequired    // 需要认证

14.3 应用层(tools/call 的 isError)

工具返回 isError: true 不是协议错误 —— 而是"工具说执行失败"。client 应该把它作为 tool_result 喂给模型,让模型自己决定怎么办。这跟协议错误截然不同。

14.4 重试次数上限

// URL elicitation 重试 3 次
const MAX_URL_ELICITATION_RETRIES = 3

// 连接重试通过 reconnectAttempt / maxReconnectAttempts
// 失败后转 failed 状态,用户需要手动 /mcp reconnect

15权限隔离与命名空间

一个 client 连多个 MCP server 时,如何防止工具名冲突、权限串扰?

15.1 命名空间

// 服务端原始工具名 "create_issue"
// 在 client 侧一律带 server 前缀:
"mcp__github__create_issue"
"mcp__gitlab__create_issue"

// 这样权限规则可以按 server 粒度控制:
"mcp__github(*)"   // 允许 GitHub 所有工具
"mcp__gitlab(create_issue)"   // 只允许 GitLab 创建 issue

15.2 channelPermissions

channelPermissions.ts 负责"哪些 MCP server 的工具可以在什么 context 下使用"。比如 plugin 提供的 MCP server 只能被该 plugin 的 agent 用,不被其他 agent 看到。

15.3 admin-trusted gate

// runAgent.ts:124 — 企业策略可锁 MCP 为 "plugin-only"
const agentIsAdminTrusted = isSourceAdminTrusted(agentDefinition.source)
if (isRestrictedToPluginOnly('mcp') && !agentIsAdminTrusted) {
  // 用户自定义 agent 不能引入 MCP server
  return { clients: parentClients, tools: [], cleanup: async() => {} }
}

16与 Agent 系统的集成

MCP client 产出的 Tool 如何流入 Claude Code 的主循环?

16.1 启动期发现

// bootstrap 阶段
const configs = await getAllMcpConfigs()      // 合并 7 层配置
const clients = await Promise.all(
  Object.entries(configs).map(([name, config]) => connectToServer(name, config))
)
// clients: Array<MCPServerConnection>

const mcpTools = await getMcpToolsCommandsAndResources(clients)
// mcpTools: Array<Tool>

16.2 注入主线程 tool pool

// assembleToolPool(tools.ts)
function assembleToolPool(permissionContext, mcpTools) {
  return [
    ...BUILTIN_TOOLS,
    ...mcpTools,              // MCP 工具作为一等公民
  ].filter(t => isAllowedByMode(t, permissionContext))
}

16.3 agent 私有 MCP server

见 multi-agent 篇的 initializeAgentMcpServers —— agent frontmatter 可以 inline 定义 server,仅在该 agent 生命周期内存在。

16.4 runtime 添加 server(/mcp add)

用户可在 REPL 里 /mcp add 动态添加一个 server,系统会:

  1. 写配置到 ~/.claude/mcp.json(或 project .claude/mcp.json)
  2. connectToServer 建立连接
  3. 通过 refreshTools 把新工具加入 tool pool
  4. 下一回合模型就能看到新工具

17实战:20 行写一个 MCP server

理解协议最好的方式是写一个。下面是一个完整的 echo server。

17.1 package.json

{
  "name": "echo-mcp-server",
  "version": "1.0.0",
  "type": "module",
  "bin": { "echo-mcp": "./index.js" },
  "dependencies": { "@modelcontextprotocol/sdk": "^1.0.0" }
}

17.2 index.js

// #!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js"
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"

const server = new Server(
  { name: "echo", version: "1.0.0" },
  { capabilities: { tools: {} } }
)

server.setRequestHandler("tools/list", async () => ({
  tools: [{
    name: "echo",
    description: "Echo the input text",
    inputSchema: {
      type: "object",
      properties: { text: { type: "string" } },
      required: ["text"]
    }
  }]
}))

server.setRequestHandler("tools/call", async (req) => ({
  content: [{ type: "text", text: req.params.arguments.text }]
}))

const transport = new StdioServerTransport()
await server.connect(transport)

17.3 在 Claude Code 里用

// ~/.claude/mcp.json
{
  "mcpServers": {
    "echo": {
      "command": "node",
      "args": ["/absolute/path/to/index.js"]
    }
  }
}

重启 Claude Code,模型即可调 mcp__echo__echo({ text: "hello" })

调试技巧
server 的 stdout 是协议通道,stderr 是日志通道。用 console.error() 打日志, Claude Code 会在 /mcp logs 里显示。永远不要用 console.log(),会污染协议流。

18可迁移设计原则

从 MCP 学到的 6 条可迁移到任何 agent 系统的原则。

  1. 标准化接口胜过更强的 SDK

    Anthropic 本可以为 Claude 做一套独占 SDK。选择开放标准意味着"我可能会失去定价权,但会赢得生态"。 事实证明生态效应远大于短期锁定。对 AI agent 开发者:优先选择开放标准而非单一厂商 SDK

  2. JSON-RPC 的简洁胜过 gRPC / 自定义协议

    JSON-RPC 文本、自描述、易调试、易嵌入任何语言。gRPC 性能更好但重量十倍。在 LLM 场景,网络延迟远大于序列化,JSON-RPC 是绝配。

  3. 能力协商胜过固定接口

    initialize 阶段 client/server 各自声明 capabilities,后续只调用双方都支持的方法。这让协议能无缝演进:新增能力不破坏旧 client。

  4. 反向 RPC(sampling)是生态杀手锏

    允许 server 调 client 的 LLM 从根本上改变了权力关系 —— server 不再需要持有模型凭证,用户控制成本。 设计协议时多考虑双向性

  5. Elicitation:对话式工具调用

    传统工具"参数不全就报错",elicitation 让 server 能反问 client。这是把"工具调用"从一次性 RPC升级到多轮对话的关键设计。

  6. 命名空间防冲突胜过全局工具名

    mcp__<server>__<tool> 的三段式名字让权限规则可按 server 粒度控制,也让多个 server 可以有同名工具(github/create_issue vs gitlab/create_issue)。 多租户协议必须命名空间化

MCP 的未来方向
2025 年活跃的演进包括:streamable HTTP 替代 SSE、sampling 的安全模型、多 root 支持、 WebTransport 低延迟传输、plugin-based MCP marketplace。 跟进 MCP 规范就是跟进整个 AI agent 生态。