Skip to content

Backport #800: fix error behavior on unregistered handlers in stateless server handler - #1104

Open
olsavmic wants to merge 1 commit into
modelcontextprotocol:0.18.xfrom
olsavmic:backport/0.18.x-method-not-found
Open

Backport #800: fix error behavior on unregistered handlers in stateless server handler#1104
olsavmic wants to merge 1 commit into
modelcontextprotocol:0.18.xfrom
olsavmic:backport/0.18.x-method-not-found

Conversation

@olsavmic

Copy link
Copy Markdown

Backport of #800 to the 0.18.x line. See #1085.

Why

DefaultMcpStatelessServerHandler.handleRequest returns Mono.error(...) for a method with no registered handler. That is an early return, so the error never reaches the onErrorResume a few lines below it that maps errors onto a JSON-RPC error response — it escapes to the transport instead, and every stateless transport maps an escaping handler error to HTTP 500:

  • HttpServletStatelessServerTransport
  • WebMvcStatelessServerTransport (io.modelcontextprotocol.sdk:mcp-spring-webmvc)
  • WebFluxStatelessServerTransport (io.modelcontextprotocol.sdk:mcp-spring-webflux)

Requests for registered methods are unaffected — tools/call with an unknown tool name already returns a proper -32602, because that path does run through onErrorResume.

The practical impact is the one described in #1072: OpenAI's hosted MCP connector sends server/discover before tools/list, so a healthy server answers a routine capability probe with a 5xx. On our side that made POST /mcp the only 5xx source in the service and burned the availability SLO budget; OpenAI surfaces the same 500 as HTTP 424 external_connector_error, which aborts the whole response.

Why a backport

The fix is on main and shipped in mcp-core 2.0.1, but 2.0.x is not reachable for Spring AI users on Spring Boot 3:

  • io.modelcontextprotocol.sdk:mcp-spring-webmvc does not exist at 2.0.x — the Spring transports moved to org.springframework.ai — so mcp-core cannot be bumped on its own.
  • spring-ai 2.0's spring-ai-starter-mcp-server-webmvc pulls spring-boot-starter-web:4.1.0, making the upgrade a Spring Framework 7 migration rather than a version bump.
  • spring-ai 1.1.x pins the 0.18.x line, and 0.18.4 (the latest published) still has the early return.

That is the situation #1085 describes, with two other reporters confirming it on 0.17.0 and 0.18.3.

Change

Three lines in DefaultMcpStatelessServerHandler, identical in effect to #800: return a JSONRPCResponse carrying -32601 Method not found: <method> instead of failing the Mono.

Tests

./mvnw -pl mcp-core test -Dtest=DefaultMcpStatelessServerHandlerTests → 1/1 green.
./mvnw -pl mcp-test test -Dtest=HttpServletStatelessIntegrationTests → 13/13 green.

Note on HTTP status

This restores parity with the stateful session handler and with 2.0.x: unknown method → HTTP 200 with a JSON-RPC -32601 body. The 2026-07-28 Streamable HTTP revision asks for HTTP 404 with -32601, which is what #1083 adds for HttpServletStatelessServerTransport on 2.0.x. That status change is deliberately left out here — it is a separate concern from the 500, the Spring transports each carry their own copy of the status logic and would need the same treatment, and 200 + -32601 is already enough for clients to fall back to the legacy initialize flow.

Happy to open the equivalent PRs against 1.0.x and 1.1.x — both carry the same early return (via McpError.builder(...)), and I have the same change prepared and green on both.

Backport of modelcontextprotocol#800 to the 0.18.x line.

Instead of returning Mono.error with an McpError, DefaultMcpStatelessServerHandler
now returns a JSON-RPC method not found error (-32601), aligned with the stateful
server session handler.

The early return meant the error never reached the onErrorResume below it, so it
escaped to the transport. HttpServletStatelessServerTransport and the Spring
WebMvc/WebFlux stateless transports all map an escaping handler error to HTTP 500,
so any request for a method with no registered handler was answered with a server
error rather than a JSON-RPC error response. Clients probing for optional methods
-- OpenAI's hosted connector sends server/discover before tools/list -- therefore
drove 5xx traffic against otherwise healthy servers.

Adds the unit test from modelcontextprotocol#800 and an integration test that asserts both the HTTP
status and the JSON-RPC error body, since the status is what regressed.

See modelcontextprotocol#1085

@eashwar-mp eashwar-mp left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed the underlying bug from the reporter side in #1085 (0.17.0 via spring-ai 1.1.4, WebMvc stateless transport). I also verified this exact change applies byte-for-byte to 0.17.x: the early-return and all McpSchema APIs it touches are identical there.

The fix reads correctly, it restores parity with the stateful handler (unknown method returns HTTP 200 with a -32601 body) and unblocks the OpenAI connector's server/discover probe. Leaving the HTTP 404 status change to #1083 as a separate concern is the right call for a backport. LGTM.

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.

2 participants