<!-- title: MCP三种传输模式横评与实战选型 -->

# MCP三种传输模式横评与实战选型：从stdio到Streamable HTTP

## 概述

本教程面向已经写过至少一个 MCP（Model Context Protocol）服务器、却卡在"该用哪种传输方式"这一步的开发者。学完之后你将能够：清晰区分 `stdio`、`HTTP+SSE`（旧版）与 `Streamable HTTP`（2025-03-26 规范引入）三种传输模式的工作原理与适用场景；用同一份 Python/TypeScript 服务器代码在三种模式之间无缝切换；针对本地工具、团队共享、公网生产三类需求做出正确的传输选型。适用人群：MCP 工具开发者、想把内部系统暴露给 Claude Code/Cursor 的平台工程师。

## 前置条件

- 已阅读过前序"造首个 MCP 工具"教程，能用 `FastMCP`（Python）或 `@modelcontextprotocol/sdk`（TypeScript）跑通一个最简单的 echo 工具。
- Node.js 18+ 与 Python 3.10+ 可用，`npx` 可执行。
- 已安装 Claude Code，会编辑 `.mcp.json` 并重启 MCP 服务器。
- 了解 HTTP 基本概念（请求方法、Header、长连接与短连接）。

---

## 一、为什么传输层是 MCP 工程化的第一道分水岭

很多人写完第一个 MCP 工具后就停下来，以为协议不过如此。但当你想把工具从"只给自己用"推进到"团队共享""部署上云""对接 Cursor 和 Claude Code 双客户端"时，第一个撞墙的问题不是工具逻辑，而是**传输模式**。

MCP 协议本身定义的是客户端与服务器之间的 JSON-RPC 2.0 消息集合（`initialize`、`tools/list`、`tools/call` 等），它**刻意把传输层抽象掉**了。也就是说，同一套业务逻辑（你的工具、资源、提示词）可以跑在完全不同的"管道"上，而协议握手代码一行都不用改。这正是传输层的价值，也是它的陷阱——选错管道，轻则延迟翻倍，重则在生产环境频繁掉线、无法水平扩展。

协议当前规范明确支持两种官方传输模式，外加一种正在被淘汰的旧模式：

1. **stdio**——本地子进程，标准输入输出管道。
2. **Streamable HTTP**——2025-03-26 规范引入，单一 HTTP 端点，是当前**远程部署的推荐方式**。
3. **HTTP+SSE**（旧版）——2024 年初版的远程方案，依赖长连接双端点，已被 Streamable HTTP 取代，规范标注为 deprecated。

下面逐一拆解。

## 二、stdio：最简单也最被低估的本地管道

### 工作原理

stdio 模式下，MCP 客户端（如 Claude Code）把你的服务器当**一个子进程**拉起，通过进程的 `stdin` 写入请求、从 `stdout` 读取响应。消息采用**换行分隔的 JSON-RPC**（newline-delimited JSON）：每条消息独占一行，以 `\n` 结尾。

```text
客户端进程 ──stdin──▶ MCP服务器子进程
          ◀──stdout─
```

一个致命的初学者坑：**stdout 被协议独占**。你在代码里写的任何 `print()`、日志输出都会污染 JSON-RPC 流，导致客户端解析失败、工具"莫名消失"。所有调试日志必须走 `stderr`。

### 代码示例（Python FastMCP）

```python
# server.py —— stdio 模式（默认）
from mcp.server.fastmcp import FastMCP
import sys

mcp = FastMCP("weather")

@mcp.tool()
def get_temp(city: str) -> str:
    """查询城市温度（模拟）"""
    sys.stderr.write(f"[debug] query {city}\n")  # 日志走 stderr
    return f"{city}: 23°C"

if __name__ == "__main__":
    mcp.run()  # 不传 transport，默认就是 stdio
```

`.mcp.json` 配置：

```json
{
  "mcpServers": {
    "weather": {
      "command": "python",
      "args": ["server.py"]
    }
  }
}
```

### 预期结果

Claude Code 启动时会 `fork` 这个进程，握手成功后 `tools/list` 返回 `get_temp`。调用时 Claude 自动把参数序列化为 JSON-RPC 写进 stdin，服务器在 stdout 返回结果，整个过程**零网络开销**，单次调用框架开销通常在亚毫秒级。

### 常见问题

- **工具列表为空**：八成是 `print()` 污染了 stdout。把所有输出改成 `sys.stderr` 或日志库。
- **进程闪退**：Windows 下用 `python.exe` 全名，避免 PATH 问题；注意 `command` 不要写成相对路径的 shell 脚本而不带解释器。
- **首次启动慢**：stdio 模式是**每客户端一个独立进程**，冷启动（尤其 Python 加载重依赖）会被放大。可用 `uv run` 或预热导入来缓解。

## 三、HTTP+SSE（旧版）：理解它是为了知道为什么被淘汰

2024 年 MCP 初版里，远程服务器用的是 **HTTP + Server-Sent Events** 双端点方案：

- 客户端先 `GET /sse` 建立一条**长期保持**的 SSE 连接，服务器通过它推送所有消息。
- 客户端再通过 `POST /messages` 发送请求。

这套方案的问题很直接：

1. **两条连接**，状态绑定在 SSE 长连接上，断了就要重新握手、重建会话。
2. **长连接**对基础设施不友好：serverless 平台会掐断、负载均衡器需要 sticky session、CDN/反代常常 buffer 事件流导致"卡住"。
3. **水平扩展困难**，因为会话状态黏在单条连接上。

正因如此，规范在 2025-03-26 版本里用 **Streamable HTTP** 替换了它。旧代码里的 `transport="sse"` 在新 SDK 中仍然可用以保持兼容，但**新项目不要再用**。本文提及它，主要是为了你在阅读老博客、老仓库时能识别并果断迁移。

## 四、Streamable HTTP：当前远程部署的标准答案

### 核心设计

Streamable HTTP 把双端点压缩成**单一端点**（通常 `/mcp`），用普通 HTTP 语义承载 JSON-RPC：

- **`POST /mcp`**：客户端→服务器的所有请求都从这里走。响应可以是普通 JSON，也可以升级为 SSE 流（当服务器需要中途推送日志、进度时）。
- **`GET /mcp`**：可选，打开一条 SSE 流接收服务器主动推送的通知（例如长任务的进度）。
- **`Mcp-Session-Id` 响应头**：初始化时服务器返回，客户端后续请求带上它，即可维持**有状态会话**；不带则进入无状态模式。
- **`Accept: application/json, text/event-stream`**：客户端声明自己两种响应格式都能处理。

它的本质优势是**回归了无状态、短连接的 HTTP 模型**：每个请求自包含、可独立重试、可被任意负载均衡器分发、可在 serverless 上跑。需要流式时再"升级"为 SSE，不需要时就是普通 REST。这种弹性让它成为云原生部署的首选。

### 代码示例（Python FastMCP，从 stdio 切换只需一行）

```python
# server_http.py —— Streamable HTTP 模式
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("weather")

@mcp.tool()
def get_temp(city: str) -> str:
    """查询城市温度（模拟）"""
    return f"{city}: 23°C"

if __name__ == "__main__":
    # 与 stdio 版本唯一的区别：指定 transport 与监听参数
    mcp.run(
        transport="streamable-http",
        host="0.0.0.0",
        port=8000,
        # FastMCP 默认挂在 /mcp 路径
    )
```

运行：

```bash
pip install "mcp[cli]"
python server_http.py
# 控制台输出：Uvicorn running on http://0.0.0.0:8000
```

`.mcp.json` 配置（Claude Code 连接远程）：

```json
{
  "mcpServers": {
    "weather-remote": {
      "url": "http://localhost:8000/mcp"
    }
  }
}
```

注意：连 stdio 用的是 `command`/`args`；连 Streamable HTTP 用的是 `url`。这是配置层面最直观的差异。

### TypeScript SDK 版本

```typescript
// server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import express from "express";

const app = express();
const server = new McpServer({ name: "weather", version: "1.0.0" });

// 注册工具（省略 schema 细节）
server.tool("get_temp", { city: z.string() }, async ({ city }) => ({
  content: [{ type: "text", text: `${city}: 23°C` }],
}));

// 每个会话一个 transport 实例（有状态）
const sessions = new Map<string, StreamableHTTPServerTransport>();

app.post("/mcp", async (req, res) => {
  const sessionId = req.headers["mcp-session-id"] as string | undefined;
  let transport: StreamableHTTPServerTransport;
  if (sessionId && sessions.has(sessionId)) {
    transport = sessions.get(sessionId)!;
  } else {
    transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => crypto.randomUUID() });
    transport.onsessioninitialized = (id) => sessions.set(id, transport);
    await server.connect(transport);
  }
  await transport.handleRequest(req, res);
});

app.listen(8000);
```

TypeScript 版本更能看清"会话与 transport 实例绑定"的细节：`sessionIdGenerator` 决定是否启用有状态模式，返回 `undefined` 即进入无状态模式。

### 常见问题

- **跨客户端共享失效**：检查是否给每个会话新建了独立 transport 却忘了复用，导致状态不连续。
- **反代后流式卡住**：Nginx 默认会 buffer 响应，需要在 location 加 `proxy_buffering off;` 并设置合理的 `proxy_read_timeout`，否则进度流永远到不了客户端。
- **公网部署裸奔**：Streamable HTTP 本身不带认证。上公网必须前置 OAuth2.1 网关或 API Key 中间件（这部分已在"远程 MCP 上 OAuth2.1"教程中展开）。

## 五、三种模式横评一览

| 维度 | stdio | HTTP+SSE（旧） | Streamable HTTP（推荐） |
|---|---|---|---|
| 连接模型 | 本地进程管道 | 双端点+长连接 | 单端点+短连接，可升级流 |
| 状态 | 进程内状态 | 黏连在 SSE 连接 | 可有状态(`Mcp-Session-Id`)可无状态 |
| 部署位置 | 仅本地 | 需公网可达端点 | 需公网可达端点 |
| 延迟 | 最低（亚 ms 框架开销） | 中（长连接，但有双端点握手） | 略高（每次 HTTP RTT，本地约 1-5ms） |
| 水平扩展 | 不适用（单机） | 差（sticky session） | 好（无状态请求可任意分发） |
| serverless 友好 | 否 | 否 | 是 |
| 多客户端共享 | 否（各自拉进程） | 是 | 是 |
| 认证需求 | 无（本地信任） | 需自建 | 需 OAuth2.1/网关 |
| 规范状态 | 官方 | deprecated | 官方推荐 |

需要强调的延迟观感：stdio 因为没有 TCP/TLS 握手，工具调用的**稳态延迟**确实最低；但 Streamable HTTP 在启用 HTTP keep-alive 后，单次工具调用的网络开销可压到很低，而它换来的可共享、可扩展、可上云收益，对绝大多数非本地场景是值得的。

## 六、实战案例：把一个本地天气工具升级为团队共享服务

目标：同一份 `get_temp` 业务代码，既能在自己机器上以 stdio 秒起，又能部署到团队服务器供所有人通过 Claude Code 调用。

**第 1 步**：抽出业务逻辑为纯函数，传输层只做薄壳。

```python
# logic.py
def get_temp(city: str) -> str:
    return f"{city}: 23°C"
```

**第 2 步**：用环境变量切换传输，避免维护两份入口。

```python
# server.py
import os
from mcp.server.fastmcp import FastMCP
from logic import get_temp

mcp = FastMCP("weather")
mcp.tool()(get_temp)

if __name__ == "__main__":
    mode = os.getenv("MCP_TRANSPORT", "stdio")
    if mode == "stdio":
        mcp.run()
    elif mode == "streamable-http":
        mcp.run(transport="streamable-http",
                host=os.getenv("HOST", "0.0.0.0"),
                port=int(os.getenv("PORT", "8000")))
```

**第 3 步**：本地自用——`python server.py`，配 `command`。

**第 4 步**：团队部署——容器化后设 `MCP_TRANSPORT=streamable-http`，配 `url`。

```dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install "mcp[cli]"
ENV MCP_TRANSPORT=streamable-http
EXPOSE 8000
CMD ["python", "server.py"]
```

**第 5 步**：用 MCP Inspector 验证远程端点（不依赖 Claude Code，便于 CI 调试）。

```bash
npx @modelcontextprotocol/inspector
# 在打开的网页里选 "Streamable HTTP"，填 http://localhost:8000/mcp，点 Connect
# 若 tools/list 能列出 get_temp，说明远程服务就绪
```

这一步是工程化的关键：Inspector 让你脱离具体客户端，直接在协议层验证传输是否通，是排查"Claude Code 里看不到工具"问题的利器。

## 七、架构与决策流程

下图展示三种传输的连接拓扑差异：

```mermaid
flowchart LR
    subgraph Local["本地场景：stdio"]
        A1[Claude Code] -->|fork stdin/stdout| S1[服务器子进程]
    end
    subgraph Legacy["旧远程：HTTP+SSE（已废弃）"]
        A2[客户端] -.GET /sse 长连接.-> S2[服务器]
        A2 -->|POST /messages| S2
    end
    subgraph Modern["现代远程：Streamable HTTP"]
        A3[Claude Code/Cursor] -->|POST /mcp\nMcp-Session-Id| S3[服务器集群]
        S3 -.可选 GET /mcp\nSSE 进度流.-> A3
        LB[负载均衡] --> S3
    end
    style Modern fill:#e8f5e9
    style Legacy fill:#ffebee
```

选型决策树：

```mermaid
flowchart TD
    Q1{只给自己用\n且在本机?}
    Q1 -->|是| STDIO[选 stdio]
    Q1 -->|否| Q2{需要多人/多客户端共享?}
    Q2 -->|否| STDIO
    Q2 -->|是| Q3{部署在 serverless\n或需水平扩展?}
    Q3 -->|是| STREAM[选 Streamable HTTP\n+ OAuth2.1]
    Q3 -->|否| Q4{现有老代码是 SSE?}
    Q4 -->|是| MIG[迁移到 Streamable HTTP]
    Q4 -->|否| STREAM
    style STREAM fill:#e8f5e9
    style STDIO fill:#e3f2fd
    style MIG fill:#fff3e0
```

一句话总结：**本地用 stdio，远程用 Streamable HTTP，任何新代码都别再碰旧版 SSE。**

## 八、延伸阅读

- **MCP 官方规范——Transports**：定义 JSON-RPC 编码、stdio 与 Streamable HTTP 的协议级要求（`Mcp-Session-Id`、`Accept` 头、流式升级规则）。
- **MCP Inspector**：官方调试器，支持本地 stdio 与远程 HTTP 两种传输的可视化握手测试，是协议层排障标配。
- **FastMCP 文档**：Python 一行切传输的最佳实践来源，涵盖 SSE 旧模式与 Streamable HTTP 的兼容矩阵。
- **OAuth2.1 for Remote MCP**：当你决定走 Streamable HTTP 上公网时，配套阅读认证方案，避免服务裸奔。

## 九、训练营适用性评估

- **MCP 工具开发训练营（进阶）**：高度适配，建议作为"首个 MCP 工具"之后的第二课，承接本地→远程的认知跃迁。建议教学时长 60-75 分钟（讲授 30 分钟 + 三种模式动手切换 30 分钟 + Inspector 调试 15 分钟）。
- **AI 智能体开发训练营**：中度适配，可作为"工具调用基础设施"模块的补充，重点讲 Streamable HTTP 的生产部署，建议 45 分钟。
- **企业级 AI 应用训练营**：高度适配，传输选型直接关系到企业内部 MCP 网关架构、多租户与水平扩展，建议配合 OAuth2.1 课程一起上，教学时长 60 分钟。
- **零基础入门营**：不适配，需要先掌握至少一个 stdio 工具的开发经验。

---

## 参考来源

1. [Transports — Model Context Protocol 官方规范](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports) — 协议级定义 JSON-RPC 编码、stdio 与 Streamable HTTP 传输要求，Mcp-Session-Id 会话语义
2. [Understanding MCP Server Transports: STDIO, SSE, and HTTP Streamable](https://dev.to/zoricic/understanding-mcp-server-transports-stdio-sse-and-http-streamable-5b1p) — 三种传输模式原理拆解与代码示例
3. [MCP Transports Compared: stdio vs SSE vs Streamable HTTP (2026)](https://rollbrains.com/mcp/mcp-transports-compared/) — 针对各客户端（Claude/Cursor/VS Code）的传输选型对比
4. [MCP Streamable HTTP Production Guide 2026: stdio vs Streamable HTTP](https://baeseokjae.github.io/posts/mcp-production-guide-streamable-http-2026/) — 生产部署视角的延迟、扩展性与容器化实践
5. [MCP Transport: Stdio vs Streamable HTTP — Architecture, Latency](https://www.truefoundry.com/blog/mcp-stdio-vs-streamable-http-enterprise) — 企业级部署的架构权衡与延迟分析

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
