MCP 传输机制详解
MCP 传输机制详解
传输层概述
MCP 的传输层负责在 Client 和 Server 之间传递 JSON-RPC 消息。传输层是 MCP 架构中最底层的一环,它将协议消息编码后通过网络或进程间通信发送出去。
MCP 目前支持两种活跃的传输方式:
| 传输方式 | 状态 | 适用场景 | 连接方式 |
|---|---|---|---|
| Stdio | 活跃 | 本地/单机 | Client 启动 Server 作为子进程,通过 stdin/stdout 通信 |
| Streamable HTTP | 活跃 | 远程/云端 | 通过 HTTP POST 请求通信,可选 SSE 流式响应 |
| HTTP + SSE | 已废弃 | 已被 Streamable HTTP 替代 | 双端点架构(/sse + POST) |
[!tip] 如何选择?
- 本地工具(如 Claude Desktop 连接本地文件系统)→ Stdio
- 远程服务(如云端部署的 MCP Server)→ Streamable HTTP
Stdio 传输
工作原理
Stdio(标准输入输出)传输是最简单的传输方式。Client 将 Server 作为子进程启动,通过进程的 stdin(标准输入)和 stdout(标准输出)交换 JSON-RPC 消息:
消息编码
每条 JSON-RPC 消息在传输时,以换行符分隔的纯文本形式存在:
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}\n
{"jsonrpc":"2.0","id":1,"result":{...}}\n
{"jsonrpc":"2.0","method":"notifications/message","params":{...}}\n
Stdio 传输的配置
Client 端配置如何启动 Server 子进程:
from mcp import StdioServerParameters
from mcp.client.stdio import stdio_client
# 定义 Server 的启动参数
server_params = StdioServerParameters(
command="python", # 启动命令
args=["server.py"], # 命令参数
env=None, # 环境变量(None = 继承当前环境)
)
在 Claude Desktop 的配置文件 claude_desktop_config.json 中配置:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/user/Documents"
]
},
"my-python-server": {
"command": "python",
"args": ["path/to/server.py"]
}
}
}
Stdio 的重要注意事项
[!warning] 永远不要在 Stdio Server 中写入 stdout!
Server 的 stdout 被完全用于 JSON-RPC 消息传输。如果你在代码中写了print()或console.log(),会污染协议消息流,导致连接崩溃。日志输出应使用 stderr 或文件:
import sys
# 正确 — 写入 stderr
print("调试信息", file=sys.stderr)
# 正确 — 使用 logging(默认输出到 stderr)
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
logger.info("这条日志不会干扰协议")
// 正确 — 写入 stderr
process.stderr.write("调试信息\n");
// 正确 — 使用 console.error(输出到 stderr)
console.error("这条日志不会干扰协议");
// 错误 — 不要使用 console.log!
// console.log("这条消息会破坏 MCP 协议");
Stdio 传输的优缺点
| 优点 | 缺点 |
|---|---|
| 配置简单,无需网络设置 | 只能本地通信,不支持远程 |
| 自动管理进程生命周期 | 每个连接需要一个独立进程 |
| 无需额外端口,安全性好 | 难以支持多 Client 同时连接 |
| 延迟极低(进程间通信) | 不适合分布式部署 |
Streamable HTTP 传输
工作原理
Streamable HTTP 是 MCP 于 2025 年 3 月 引入的新传输方式,用于替代已废弃的 HTTP+SSE。它使用单一 HTTP 端点进行通信:
请求-响应模式
Client 向 Server 的 MCP 端点发送 HTTP POST 请求,请求体包含 JSON-RPC 消息:
请求:
POST /mcp HTTP/1.1
Content-Type: application/json
Accept: application/json, text/event-stream
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"read_file","arguments":{"path":"/tmp/test.txt"}}}
响应(普通 JSON 响应):
HTTP/1.1 200 OK
Content-Type: application/json
{"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"Hello, World!"}]}}
SSE 流式响应
对于需要返回多个消息的场景(如长时间运行的工具调用进度),Server 可以使用 Server-Sent Events (SSE) 进行流式响应:
请求:
POST /mcp HTTP/1.1
Content-Type: application/json
Accept: application/json, text/event-stream
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"long_task","arguments":{}}}
流式响应:
HTTP/1.1 200 OK
Content-Type: text/event-stream
event: message
data: {"jsonrpc":"2.0","method":"notifications/progress","params":{"progressToken":"abc","progress":50,"total":100}}
event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"Task completed!"}]}}
Streamable HTTP 的特性
| 特性 | 说明 |
|---|---|
| 单一端点 | 所有通信通过一个 URL(如 /mcp),简化部署 |
| 可选 SSE | 普通请求返回 JSON,需要流式时自动切换 SSE |
| 无状态支持 | Server 可以设计为无状态,便于水平扩展 |
| 多 Client | 一个 Server 可以同时服务多个 Client |
| Session 管理 | 通过 Mcp-Session-Id 头部标识会话 |
Session 机制
Streamable HTTP 使用 Mcp-Session-Id 头部管理会话:
代码示例
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("my-remote-server")
@mcp.tool()
async def search(query: str) -> str:
"""搜索远程知识库"""
return f"搜索结果: {query}"
# 使用 Streamable HTTP 传输
mcp.run(transport="streamable-http", host="0.0.0.0", port=8080)
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import express from "express";
const app = express();
app.use(express.json());
const server = new McpServer({ name: "my-remote-server", version: "1.0.0" });
// 处理 MCP 请求
app.post("/mcp", async (req, res) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
app.listen(8080);
Streamable HTTP 的优缺点
| 优点 | 缺点 |
|---|---|
| 支持远程连接,适合云端部署 | 需要配置网络和端口 |
| 支持多 Client 同时连接 | 需要处理 HTTP 相关的安全问题 |
| 可无状态设计,便于扩展 | 延迟高于 Stdio(网络开销) |
| 兼容现有 Web 基础设施(CDN、LB) | 配置比 Stdio 复杂 |
HTTP+SSE(已废弃)
为什么被废弃
HTTP+SSE 传输方式在 2025-03-26 版本中被标记为废弃,原因如下:
| 问题 | 说明 |
|---|---|
| 双端点架构 | 需要维护 /sse 和 /messages 两个端点,增加复杂度 |
| 状态管理困难 | SSE 连接与 POST 请求需要关联,状态管理复杂 |
| 扩展性差 | 每个客户端需要独立的 SSE 连接,不利于多租户场景 |
| 与现代 Web 不兼容 | 难以与 CDN、负载均衡器等基础设施配合使用 |
迁移到 Streamable HTTP
如果你有使用 HTTP+SSE 的旧代码,迁移到 Streamable HTTP 通常只需:
- 将 Server 端点从
/sse+/messages合并为单个/mcp端点 - 将 Client 端从 GET SSE + POST 改为统一 POST 请求
- 更新 MCP SDK 到最新版本(新版 SDK 已默认使用 Streamable HTTP)
传输方式对比总结
| 对比维度 | Stdio | Streamable HTTP |
|---|---|---|
| 通信方式 | 进程 stdin/stdout | HTTP POST + 可选 SSE |
| 适用场景 | 本地工具 | 远程/云端服务 |
| 连接数 | 1 Client : 1 Server 进程 | 多 Client : 1 Server |
| 配置复杂度 | 低 | 中 |
| 网络要求 | 无 | 需要 HTTP 网络访问 |
| 延迟 | 极低 | 较低 |
| 部署方式 | Client 启动子进程 | Server 独立部署 |
| 安全性 | 本地信任模型 | 需要 HTTPS + 认证 |
总结
| 要点 | 内容 |
|---|---|
| Stdio | 本地通信,Client 启动 Server 子进程,通过 stdin/stdout 交换消息 |
| Streamable HTTP | 远程通信,单一 HTTP 端点,可选 SSE 流式响应,支持多 Client |
| HTTP+SSE | 已废弃,应迁移到 Streamable HTTP |
| 选择原则 | 本地 → Stdio,远程 → Streamable HTTP |
| 核心注意 | Stdio Server 绝不能写 stdout,只能用 stderr 输出日志 |