8.1 传输抽象:读写流与业务解耦 本节摘要:第 8 章开篇,先讲传输层的总纲——传输与业务完全解耦。核心洞见是: 是一个异步上下文管理器,进入时产出一对 读写流,业务逻辑通过它收发消息,对「跑在哪」毫无感知。这一节用一张图说清「同一套服务端代码 + 不同传输 = 不同部署形态」,这是整个 SDK 工程设计的精华,也是后续四节(stdio/HTTP/SSE/内存)的总纲。 一、传输层要解决的问题 先明确传输层干什么。MCP 消息(JSON-RPC)从客户端到服务端,总要有东西把它搬过去。这个「搬运」就是传输层: 「搬运」的方式有很多——可以是子进程的标准输入输出、可以是 HTTP 请求、可以是服务器发送事件。这些方式的底层机制天差地别,但它们要干的事是一样的:把消息从一端搬到另一端。
本节摘要:第 8 章开篇,先讲传输层的总纲——传输与业务完全解耦。核心洞见是:
Transport是一个异步上下文管理器,进入时产出一对(read, write)读写流,业务逻辑通过它收发消息,对「跑在哪」毫无感知。这一节用一张图说清「同一套服务端代码 + 不同传输 = 不同部署形态」,这是整个 SDK 工程设计的精华,也是后续四节(stdio/HTTP/SSE/内存)的总纲。
先明确传输层干什么。MCP 消息(JSON-RPC)从客户端到服务端,总要有东西把它搬过去。这个「搬运」就是传输层:
客户端 服务端 │ │ │ 消息怎么过去? │ │ ┌──────────────────┐ │ │ │ 传输层(搬运) │ │ │ └──────────────────┘ │ │ │
「搬运」的方式有很多——可以是子进程的标准输入输出、可以是 HTTP 请求、可以是服务器发送事件。这些方式的底层机制天差地别,但它们要干的事是一样的:把消息从一端搬到另一端。
SDK 把「搬运」抽象成一个统一接口——Transport,它是一个异步上下文管理器,进入时产出一对读写流:
# 概念性伪代码,展示 Transport 的本质 class Transport(Protocol): async def __aenter__(self) -> tuple[ReadStream, WriteStream]: ...
read 与 write 都是异步流:
read:从对端收消息(异步迭代或异步读)write:向对端发消息(异步写)业务逻辑(服务端或客户端核心)只通过这对读写流收发消息,完全不知道传输底层是子进程、HTTP 还是别的。
这是本章最关键的图,展示「同一套业务 + 不同传输 = 不同部署形态」:
你的服务端代码 (MCPServer + 工具/资源/提示词) │ │ 通过读写流收发消息 ▼ ┌─────────────────┐ │ Transport 抽象 │ └─────────────────┘ ┌─────────┼─────────┐ ▼ ▼ ▼ ┌────────┐ ┌──────┐ ┌──────┐ │ stdio │ │ HTTP │ │ SSE │ ... 不同传输 └────────┘ └──────┘ └──────┘ │ │ │ ▼ ▼ ▼ 子进程 HTTP 服务 旧版 HTTP (本地) (生产) (遗留)
关键点:业务代码(最上层)只写一份。你不用为每种传输写不同版本——同一套 MCPServer + 工具/资源/提示词,可以分别用 stdio、HTTP、SSE 跑。这就是「业务与传输解耦」的字面含义。
这个解耦不是炫技,它带来三个实际好处:
| 好处 | 说明 |
|---|---|
| 代码复用 | 业务写一次,多种部署形态通用 |
| 测试简化 | 用内存传输测,不用真起子进程/HTTP 服务 |
| 演进自由 | 传输升级(如 SSE→HTTP),业务不动 |
第三个尤其重要——历史上 MCP 的 HTTP 传输从 SSE 演进到流式 HTTP(2025-03-26)。因为业务与传输解耦,这个演进对业务代码几乎透明,只需换传输配置。
无论哪种传输,读写流的消息格式是一样的——都是 JSON-RPC 帧(或内存传输的直接对象)。这意味着业务逻辑处理消息的代码,对不同传输完全一致:
# 业务逻辑(概念性,服务端核心) async def run_server(read, write): async for message in read: # 收消息(任何传输都这样) result = handle(message) # 业务处理 await write.send(result) # 发消息(任何传输都这样)
不同传输的差别,只在「read/write 怎么实现」:
业务逻辑不用关心这些差异——它只看到统一的读写流。
传输不只服务端有——客户端也有传输。两端通过各自的传输「对接」:
客户端核心 服务端核心 │ │ │ 用客户端传输发 │ 用服务端传输收 ▼ ▼ ┌──────────┐ 消息流 ┌──────────┐ │ 客户端 │ ◄─────► │ 服务端 │ │ Transport│ │ Transport│ └──────────┘ └──────────┘ │ │ │ 用客户端传输收 │ 用服务端传输发 ▼ ▼
两端的传输必须匹配——客户端用 stdio 传输,服务端也得用 stdio;客户端用 HTTP,服务端也得用 HTTP。第 9、10 章讲客户端时,会讲客户端的传输接入;本章先聚焦服务端传输。
💡 技巧:理解了「传输是读写流抽象」,你就理解了为什么第 1.3 节的内存连接(
Client(mcp))能工作——它就是用「直接分发」代替了读写流,跳过了 JSON-RPC 序列化。这是同一个解耦思想的极致体现:既然业务只关心读写流,那「读写流」的实现可以是真 IO,也可以是直接函数调用。
本章接下来四节,分别讲四种传输:
| 节 | 传输 | 重点 |
|---|---|---|
| 8.2 | stdio | 本地默认,子进程模型 |
| 8.3 | 流式 HTTP | 生产级,2025-03-26 主力 |
| 8.4 | SSE | 旧版遗留,已弃用 |
| 8.5 | 内存 + 传输安全 | 测试传输 + DNS 重绑定防护 |
每节讲清「这种传输怎么工作、怎么启动、适用场景、注意事项」。读完本章,你能为不同部署场景选对传输,并理解它们为什么能互换。
(read, write) 读写流。总纲清楚了,下一节讲最常见的 stdio 传输——本地默认,子进程模型。