fix: route server-initiated requests via GET SSE in responseStream by armando-sepulveda · Pull Request #890 · modelcontextprotocol/java-sdk
When a client sends a POST request to initiate a tool/handler, the server uses ServerResponse.sse() (WebMVC) which calls request.startAsync(), putting Tomcat's OutputBuffer into 'suspended' async mode. While the request handler is executing (e.g. waiting for a sampling/createMessage response), any writes to the POST SSE transport's OutputBuffer are buffered and never flushed to the network — because Tomcat only flushes the buffer once the async context dispatches (i.e. when the consumer lambda returns). This causes a deadlock for server-initiated requests such as MCP Sampling (sampling/createMessage) and Elicitation: the handler blocks waiting for the client's response, but the client never receives the request because it is stuck in the suspended buffer. The GET SSE stream (listeningStreamRef) does not have this problem: its consumer lambda exits immediately, so Tomcat releases the OutputBuffer and writes from any thread reach the socket immediately. Fix: In responseStream(), construct the McpAsyncServerExchange using McpStreamableServerSession.this (the outer session) as the session instead of the inner McpStreamableServerSessionStream tied to the POST SSE transport. McpStreamableServerSession.sendRequest() already delegates to listeningStreamRef (GET SSE), so server-initiated requests are now correctly sent through the GET SSE channel. Existing integration tests in AbstractMcpClientServerIntegrationTests (testCreateMessageSuccess, testCreateMessageWithRequestTimeoutSuccess) cover this scenario for HttpServletStreamableIntegrationTests.