MCP Streamable HTTP 会话恢复:Session、SSE 游标与重放边界
SSE 断线、HTTP 404 与 DELETE 都不等于同一种结束。本文绑定 MCP session、协议版本、原始 stream 和 Last-Event-ID,避免跨流重放、会话劫持与重复副作用。
本文要点
- 主题聚焦:AI Agent、MCP、DevOps
- 适合人群:独立开发者、全栈工程师、AI 应用开发者
- 核心价值:快速理解背景、实践路径、常见误区和工程建议
一次工具请求通过 POST 打开 SSE,Server 已执行写操作,却在返回 JSON-RPC response 前断线。Client 把网络断开当成取消,又用新请求 ID 重试;与此同时,它把另一个 GET stream 的 Last-Event-ID 带去恢复。最终既可能重复写入,也可能把不属于该请求的消息送进状态机。
Streamable HTTP 只有一个同时支持 POST 与 GET 的 MCP endpoint,但并不意味着所有连接共享一条无边界消息流。MCP 超时、重试与幂等解决业务请求重试,长运行工具状态机处理异步终态;本篇固定 transport session、SSE stream 与重放游标的关系。
分开记录会话、连接、stream 与请求
Server 可在 InitializeResult 的 HTTP 响应中返回密码学安全的 MCP-Session-Id。一旦返回,Client 后续所有 HTTP 请求都必须携带它;Server 对缺失 ID 应返回 400,终止后的 ID 必须得到 404。Client 收到带 session ID 请求的 404 时必须无 ID 重新 initialize,而不是把旧请求直接搬入新会话。主动离开时 Client 应发 DELETE;405 只表示 Server 不支持客户端终止,不能伪造已删除收据。
账本 mcpHttpSession.v1 记录 session ID 的 HMAC、主体、握手 receipt、协议版本、Origin 策略、授权 subject、创建/过期时间和 generation,绝不把原始 session ID写入普通日志。mcpSseStream.v1 另存 stream ID、发起方式 POST/GET、关联 request ID 与 payload digest、最后发送和最后确认 event ID、retry 建议及关闭原因。event ID 在 session 内必须全局唯一,并含足够信息定位原始 stream;Server 只能在该 stream 上重放遗漏消息,不能跨 stream 广播。
SESSION_ACTIVE -> STREAM_OPEN -> DISCONNECTED -> RESUME_PENDING
RESUME_PENDING -> STREAM_OPEN | CURSOR_REJECTED | SESSION_EXPIRED
SESSION_ACTIVE -> DELETE_REQUESTED -> TERMINATED
SESSION_EXPIRED | TERMINATED -> REINITIALIZE_REQUIRED
恢复总是 GET endpoint 并携带 Last-Event-ID。Client 先验证游标属于当前 session 与原 stream,再从本地 durable inbox 的最后已接受水位请求重放;收到重复 event 时按 (sessionGeneration, streamId, eventId, messageDigest) 去重。连接断开不代表请求取消,取消必须发送 MCP CancelledNotification。Server 给出的 SSE retry 是重连等待毫秒数,Client 要遵守并叠加全局重连预算,防止断网时放大流量。
唯一消息接受点 acceptStreamEvent 同时核验 session、协议 header、授权 subject、stream、单调 event cursor、JSON-RPC 关联和内容摘要,再原子写 inbox receipt 与业务派发 outbox。JSON-RPC response 只能结束它关联的 POST stream;独立 GET stream 除恢复旧 POST stream 外不得承载 response。这样迟到 response 可以结束原请求,却不会因浏览器重连生成第二次副作用。Agent 运行时状态机只读取已接受 inbox。
把 transport 安全纳入恢复测试
所有连接都要验证 Origin,非法 Origin 返回 403;本地 Server 只绑定 loopback,远程连接使用认证。session ID 不能替代用户身份或授权,MCP 认证边界仍需验证 token subject;入口的 DNS rebinding 防护可复用Agent 出站 SSRF 与重绑定防线的解析原则。
故障注入覆盖初始空 SSE event 丢失、两个 stream event ID 交叉、ACK 后重放、错误 Last-Event-ID、session 过期与 response 同时到达、DELETE 响应丢失、旧 token 配新 session、无版本 header、代理修改 Origin、429/503 重连风暴。门禁要求十万事件中跨流接受为 0、重复业务派发为 0、所有携带 session ID 且收到 404 的请求都触发新握手、非法 Origin 拒绝率 100%、恢复 p99 小于 5 秒、重连 QPS 不超预算。
上线先让 1% 只读 session 生成 shadow stream ledger,再逐步启用 resumable event;写工具需同时满足幂等收据才放量。回滚时关闭新 stream 的恢复能力,但保留旧 generation 的游标索引直到过期,并 drain 旧请求;删除账本或把所有断线改成重新 POST 都会失去唯一性证据,不是可接受的回滚。
Related