-
Notifications
You must be signed in to change notification settings - Fork 850
Description
Description
When an MCP server returns an SSE response whose body exceeds the Spring WebClient maxInMemorySize buffer limit (default 256KB), the DataBufferLimitException is silently dropped and the callTool() call hangs until the request timeout expires. The caller receives a TimeoutException instead of a meaningful error.
Root Cause
In WebClientStreamableHttpTransport.sendMessage(), for SSE responses, sink.success() is called as soon as the SSE headers arrive — before the body is read:
else if (mediaType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM)) {
logger.debug("Established SSE stream via POST");
sink.success(); // <-- signals success before body is read
return newEventStream(response, sessionRepresentation);
}When the body reading subsequently fails (e.g., DataBufferLimitException from an oversized response), the error flows through:
DefaultMcpTransportStream.consumeSseStream()→doOnErrorsendMessage().onErrorComplete()handler → callshandleException(t)thensink.error(t)sink.error(t)is a no-op becausesink.success()was already called
The pendingResponses entry in McpClientSession is orphaned — no response or error is ever delivered. The callTool() Mono waits indefinitely until requestTimeout.
Note: the same early sink.success() pattern also exists for JSON (APPLICATION_JSON) responses in the same method, so the issue may affect non-SSE responses as well.
Reproduction
- Configure a WebClient with a small
maxInMemorySize(e.g., 256KB default) - Have an MCP server tool return a response larger than that limit
- Call the tool via
McpSyncClient.callTool() - Observe: the call hangs for the full
requestTimeoutduration, then throwsTimeoutException
Expected Behavior
The callTool() call should fail promptly with a meaningful error (e.g., wrapping the DataBufferLimitException) instead of hanging until timeout.
Suggested Fix
Either:
- Don't call
sink.success()until the body is fully consumed, or - Propagate body-reading errors to the
pendingResponsessink inMcpClientSessionvia a separate error path (since theMonoSinkis already terminated)
Environment
io.modelcontextprotocol.sdk:mcp-spring-webflux:0.17.0io.modelcontextprotocol.sdk:mcp-core:0.17.0- Spring Boot 3.5.x
- Spring WebFlux (Reactor Netty)