feat(mcp): correlated subscription streams (MCP 2026-07-28 subscriptions/listen) - #2512
Merged
Merged
Conversation
…ons/listen) Implement the client side of the MCP 2026-07-28 subscription revision so list-change notifications become attributable to a specific connection when an agent talks to several MCP servers concurrently. - internal/mcp/subscriptions.go: Subscription lifecycle (listen request, acknowledged control message with granted-filter subset, graceful stream closure, notifications/cancelled on client-side cancel, MUST gating that drops notifications correlating to unknown/unacked subscriptions), plus a self-detecting EnableModernSubscriptions entry point that latches a permanent downgrade on legacy -32601 servers. stdio transports stream; ws is explicitly rejected. - internal/mcp/client.go: subscription registry fields, notification routing hook in the legacy dispatch path (uncorrelated traffic is untouched), Abort closes all open subscriptions. - internal/plugin/mcp_loader.go: setupModernSubscriptions on the shared connect path so reconnects re-open the stream automatically. - Test mocks now echo real request ids (the probe occupies an id slot); new pipe-based stdio fake-server tests cover ack/graceful, legacy downgrade latching, cancelled propagation, MUST-gating matrix, abort teardown; race-clean under -race -count=2. - docs/guide/mcp.md: document the feature and its fallback semantics. Co-Authored-By: ggcode <noreply@ggcode.dev> Co-Authored-By: ggcode <noreply@ggcode.dev>
topcheer
commented
Sep 17, 2026
topcheer
left a comment
Owner
Author
There was a problem hiding this comment.
复审通过 ✅(三重点核销)
① 与 #2480 关联升级——叠加非替代:listen 流通知 correlate 后剥离 _meta 转发到既有 legacy handler(工具/资源刷新链零改动收 correlation 收益);#2480 的 per-URI 订阅在 modern 服务器上由 filter.ResourceSubscriptions 承载(升级面),legacy 服务器维持原路径——单订阅单流(modernSub 唯一+活流 short-circuit),无多 listen 扇出需求面(filter 合并即全量)。
② correlation ID 作用域:subscriptionId=listen 请求的 JSON-RPC id(服务器 echo 回)——client 实例内 nextID 单调分配=天然唯一;MUST-gating:未 ack/未知 subscriptionId 通知=协议违反 drop+log(服务器不得先发后 ack 的客户端侧强制)——乱序攻击面闭合。
③ 与 #2510 stateless 兼容:-32601 永久降级(对齐 GET 流 405 降级形态)——legacy 服务器零重复调用;listen 长流是请求内长响应(stdio waiter/HTTP SSE POST 内嵌)非跨请求会话——stateless 的 per-request _meta 与长流响应正交;15s ack 钳+取消走既有 notifyCancelled(复用 #2490 管道)。WS 显式不支持(legacy bridge 域声明)。
CI 全绿。可合并。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the client side of the MCP 2026-07-28 subscription revision (
subscriptions/listen): correlated notification streams that make list-change notifications attributable to a specific connection when an agent talks to several MCP servers concurrently.Changes
internal/mcp/subscriptions.go(new): fullSubscriptionlifecycle —ListenSubscriptionssendssubscriptions/listenwith the exact change set ggcode needs and waits for the specnotifications/subscriptions/acknowledgedcontrol message, which carries the granted subset.notifications/cancelledreferencing the listen request id._metasubscription id that is unknown or unacknowledged are dropped, so a buggy server cannot poison cache invalidation; valid correlated traffic flows through the unchanged legacy dispatch.EnableModernSubscriptionsself-detects: a legacy server-32601latches a permanent per-connection downgrade.internal/mcp/client.go: subscription registry, a single routing hook at the top of the notification dispatch path (zero behavior change for uncorrelated traffic), Abort closes all subscriptions.internal/plugin/mcp_loader.go: auto-enable on the shared connect path so auto-reconnect re-opens the stream.-race -count=2. HTTP mocks now echo real request ids and answer the probe with spec-32601.docs/guide/mcp.md.Compatibility
🤖 Generated with ggcode