Chapter 02

MCP 架构深度解析

掌握 Host / Client / Server 三层架构与 JSON-RPC 2.0 通信协议的每一个细节

Host / Client / Server 三层架构

MCP 采用严格的三层架构设计,每一层都有明确的职责边界。理解这个架构是所有 MCP 开发工作的基础。

  ╔═══════════════════════════════════════════════════════════════╗
                      第一层:MCP Host                          
    用户界面 + LLM 推理引擎 + 对话管理 + 权限控制                   
                                                                 
    ┌───────────────┐  ┌───────────────┐  ┌───────────────┐    
    │  MCP Client A │  │  MCP Client B │  │  MCP Client C │    
    │  连接管理      │  │  消息序列化    │  │  能力缓存      │    
    └───────┬───────┘  └───────┬───────┘  └───────┬───────┘    
  ╚═══════╪═══════════════════╪═══════════════════╪═══════════════╝
          │ Transport        │ Transport        │ Transport
  ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
  ┌───────┴───────┐  ┌───────┴───────┐  ┌───────┴───────┐
  │  MCP Server A │  │  MCP Server B │  │  MCP Server C │
  │  Tools        │  │  Tools        │  │  Tools        │
  │  Resources    │  │  Resources    │  │  Resources    │
  │  Prompts      │  │  Prompts      │  │  Prompts      │
  └───────────────┘  └───────────────┘  └───────────────┘
       第三层:MCP Server(每个 Server 通过独立进程运行)

Host 层的职责

Host 是 MCP 架构中的协调者,它同时具备两个身份:一方面是 LLM 的运行环境和用户交互界面,另一方面是所有 MCP Client 的管理者。

Client 层的职责

Client 是 Host 内部的协议适配器,每个 Client 对应一个 Server 连接。

Server 层的职责

Server 是能力提供者,通常是一个轻量级的独立进程,专注于特定领域的功能。

JSON-RPC 2.0 通信协议

MCP 使用 JSON-RPC 2.0 作为消息格式规范。这是一个轻量级的远程过程调用协议,消息格式简单清晰,易于调试。

JSON-RPC 2.0
一种无状态的轻量级远程过程调用协议,使用 JSON 作为数据格式。它定义了请求(Request)、响应(Response)和通知(Notification)三种消息类型,通过传输层发送。

三种消息类型

1. Request(请求)

需要对方响应的消息,包含唯一的 id 字段:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "read_file",
    "arguments": {
      "path": "/home/user/notes.txt"
    }
  }
}

2. Response(响应)

对 Request 的回复,id 必须与对应 Request 匹配:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "今日待办:学习 MCP 协议..."
      }
    ]
  }
}

3. Notification(通知)

不需要响应的单向消息,没有 id 字段:

{
  "jsonrpc": "2.0",
  "method": "notifications/resources/updated",
  "params": {
    "uri": "file:///home/user/notes.txt"
  }
}

错误响应格式

当 Request 处理失败时,响应中包含 error 字段而非 result

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params",
    "data": { "detail": "path is required" }
  }
}

MCP 使用标准 JSON-RPC 错误码(-32700 到 -32603)加上自定义扩展错误码。

传输层:stdio / SSE / Streamable HTTP

MCP 支持多种传输方式,传输层负责将 JSON-RPC 消息在 Client 与 Server 之间物理传递。

stdio 传输

最简单、最常用的传输方式。Host 启动 Server 子进程,通过进程的标准输入输出(stdin/stdout)交换消息。

  Host Process
  ┌──────────────────────────────────────┐
  │  MCP Client                          │
  │    │                                 │
  │    │ spawn()   Server Process         │
  │    │ ─────────►┌──────────────────┐  │
  │    │  stdin    │                  │  │
  │    ├──────────►│   MCP Server     │  │
  │    │  stdout   │                  │  │
  │    │◄──────────│   node server.js │  │
  │    │  stderr   │   (日志输出)      │  │
  │    │           └──────────────────┘  │
  └──────────────────────────────────────┘

  每行一个完整的 JSON 消息(换行符分隔)

适用场景:本地工具(文件系统、本地数据库、代码执行),Claude Desktop 配置的 MCP Server 几乎都使用 stdio。

HTTP + SSE 传输(旧规范)

MCP 1.0 之前的 HTTP 传输方式,使用 Server-Sent Events(SSE) 实现服务端推送。Client 通过 HTTP POST 发送请求,Server 通过 SSE 流推送响应和通知。

注意 HTTP+SSE 传输在 MCP 规范 2025-03-26 版本中已被标记为废弃(deprecated),新实现应使用 Streamable HTTP 传输。

Streamable HTTP 传输(新规范)

MCP 1.0 引入的新传输方式,解决了旧 SSE 方案的多个问题:

POST /mcp HTTP/1.1
Host: api.example.com
Content-Type: application/json
Accept: application/json, text/event-stream

{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{...}}

消息生命周期

一次完整的 MCP 会话包含以下阶段:

  Host / Client                              MCP Server

  ─── 阶段一:初始化 ────────────────────────────────────────────
  initialize ──────────────────────────────►
             ◄──────────────────────────── InitializeResult
  initialized ─────────────────────────────►  (notification)

  ─── 阶段二:能力发现 ───────────────────────────────────────────
  tools/list ──────────────────────────────►
             ◄──────────────────────────── {tools: [...]}
  resources/list ──────────────────────────►
             ◄──────────────────────────── {resources: [...]}
  prompts/list ────────────────────────────►
             ◄──────────────────────────── {prompts: [...]}

  ─── 阶段三:正常使用 ───────────────────────────────────────────
  tools/call ──────────────────────────────►
             ◄──────────────────────────── {content: [...]}
  resources/read ──────────────────────────►
             ◄──────────────────────────── {contents: [...]}
             ◄──────────────────────────── notifications/...  (push)

  ─── 阶段四:关闭 ──────────────────────────────────────────────
  (关闭传输连接)

能力协商(Capability Negotiation)

MCP 连接建立时,Client 和 Server 都会通过 initialize 握手声明自己支持的能力。这使得协议具有良好的向后兼容性和扩展性。

能力协商(Capability Negotiation)
连接建立时,Client 和 Server 互相告知对方自己支持哪些可选功能。双方只能使用两者都声明支持的功能,这样新版本的实现可以与旧版本安全协作。

初始化请求示例

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "sampling": {},           // Client 支持 sampling
      "roots": {
        "listChanged": true     // 支持 roots 变更通知
      }
    },
    "clientInfo": {
      "name": "Claude Desktop",
      "version": "1.5.0"
    }
  }
}

初始化响应示例

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "tools": { "listChanged": true },     // 支持工具列表动态变更
      "resources": {
        "subscribe": true,                 // 支持资源订阅
        "listChanged": true
      },
      "prompts": { "listChanged": true }
    },
    "serverInfo": {
      "name": "my-filesystem-server",
      "version": "1.0.0"
    }
  }
}

可协商的 Server 能力

tools.listChanged
Server 可以在运行时动态增减工具列表,并主动通知 Client 更新
resources.subscribe
Client 可以订阅特定资源的变更通知,Server 在资源更新时主动推送
resources.listChanged
Server 可以动态变更可用资源列表
prompts.listChanged
Server 可以动态变更可用提示词模板列表
logging
Server 支持向 Client 发送日志消息(notifications/message)
设计建议 开发 MCP Server 时,只声明你真正实现了的能力。不要声明 resources.subscribe 但不处理订阅请求——这会导致 Client 行为异常,也会破坏协议的信任机制。