MCP

MCP 传输机制深度解析:stdio、SSE 与 Streamable HTTP

深入分析 Model Context Protocol 的三种传输机制的工作原理、性能特点和适用场景,指导开发者选择最佳通信方案。

MCP 的传输层决定了 Client 和 Server 之间如何交换消息。选择合适的传输机制直接影响系统的延迟、可扩展性和部署复杂度。这篇文章将深入解析 MCP 支持的三种传输机制。

MCP 传输层概述

MCP 使用 JSON-RPC 2.0 作为消息格式,传输层负责将这些消息在 Client 和 Server 之间传递。协议设计了清晰的分层,使得传输层可以灵活替换。

┌─────────────────┐
│   MCP 协议层    │
│  (JSON-RPC 2.0) │
├─────────────────┤
│   传输层        │
│  (stdio/SSE/HTTP)│
├─────────────────┤
│   网络层        │
│  (TCP/Unix)     │
└─────────────────┘

stdio 传输

stdio 是 MCP 最基础的传输方式,通过标准输入输出进行进程间通信。

工作原理

Client 启动 Server 作为子进程,通过 stdin 发送请求,从 stdout 读取响应。stderr 用于日志输出,不参与协议通信。

Client                    Server (子进程)
  │                           │
  │──── stdin (JSON-RPC) ────>│
  │                           │
  │<─── stdout (JSON-RPC) ────│
  │                           │
  │<─── stderr (日志) ────────│

代码示例

Server 端:

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';

const server = new McpServer({ name: 'my-server', version: '1.0.0' });

// 添加工具...

const transport = new StdioServerTransport();
await server.connect(transport);
// Server 开始监听 stdin,响应写入 stdout

Client 端:

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';

const transport = new StdioClientTransport({
  command: 'node',
  args: ['server.js'],
  env: { NODE_ENV: 'production' },
});

const client = new Client({ name: 'my-client', version: '1.0.0' });
await client.connect(transport);

优势

零网络配置——不需要开放端口、配置防火墙或设置反向代理。

操作系统隔离——进程级别的隔离提供了天然的安全边界。

低延迟——进程间通信的延迟通常在微秒级别,远低于网络通信。

局限

本地限制——只能用于同一台机器上的进程间通信。

单客户端——一个 Server 进程通常只能服务一个 Client。

生命周期绑定——Server 进程随 Client 的启动而启动,随 Client 的退出而终止。

SSE 传输

Server-Sent Events(SSE)是一种基于 HTTP 的单向流式通信机制。MCP 使用 SSE 传输实现远程 Server 的访问。

工作原理

Client 通过 HTTP GET 建立 SSE 连接接收 Server 推送的消息,通过 HTTP POST 发送请求到 Server。

Client                         Server
  │                               │
  │──── GET /sse ────────────────>│  (建立 SSE 连接)
  │<─── SSE: endpoint URL ───────│
  │                               │
  │──── POST /messages ──────────>│  (发送 JSON-RPC 请求)
  │<─── SSE: JSON-RPC response ──│  (通过 SSE 流返回)
  │                               │
  │──── POST /messages ──────────>│
  │<─── SSE: JSON-RPC response ──│

代码示例

Server 端:

import express from 'express';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';

const app = express();
const server = createMcpServer();

app.get('/sse', (req, res) => {
  const transport = new SSEServerTransport('/messages', res);
  server.connect(transport);
});

app.post('/messages', express.json(), (req, res) => {
  // SSE transport 自动处理消息路由
});

app.listen(3000);

Client 端:

import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';

const transport = new SSEClientTransport(
  new URL('http://localhost:3000/sse')
);

const client = new Client({ name: 'remote-client', version: '1.0.0' });
await client.connect(transport);

优势

远程访问——Client 和 Server 可以位于不同的机器上。

Web 兼容——基于标准 HTTP 协议,可以穿过大多数防火墙和代理。

流式响应——SSE 支持流式推送,适合长时间运行的操作。

局限

单向推送——SSE 只支持 Server 到 Client 的推送,Client 到 Server 需要额外的 HTTP POST。

连接管理——需要处理 SSE 连接的断开和重连。

并发限制——浏览器对同一域名的 SSE 连接数有限制(通常 6 个)。

Streamable HTTP 传输

Streamable HTTP 是 MCP 最新引入的传输方式,基于 HTTP 的完整双向通信。

工作原理

所有消息都通过 HTTP POST 传递,响应可以是普通的 JSON 响应或 SSE 流。Client 可以选择接收流式或非流式响应。

Client                         Server
  │                               │
  │──── POST /mcp ───────────────>│  (请求)
  │<─── 200 OK (JSON) ───────────│  (普通响应)
  │                               │
  │──── POST /mcp ───────────────>│  (流式请求)
  │<─── 200 OK (SSE stream) ─────│  (流式响应)

代码示例

Server 端:

import express from 'express';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';

const app = express();
app.use(express.json());

app.all('/mcp', async (req, res) => {
  const transport = new StreamableHTTPServerTransport({
    sessionIdGenerator: () => crypto.randomUUID(),
  });
  
  await server.connect(transport);
  await transport.handleRequest(req, res, req.body);
});

app.listen(3000);

Client 端:

import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';

const transport = new StreamableHTTPClientTransport(
  new URL('http://localhost:3000/mcp')
);

const client = new Client({ name: 'http-client', version: '1.0.0' });
await client.connect(transport);

优势

完全兼容 HTTP——可以利用现有的 HTTP 基础设施(负载均衡、CDN、认证)。

灵活的响应模式——可以根据需要选择流式或非流式响应。

无连接限制——不受 SSE 的连接数限制。

会话管理——内置会话标识,支持多客户端并发。

如何选择

本地开发和简单集成

选择 stdio。零配置、低延迟,适合本地工具集成和开发调试。

远程服务和 Web 部署

选择 Streamable HTTP。兼容现有 HTTP 基础设施,适合生产环境部署。

需要服务端推送

选择 SSE 或 Streamable HTTP。两者都支持服务端推送,但 Streamable HTTP 更加灵活。

性能敏感场景

选择 stdio,其次是 Streamable HTTP。stdio 的进程间通信延迟最低。

常见问题(FAQ)

可以同时支持多种传输方式吗?

可以。你可以在同一个 Server 中同时暴露 stdio 和 HTTP 端点,让不同的 Client 选择不同的连接方式。

SSE 传输的安全性如何保障?

使用 HTTPS 加密传输,配合标准的 HTTP 认证机制(Bearer Token、OAuth 等)。

传输层的选择会影响 MCP 协议的使用吗?

不会。传输层是透明的——无论使用哪种传输方式,MCP 协议层的 API(listTools、callTool 等)保持完全一致。

总结

stdio 适合本地集成,Streamable HTTP 适合远程部署,SSE 是两者之间的过渡方案。在实际项目中,你可能需要同时支持多种传输方式,以适应不同的部署环境和使用场景。