You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(server): reject server-to-client requests on 2026-07-28 - #1326
The 2026-07-28 spec forbids servers from sending JSON-RPC requests to clients over any transport. For streamable HTTP, it says, "The server MUST NOT send independent JSON-RPC requests." For stdio, it says, "The server MUST NOT write JSON-RPC requests." Sampling, elicitation, and roots are now handled through InputRequiredResult (MRTR).
However, rmcp still let a handler call peer.create_message() with a 2026-07-28 client, as long as the call came from inside a request handler (SEP-2260). Over stateless streamable HTTP, the request reached the client, but the client's response came back as a new POST. The server acknowledged it with 202 and then dropped it. The handler, the SSE stream, and the client's call_tool all hung without an error.
This PR makes Peer<RoleServer> reject all outbound requests, including sampling, elicitation, roots, ping, and custom requests, once the peer has negotiated version 2026-07-28 or later. The error is invalid_request and points to InputRequiredResult. The stateless HTTP branch also returns 400 with a JSON-RPC invalid_request error for POSTed Response or Error messages. A stateless request has no pending server request to respond to, so those messages can't be delivered.
How Has This Been Tested?
Added tests
Breaking Changes
There are no changes to the public API, but runtime behavior does change. When the peer negotiated on or after 2026-07-28, create_message, list_roots, create_elicitation, elicit, and send_request now return an error.
This only affects setups using rmcp on both ends over stdio or another bidirectional transport, negotiated on or after 2026-07-28. They worked before because rmcp was lenient on both sides, but they already violated the spec and fail with clients from other SDKs that follow it. Those servers should return InputRequiredResult instead. See the servers_mrtr example. Clients on 2025-11-25 or earlier are unaffected.
Types of changes
Bug fix (non-breaking change which fixes an issue)
New feature (non-breaking change which adds functionality)
Breaking change (fix or feature that would cause existing functionality to change)
I have a question on the scope: this covers the send side, but on the receive side we still allow dispatching an associated sampling/createMessage, elicitation/create or roots/list at ≥ 2026-07-28 (RoleClient::enforce_peer_request_association, service/client.rs:312), since #1055 only checks association. That's the path in #1321: the client runs the handler (an LLM call or a user prompt), and then the answer gets dropped. Should the client mirror this PR and reject those at ≥ 2026-07-28 regardless of association? The spec text is a server MUST NOT, so on the client it'd be hardening against non-rmcp and older servers rather than conformance. If you like I can file an issue and take it once this PR is merged.
Good question, @gocamille. I kept this PR scoped to what the issue reported, which is the server-side hang. I'm open to tightening the client side too, though. Feel free to file an issue! :)
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
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.
Fixes #1321.
Motivation and Context
The 2026-07-28 spec forbids servers from sending JSON-RPC requests to clients over any transport. For streamable HTTP, it says, "The server MUST NOT send independent JSON-RPC requests." For stdio, it says, "The server MUST NOT write JSON-RPC requests." Sampling, elicitation, and roots are now handled through
InputRequiredResult(MRTR).However, rmcp still let a handler call
peer.create_message()with a 2026-07-28 client, as long as the call came from inside a request handler (SEP-2260). Over stateless streamable HTTP, the request reached the client, but the client's response came back as a new POST. The server acknowledged it with202and then dropped it. The handler, the SSE stream, and the client'scall_toolall hung without an error.This PR makes
Peer<RoleServer>reject all outbound requests, including sampling, elicitation, roots, ping, and custom requests, once the peer has negotiated version 2026-07-28 or later. The error isinvalid_requestand points toInputRequiredResult. The stateless HTTP branch also returns400with a JSON-RPCinvalid_requesterror for POSTedResponseorErrormessages. A stateless request has no pending server request to respond to, so those messages can't be delivered.How Has This Been Tested?
Added tests
Breaking Changes
There are no changes to the public API, but runtime behavior does change. When the peer negotiated on or after 2026-07-28,
create_message,list_roots,create_elicitation,elicit, andsend_requestnow return an error.This only affects setups using rmcp on both ends over stdio or another bidirectional transport, negotiated on or after 2026-07-28. They worked before because rmcp was lenient on both sides, but they already violated the spec and fail with clients from other SDKs that follow it. Those servers should return
InputRequiredResultinstead. See theservers_mrtrexample. Clients on 2025-11-25 or earlier are unaffected.Types of changes
Checklist