Skip to content

fix(server): reject server-to-client requests on 2026-07-28 - #1326

Open
DaleSeo wants to merge 1 commit into
mainfrom
DaleSeo/streamable-http-server-client-responses-to-serve
Open

DaleSeo wants to merge 1 commit into
mainfrom
DaleSeo/streamable-http-server-client-responses-to-serve

Conversation

@DaleSeo

@DaleSeo DaleSeo commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

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 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)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

@github-actions github-actions Bot added T-test Testing related changes T-core Core library changes T-service Service layer changes T-transport Transport layer changes labels Oct 6, 2026
@DaleSeo DaleSeo self-assigned this Oct 6, 2026
@DaleSeo
DaleSeo marked this pull request as ready for review October 9, 2026 19:40
@DaleSeo
DaleSeo requested a review from a team as a code owner October 9, 2026 19:40

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-core Core library changes T-service Service layer changes T-test Testing related changes T-transport Transport layer changes

Projects

None yet

1 participant