Skip to content

Refuse server-to-client requests in the modern lifecycle per SEP-2575 - #503

Open
koic wants to merge 1 commit into
modelcontextprotocol:mainfrom
koic:reject_modern_lifecycle_server_requests
Open

Refuse server-to-client requests in the modern lifecycle per SEP-2575#503
koic wants to merge 1 commit into
modelcontextprotocol:mainfrom
koic:reject_modern_lifecycle_server_requests

Conversation

@koic

@koic koic commented Aug 10, 2026

Copy link
Copy Markdown
Member

Motivation and Context

The modern lifecycle removes server-initiated JSON-RPC requests: a stateless server has no connection to send them on, and SEP-2322 multi round-trip input_required results replace what sampling and elicitation covered before. StdioTransport#send_request already says so, refusing outright when its session is modern. StreamableHTTPTransport#send_request says nothing, so a modern handler that reaches for elicitation or sampling falls through to the session lookup and is told Session not found: <uuid> against a session id it never chose. Failing is right, the explanation is not: handle_modern mints a session per request without ever registering it in @sessions, so that lookup cannot succeed for a modern request no matter what the handler does.

send_request now refuses a modern-era session up front with the message StdioTransport uses, which turns an incidental registration detail into a stated rule and leaves the rest of the method a legacy-only path by construction. Callers see the same RuntimeError class as before, only with a message naming the actual constraint.

How Has This Been Tested?

A new test in test/mcp/server/transports/streamable_http_transport_test.rb sends elicitation/create from a session constructed with era: :modern and asserts the SEP-2575 message. It sits with the existing guard tests for stateless mode, a missing session_id, and an unknown session, which still pass unchanged. bundle exec rake is green, and both conformance legs pass their baseline check.

Breaking Changes

None. The call already failed; only the message changes.

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

## Motivation and Context

The modern lifecycle removes server-initiated JSON-RPC requests: a stateless server has no connection to send them on,
and SEP-2322 multi round-trip `input_required` results replace what sampling and elicitation covered before.
`StdioTransport#send_request` already says so, refusing outright when its session is modern.
`StreamableHTTPTransport#send_request` says nothing, so a modern handler that reaches for elicitation
or sampling falls through to the session lookup and is told `Session not found: <uuid>` against a session id it never chose.
Failing is right, the explanation is not: `handle_modern` mints a session per request without ever registering it in `@sessions`,
so that lookup cannot succeed for a modern request no matter what the handler does.

`send_request` now refuses a modern-era session up front with the message `StdioTransport` uses, which turns
an incidental registration detail into a stated rule and leaves the rest of the method a legacy-only path by construction.
Callers see the same `RuntimeError` class as before, only with a message naming the actual constraint.

## How Has This Been Tested?

A new test in `test/mcp/server/transports/streamable_http_transport_test.rb` sends `elicitation/create` from
a session constructed with `era: :modern` and asserts the SEP-2575 message. It sits with the existing guard tests for stateless mode,
a missing `session_id`, and an unknown session, which still pass unchanged.
`bundle exec rake` is green, and both conformance legs pass their baseline check.

## Breaking Changes

None. The call already failed; only the message changes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant