Skip to content

Commit 128c06f

Browse files
Avoid opening SSE stream after non-success POST response
markInitialized() and reconnect() ran before the status check, so a 405 from a server without Streamable HTTP still triggered a GET and left a duplicate SSE session during transport fallback. Only do this on 2xx. Fixes #773.
1 parent c7fef64 commit 128c06f

2 files changed

Lines changed: 25 additions & 8 deletions

File tree

‎mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransport.java‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -574,17 +574,18 @@ public Mono<Void> sendMessage(McpSchema.JSONRPCMessage sentMessage) {
574574
"Authorization error when sending message", requestSnapshot, responseEvent.responseInfo()));
575575
}
576576

577-
if (transportSession.markInitialized(
578-
responseEvent.responseInfo().headers().firstValue("mcp-session-id").orElseGet(() -> null))) {
579-
// Once we have a session, we try to open an async stream for
580-
// the server to send notifications and requests out-of-band.
581-
582-
reconnect(null).contextWrite(deliveredSink.contextView()).subscribe();
583-
}
584-
585577
String sessionRepresentation = sessionIdOrPlaceholder(transportSession);
586578

587579
if (statusCode >= 200 && statusCode < 300) {
580+
if (transportSession.markInitialized(responseEvent.responseInfo()
581+
.headers()
582+
.firstValue("mcp-session-id")
583+
.orElseGet(() -> null))) {
584+
// Once we have a session, we try to open an async stream
585+
// for the server to send notifications and requests
586+
// out-of-band.
587+
reconnect(null).contextWrite(deliveredSink.contextView()).subscribe();
588+
}
588589

589590
String contentType = responseEvent.responseInfo()
590591
.headers()

‎mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportErrorHandlingTest.java‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,22 @@ void test405OnConnectReturnsEmptyFlux() {
389389
StepVerifier.create(transport.closeGracefully()).verifyComplete();
390390
}
391391

392+
@Test
393+
void test405OnSendMessageDoesNotOpenSseConnection() {
394+
serverResponseStatus.set(405);
395+
currentServerSessionId.set("ignored-session-id");
396+
397+
StepVerifier.create(transport.sendMessage(createTestRequestMessage()))
398+
.expectErrorMatches(
399+
error -> error instanceof McpTransportException && error.getMessage().contains("Status code: 405"))
400+
.verify();
401+
402+
Awaitility.await()
403+
.during(Duration.ofMillis(300))
404+
.atMost(Duration.ofSeconds(1))
405+
.untilAsserted(() -> assertThat(processedSseConnectCount.get()).isZero());
406+
}
407+
392408
@Nested
393409
class AuthorizationError {
394410

0 commit comments

Comments
 (0)