让 MCP 客户端手里的资源保持最新,靠的不是轮询 resources/read,而是订阅:客户端发起订阅,资源变更时服务器主动推送通知,客户端再按需回读。 2026-07-28 版规范把这套机制换成了长连接的 subscriptions/listen,取代了早期的 resources/subscribe,订阅从「会话级开关」变成了「有生命周期的流」。
新机制长什么样
客户端发送 subscriptions/listen,在 notifications 字段里声明想要哪几类事件:
toolsListChanged:工具列表变化时收到通知;promptsListChanged:提示模板列表变化;resourcesListChanged:资源清单变化;resourceSubscriptions:一个 URI 数组,这些资源更新时收到notifications/resources/updated。
所有字段都是可选的,不填即视为不订阅。服务器必须先回一条 notifications/subscriptions/acknowledged,在 _meta 里带上 io.modelcontextprotocol/subscriptionId,并且在这个 ID 上不得先于确认消息发出任何通知。确认消息里的 notifications 字段反映服务器实际同意的子集——不支持的类型会被省略,客户端应当拿它和自己请求的内容做比对,对不支持的类型做优雅降级。
_meta 里的 subscriptionId 做多路分解。客户端必须按这个字段把通知归回各自的订阅,而不是假定「先到先属于先订阅的」。订阅 ID 与并发订阅
订阅的 ID 就是发起 subscriptions/listen 那个请求的 JSON-RPC id 。客户端可以同时开着多个订阅——一个盯工具列表,一个盯资源更新——每条通知都带 ID,客户端据此分发。设计客户端时不要把它当成「全局只有一个订阅通道」。
三种结束方式与优雅关闭
- 客户端取消:HTTP 下关闭 SSE 流,stdio 下发送引用原请求 ID 的
notifications/cancelled; - 服务器主动拆除(例如关闭时):应先回一条
resultType为 complete 的响应,再关闭流,表示正常结束; - 传输断开:HTTP 超时、 TCP 断开、 stdio 进程退出,这类没有响应,属于异常中断。
区分后两种情况很重要:收到 complete 响应说明订阅干净结束,可以不重连;传输突然中断则需要重连逻辑。 Inspector 在流断开时会显示重连状态并重新发送 listen,这是正确的客户端行为参考。
把订阅接到缓存失效上
订阅本身只告诉你「变了」,不告诉你变成什么。真正有用的链路是三段:
- 订阅时登记本地缓存项:把 URI 和本地缓存的读取时间一起存下来;
- 收到 updated 后标记失效而不是立刻回读:高频变更的资源(日志尾部、实时配置)若每次通知都回读,会把通知风暴转成读风暴;
- 真正需要内容时再
resources/read:把失效标记和实际读取解耦,是控制上下文占用的关键。
这条链路和大结果集的处理是配套的:资源本来就可能很大,订阅只负责变脏,读取时才考虑分页与裁剪,相关设计见MCP 分页与返回体裁剪。
服务端实现要注意的三点
- 不要自己发明订阅状态存放位置:SDK 通常不托管订阅状态,需要服务端自行维护已订阅 URI 集合,并在资源变更时主动调用通知接口;
- 协商能力要如实声明:不支持的类型就在确认消息里省略,不要硬发客户端没请求的事件——规范明确要求服务器不得发送未被显式请求的通知类型;
- 订阅关系要随连接生命周期清理:stdio 进程退出后订阅自然消失,HTTP 场景必须显式处理连接断开时的集合清理,否则会内存泄漏。
服务器整体能力的声明方式在MCP 能力协商里有更完整的说明,接入前建议先对齐双方声明的能力集合。运行期的排障则依赖日志与通知通道,这部分可以参考MCP 服务器日志与监控。
什么时候不该用订阅
订阅引入的是长连接状态,不是免费的。以下情况更适合直接读:
- 资源变更频率高于读取频率——通知比读取还多,纯属浪费;
- 内容本身就带版本或时间戳,客户端可以自己比对;
- 只需要一次性快照的任务型调用。
选择接入哪些服务器、要不要自己实现订阅,本质上是能力取舍问题,可参考MCP 服务器选型的判断框架。
resources/subscribe 还能用吗
subscriptions/listen 取代。新实现应按长连接订阅设计,旧方式属于遗留行为,跨版本互连时要先确认对方支持的协议版本。服务器不支持某类通知怎么办
收到更新通知后要立刻回读吗
订阅断开后怎么判断要不要重连
核心结论:订阅解决的是「什么时候变」,读取解决的是「变成什么」。把两者解耦,并用失效标记做缓冲,MCP 的资源同步才能在高频变更场景下既不丢更新也不炸上下文。










评论 (0)