Add downstream subscriptions/listen admission - #78
Conversation
11c4627 to
a51d96b
Compare
lucarlig
left a comment
There was a problem hiding this comment.
Requesting changes because the dataplane downstream contract is modern MCP 2026-07-28 only. This change still advertises and admits legacy protocol behavior, leaves RMCP legacy session routing enabled, and advertises subscriptions before any notification delivery path exists.
Signed-off-by: Pratik Gandhi <gandhipratik203@gmail.com>
Signed-off-by: Pratik Gandhi <gandhipratik203@gmail.com>
Signed-off-by: Pratik Gandhi <gandhipratik203@gmail.com>
Signed-off-by: Pratik Gandhi <gandhipratik203@gmail.com>
7e66cd7 to
8ebda2c
Compare
lucarlig
left a comment
There was a problem hiding this comment.
The protocol allowlist issue is fixed and its thread is resolved. Two P1 blockers remain: the downstream endpoint still exposes RMCP legacy session/fallback routing, and it advertises subscription capabilities before any notification delivery path exists.
Signed-off-by: Pratik Gandhi <gandhipratik203@gmail.com>
|
Updated the checked-in wiki docs under |
Summary
Carry request user + virtual host context into RMCP.
Advertise virtual-host-specific capabilities via
server/discover.Accept only supported/routable
subscriptions/listenfilters.Register active downstream subscription sinks and clean them up on close.
Test modern/legacy subscription method gating.
Closes [CF-DATAPLANE] Downstream subscriptions/listen: context-carried admission and sink registry IBM/mcp-context-forge#6117.
Parent tracker: [Major-3] Notification rework: subscriptions/listen IBM/mcp-context-forge#5681.
Explanatory Diagrams
This PR adds the downstream subscription admission and registry path. It lets modern clients open
subscriptions/listenstreams, narrows each request to what the selected virtual host can expose, stores accepted sinks for future notification relay, and cleans them up when the stream closes.Changed files layout
Runtime component flow
Notes
std::sync::Mutexintentionally: registration and cleanup are shortHashMapoperations, no await happens while the lock is held, and cleanup runs from the guard Drop path where an async mutex cannot be awaited.Validation
cargo +1.96 test -p contextforge-data-plane-lib gateway:: --libcargo +1.96 test -p contextforge-data-plane-lib --test gateway_modern_subscriptions -- --nocapturecargo +1.96 test -p contextforge-data-plane-libgit diff --checkE2E Verification
How to run
This E2E test is intentionally ignored by default because it starts a temporary
redis-serverand the realcontextforge-data-planebinary.cargo +1.96 test -p contextforge-data-plane --test modern_subscriptions_e2e -- --ignored --nocaptureThe test pauses before the two MCP calls. Press Enter at each prompt:
E2E test script
Expected result
This confirms
server/discoverreports the expected virtual-host capabilities,subscriptions/listenis accepted and acknowledged, and the unroutableresourceSubscriptionsentry is not accepted.Manual Verification
Manual test steps
Start Redis and sample backends:
Run the gateway with local helper routes enabled:
Mint a local token and seed a two-backend virtual host:
Run
server/discover:Run
subscriptions/listenwith one unroutable resource URI:Manual test results
server/discoverreturnedHTTP/1.1 200 OKwithtext/event-streamand vhost-derived notification capabilities:{ "capabilities": { "completions": {}, "prompts": {"listChanged": true}, "resources": {"subscribe": true, "listChanged": true}, "tools": {"listChanged": true} } }subscriptions/listenreturnednotifications/subscriptions/acknowledged:{ "method": "notifications/subscriptions/acknowledged", "params": { "_meta": {"io.modelcontextprotocol/subscriptionId": 2}, "notifications": { "toolsListChanged": true, "resourcesListChanged": true } } }The unroutable
resourceSubscriptionsentry was dropped from the acknowledged filter. The curl timeout after 5 seconds is expected becausesubscriptions/listenis a long-lived stream.