ruby-sdk/lib/mcp
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Koichi ITO eb6ff5d805 Isolate Stateless Requests in Ephemeral Sessions per SEP-2567
## Motivation and Context

SEP-2567 (modelcontextprotocol/modelcontextprotocol#2567, merged for the 2026-07-28 spec release)
makes MCP sessionless: every Streamable HTTP POST must be fully self-contained,
with no protocol-level session state shared between requests.

`StreamableHTTPTransport` already implements the SEP's transport surface via
`stateless: true` (no `Mcp-Session-Id` issued or required, GET returns 405,
DELETE is a no-op, server-to-client requests raise), but its dispatch had a state leak:
stateless POSTs were handled with `session: nil`, so `Server#init` wrote `@client` and
`@client_capabilities` onto the shared `Server` instance.
Concurrent stateless requests could therefore observe another client's identity,
and the data persisted across requests, which is exactly what SEP-2567 forbids.
The TypeScript SDK's stateless prototypes (the closed typescript-sdk#2058/#2131/#2251 stack)
solve this with per-request dispatch; this change applies the equivalent fix within the existing architecture:

- Stateless `handle_initialization`, `handle_regular_request`, and `dispatch_notification` now
  run handlers against an ephemeral per-request `ServerSession` (with `session_id: nil`),
  so client info, logging level, and initialized state live only for the duration of that POST.
  Repeated `initialize` requests are naturally permitted because each POST gets a fresh,
  never-initialized session.
- `send_notification` in stateless mode now returns `false` (non-delivery) instead of raising.
  With ephemeral sessions in place, a tool calling `server_context.report_progress` or `notify_log_message`
  would otherwise route every call into the exception reporter; non-delivery matches how
  these helpers already degrade when no session exists. `send_request` (server-to-client requests) still raises,
  as those are genuinely unsupported without a stream.

Resolves #388.

## How Has This Been Tested?

New tests in `test/mcp/server/transports/streamable_http_transport_test.rb`:

- a stateless `initialize` POST leaves `Server#client_capabilities` and the server's `@client` untouched
  (the leak regression)
- repeated `initialize` POSTs both succeed with 200 and no `Mcp-Session-Id` header
- a tool calling `server_context.report_progress` under stateless mode returns its result normally and
  the exception reporter is never invoked
- the existing "stateless mode does not support server-sent events" test is updated to assert
  the new `false` return instead of the removed raise

All other existing stateless-mode tests pass unchanged.

## Breaking Changes

`StreamableHTTPTransport#send_notification` in stateless mode now returns `false` instead of raising `RuntimeError`.
The raise message was not a documented contract, broadcasting in stateless mode was always a non-deliverable operation,
and the boolean return matches the method's documented semantics in every other non-delivery case.
Default (session-oriented) mode is unchanged.
2026-06-16 11:13:15 +09:00
..
client Merge pull request #395 from koic/rfc8414_well_known_suffix 2026-06-13 20:23:10 +09:00
prompt Add _meta field to resource, content, and result classes 2026-04-11 15:08:55 +09:00
resource Add _meta field to resource, content, and result classes 2026-04-11 15:08:55 +09:00
server Isolate Stateless Requests in Ephemeral Sessions per SEP-2567 2026-06-16 11:13:15 +09:00
tool Speed Up Tool::Schema Validation by 5x to 100x 2026-06-01 21:47:30 +09:00
annotations.rb Add range validation for MCP::Annotations#priority per MCP specification 2026-06-16 00:33:10 +09:00
cancellation.rb Support notifications/cancelled per MCP specification 2026-05-03 04:02:22 +09:00
cancelled_error.rb Support notifications/cancelled per MCP specification 2026-05-03 04:02:22 +09:00
client.rb Support Capability Extensions per SEP-2133 2026-06-14 17:20:45 +09:00
configuration.rb Default missing MCP-Protocol-Version to 2025-03-26 in StreamableHTTPTransport 2026-06-07 10:18:33 +09:00
content.rb Add _meta field to resource, content, and result classes 2026-04-11 15:08:55 +09:00
icon.rb Support icons parameter 2025-12-30 05:35:22 +09:00
instrumentation.rb Add around_request hook for request instrumentation 2026-04-11 13:54:47 +09:00
logging_message_notification.rb logging_message_notification: Remove unused require 2026-02-05 16:05:08 -05:00
methods.rb Add roots/list and notifications/roots/list_changed support 2026-04-15 02:39:14 +09:00
progress.rb Support POST response SSE streams for server-to-client messages 2026-04-04 19:20:31 +09:00
prompt.rb Use autoload to defer loading of unused subsystems 2026-03-12 14:08:27 +09:00
resource.rb Add annotations field to MCP::Resource and MCP::ResourceTemplate per MCP specification 2026-06-14 16:05:25 +09:00
resource_template.rb Add annotations field to MCP::Resource and MCP::ResourceTemplate per MCP specification 2026-06-14 16:05:25 +09:00
server.rb Document That Tool Arguments Arrive with Symbol Keys 2026-06-16 03:09:55 +09:00
server_context.rb Support server-to-client ping per MCP specification 2026-05-22 11:28:51 +09:00
server_session.rb Support server-to-client ping per MCP specification 2026-05-22 11:28:51 +09:00
string_utils.rb Fix ReDoS vulnerability in StringUtils#underscore 2026-01-09 01:14:52 +09:00
tool.rb Use autoload to defer loading of unused subsystems 2026-03-12 14:08:27 +09:00
trace_context.rb Support W3C Trace Context Propagation via _meta per SEP-414 2026-06-12 14:36:24 +09:00
transport.rb Auto-set server.transport in Transport#initialize 2026-04-09 12:08:55 +09:00
version.rb Release 0.20.0 2026-06-14 16:58:51 +09:00