- 人工智能
- AI Agent
- 工具调用
【免费下载链接】specification
Specification and documentation for the Model Context Protocol
导读:SEP-1699(Status: Final,Standards Track,作者 Jonathan Hefner,创建于 2025-10-22)针对 MCP 的 Streamable HTTP 传输提出了一组连接语义变更,核心是允许服务端在已向客户端发送 SSE 事件 ID 之后随时主动断开连接,由客户端依照 SSE 标准携带Last-Event-ID轮询重连,从而缓解长连接占用与流可恢复性(resumability)问题。读完本文,你将掌握 SEP-1699 定义的 MUST/MAY/SHOULD 规则、SSE 的id/data/retry字段语义、该设计在 2025-11-25 修订版中的具体落地条款,以及 2026-07-28 修订版对轮询与恢复机制的后续调整。
背景与动机:长连接之痛
SEP-1699 的出发点非常直接:在当时的 Streamable HTTP 传输规范中,服务端不允许在计算一个结果的过程中关闭连接。也就是说,除非客户端主动断开,服务端必须一直维持可能持续很久的连接(原文称 "barring client-side disconnection, servers must maintain potentially long-running connections")。
这种"只能由客户端断开"的不对称设计带来三类问题:
- 资源占用:服务端必须为每个未完成的请求挂起一条 HTTP 连接,请求量大时连接数会失控,容易触发代理、负载均衡器或操作系统的空闲/超时策略;
- 恢复困难:一旦网络抖动导致连接中断,客户端无法区分"服务端还在算"与"连接已死",更无法从断点续传尚未送达的 JSON-RPC 响应;
- 实现负担:服务端被迫对每条请求做长连接保活(keep-alive),增加了中间层(反向代理等)的配置复杂度。
SEP-1699 的解决方案是:把"断开"从服务端的禁区变成一种受控的轮询信号——服务端先给出一个事件 ID,之后想断就断,客户端把断开当作网络故障处理并携带该 ID 重连,从而把长连接转化为可恢复的短连接轮询。
核心规范变更:先发事件 ID,再随时断开
SEP-1699 对 Streamable HTTP 传输规范做了三处关键改动,全部围绕 SSE(Server-Sent Events)标准语义展开。
改动一:启动 SSE 流时必须立即发送带id的空事件(MUST)
When a server starts an SSE stream, itMUSTimmediately send an SSE event consisting of an
idand an emptydatastring in order to prime the client to reconnect with that event ID as theLast-Event-ID.
即:服务端一旦开始 SSE 流,必须立刻发送一个形如id: <事件ID>+ 空data的事件。这个事件的唯一作用是预置(prime)重连游标——让客户端记住该事件 ID,以便将来断开后用它作为Last-Event-ID重连。
改动二:发出事件 ID 后,服务端可随时断开(SHOULD NOT → MAY)
原规范条款:
The serverSHOULD NOTclose the SSE stream before sending the JSON-RPCresponsefor the received JSON-RPCrequest
被修改为:
The serverMAYclose the connection before sending the JSON-RPCresponseif it has sent an SSE event with an event ID to the client
注意这里是从SHOULD NOT(强烈禁止)放宽为MAY(允许),但附带前置条件:必须先发送过带事件 ID 的 SSE 事件。换句话说,事件 ID 是服务端"断开的许可证"——先给客户端一个恢复点,然后才允许断开。
改动三:用retry字段约束重连节奏(SHOULD + MUST)
In order to prevent clients from reconnecting / polling excessively, the serverSHOULDsend an SSE event with a
retryfield indicating how long the client should wait before reconnecting. ClientsMUSTrespect theretryfield.
服务端断开前应当发送带retry字段的 SSE 事件,告知客户端等待多少毫秒再重连;客户端必须遵守该值,防止对服务端造成"狂轰滥炸"式的过度轮询。
关于空data的标准语义
SEP-1699 特别强调:SSE 标准明确允许data为空字符串,且此时客户端正确的处理方式是一边记录id用于Last-Event-ID,一边忽略该事件本身(即不调用事件处理器回调)。这是因为在 WHATWG HTML 标准的 SSE 事件流解析算法中,空data缓冲区的事件在派发(dispatch)阶段会被直接丢弃,但id字段在解析阶段就已写入"最后事件 ID 缓冲区",两者互不干扰。
SSE 标准语义详解:id、data、retry与Last-Event-ID
SEP-1699 的设计完全建立在 SSE(Server-Sent Events,WHATWG HTML 标准)之上,理解下列字段语义是读懂本 SEP 的前提:
| SSE 字段/机制 | 语义 | 在 SEP-1699 中的作用 |
|---|---|---|
id: <value> | 将"最后事件 ID 缓冲区"设为该值 | 客户端重连时作为Last-Event-ID头发送,构成恢复游标 |
data:(空) | 数据缓冲区为空字符串 | 事件被记录 ID 后忽略、不派发回调,用于"预置"游标而不会干扰业务事件 |
retry: <ms> | 设置客户端重连等待时间(仅接受 ASCII 数字,否则忽略) | 防止客户端断开后立即、高频重连 |
Last-Event-ID请求头 | 客户端重连时回传它最后收到的事件 ID | 服务端据此判断客户端已消费到哪个事件,可做断点续传 |
在 2025-11-25 修订版规范中,这些机制被进一步明确:事件 ID 在同一会话(session)内的所有流中必须全局唯一,且事件 ID应当编码足够的流身份信息,使服务端能把Last-Event-ID关联回正确的流;无论原始流是通过 POST 还是 GET 建立的,恢复(resumption)一律通过 HTTP GET 携带Last-Event-ID进行。相关条款见 2025-11-25 Streamable HTTP 传输规范。
协议落地:2025-11-25 修订版中的实现细节
SEP-1699 被接受后进入协议,在 2025-11-25 修订版的 Streamable HTTP 传输规范中体现为一系列可执行条款(见 transports.mdx):
- 服务端开启 SSE 流后:应当立即发送一个"事件 ID + 空
data"事件,预置客户端重连(第 106-108 行); - 连接 vs 流:服务端在已发送事件 ID 后,可以随时关闭"连接"而不终止"SSE 流"(第 109-111 行)——这是 SEP-1699 引入的关键概念区分,"流"是逻辑实体,"连接"是承载它的物理通道;
- 轮询行为:连接被服务端关闭后,客户端应当通过尝试重连来"轮询"该 SSE 流(第 112 行);
retry约束:服务端在关闭连接前应当发送带retry字段的事件,客户端必须尊重该字段、等待指定毫秒数后再重连(第 113-116 行);- 断开不等于取消:任何时刻的断开(含网络原因)都不应被解读为客户端取消请求;客户端要取消必须显式发送
CancelledNotification(第 126-129 行); - 可恢复性:为避免断开导致消息丢失,服务端可以使流可恢复(resumable),通过
Last-Event-ID重放断点之后的消息(第 130-131 行及 Resumability and Redelivery 一节)。
规范的变更日志也明确记录了这两笔账:2025-11-25 变更日志 第 6 条写明"支持 SSE 流轮询:允许服务端随时断开(SEP-1699)",第 7 条进一步澄清 SEP-1699 的细节——GET 流同样支持轮询、恢复一律走 GET、事件 ID 应编码流身份、断开包括服务端主动关闭(对应 Issue #1847)。这印证了 SEP-1699 落地时并非简单放宽限制,而是连同恢复语义一起设计。
另外值得注意,SEP-1699 原文在 Additional Information 中注明:它部分取代了 SEP-1335(该 SEP 提出了相关的早期方案)。这也是理解其历史脉络的一条线索——轮询式 SSE 设计并非一蹴而就。
兼容性分析:新旧组合矩阵
SEP-1699 自带完整的向后兼容性分析,组合矩阵如下:
| 组合 | 影响 | 结论 |
|---|---|---|
| 新客户端 + 旧服务端 | 无变化 | 无向后不兼容 |
| 旧客户端 + 新服务端 | 客户端应把服务端随时断开视为网络故障;retry字段本就属于 SSE 标准 | 只要客户端已实现正确的 SSE 恢复逻辑,即无向后不兼容 |
核心论断是:retry字段与"断开即网络故障"的解读都来自 SSE 标准本身,因此一个符合 SSE 标准的旧客户端天然能与启用新行为的新服务端协作——这保证了协议演进的安全性。
演进:2026-07-28 修订版的变化
需要特别说明的是,SEP-1699 的轮询/恢复设计主要作用于协议版本 2025-03-26 至 2025-11-25 的 Streamable HTTP。在 draft(对应 2026-07-28)修订版 中,传输层发生了结构性调整:
- 移除 GET 流端点:客户端不再通过 HTTP GET 打开独立 SSE 流;
- 移除协议级 session:不再有
MCP-Session-Id与 HTTP DELETE 终止会话的机制; Last-Event-ID恢复不再支持:规范明确写着 "Resumable SSE streams viaLast-Event-IDare not supported",服务端应忽略Last-Event-ID请求头;- 断开的语义反转:新规范规定,关闭 SSE 响应流必须被服务端视为该请求的取消(cancellation)——这与 2025-11-25 版本"断开不等于取消、需显式发送 CancelledNotification"的语义正好相反。
也就是说,在最新修订中,"服务端边算边断、客户端轮询续传"的模式被"每条请求独立 POST、响应流即生命周期"的模型取代:长生命周期变更通知改由subscriptions/listen的专用流承载,服务端对客户端交互(sampling、elicitation、roots)则通过 MRTR(Multi Round-Trip Requests,SEP-2322)内嵌到结果中。因此,阅读 SEP-1699 时务必结合协议版本语境:它的价值在 2025-11-25 及之前的实现中体现得最完整,而新修订通过另一种方式解决了长连接问题。
面向实现者的实践建议
结合 SEP-1699 原文与 2025-11-25 规范的条款,给实现者梳理可落地的行为清单。
服务端(Server)要点
- 开启 SSE 流后,立即发送首个事件:
id: stream-1-event-0 data:其中
id在同一会话的所有流中保持全局唯一,并编码流身份信息(便于服务端把后续的Last-Event-ID关联回正确的流)。 - 计算耗时可能很长时,可以主动断开连接释放通道,但必须在断开前发送带
retry字段的事件,例如retry: 5000(毫秒),指导客户端等待 5 秒再重连。 - 保留流的逻辑状态,在客户端携带
Last-Event-ID重连时,从断点之后的消息开始重放,且不得重放本应投递到其他流上的消息。 - 断开 ≠ 取消:在 2025-11-25 语义下,服务端不应把客户端重连当作取消;只有显式的
CancelledNotification才是取消信号。
客户端(Client)要点
- 收到空
data事件时:记录其id为最后事件 ID,但不触发任何事件处理器回调(SEP-1699 特别强调的标准行为)。 - 服务端断开后,按网络故障处理并尝试重连;重连请求携带
Last-Event-ID头(2025-11-25 版本中恢复一律走 HTTP GET)。 - 必须尊重
retry字段,按指定毫秒数等待后再重连,避免对服务端形成重连风暴。
一次完整的轮询时序示例
--- 第一次 POST,服务端返回 SSE 流 --- HTTP/1.1 200 OK Content-Type: text/event-stream id: stream-1-event-0 data: retry: 5000 <服务端断开连接,计算仍在后台进行> --- 客户端等待 5 秒后轮询重连 --- GET /mcp HTTP/1.1 Accept: text/event-stream Last-Event-ID: stream-1-event-0参考依据
- SEP-1699 原始提案:本文章的直接依据(Abstract / Motivation / Specification / Rationale / Backward Compatibility / Additional Information);
- SEP-1699 站点渲染版:Mintlify 渲染后的同文档案;
- 2025-11-25 Streamable HTTP 传输规范:SEP-1699 的落地条款(轮询、
retry、Resumability and Redelivery、会话管理); - 2025-11-25 修订版变更日志:SEP-1699 及澄清 Issue #1847 的官方记录;
- draft(2026-07-28)Streamable HTTP 传输规范:展示后续修订对 GET 流、
Last-Event-ID恢复与取消语义的调整。
- 人工智能
- AI Agent
- 工具调用
【免费下载链接】specification
Specification and documentation for the Model Context Protocol
相关推荐
TypeScript SDK 实战:基于 SEP-1699 的 SSE 服务端主动断开与 Last-Event-ID 重连恢复机制
TypeScript SDK 实战:基于 SEP 1699 的 SSE 服务端主动断开与 Last Event ID 重连恢复机制 导读 在 typescrip
人工智能MCP 服务MCP Clients用 python-sdk 实现 SSE Polling:服务器主动关闭流的长任务轮询模式实战(SEP-1699)
用 python sdk 实现 SSE Polling:服务器主动关闭流的长任务轮询模式实战(SEP 1699) 本文以仓库中的官方示例 examples/se
人工智能MCP 服务MCP ClientsMCP传输协议详解:Stdio、SSE与Streamable HTTP
MCP传输协议详解:Stdio、SSE与Streamable HTTP 本文深入解析Model Context Protocol MCP 的三种核心传输机制:标
人工智能MCP 服务MCP Clients
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考