Associate server-to-client requests with the originating client request per SEP-2260#440
Open
koic wants to merge 1 commit into
Open
Conversation
…st per SEP-2260 ## Motivation and Context SEP-2260 (modelcontextprotocol/modelcontextprotocol#2260, merged for the 2026-07-28 spec release) requires servers to send `roots/list`, `sampling/createMessage`, and `elicitation/create` only in association with an originating client request (`ping` is exempt); standalone server-initiated requests on independent streams must not be implemented. On the Streamable HTTP transport, an associated request rides the originating POST's SSE response stream instead of the standalone GET stream. The Ruby SDK's `ServerContext` already satisfies the requirement for handler-scoped calls: `list_roots`, `create_sampling_message`, `create_form_elicitation`, and `create_url_elicitation` stamp the originating request id as `related_request_id`, and because the literal keyword appears after `**kwargs`, a caller-supplied `related_request_id:` is overridden. That behavior was previously untested and undocumented. This change pins it down and adds a migration signal for direct `ServerSession` calls, mirroring the TypeScript SDK's approach (typescript-sdk#2228, non-overridable `relatedRequestId` stamping on ctx-scoped requests): - `ServerContext` documents the non-overridable stamping, and new tests lock it in for all four request helpers. - `ServerSession#list_roots`, `#create_sampling_message`, `#create_form_elicitation`, and `#create_url_elicitation` emit a deprecation warning when called without `related_request_id:` (the call still goes out unchanged, on the GET stream for Streamable HTTP). `ServerSession#ping` is exempt per the SEP. Hard enforcement is deferred to a future protocol-version gate for 2026-07-28. - A new transport test verifies that `send_request` with `related_request_id` delivers on the originating POST stream and never falls back to the GET stream. Resolves modelcontextprotocol#381. ## How Has This Been Tested? - `test/mcp/server_context_test.rb`: `list_roots` stamps the originating request id; `create_sampling_message`, `create_form_elicitation`, and `create_url_elicitation` stamp it non-overridably (a caller-supplied `related_request_id: "attacker"` is replaced). - `test/mcp/server_roots_test.rb`, `test/mcp/server_sampling_test.rb`, `test/mcp/server_elicitation_test.rb`: each `ServerSession` method warns (matching /SEP-2260/) without `related_request_id:` and stays silent with it. The warning assertions temporarily set `$VERBOSE = false` because the rake test task runs with `-W0`, following the existing `assert_implicit_connect_deprecation_warning` precedent. - `test/mcp/server/transports/streamable_http_transport_test.rb`: a `roots/list` request with `related_request_id` is written to the registered POST request stream and not to the GET SSE stream. - Existing direct-call test sites were updated to pass `related_request_id:` (or capture the warning) so the suite output stays clean; their assertions are unchanged. ## Breaking Changes None. Behavior is unchanged; direct `ServerSession` calls without `related_request_id:` now emit a deprecation warning but are still sent exactly as before.
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.
Motivation and Context
SEP-2260 (modelcontextprotocol/modelcontextprotocol#2260, merged for the 2026-07-28 spec release) requires servers to send
roots/list,sampling/createMessage, andelicitation/createonly in association with an originating client request (pingis exempt); standalone server-initiated requests on independent streams must not be implemented. On the Streamable HTTP transport, an associated request rides the originating POST's SSE response stream instead of the standalone GET stream.The Ruby SDK's
ServerContextalready satisfies the requirement for handler-scoped calls:list_roots,create_sampling_message,create_form_elicitation, andcreate_url_elicitationstamp the originating request id asrelated_request_id, and because the literal keyword appears after**kwargs, a caller-suppliedrelated_request_id:is overridden. That behavior was previously untested and undocumented. This change pins it down and adds a migration signal for directServerSessioncalls, mirroring the TypeScript SDK's approach (typescript-sdk#2228, non-overridablerelatedRequestIdstamping on ctx-scoped requests):ServerContextdocuments the non-overridable stamping, and new tests lock it in for all four request helpers.ServerSession#list_roots,#create_sampling_message,#create_form_elicitation, and#create_url_elicitationemit a deprecation warning when called withoutrelated_request_id:(the call still goes out unchanged, on the GET stream for Streamable HTTP).ServerSession#pingis exempt per the SEP. Hard enforcement is deferred to a future protocol-version gate for 2026-07-28.send_requestwithrelated_request_iddelivers on the originating POST stream and never falls back to the GET stream.Resolves #381.
How Has This Been Tested?
test/mcp/server_context_test.rb:list_rootsstamps the originating request id;create_sampling_message,create_form_elicitation, andcreate_url_elicitationstamp it non-overridably (a caller-suppliedrelated_request_id: "attacker"is replaced).test/mcp/server_roots_test.rb,test/mcp/server_sampling_test.rb,test/mcp/server_elicitation_test.rb: eachServerSessionmethod warns (matching /SEP-2260/) withoutrelated_request_id:and stays silent with it. The warning assertions temporarily set$VERBOSE = falsebecause the rake test task runs with-W0, following the existingassert_implicit_connect_deprecation_warningprecedent.test/mcp/server/transports/streamable_http_transport_test.rb: aroots/listrequest withrelated_request_idis written to the registered POST request stream and not to the GET SSE stream.related_request_id:(or capture the warning) so the suite output stays clean; their assertions are unchanged.Breaking Changes
None. Behavior is unchanged; direct
ServerSessioncalls withoutrelated_request_id:now emit a deprecation warning but are still sent exactly as before.Types of changes
Checklist