05. MCP

什么是 Model Context Protocol?

Claude Code 本身具有强大的内置工具——读写文件、执行命令、搜索代码。但现实世界的开发工作远不止于此:你需要查看 GitHub Issue、发送 Slack 消息、查询数据库、操作浏览器、访问 Jira 看板…

Model Context Protocol(MCP) 就是为解决这个问题而设计的开放协议。它定义了一种标准化的方式,让 AI 应用能够连接到外部服务和数据源。你可以把 MCP 想象成 AI 世界的 USB 接口——任何符合协议的设备都可以即插即用。

MCP 架构

┌──────────────────────────────────────────────────┐ │ Claude Code (Host) │ │ │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │ │ MCP Client │ │ MCP Client │ │ MCP Client │ │ │ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ │ └────────┼───────────────┼───────────────┼──────────┘ │ │ │ ┌─────▼──────┐ ┌─────▼──────┐ ┌─────▼──────┐ │ MCP Server │ │ MCP Server │ │ MCP Server │ │ (GitHub) │ │ (Slack) │ │ (Database) │ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ │ │ │ ┌─────▼──────┐ ┌─────▼──────┐ ┌─────▼──────┐ │ GitHub │ │ Slack │ │ PostgreSQL │ │ API │ │ API │ │ Server │ └────────────┘ └────────────┘ └────────────┘

MCP 采用客户端-服务器架构:

  • Host(宿主):Claude Code 本身,负责管理多个 MCP 客户端
  • Client(客户端):由 Host 创建,与单个 MCP Server 保持一对一连接
  • Server(服务器):独立进程,连接到外部服务,向 Client 暴露能力

MCP Server 暴露的三种能力

每个 MCP Server 可以向 AI 提供三种类型的能力:

1. Tools(工具)

可供 AI 调用的函数。例如 GitHub MCP Server 可能提供 create_issuelist_pull_requestsmerge_pr 等工具。Claude 可以像使用内置工具一样调用它们。

2. Resources(资源)

可供 AI 读取的数据。类似于 REST API 的 GET 端点。例如,一个数据库 MCP Server 可能暴露表结构作为资源,让 Claude 直接读取 schema 信息。

3. Prompts(提示模板)

预定义的交互模板。Server 可以提供特定场景下的提示模板,帮助用户更有效地与 AI 交互。

如何配置 MCP Server

在 Claude Code 中配置 MCP Server 非常简单。在项目的 .claude/settings.json 中添加:

{
"mcpServers": {
  "github": {
    "command": "npx",
    "args": ["-y", "@modelcontextprotocol/server-github"],
    "env": {
      "GITHUB_TOKEN": "ghp_xxxxxxxxxxxx"
    }
  },
  "postgres": {
    "command": "npx",
    "args": [
      "-y",
      "@modelcontextprotocol/server-postgres",
      "postgresql://localhost:5432/mydb"
    ]
  },
  "playwright": {
    "command": "npx",
    "args": ["-y", "@playwright/mcp@latest"]
  }
}
}

每个 Server 配置包含:

  • command:启动 Server 的命令
  • args:命令行参数
  • env(可选):环境变量,用于传递 API 密钥等

传输协议

MCP 支持五种传输方式,覆盖从本地进程到远程服务的各种场景:

1. stdio(标准输入/输出)

通过 StdioClientTransport 实现,用于本地进程。Host 直接启动 Server 进程,通过 stdin/stdout 通信。这是最常见的方式——Server 作为子进程运行,无需网络配置。

Host                    Server (子进程)
│                         │
│── JSON-RPC via stdin ──▶│
│◀── JSON-RPC via stdout ─│
│                         │
│  (stderr 用于日志输出)    │

2. SSE(Server-Sent Events)

通过 SSEClientTransport 实现,用于远程服务器。通过 HTTP 连接到远程 MCP Server,适合团队共享的服务或云端部署的工具。这是较早的远程传输方式。

{
"remote-server": {
  "url": "https://mcp.example.com/sse",
  "headers": {
    "Authorization": "Bearer token_xxx"
  }
}
}

3. HTTP Streamable

通过 StreamableHTTPClientTransport 实现,是现代 HTTP 流式传输方式。相比 SSE,它支持双向流式通信,是远程 MCP Server 的推荐传输方式。

4. WebSocket

通过 WebSocketTransport 实现,使用 WebSocket 连接进行全双工通信。适合需要低延迟双向通信的场景。

5. In-Process(进程内)

通过 InProcessTransport / SdkControlTransport 实现,用于SDK 内部调用。不经过网络或子进程,而是通过直接的函数回调通信。这是 Claude Code SDK 内部使用的传输方式。

JSON-RPC:底层协议

MCP 的通信基于 JSON-RPC 2.0 协议。每一次工具调用都是一个标准的 JSON-RPC 请求/响应对:

// Client → Server: 调用工具
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
  "name": "create_issue",
  "arguments": {
    "repo": "user/project",
    "title": "Fix login bug",
    "body": "The login form crashes on submit..."
  }
}
}

// Server → Client: 返回结果
{
"jsonrpc": "2.0",
"id": 1,
"result": {
  "content": [
    {
      "type": "text",
      "text": "Created issue #42: Fix login bug"
    }
  ]
}
}

常用 MCP Server

MCP 生态系统已经有大量可用的 Server:

  • GitHub:管理 Issue、PR、代码审查
  • Slack:发送消息、查看频道、管理工作流
  • PostgreSQL / MySQL:查询数据库、管理表结构
  • Playwright:操控浏览器、截屏、自动化测试
  • Filesystem:沙盒化的文件系统访问
  • Sentry:查看错误报告和性能数据
  • Linear / Jira:项目管理和任务追踪

MCP vs 内置工具

你可能会问:为什么不把所有工具都做成内置的?

  • 内置工具:由 Claude Code 团队开发和维护,数量有限但深度集成。包括 Read、Edit、Bash、Grep 等核心开发工具。
  • MCP 工具:任何人都可以编写 MCP Server。生态系统是开放的、可扩展的。你可以为自己公司的内部系统编写 MCP Server,让 Claude Code 直接操作。

MCP 的核心价值在于可扩展性。它把 Claude Code 从一个固定功能的工具变成了一个可以连接任何服务的平台。每一个新的 MCP Server 都在扩展 Claude 能力的边界。

安全考虑

MCP Server 在独立进程中运行,Claude Code 的权限系统同样适用于 MCP 工具调用。敏感操作(如写入数据库、发送消息)会在执行前请求用户确认。API 密钥通过环境变量传递,不会出现在对话上下文中。

源码探秘

不再是伪代码——这是 Claude Code 中 MCP 工具调用的真实源码(精简版)。点击高亮行或右侧批注,阅读每段代码的作用:

📄src/services/mcp/client.tstypescript
6 个注解
1async function callMCPTool({
2 client: { client, name, config },
3 tool,
4 args,
5 meta,
6 signal,
7 onProgress,
8}: {
9 client: ConnectedMCPServer
10 tool: string
11 args: Record<string, unknown>
12 meta?: Record<string, unknown>
13 signal: AbortSignal
14 onProgress?: (data: MCPProgress) => void
15}): Promise<{
16 content: MCPToolResult
17 _meta?: Record<string, unknown>
18 structuredContent?: Record<string, unknown>
19}> {
20 const toolStartTime = Date.now()
21 let progressInterval: NodeJS.Timeout | undefined
22
23 try {
24 logMCPDebug(name, `Calling MCP tool: ${tool}`)
25
26 // Set up progress logging for long-running tools
27 progressInterval = setInterval(() => {
28 const elapsed = Date.now() - toolStartTime
29 logMCPDebug(name,
30 `Tool '${tool}' still running (${elapsed}ms)`)
31 }, 30000)
32
33 // Use Promise.race with timeout to handle cases
34 // where SDK's internal timeout doesn't work
35 const timeoutMs = getMcpToolTimeoutMs()
36 const timeoutPromise = new Promise<never>((_, reject) => {
37 setTimeout(() => reject(
38 new Error(`MCP tool "${tool}" timed out`)
39 ), timeoutMs)
40 })
41
42 const result = await Promise.race([
43 client.callTool(
44 {
45 name: tool,
46 arguments: args,
47 _meta: meta,
48 },
49 CallToolResultSchema,
50 {
51 signal,
52 timeout: timeoutMs,
53 onprogress: onProgress
54 ? sdkProgress => {
55 onProgress({
56 type: 'mcp_progress',
57 serverName: name,
58 toolName: tool,
59 progress: sdkProgress.progress,
60 total: sdkProgress.total,
61 })
62 }
63 : undefined,
64 },
65 ),
66 timeoutPromise,
67 ])
68
69 if ('isError' in result && result.isError) {
70 throw new MCPToolError(name, tool, result)
71 }
72
73 return {
74 content: result.content as MCPToolResult,
75 _meta: result._meta,
76 structuredContent: result.structuredContent,
77 }
78 } finally {
79 if (progressInterval) clearInterval(progressInterval)
80 }
81}
注解

MCP 客户端管理器位于 src/services/mcp/client.ts(119KB),是整个 MCP 子系统的核心。它负责服务器生命周期管理、工具调用、资源读取、认证流程和错误恢复。每当你在配置中添加一个 MCP Server,就是这个模块在背后启动进程、建立连接、维持心跳。

工具命名约定:MCP 工具在 Claude Code 内部以 mcp__<server>__<tool> 的格式命名。例如,GitHub Server 的 create_issue 工具在 Claude 的工具列表中显示为 mcp__github__create_issue。这种前缀机制避免了不同 Server 之间的工具名冲突。

What is the Model Context Protocol?

Claude Code comes with powerful built-in tools — reading and writing files, executing commands, searching code. But real-world development goes far beyond that: you need to check GitHub Issues, send Slack messages, query databases, operate browsers, access Jira boards…

Model Context Protocol (MCP) is an open protocol designed to solve this problem. It defines a standardized way for AI applications to connect to external services and data sources. Think of MCP as the USB port of the AI world — any device that follows the protocol can plug and play.

MCP Architecture

┌──────────────────────────────────────────────────┐ │ Claude Code (Host) │ │ │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │ │ MCP Client │ │ MCP Client │ │ MCP Client │ │ │ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ │ └────────┼───────────────┼───────────────┼──────────┘ │ │ │ ┌─────▼──────┐ ┌─────▼──────┐ ┌─────▼──────┐ │ MCP Server │ │ MCP Server │ │ MCP Server │ │ (GitHub) │ │ (Slack) │ │ (Database) │ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ │ │ │ ┌─────▼──────┐ ┌─────▼──────┐ ┌─────▼──────┐ │ GitHub │ │ Slack │ │ PostgreSQL │ │ API │ │ API │ │ Server │ └────────────┘ └────────────┘ └────────────┘

MCP uses a client-server architecture:

  • Host: Claude Code itself, responsible for managing multiple MCP clients
  • Client: Created by the Host, maintains a one-to-one connection with a single MCP Server
  • Server: Independent process that connects to an external service and exposes capabilities to the Client

Three Capabilities Exposed by MCP Servers

Each MCP Server can provide three types of capabilities to the AI:

1. Tools

Functions the AI can call. For example, a GitHub MCP Server might provide create_issue, list_pull_requests, and merge_pr tools. Claude can call them just like built-in tools.

2. Resources

Data the AI can read. Similar to REST API GET endpoints. For example, a database MCP Server might expose table schemas as resources, letting Claude directly read schema information.

3. Prompts

Predefined interaction templates. Servers can provide prompt templates for specific scenarios, helping users interact with the AI more effectively.

How to Configure MCP Servers

Configuring MCP Servers in Claude Code is straightforward. Add the following to your project’s .claude/settings.json:

{
"mcpServers": {
  "github": {
    "command": "npx",
    "args": ["-y", "@modelcontextprotocol/server-github"],
    "env": {
      "GITHUB_TOKEN": "ghp_xxxxxxxxxxxx"
    }
  },
  "postgres": {
    "command": "npx",
    "args": [
      "-y",
      "@modelcontextprotocol/server-postgres",
      "postgresql://localhost:5432/mydb"
    ]
  },
  "playwright": {
    "command": "npx",
    "args": ["-y", "@playwright/mcp@latest"]
  }
}
}

Each Server configuration contains:

  • command: The command to start the Server
  • args: Command-line arguments
  • env (optional): Environment variables for passing API keys, etc.

Transport Protocols

MCP supports five transport methods, covering scenarios from local processes to remote services:

1. stdio (Standard I/O)

Implemented via StdioClientTransport, used for local processes. The Host directly spawns the Server process and communicates via stdin/stdout. This is the most common approach — the Server runs as a child process with no network configuration needed.

Host                    Server (child process)
│                         │
│── JSON-RPC via stdin ──▶│
│◀── JSON-RPC via stdout ─│
│                         │
│  (stderr used for logs)  │

2. SSE (Server-Sent Events)

Implemented via SSEClientTransport, used for remote servers. Connects to a remote MCP Server via HTTP, suitable for team-shared services or cloud-deployed tools. This is the legacy remote transport method.

{
"remote-server": {
  "url": "https://mcp.example.com/sse",
  "headers": {
    "Authorization": "Bearer token_xxx"
  }
}
}

3. HTTP Streamable

Implemented via StreamableHTTPClientTransport, this is the modern HTTP streaming transport. Compared to SSE, it supports bidirectional streaming and is the recommended transport for remote MCP Servers.

4. WebSocket

Implemented via WebSocketTransport, uses WebSocket connections for full-duplex communication. Suitable for scenarios requiring low-latency bidirectional communication.

5. In-Process

Implemented via InProcessTransport / SdkControlTransport, used for SDK internal calls. Instead of going through the network or child processes, it communicates via direct function callbacks. This is the transport method used internally by the Claude Code SDK.

JSON-RPC: The Underlying Protocol

MCP communication is based on the JSON-RPC 2.0 protocol. Every tool call is a standard JSON-RPC request/response pair:

// Client → Server: Call tool
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
  "name": "create_issue",
  "arguments": {
    "repo": "user/project",
    "title": "Fix login bug",
    "body": "The login form crashes on submit..."
  }
}
}

// Server → Client: Return result
{
"jsonrpc": "2.0",
"id": 1,
"result": {
  "content": [
    {
      "type": "text",
      "text": "Created issue #42: Fix login bug"
    }
  ]
}
}

Popular MCP Servers

The MCP ecosystem already has a large number of available Servers:

  • GitHub: Manage Issues, PRs, code reviews
  • Slack: Send messages, browse channels, manage workflows
  • PostgreSQL / MySQL: Query databases, manage table schemas
  • Playwright: Control browsers, take screenshots, automate testing
  • Filesystem: Sandboxed file system access
  • Sentry: View error reports and performance data
  • Linear / Jira: Project management and task tracking

MCP vs Built-in Tools

You might ask: why not make everything built-in?

  • Built-in tools: Developed and maintained by the Claude Code team. Limited in number but deeply integrated. Includes core dev tools like Read, Edit, Bash, and Grep.
  • MCP tools: Anyone can write an MCP Server. The ecosystem is open and extensible. You can write an MCP Server for your company’s internal systems and let Claude Code operate them directly.

The core value of MCP is extensibility. It transforms Claude Code from a fixed-function tool into a platform that can connect to any service. Every new MCP Server expands the boundaries of Claude’s capabilities.

Security Considerations

MCP Servers run in independent processes, and Claude Code’s permission system applies equally to MCP tool calls. Sensitive operations (like writing to a database or sending messages) prompt for user confirmation before execution. API keys are passed via environment variables and never appear in the conversation context.

Source Code Deep Dive

No more pseudocode — this is the actual source code of Claude Code’s MCP tool call mechanism (simplified). Click highlighted lines or sidebar annotations to read what each section does:

📄src/services/mcp/client.tstypescript
6 个注解
1async function callMCPTool({
2 client: { client, name, config },
3 tool,
4 args,
5 meta,
6 signal,
7 onProgress,
8}: {
9 client: ConnectedMCPServer
10 tool: string
11 args: Record<string, unknown>
12 meta?: Record<string, unknown>
13 signal: AbortSignal
14 onProgress?: (data: MCPProgress) => void
15}): Promise<{
16 content: MCPToolResult
17 _meta?: Record<string, unknown>
18 structuredContent?: Record<string, unknown>
19}> {
20 const toolStartTime = Date.now()
21 let progressInterval: NodeJS.Timeout | undefined
22
23 try {
24 logMCPDebug(name, `Calling MCP tool: ${tool}`)
25
26 // Set up progress logging for long-running tools
27 progressInterval = setInterval(() => {
28 const elapsed = Date.now() - toolStartTime
29 logMCPDebug(name,
30 `Tool '${tool}' still running (${elapsed}ms)`)
31 }, 30000)
32
33 // Use Promise.race with timeout to handle cases
34 // where SDK's internal timeout doesn't work
35 const timeoutMs = getMcpToolTimeoutMs()
36 const timeoutPromise = new Promise<never>((_, reject) => {
37 setTimeout(() => reject(
38 new Error(`MCP tool "${tool}" timed out`)
39 ), timeoutMs)
40 })
41
42 const result = await Promise.race([
43 client.callTool(
44 {
45 name: tool,
46 arguments: args,
47 _meta: meta,
48 },
49 CallToolResultSchema,
50 {
51 signal,
52 timeout: timeoutMs,
53 onprogress: onProgress
54 ? sdkProgress => {
55 onProgress({
56 type: 'mcp_progress',
57 serverName: name,
58 toolName: tool,
59 progress: sdkProgress.progress,
60 total: sdkProgress.total,
61 })
62 }
63 : undefined,
64 },
65 ),
66 timeoutPromise,
67 ])
68
69 if ('isError' in result && result.isError) {
70 throw new MCPToolError(name, tool, result)
71 }
72
73 return {
74 content: result.content as MCPToolResult,
75 _meta: result._meta,
76 structuredContent: result.structuredContent,
77 }
78 } finally {
79 if (progressInterval) clearInterval(progressInterval)
80 }
81}
注解

The MCP client manager lives at src/services/mcp/client.ts (119KB) and is the heart of the entire MCP subsystem. It handles server lifecycle management, tool calls, resource reading, authentication flows, and error recovery.

Tool naming convention: MCP tools are internally named using the format mcp__<server>__<tool>. For example, the GitHub Server’s create_issue tool appears in Claude’s tool list as mcp__github__create_issue. This prefix mechanism prevents tool name conflicts between different Servers.

上一章 / PreviousCh.16 Message Pipeline下一章 / NextCh.6 Memory