Fix #5239: tolerate non-SSE 200 OK on streamable-http GET /mcp probe#6345
Closed
redinside-dev wants to merge 1 commit into
Closed
Fix #5239: tolerate non-SSE 200 OK on streamable-http GET /mcp probe#6345redinside-dev wants to merge 1 commit into
redinside-dev wants to merge 1 commit into
Conversation
The MCP streamable-http spec requires the server to return either Content-Type: text/event-stream or HTTP 405 Method Not Allowed on the GET /mcp probe. Some servers (e.g. ModelScope Model API MCP) instead return a plain 200 OK with a non-SSE content type (typically application/json or no Content-Type header) to signal that they do not offer a server-initiated stream. The Spring AI client must treat that as a valid no-stream response and fall back to request-response mode rather than failing initialization with an McpTransportException. The exchangeToFlux handler in WebClientStreamableHttpTransport already handled isEventStream and isNotAllowed, but fell through to the default error path for any other 2xx response, which is what triggered 'McpTransportException: Invalid SSE response' (or its WebFlux equivalent) during the GET probe. Add an isStatelessOk helper that detects a 2xx response that is not an SSE stream and treat it the same as 405: return an empty Flux and let the client continue in request-response mode. Add WebClientStreamableHttpGetProbeIT with three regression tests: - 200 OK with Content-Type: application/json - 200 OK with no Content-Type header - 405 Method Not Allowed (sanity check that the existing well-formed path still works) The first two tests fail on the unmodified code and pass with the fix; the third passes both before and after. Closes spring-projects#5239 Signed-off-by: Anurag Saxena <anuragsaxena.98@gmail.com>
Author
|
Rebased with |
Contributor
|
Declining this one as we don't accept contribution from AI bots. |
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.
Summary
Fix #5239.
The
streamable-httpMCP client fails to initialize against MCP servers that return a plain200 OKwith a non-SSEContent-Type(e.g.application/json) on theGET /mcpprobe. The spec requires the server to return eitherContent-Type: text/event-streamorHTTP 405 Method Not Allowedon the GET probe, but real-world servers (ModelScope Model API MCP is the canonical example) instead return200 OKwith a JSON body or no content type to signal that they do not offer a server-initiated stream. The client must treat that as a valid no-stream response and fall back to request-response mode.Fix
In
WebClientStreamableHttpTransport.reconnect'sexchangeToFluxhandler, add a new branch for "2xx that is not text/event-stream". A new helperisStatelessOk(ClientResponse)returns true for any 2xx that isn't an SSE stream (including 200 withapplication/json, 200 with no content type, 204, etc.). The branch returnsFlux.empty()— the same behavior as the existing 405 path — so the client continues in request-response mode.Tests
New
WebClientStreamableHttpGetProbeITcovers three cases against a localcom.sun.net.httpserver.HttpServer:testGetProbeWith200ApplicationJson—200+Content-Type: application/json(fails on the unmodified code, passes with the fix)testGetProbeWith200NoContentType—200with noContent-Typeheader (fails on the unmodified code, passes with the fix)testGetProbeWith405—405(sanity check; passes before and after)Each test asserts that (1) the server actually received the GET probe, and (2) the transport's exception handler was not invoked. Without the fix, the default
exchangeToFluxelse branch (response.createError()) raises anMcpTransportException, which the background reconnect loop surfaces via the exception handler.The existing
WebClientStreamableHttpTransportErrorHandlingITregression tests continue to pass.Notes
text/event-streamkeep going through the originaleventStreampath.mcp-core0.17.1 workaround mentioned in the issue is a different code path (the JDK HTTP-client-based transport usesResponseSubscribers$SseLineSubscriberdirectly). That variant does not need this fix because it returns its body to the caller as a singleStringand does not require an SSE-shaped response on the GET probe. The fix here covers the WebFlux variant, which is the one that fails per the issue body.