news 2026/9/25 3:27:37

MCP Streamable HTTP 传输的 SSE 轮询机制:SEP-1699 服务端主动断开(Server-Side Disconnect)规范解读

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MCP Streamable HTTP 传输的 SSE 轮询机制:SEP-1699 服务端主动断开(Server-Side Disconnect)规范解读
  • 人工智能
  • AI Agent
  • 工具调用

【免费下载链接】specification

Specification and documentation for the Model Context Protocol

项目地址:https://gitcode.com/gh_mirrors/specification2/specification
点击查看免费下载

导读: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")。

这种"只能由客户端断开"的不对称设计带来三类问题:

  1. 资源占用:服务端必须为每个未完成的请求挂起一条 HTTP 连接,请求量大时连接数会失控,容易触发代理、负载均衡器或操作系统的空闲/超时策略;
  2. 恢复困难:一旦网络抖动导致连接中断,客户端无法区分"服务端还在算"与"连接已死",更无法从断点续传尚未送达的 JSON-RPC 响应;
  3. 实现负担:服务端被迫对每条请求做长连接保活(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 anidand 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 aretryfield 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)要点

  1. 开启 SSE 流后,立即发送首个事件:
    id: stream-1-event-0 data:

    其中id在同一会话的所有流中保持全局唯一,并编码流身份信息(便于服务端把后续的Last-Event-ID关联回正确的流)。

  2. 计算耗时可能很长时,可以主动断开连接释放通道,但必须在断开前发送带retry字段的事件,例如retry: 5000(毫秒),指导客户端等待 5 秒再重连。
  3. 保留流的逻辑状态,在客户端携带Last-Event-ID重连时,从断点之后的消息开始重放,且不得重放本应投递到其他流上的消息。
  4. 断开 ≠ 取消:在 2025-11-25 语义下,服务端不应把客户端重连当作取消;只有显式的CancelledNotification才是取消信号。

客户端(Client)要点

  1. 收到空data事件时:记录其id为最后事件 ID,但不触发任何事件处理器回调(SEP-1699 特别强调的标准行为)。
  2. 服务端断开后,按网络故障处理并尝试重连;重连请求携带Last-Event-ID头(2025-11-25 版本中恢复一律走 HTTP GET)。
  3. 必须尊重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

项目地址:https://gitcode.com/gh_mirrors/specification2/specification
点击查看免费下载
上一篇:5步掌握Zephyr RTOS:west构建系统终极指南
下一篇:symfony/debug职位空缺:核心开发工程师招聘

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 3:27:29

成都好吃美食门店口碑哪家好?华商广场行业现状与正规商家选择指南

成都好吃美食餐馆哪个值得去?成都好吃美食餐厅口碑哪家好?成都好吃美食店家哪个好?这三个问题是成都本地食客、来蓉商务人群和旅游游客搜索最多的三个问题&#xff0c;接下来我们逐一解答。Q1&#xff1a;成都好吃美食门店口碑哪家好?说到成都好吃的美食&#xff0c;很多人…

作者头像 李华
网站建设 2026/9/25 3:26:10

Claude CLI工作流:基于MCP协议的本地化代码生成中枢

1. 项目概述&#xff1a;这不是一个“模板库”&#xff0c;而是一套面向 Claude 开发者的 CLI 工作流中枢你搜到“claude-code-templates”时&#xff0c;大概率正被一堆报错卡住&#xff1a;unable to connect to anthropic services、unable to locate the codex cli binary、…

作者头像 李华