ruby-sdk/test
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Koichi ITO 09545f1272 Serve both lifecycle eras over stdio with an era lock per SEP-2575
## Motivation and Context

Third step of the stateless lifecycle (SEP-2575, modelcontextprotocol/modelcontextprotocol#2575) for
the 2026-07-28 MCP spec release. `StdioTransport` now serves the legacy handshake lifecycle
and the modern per-request-envelope lifecycle on one connection, with the same era-lock semantics as
the TypeScript and Python SDKs: the first era-distinctive message to SUCCEED locks the connection era
for its lifetime.

- Each stdin frame is parsed once (`symbolize_names: true`) so era classification can inspect its method
  and `_meta`. Frames that are not JSON objects fall back to `ServerSession#handle_json`,
  keeping protocol-level error responses byte-identical.
- A successful `initialize` locks `:legacy` (already a side effect of `ServerSession#mark_initialized!`).
  A successful `server/discover` or a successful request carrying the full modern `_meta` triple locks `:modern`.
  Failed era-distinctive messages (for example an unsupported envelope version) leave the connection unlocked,
  so a client probe can still fall back to the other era.
- Era violations are rejected in-band by `Server#lift_request_envelope`, which now also covers the legacy side:
  a modern envelope arriving on a legacy-locked session is an invalid request (`-32600`), mirroring
  the existing modern-side rules (`initialize` after a modern lock is `-32022`; a missing envelope after
  a modern lock is `-32600`).
- `StdioTransport#send_request` raises on a modern-locked session: the modern lifecycle forbids
  server-initiated JSON-RPC requests, which multi round-trip `input_required` results (SEP-2322) replace.
  The inline read loop inside `send_request` dispatches through the same era-aware path as the main loop.

Refs #389.

## How Has This Been Tested?

New tests in `test/mcp/server/transports/stdio_transport_test.rb` drive full stdin/stdout round trips:
legacy lock via `initialize` then rejection of a modern envelope, modern lock via `server/discover`
then `-32022` (with `data.supported`) for a late `initialize`, modern lock via an envelope-carrying request,
no lock when the probe fails with an unsupported version (a legacy `initialize` still succeeds afterwards),
the envelope requirement after a modern lock, and the `send_request` prohibition on modern sessions.

`bundle exec rake` (tests, RuboCop, and conformance baseline, including the stdio conformance scenarios) passes.

## Breaking Changes

None. Legacy clients send `initialize` first and take the same code path as before;
the era lock only constrains message sequences that mix lifecycles on one connection,
which no legacy client produces.
2026-08-03 01:54:06 +09:00
..
mcp Serve both lifecycle eras over stdio with an era lock per SEP-2575 2026-08-03 01:54:06 +09:00
initialize_params_test_helper.rb Validate Required Params for initialize and resources/subscribe 2026-07-11 22:36:34 +09:00
instrumentation_test_helper.rb Internal Release 0.5.1 2025-05-08 17:38:05 -04:00
json_rpc_handler_test.rb Handle non-object JSON-RPC messages without raising 2026-07-11 17:06:37 +09:00
test_helper.rb Validate Required Params for initialize and resources/subscribe 2026-07-11 22:36:34 +09:00