Add server/discover and stateless lifecycle error codes per SEP-2575#438
Open
koic wants to merge 1 commit into
Open
Add server/discover and stateless lifecycle error codes per SEP-2575#438koic wants to merge 1 commit into
server/discover and stateless lifecycle error codes per SEP-2575#438koic wants to merge 1 commit into
Conversation
## Motivation and Context SEP-2575 (modelcontextprotocol/modelcontextprotocol#2575, merged for the 2026-07-28 spec release) makes MCP stateless, removing the `initialize`/`notifications/initialized` handshake in favor of per-request `_meta`-carried negotiation. The full lifecycle rewrite is unmerged in both the TypeScript SDK (the closed #2063/#2064/#2251 prototype stack, on hold since 2026-06-08) and the Python SDK, but two pieces of its wire contract stayed stable across every prototype iteration and are purely additive: - The `server/discover` method, which servers MUST implement: sessionless capability discovery returning `supportedVersions`, `capabilities`, `serverInfo`, and `instructions`. The new `Server#discover` handler reuses the same data `init` already builds, but is state-free and idempotent: it stores no client info, never marks a session initialized, and responds regardless of declared capabilities or initialization state (added to the no-capability group in `Methods.ensure_capability!`). `serverInfo` is returned unfiltered because discovery precedes version negotiation; the draft's `ttlMs`/`cacheScope` fields on `DiscoverResult` are left to the SEP-2549 work. On `StreamableHTTPTransport`, a `server/discover` POST is exempt from the `Mcp-Session-Id` requirement and the `MCP-Protocol-Version` header check, like `initialize`, so clients can probe a server with a bare POST in both stateful and stateless modes. - The error code vocabulary: the new `MCP::ErrorCodes` module exports `MISSING_REQUIRED_CLIENT_CAPABILITY = -32003` and `UNSUPPORTED_PROTOCOL_VERSION = -32004`. Constants only; nothing raises them yet (that belongs to the 2026-07-28 lifecycle work), and the existing inline `-32042` of `URLElicitationRequiredError` is intentionally left untouched. The removal of `initialize`, `ping`, and `logging/setLevel` that SEP-2575 also specifies is deliberately NOT implemented: the SDK targets protocol versions up to 2025-11-25, where those methods are required. Part of modelcontextprotocol#389. ## How Has This Been Tested? - `test/mcp/server_test.rb`: `server/discover` returns the supported versions, the server's capabilities, server info, and instructions; it responds before `initialize` and with empty capabilities; it does not mark the session initialized (a subsequent `initialize` still succeeds); `instructions` is omitted when the server has none; and `define_custom_method(method_name: "server/discover")` now raises `MethodAlreadyDefinedError`. - `test/mcp/methods_test.rb`: `ensure_capability!` does not raise for `SERVER_DISCOVER` with empty capabilities. - `test/mcp/server/transports/streamable_http_transport_test.rb`: a `server/discover` POST without a session ID returns 200 with the discover result in both stateful and stateless modes, without issuing an `Mcp-Session-Id`. - New `test/mcp/error_codes_test.rb` pins the -32003/-32004 values. `bundle exec rake` (tests, RuboCop, and conformance baseline) passes. ## Breaking Changes None at the wire level for existing clients. One narrow API consequence: servers that previously registered their own `"server/discover"` handler via `define_custom_method` now get `MethodAlreadyDefinedError` because the method is built in.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Motivation and Context
SEP-2575 (modelcontextprotocol/modelcontextprotocol#2575, merged for the 2026-07-28 spec release) makes MCP stateless, removing the
initialize/notifications/initializedhandshake in favor of per-request_meta-carried negotiation. The full lifecycle rewrite is unmerged in both the TypeScript SDK (the closed #2063/#2064/#2251 prototype stack, on hold since 2026-06-08) and the Python SDK, but two pieces of its wire contract stayed stable across every prototype iteration and are purely additive:server/discovermethod, which servers MUST implement: sessionless capability discovery returningsupportedVersions,capabilities,serverInfo, andinstructions. The newServer#discoverhandler reuses the same datainitalready builds, but is state-free and idempotent: it stores no client info, never marks a session initialized, and responds regardless of declared capabilities or initialization state (added to the no-capability group inMethods.ensure_capability!).serverInfois returned unfiltered because discovery precedes version negotiation; the draft'sttlMs/cacheScopefields onDiscoverResultare left to the SEP-2549 work. OnStreamableHTTPTransport, aserver/discoverPOST is exempt from theMcp-Session-Idrequirement and theMCP-Protocol-Versionheader check, likeinitialize, so clients can probe a server with a bare POST in both stateful and stateless modes.MCP::ErrorCodesmodule exportsMISSING_REQUIRED_CLIENT_CAPABILITY = -32003andUNSUPPORTED_PROTOCOL_VERSION = -32004. Constants only; nothing raises them yet (that belongs to the 2026-07-28 lifecycle work), and the existing inline-32042ofURLElicitationRequiredErroris intentionally left untouched.The removal of
initialize,ping, andlogging/setLevelthat SEP-2575 also specifies is deliberately NOT implemented: the SDK targets protocol versions up to 2025-11-25, where those methods are required.Part of #389.
How Has This Been Tested?
test/mcp/server_test.rb:server/discoverreturns the supported versions, the server's capabilities, server info, and instructions; it responds beforeinitializeand with empty capabilities; it does not mark the session initialized (a subsequentinitializestill succeeds);instructionsis omitted when the server has none; anddefine_custom_method(method_name: "server/discover")now raisesMethodAlreadyDefinedError.test/mcp/methods_test.rb:ensure_capability!does not raise forSERVER_DISCOVERwith empty capabilities.test/mcp/server/transports/streamable_http_transport_test.rb: aserver/discoverPOST without a session ID returns 200 with the discover result in both stateful and stateless modes, without issuing anMcp-Session-Id.test/mcp/error_codes_test.rbpins the -32003/-32004 values.bundle exec rake(tests, RuboCop, and conformance baseline) passes.Breaking Changes
None at the wire level for existing clients. One narrow API consequence: servers that previously registered their own
"server/discover"handler viadefine_custom_methodnow getMethodAlreadyDefinedErrorbecause the method is built in.Types of changes
Checklist