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 基础设施,适合生产环境部署。

需要服务端推送

选择 SSEStreamable 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 是两者之间的过渡方案。在实际项目中,你可能需要同时支持多种传输方式,以适应不同的部署环境和使用场景。