subscriptions/listen 会从服务器到客户端打开一个长期存在的通知流。与一次性请求不同,该流会保持打开状态并持续发送通知,直到客户端取消它。它取代了原先的 resources/subscribe RPC 和 HTTP GET 端点。
打开流
客户端发送一个带有notifications 过滤器的 subscriptions/listen 请求,用于指定希望接收哪些事件类型。服务器 不得 发送客户端未明确请求的通知类型。
通知过滤器
| 字段 | 类型 | 描述 |
|---|---|---|
toolsListChanged | boolean | 当工具发生变化时接收 notifications/tools/list_changed |
promptsListChanged | boolean | 当提示词发生变化时接收 notifications/prompts/list_changed |
resourcesListChanged | boolean | 当列表发生变化时接收 notifications/resources/list_changed |
resourceSubscriptions | string[] | 为这些资源 URI 接收 notifications/resources/updated |
确认
服务器 必须 作为流上的第一条消息发送notifications/subscriptions/acknowledged。确认中的 notifications 字段反映了服务器同意支持的子集——服务器不支持的通知类型会被省略。
接收通知
流上发送的所有通知都会在_meta 中携带 io.modelcontextprotocol/subscriptionId,用于标识打开该流的 subscriptions/listen 请求。该值就是 subscriptions/listen 请求的 JSON-RPC ID。在上面的示例中,请求使用了 "id": 1,因此确认消息以及后续所有通知都会携带订阅 ID 1。在 stdio 中,所有消息共享单一通道,客户端 必须 使用该字段将通知与其来源订阅进行关联。
多个并发订阅
客户端 可以 同时拥有多个活跃订阅——例如,一个监听工具列表变更,另一个监听资源更新。每个订阅都由其subscriptions/listen 请求的 JSON-RPC 请求 ID 标识,并且流上的每条通知都会在 io.modelcontextprotocol/subscriptionId 中携带该 ID,以便客户端对它们进行解复用。
取消
订阅在以下情况下结束:- 客户端 取消它——关闭 SSE 流(HTTP)或发送
notifications/cancelled,引用subscriptions/listen请求 ID(stdio)。 - 服务器 主动终止它(例如,在关闭期间)——它 应该 发送
空的
subscriptions/listen响应以表示正常结束(参见 优雅关闭),然后关闭流。 - 底层传输关闭(HTTP 超时、TCP 断开、stdio 进程 退出)。
优雅关闭
当服务器主动结束订阅时(例如,在 关闭期间),它 应该 在关闭流之前,先对原始的subscriptions/listen 请求
返回一个空结果。 这是对这个长生命周期请求的 JSON-RPC 响应,通过其 id 关联,并表明订阅
已优雅结束——而不是突发的传输中断,后者不会携带任何
响应。
_meta 中携带
io.modelcontextprotocol/subscriptionId,用于标识它关闭的是哪一个
订阅。该值与原始
subscriptions/listen 请求的 JSON-RPC id 相匹配。
收到此响应的客户端会知道订阅已正常关闭;而没有该响应就关闭的
传输则表示一次意外断开,客户端 可以 将其视为重新连接的触发条件。
在 stdio 中,如果连接被终止后又重新建立,
客户端 必须 重新发送 subscriptions/listen 以重新建立其
订阅——服务器不会在重连之间保留任何订阅状态。
完整规则请参见 取消。