Skip to content

Commit 98ac45e

Browse files
Resolve HTTP transport merge conflict
2 parents 1cf7903 + 618ff85 commit 98ac45e

1 file changed

Lines changed: 86 additions & 0 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright 2024-2026 the original author or authors.
3+
*/
4+
5+
package io.modelcontextprotocol.client.transport;
6+
7+
import java.io.IOException;
8+
import java.net.InetSocketAddress;
9+
import java.time.Duration;
10+
11+
import org.junit.jupiter.api.AfterAll;
12+
import org.junit.jupiter.api.BeforeAll;
13+
import org.junit.jupiter.api.Test;
14+
import org.junit.jupiter.api.Timeout;
15+
16+
import com.sun.net.httpserver.HttpServer;
17+
18+
import io.modelcontextprotocol.spec.McpSchema;
19+
import io.modelcontextprotocol.spec.McpTransportException;
20+
import io.modelcontextprotocol.spec.ProtocolVersions;
21+
import io.modelcontextprotocol.server.transport.TomcatTestUtil;
22+
import reactor.test.StepVerifier;
23+
24+
import static org.assertj.core.api.Assertions.assertThat;
25+
26+
/**
27+
* Verifies that an {@code application/json} response whose body is not valid JSON fails
28+
* the {@link HttpClientStreamableHttpTransport#sendMessage} mono with the parsing error
29+
* instead of completing it successfully.
30+
*
31+
* <p>
32+
* Completing the delivery sink before deserialization used to swallow the parse failure:
33+
* the {@code McpClientSession} then never received the error, kept the pending response
34+
* entry and the caller only saw a {@code TimeoutException} once the request timeout
35+
* elapsed.
36+
*
37+
* @see <a href="https://github.com/modelcontextprotocol/java-sdk/issues/1147">#1147</a>
38+
*/
39+
public class HttpClientStreamableHttpTransportInvalidJsonResponseTest {
40+
41+
static int PORT = TomcatTestUtil.findAvailablePort();
42+
43+
static String host = "http://localhost:" + PORT;
44+
45+
static HttpServer server;
46+
47+
@BeforeAll
48+
static void startServer() throws IOException {
49+
server = HttpServer.create(new InetSocketAddress(PORT), 0);
50+
51+
// 200 OK with an invalid JSON body for the /mcp endpoint
52+
server.createContext("/mcp", exchange -> {
53+
byte[] body = "{broken".getBytes();
54+
exchange.getResponseHeaders().set("Content-Type", "application/json");
55+
exchange.sendResponseHeaders(200, body.length);
56+
exchange.getResponseBody().write(body);
57+
exchange.close();
58+
});
59+
60+
server.setExecutor(null);
61+
server.start();
62+
}
63+
64+
@AfterAll
65+
static void stopServer() {
66+
server.stop(1);
67+
}
68+
69+
@Test
70+
@Timeout(10)
71+
void testInvalidJsonResponseFailsWithParseError() {
72+
var transport = HttpClientStreamableHttpTransport.builder(host).build();
73+
74+
var initializeRequest = McpSchema.InitializeRequest
75+
.builder(ProtocolVersions.MCP_2025_03_26, McpSchema.ClientCapabilities.builder().roots(true).build(),
76+
McpSchema.Implementation.builder("MCP Client", "0.3.1").build())
77+
.build();
78+
var testMessage = new McpSchema.JSONRPCRequest(McpSchema.METHOD_INITIALIZE, "test-id", initializeRequest);
79+
80+
StepVerifier.create(transport.sendMessage(testMessage)).expectErrorSatisfies(error -> {
81+
// The parse failure must surface as the delivery error, not a timeout
82+
assertThat(error).isInstanceOf(McpTransportException.class);
83+
}).verify(Duration.ofSeconds(5));
84+
}
85+
86+
}

0 commit comments

Comments
 (0)