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
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