Skip to content

Commit 735a851

Browse files
committed
Remove 2024-11-05 protocol from Streamable HTTP transport
Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
1 parent 39c225e commit 735a851

4 files changed

Lines changed: 115 additions & 75 deletions

File tree

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -763,8 +763,8 @@ public static class Builder {
763763

764764
private Duration connectTimeout = Duration.ofSeconds(10);
765765

766-
private List<String> supportedProtocolVersions = List.of(ProtocolVersions.MCP_2024_11_05,
767-
ProtocolVersions.MCP_2025_03_26, ProtocolVersions.MCP_2025_06_18, ProtocolVersions.MCP_2025_11_25);
766+
private List<String> supportedProtocolVersions = List.of(ProtocolVersions.MCP_2025_03_26,
767+
ProtocolVersions.MCP_2025_06_18, ProtocolVersions.MCP_2025_11_25);
768768

769769
private McpHttpClientTransportAuthorizationErrorHandler authorizationErrorHandler = McpHttpClientTransportAuthorizationErrorHandler.NOOP;
770770

‎mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStreamableServerTransportProvider.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import io.modelcontextprotocol.spec.McpStreamableServerSession;
2525
import io.modelcontextprotocol.spec.McpStreamableServerTransport;
2626
import io.modelcontextprotocol.spec.McpStreamableServerTransportProvider;
27+
import io.modelcontextprotocol.spec.ProtocolVersions;
2728
import io.modelcontextprotocol.util.Assert;
2829
import io.modelcontextprotocol.util.KeepAliveScheduler;
2930
import jakarta.servlet.AsyncContext;
@@ -820,6 +821,12 @@ public void close() {
820821

821822
}
822823

824+
@Override
825+
public List<String> protocolVersions() {
826+
return List.of(ProtocolVersions.MCP_2025_03_26, ProtocolVersions.MCP_2025_06_18,
827+
ProtocolVersions.MCP_2025_11_25);
828+
}
829+
823830
public static Builder builder() {
824831
return new Builder();
825832
}

‎mcp-test/src/test/java/io/modelcontextprotocol/common/HttpClientStreamableHttpVersionNegotiationIntegrationTests.java‎

Lines changed: 102 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,30 @@
1717
import io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider;
1818
import io.modelcontextprotocol.server.transport.McpTestRequestRecordingServletFilter;
1919
import io.modelcontextprotocol.server.transport.TomcatTestUtil;
20+
import io.modelcontextprotocol.spec.McpError;
2021
import io.modelcontextprotocol.spec.McpSchema;
2122
import io.modelcontextprotocol.spec.ProtocolVersions;
2223
import org.apache.catalina.LifecycleException;
2324
import org.apache.catalina.LifecycleState;
2425
import org.apache.catalina.startup.Tomcat;
25-
import org.junit.jupiter.api.AfterEach;
26+
import org.junit.jupiter.api.AfterAll;
27+
import org.junit.jupiter.api.BeforeAll;
28+
import org.junit.jupiter.api.BeforeEach;
2629
import org.junit.jupiter.api.Test;
2730

2831
import static org.assertj.core.api.Assertions.assertThat;
32+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
33+
import static org.assertj.core.api.InstanceOfAssertFactories.type;
2934

3035
class HttpClientStreamableHttpVersionNegotiationIntegrationTests {
3136

32-
private Tomcat tomcat;
37+
private static Tomcat tomcat;
3338

3439
private static final int PORT = TomcatTestUtil.findAvailablePort();
3540

36-
private final McpTestRequestRecordingServletFilter requestRecordingFilter = new McpTestRequestRecordingServletFilter();
41+
private static final McpTestRequestRecordingServletFilter requestRecordingFilter = new McpTestRequestRecordingServletFilter();
3742

38-
private final HttpServletStreamableServerTransportProvider transport = HttpServletStreamableServerTransportProvider
43+
private static final HttpServletStreamableServerTransportProvider transport = HttpServletStreamableServerTransportProvider
3944
.builder()
4045
.contextExtractor(
4146
req -> McpTransportContext.create(Map.of("protocol-version", req.getHeader("MCP-protocol-version"))))
@@ -51,78 +56,13 @@ class HttpClientStreamableHttpVersionNegotiationIntegrationTests {
5156
.isError(false)
5257
.build();
5358

54-
McpSyncServer mcpServer = McpServer.sync(transport)
59+
private final McpSyncServer mcpServer = McpServer.sync(transport)
5560
.capabilities(McpSchema.ServerCapabilities.builder().tools(false).build())
5661
.tools(McpServerFeatures.SyncToolSpecification.builder().tool(toolSpec).callHandler(toolHandler).build())
5762
.build();
5863

59-
@AfterEach
60-
void tearDown() {
61-
stopTomcat();
62-
}
63-
64-
@Test
65-
void usesLatestVersion() {
66-
startTomcat();
67-
68-
var client = McpClient.sync(HttpClientStreamableHttpTransport.builder("http://localhost:" + PORT).build())
69-
.build();
70-
71-
client.initialize();
72-
McpSchema.CallToolResult response = client
73-
.callTool(McpSchema.CallToolRequest.builder("test-tool").arguments(Map.of()).build());
74-
75-
var calls = requestRecordingFilter.getCalls();
76-
77-
assertThat(calls).filteredOn(c -> !c.body().contains("\"method\":\"initialize\""))
78-
// GET /mcp ; POST notification/initialized ; POST tools/call
79-
.hasSize(3)
80-
.map(McpTestRequestRecordingServletFilter.Call::headers)
81-
.allSatisfy(headers -> assertThat(headers).containsEntry("mcp-protocol-version",
82-
ProtocolVersions.MCP_2025_11_25));
83-
84-
assertThat(response).isNotNull();
85-
assertThat(response.content()).hasSize(1)
86-
.first()
87-
.extracting(McpSchema.TextContent.class::cast)
88-
.extracting(McpSchema.TextContent::text)
89-
.isEqualTo(ProtocolVersions.MCP_2025_11_25);
90-
mcpServer.close();
91-
}
92-
93-
@Test
94-
void usesServerSupportedVersion() {
95-
startTomcat();
96-
97-
var transport = HttpClientStreamableHttpTransport.builder("http://localhost:" + PORT)
98-
.supportedProtocolVersions(List.of(ProtocolVersions.MCP_2025_11_25, "2263-03-18"))
99-
.build();
100-
var client = McpClient.sync(transport).build();
101-
102-
client.initialize();
103-
McpSchema.CallToolResult response = client
104-
.callTool(McpSchema.CallToolRequest.builder("test-tool").arguments(Map.of()).build());
105-
106-
var calls = requestRecordingFilter.getCalls();
107-
// Initialize tells the server the Client's latest supported version
108-
// FIXME: Set the correct protocol version on GET /mcp
109-
assertThat(calls).filteredOn(c -> c.method().equals("POST") && !c.body().contains("\"method\":\"initialize\""))
110-
// POST notification/initialized ; POST tools/call
111-
.hasSize(2)
112-
.map(McpTestRequestRecordingServletFilter.Call::headers)
113-
.allSatisfy(headers -> assertThat(headers).containsEntry("mcp-protocol-version",
114-
ProtocolVersions.MCP_2025_11_25));
115-
116-
assertThat(response).isNotNull();
117-
assertThat(response.content()).hasSize(1)
118-
.first()
119-
.extracting(McpSchema.TextContent.class::cast)
120-
.extracting(McpSchema.TextContent::text)
121-
.isEqualTo(ProtocolVersions.MCP_2025_11_25);
122-
mcpServer.close();
123-
}
124-
125-
private void startTomcat() {
64+
@BeforeAll
65+
static void startTomcat() {
12666
tomcat = TomcatTestUtil.createTomcatServer("", PORT, transport, requestRecordingFilter);
12767
try {
12868
tomcat.start();
@@ -133,7 +73,8 @@ private void startTomcat() {
13373
}
13474
}
13575

136-
private void stopTomcat() {
76+
@AfterAll
77+
static void stopTomcat() {
13778
if (tomcat != null) {
13879
try {
13980
tomcat.stop();
@@ -145,4 +86,92 @@ private void stopTomcat() {
14586
}
14687
}
14788

89+
@BeforeEach
90+
void setUp() {
91+
requestRecordingFilter.clear();
92+
}
93+
94+
@Test
95+
void usesLatestVersion() {
96+
try (var client = McpClient.sync(HttpClientStreamableHttpTransport.builder("http://localhost:" + PORT).build())
97+
.build()) {
98+
client.initialize();
99+
McpSchema.CallToolResult response = client
100+
.callTool(McpSchema.CallToolRequest.builder("test-tool").arguments(Map.of()).build());
101+
102+
var calls = requestRecordingFilter.getCalls();
103+
104+
assertThat(calls).filteredOn(c -> !c.body().contains("\"method\":\"initialize\""))
105+
// GET /mcp ; POST notification/initialized ; POST tools/call
106+
.hasSize(3)
107+
.map(McpTestRequestRecordingServletFilter.Call::headers)
108+
.allSatisfy(headers -> assertThat(headers).containsEntry("mcp-protocol-version",
109+
ProtocolVersions.MCP_2025_11_25));
110+
111+
assertThat(response).isNotNull();
112+
assertThat(response.content()).hasSize(1)
113+
.first()
114+
.extracting(McpSchema.TextContent.class::cast)
115+
.extracting(McpSchema.TextContent::text)
116+
.isEqualTo(ProtocolVersions.MCP_2025_11_25);
117+
}
118+
119+
}
120+
121+
@Test
122+
void usesServerSupportedVersion() {
123+
var transport = HttpClientStreamableHttpTransport.builder("http://localhost:" + PORT)
124+
.supportedProtocolVersions(List.of(ProtocolVersions.MCP_2025_11_25, "2263-03-18"))
125+
.build();
126+
try (var client = McpClient.sync(transport).build()) {
127+
client.initialize();
128+
McpSchema.CallToolResult response = client
129+
.callTool(McpSchema.CallToolRequest.builder("test-tool").arguments(Map.of()).build());
130+
131+
var calls = requestRecordingFilter.getCalls();
132+
// Initialize tells the server the Client's latest supported version
133+
// FIXME: Set the correct protocol version on GET /mcp
134+
assertThat(calls)
135+
.filteredOn(c -> c.method().equals("POST") && !c.body().contains("\"method\":\"initialize\""))
136+
// POST notification/initialized ; POST tools/call
137+
.hasSize(2)
138+
.map(McpTestRequestRecordingServletFilter.Call::headers)
139+
.allSatisfy(headers -> assertThat(headers).containsEntry("mcp-protocol-version",
140+
ProtocolVersions.MCP_2025_11_25));
141+
142+
assertThat(response).isNotNull();
143+
assertThat(response.content()).hasSize(1)
144+
.first()
145+
.extracting(McpSchema.TextContent.class::cast)
146+
.extracting(McpSchema.TextContent::text)
147+
.isEqualTo(ProtocolVersions.MCP_2025_11_25);
148+
}
149+
}
150+
151+
@Test
152+
void clientDoesNotSupportProtocolVersion20241105ByDefault() {
153+
var transport = HttpClientStreamableHttpTransport.builder("http://localhost:" + PORT).build();
154+
155+
// Testing it on the wire would require building a custom transport
156+
// We trust the protocolVersions() accessor instead
157+
assertThat(transport.protocolVersions()).containsExactly(ProtocolVersions.MCP_2025_03_26,
158+
ProtocolVersions.MCP_2025_06_18, ProtocolVersions.MCP_2025_11_25);
159+
}
160+
161+
@Test
162+
void serverDoesNotSupportProtocolVersion20241105() {
163+
var clientTransport = HttpClientStreamableHttpTransport.builder("http://localhost:" + PORT)
164+
.supportedProtocolVersions(List.of(ProtocolVersions.MCP_2024_11_05))
165+
.build();
166+
try (var client = McpClient.sync(clientTransport).build()) {
167+
assertThatThrownBy(client::initialize).rootCause()
168+
.isInstanceOf(McpError.class)
169+
.asInstanceOf(type(McpError.class))
170+
.extracting(Throwable::getMessage)
171+
.isEqualTo("Unsupported protocol version");
172+
mcpServer.close();
173+
}
174+
175+
}
176+
148177
}

‎mcp-test/src/test/java/io/modelcontextprotocol/server/transport/McpTestRequestRecordingServletFilter.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ public List<Call> getCalls() {
6060
return List.copyOf(calls);
6161
}
6262

63+
public void clear() {
64+
calls.clear();
65+
}
66+
6367
public record Call(String method, Map<String, String> headers, String body) {
6468

6569
}

0 commit comments

Comments
 (0)