自从 Model Context Protocol(MCP,模型上下文协议)推出以来,它一直在快速演进,不断改进 AI 智能体与外部服务的通信方式。最近一项重大变化是:MCP 将传输机制从 Server-Sent Events(SSE,服务器发送事件)切换为 Streamable HTTP(可流式 HTTP)。
这一架构调整在 2025 年 3 月 26 日的 MCP 规范更新(版本 2025-03-26)中正式引入,并在 2025 年 4 月 17 日发布的 TypeScript SDK 1.10.0 中首次支持。
原方案:Server-Sent Events(SSE)
最初,远程 MCP 使用 SSE 作为传输层,需要两个独立端点:
- /sse:用于建立持久连接,客户端在此接收服务器推送的消息;
- /sse/messages:客户端通过此端点向服务器发送请求。
这种"双通道"设计就像打电话时用一部手机说话、另一部听声音------虽然能工作,但带来很多问题:
- 连接管理复杂:需同时维护两个连接;
- 扩展性差:SSE 是长连接,占用服务器资源多,难以横向扩展;
- 可靠性低:若连接中断,正在处理的响应可能丢失;
- 实现繁琐:客户端和服务器都要协调两个通道之间的请求/响应匹配。
新方案:Streamable HTTP(可流式 HTTP)
Streamable HTTP 通过单个端点实现双向通信,解决了上述问题,同时保持向后兼容。
它的核心优势:
-
单一端点通信
所有交互(发送 + 接收)都通过 /mcp 完成,简化架构。
-
动态升级连接
简单请求 → 普通 HTTP 请求/响应;
复杂或流式操作 → 自动"升级"为类似 SSE 的流式连接。
-
真正的双向通信
服务器不仅能推数据,还能主动向客户端"要信息",比如:"请再提供一个参数"。
-
错误处理更清晰
所有错误(请求错误、处理中错误)都在同一个通道返回,调试更容易。
向后兼容怎么做?
MCP SDK 支持自动降级:
- 客户端先尝试连接 Streamable HTTP;
- 如果失败,自动回退到旧版 SSE。
服务器也可以同时暴露 /mcp(新)和 /sse + /messages(旧),服务新老客户端。
为什么弃用 SSE?
尽管 SSE 曾经有效,但它存在根本性缺陷:
- 双端点太麻烦 → 代码复杂、易出错;
- 长连接难扩展 → 每个连接都占内存,高并发时服务器压力大;
- 断连无法恢复 → 一旦网络抖动,任务可能白跑;
- 与 HTTP/2、HTTP/3 兼容性差 → 无法利用现代协议的性能优势;
- 只能单向通信 → 客户端发请求、服务器推结果,不能反向互动。
未来展望
Streamable HTTP 还将支持:
可恢复性(Resumability):断线后能从断点继续;
可取消性(Cancellability):客户端可主动终止任务;
会话管理(Session Management):跨请求保持状态,支持复杂对话。
开发者建议
新项目:直接用 Streamable HTTP;
老项目:同时支持 SSE 和 Streamable HTTP,逐步迁移;
客户端:优先尝试新协议,保留 SSE 降级能力。
结论
从 SSE 切换到 Streamable HTTP 是 MCP 的一次重要升级。它简化了架构、增强了功能、提升了可靠性,更符合现代 Web 和 AI 智能体的需求。虽然 SSE 还会存在一段时间以保证兼容,但 Streamable HTTP 是未来的标准。
实际场景对比
下面通过实际应用场景中稳定性,性能和客户端复杂度三个角度对比说明 Streamable HTTP 相比 HTTP + SSE 的优势,AI 网关 Higress 目前已经支持了 Streamable HTTP 协议,通过 MCP 官方 Python SDK 的样例 Server 部署了一个 HTTP + SSE 协议的 MCP Server,通过 Higress 部署了一个 Streamable HTTP 协议的 MCP Server。
稳定性对比
TCP 连接数对比
利用 Python 程序模拟 1000 个用户同时并发访问远程的 MCP Server 并调用获取工具列表,图中可以看出 SSE Server 的 SSE 连接无法复用且需要长期维护,高并发的需求也会带来 TCP 连接数的突增,而 Streamable HTTP 协议则可以直接返回响应,多个请求可以复用同一个 TCP 连接,TCP 连接数最高只到几十条,并且整体执行时间也只有 SSE Server 的四分之一。

在 1000 个并发用户的测试场景下,Higress 部署的 Streamable HTTP 方案的 TCP 连接数明显低于 HTTP + SSE 方案:
- HTTP + SSE:需要维持大量长连接,TCP 连接数随时间持续增长
- Streamable HTTP:按需建立连接,TCP 连接数维持在较低水平
请求成功率对比
实际应用场景中进程级别通常会限制最大连接数,linux 默认通常是 1024。利用 Python 程序模拟不同数量的用户访问远程的 MCP Server 并调用获取工具列表,SSE Server 在并发请求数到达最大连接数限制后,成功率会极速下降,大量的并发请求无法建立新的 SSE 连接而访问失败。

在不同并发用户数下的请求成功率测试中,Higress 部署的 Streamable HTTP 的成功率显著高于 HTTP + SSE 方案:
- HTTP + SSE:随着并发用户数增加,成功率显著下降
- Streamable HTTP:即使在高并发场景下仍能保持较高的请求成功率
性能对比
这里对比的是社区 Python 版本的 GitHub MCP Server【2】 和 Higress MCP 市场的 GitHub MCP Server
利用 Python 程序模拟不同数量的用户同时并发访问远程的 MCP Server 并调用获取工具列表,并统计调用返回响应的时间,图中给出的响应时间对比为对数刻度,SSE Server 在并发用户数量较多时平均响应时间会从 0.0018s 显著增加到 1.5112s,而 Higress 部署的 Streamable HTTP Server 则依然维持在 0.0075s 的响应时间,也得益于 Higress 生产级的性能相比于 Python Starlette 框架。

性能测试结果显示,Higress 部署的 Streamable HTTP 在响应时间方面具有明显优势:
- Streamable HTTP 的平均响应时间更短,响应时间波动较小,随并发用户数增加,响应时间增长更平
- HTTP + SSE 的平均响应时间更长,在高并发场景下响应时间波动较大
客户端复杂度对比
Streamable HTTP 支持无状态的服务和有状态的服务,目前的大部分场景无状态的 Streamable HTTP 的可以解决,通过对比两种传输方案的客户端实现代码,可以直观地看到无状态的 Streamable HTTP 的客户端实现简洁性。
HTTP + SSE 客户端样例代码
python
class SSEClient:
def __init__(self, url: str, headers: dict = None):
self.url = url
self.headers = headers or {}
self.event_source = None
self.endpoint = None
async def connect(self):
# 1. 建立 SSE 连接
async with aiohttp.ClientSession(headers=self.headers) as session:
self.event_source = await session.get(self.url)
# 2. 处理连接事件
print('SSE connection established')
# 3. 处理消息事件
async for line in self.event_source.content:
if line:
message = json.loads(line)
await self.handle_message(message)
# 4. 处理错误和重连
if self.event_source.status != 200:
print(f'SSE error: {self.event_source.status}')
await self.reconnect()
async def send(self, message: dict):
# 需要额外的 POST 请求发送消息
async with aiohttp.ClientSession(headers=self.headers) as session:
async with session.post(self.endpoint, json=message) as response:
return await response.json()
async def handle_message(self, message: dict):
# 处理接收到的消息
print(f'Received message: {message}')
async def reconnect(self):
# 实现重连逻辑
print('Attempting to reconnect...')
await self.connect()
Streamable HTTP 客户端样例代码
python
class StreamableHTTPClient:
def __init__(self, url: str, headers: dict = None):
self.url = url
self.headers = headers or {}
async def send(self, message: dict):
# 1. 发送 POST 请求
async with aiohttp.ClientSession(headers=self.headers) as session:
async with session.post( self.url, json=message,
headers={'Content-Type': 'application/json'}
) as response:
# 2. 处理响应
if response.status == 200:
return await response.json()
else:
raise Exception(f'HTTP error: {response.status}')
从代码对比可以看出:
- 复杂度:Streamable HTTP 无需处理连接维护、重连等复杂逻辑
- 可维护性:Streamable HTTP 代码结构更清晰,更易于维护和调试
- 错误处理:Streamable HTTP 的错误处理更直接,无需考虑连接状态