Skip to content

Commit ef8399c

Browse files
feat: broadcast 2025-11-25 as latest supported client version (#758)
* feat: broadcast 2025-11-25 as latest supported client version * fix: ignore empty SSE events
1 parent 5ed6063 commit ef8399c

File tree

10 files changed

+43
-22
lines changed

10 files changed

+43
-22
lines changed

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

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -295,12 +295,23 @@ private Mono<Disposable> reconnect(McpTransportStream<Disposable> stream) {
295295
if (statusCode >= 200 && statusCode < 300) {
296296

297297
if (MESSAGE_EVENT_TYPE.equals(responseEvent.sseEvent().event())) {
298+
String data = responseEvent.sseEvent().data();
299+
// Per 2025-11-25 spec (SEP-1699), servers may
300+
// send SSE events
301+
// with empty data to prime the client for
302+
// reconnection.
303+
// Skip these events as they contain no JSON-RPC
304+
// message.
305+
if (data == null || data.isBlank()) {
306+
logger.debug("Skipping SSE event with empty data (stream primer)");
307+
return Flux.empty();
308+
}
298309
try {
299310
// We don't support batching ATM and probably
300311
// won't since the next version considers
301312
// removing it.
302-
McpSchema.JSONRPCMessage message = McpSchema.deserializeJsonRpcMessage(
303-
this.jsonMapper, responseEvent.sseEvent().data());
313+
McpSchema.JSONRPCMessage message = McpSchema
314+
.deserializeJsonRpcMessage(this.jsonMapper, data);
304315

305316
Tuple2<Optional<String>, Iterable<McpSchema.JSONRPCMessage>> idWithMessages = Tuples
306317
.of(Optional.ofNullable(responseEvent.sseEvent().id()),
@@ -503,13 +514,22 @@ public Mono<Void> sendMessage(McpSchema.JSONRPCMessage sentMessage) {
503514
else if (contentType.contains(TEXT_EVENT_STREAM)) {
504515
return Flux.just(((ResponseSubscribers.SseResponseEvent) responseEvent).sseEvent())
505516
.flatMap(sseEvent -> {
517+
String data = sseEvent.data();
518+
// Per 2025-11-25 spec (SEP-1699), servers may send SSE
519+
// events
520+
// with empty data to prime the client for reconnection.
521+
// Skip these events as they contain no JSON-RPC message.
522+
if (data == null || data.isBlank()) {
523+
logger.debug("Skipping SSE event with empty data (stream primer)");
524+
return Flux.empty();
525+
}
506526
try {
507527
// We don't support batching ATM and probably
508528
// won't
509529
// since the
510530
// next version considers removing it.
511531
McpSchema.JSONRPCMessage message = McpSchema
512-
.deserializeJsonRpcMessage(this.jsonMapper, sseEvent.data());
532+
.deserializeJsonRpcMessage(this.jsonMapper, data);
513533

514534
Tuple2<Optional<String>, Iterable<McpSchema.JSONRPCMessage>> idWithMessages = Tuples
515535
.of(Optional.ofNullable(sseEvent.id()), List.of(message));
@@ -641,7 +661,7 @@ public static class Builder {
641661
private Duration connectTimeout = Duration.ofSeconds(10);
642662

643663
private List<String> supportedProtocolVersions = List.of(ProtocolVersions.MCP_2024_11_05,
644-
ProtocolVersions.MCP_2025_03_26, ProtocolVersions.MCP_2025_06_18);
664+
ProtocolVersions.MCP_2025_03_26, ProtocolVersions.MCP_2025_06_18, ProtocolVersions.MCP_2025_11_25);
645665

646666
/**
647667
* Creates a new builder with the specified base URI.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ private HttpServletStreamableServerTransportProvider(McpJsonMapper jsonMapper, S
171171
@Override
172172
public List<String> protocolVersions() {
173173
return List.of(ProtocolVersions.MCP_2024_11_05, ProtocolVersions.MCP_2025_03_26,
174-
ProtocolVersions.MCP_2025_06_18);
174+
ProtocolVersions.MCP_2025_06_18, ProtocolVersions.MCP_2025_11_25);
175175
}
176176

177177
@Override

mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ private McpSchema() {
4242
}
4343

4444
@Deprecated
45-
public static final String LATEST_PROTOCOL_VERSION = ProtocolVersions.MCP_2025_06_18;
45+
public static final String LATEST_PROTOCOL_VERSION = ProtocolVersions.MCP_2025_11_25;
4646

4747
public static final String JSONRPC_VERSION = "2.0";
4848

mcp-core/src/main/java/io/modelcontextprotocol/spec/McpStatelessServerTransport.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ default void close() {
2929
Mono<Void> closeGracefully();
3030

3131
default List<String> protocolVersions() {
32-
return List.of(ProtocolVersions.MCP_2025_03_26, ProtocolVersions.MCP_2025_06_18);
32+
return List.of(ProtocolVersions.MCP_2025_03_26, ProtocolVersions.MCP_2025_06_18,
33+
ProtocolVersions.MCP_2025_11_25);
3334
}
3435

3536
}

mcp-core/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ void testRequestCustomizer() throws URISyntaxException {
9090

9191
// Verify the customizer was called
9292
verify(mockRequestCustomizer, atLeastOnce()).customize(any(), eq("POST"), eq(uri), eq(
93-
"{\"jsonrpc\":\"2.0\",\"method\":\"initialize\",\"id\":\"test-id\",\"params\":{\"protocolVersion\":\"2025-06-18\",\"capabilities\":{\"roots\":{\"listChanged\":true}},\"clientInfo\":{\"name\":\"MCP Client\",\"version\":\"0.3.1\"}}}"),
93+
"{\"jsonrpc\":\"2.0\",\"method\":\"initialize\",\"id\":\"test-id\",\"params\":{\"protocolVersion\":\"2025-11-25\",\"capabilities\":{\"roots\":{\"listChanged\":true}},\"clientInfo\":{\"name\":\"MCP Client\",\"version\":\"0.3.1\"}}}"),
9494
eq(context));
9595
});
9696
}
@@ -120,7 +120,7 @@ void testAsyncRequestCustomizer() throws URISyntaxException {
120120

121121
// Verify the customizer was called
122122
verify(mockRequestCustomizer, atLeastOnce()).customize(any(), eq("POST"), eq(uri), eq(
123-
"{\"jsonrpc\":\"2.0\",\"method\":\"initialize\",\"id\":\"test-id\",\"params\":{\"protocolVersion\":\"2025-06-18\",\"capabilities\":{\"roots\":{\"listChanged\":true}},\"clientInfo\":{\"name\":\"MCP Client\",\"version\":\"0.3.1\"}}}"),
123+
"{\"jsonrpc\":\"2.0\",\"method\":\"initialize\",\"id\":\"test-id\",\"params\":{\"protocolVersion\":\"2025-11-25\",\"capabilities\":{\"roots\":{\"listChanged\":true}},\"clientInfo\":{\"name\":\"MCP Client\",\"version\":\"0.3.1\"}}}"),
124124
eq(context));
125125
});
126126
}

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,14 @@ void usesLatestVersion() {
7777
.hasSize(3)
7878
.map(McpTestRequestRecordingServletFilter.Call::headers)
7979
.allSatisfy(headers -> assertThat(headers).containsEntry("mcp-protocol-version",
80-
ProtocolVersions.MCP_2025_06_18));
80+
ProtocolVersions.MCP_2025_11_25));
8181

8282
assertThat(response).isNotNull();
8383
assertThat(response.content()).hasSize(1)
8484
.first()
8585
.extracting(McpSchema.TextContent.class::cast)
8686
.extracting(McpSchema.TextContent::text)
87-
.isEqualTo(ProtocolVersions.MCP_2025_06_18);
87+
.isEqualTo(ProtocolVersions.MCP_2025_11_25);
8888
mcpServer.close();
8989
}
9090

@@ -93,7 +93,7 @@ void usesServerSupportedVersion() {
9393
startTomcat();
9494

9595
var transport = HttpClientStreamableHttpTransport.builder("http://localhost:" + PORT)
96-
.supportedProtocolVersions(List.of(ProtocolVersions.MCP_2025_06_18, "2263-03-18"))
96+
.supportedProtocolVersions(List.of(ProtocolVersions.MCP_2025_11_25, "2263-03-18"))
9797
.build();
9898
var client = McpClient.sync(transport).build();
9999

@@ -108,14 +108,14 @@ void usesServerSupportedVersion() {
108108
.hasSize(2)
109109
.map(McpTestRequestRecordingServletFilter.Call::headers)
110110
.allSatisfy(headers -> assertThat(headers).containsEntry("mcp-protocol-version",
111-
ProtocolVersions.MCP_2025_06_18));
111+
ProtocolVersions.MCP_2025_11_25));
112112

113113
assertThat(response).isNotNull();
114114
assertThat(response.content()).hasSize(1)
115115
.first()
116116
.extracting(McpSchema.TextContent.class::cast)
117117
.extracting(McpSchema.TextContent::text)
118-
.isEqualTo(ProtocolVersions.MCP_2025_06_18);
118+
.isEqualTo(ProtocolVersions.MCP_2025_11_25);
119119
mcpServer.close();
120120
}
121121

mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebClientStreamableHttpTransport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ public static class Builder {
518518
private boolean openConnectionOnStartup = false;
519519

520520
private List<String> supportedProtocolVersions = List.of(ProtocolVersions.MCP_2024_11_05,
521-
ProtocolVersions.MCP_2025_03_26, ProtocolVersions.MCP_2025_06_18);
521+
ProtocolVersions.MCP_2025_03_26, ProtocolVersions.MCP_2025_06_18, ProtocolVersions.MCP_2025_11_25);
522522

523523
private Builder(WebClient.Builder webClientBuilder) {
524524
Assert.notNull(webClientBuilder, "WebClient.Builder must not be null");

mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxStreamableServerTransportProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ private WebFluxStreamableServerTransportProvider(McpJsonMapper jsonMapper, Strin
106106
@Override
107107
public List<String> protocolVersions() {
108108
return List.of(ProtocolVersions.MCP_2024_11_05, ProtocolVersions.MCP_2025_03_26,
109-
ProtocolVersions.MCP_2025_06_18);
109+
ProtocolVersions.MCP_2025_06_18, ProtocolVersions.MCP_2025_11_25);
110110
}
111111

112112
@Override

mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/WebFluxStreamableHttpVersionNegotiationIntegrationTests.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,22 +105,22 @@ void usesLatestVersion() {
105105
.hasSize(3)
106106
.map(McpTestRequestRecordingExchangeFilterFunction.Call::headers)
107107
.allSatisfy(headers -> assertThat(headers).containsEntry("mcp-protocol-version",
108-
ProtocolVersions.MCP_2025_06_18));
108+
ProtocolVersions.MCP_2025_11_25));
109109

110110
assertThat(response).isNotNull();
111111
assertThat(response.content()).hasSize(1)
112112
.first()
113113
.extracting(McpSchema.TextContent.class::cast)
114114
.extracting(McpSchema.TextContent::text)
115-
.isEqualTo(ProtocolVersions.MCP_2025_06_18);
115+
.isEqualTo(ProtocolVersions.MCP_2025_11_25);
116116
mcpServer.close();
117117
}
118118

119119
@Test
120120
void usesServerSupportedVersion() {
121121
var transport = WebClientStreamableHttpTransport
122122
.builder(WebClient.builder().baseUrl("http://localhost:" + PORT))
123-
.supportedProtocolVersions(List.of(ProtocolVersions.MCP_2025_06_18, "2263-03-18"))
123+
.supportedProtocolVersions(List.of(ProtocolVersions.MCP_2025_11_25, "2263-03-18"))
124124
.build();
125125
var client = McpClient.sync(transport).requestTimeout(Duration.ofHours(10)).build();
126126

@@ -137,14 +137,14 @@ void usesServerSupportedVersion() {
137137
.hasSize(2)
138138
.map(McpTestRequestRecordingExchangeFilterFunction.Call::headers)
139139
.allSatisfy(headers -> assertThat(headers).containsEntry("mcp-protocol-version",
140-
ProtocolVersions.MCP_2025_06_18));
140+
ProtocolVersions.MCP_2025_11_25));
141141

142142
assertThat(response).isNotNull();
143143
assertThat(response.content()).hasSize(1)
144144
.first()
145145
.extracting(McpSchema.TextContent.class::cast)
146146
.extracting(McpSchema.TextContent::text)
147-
.isEqualTo(ProtocolVersions.MCP_2025_06_18);
147+
.isEqualTo(ProtocolVersions.MCP_2025_11_25);
148148
mcpServer.close();
149149
}
150150

mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcStreamableServerTransportProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ private WebMvcStreamableServerTransportProvider(McpJsonMapper jsonMapper, String
157157
@Override
158158
public List<String> protocolVersions() {
159159
return List.of(ProtocolVersions.MCP_2024_11_05, ProtocolVersions.MCP_2025_03_26,
160-
ProtocolVersions.MCP_2025_06_18);
160+
ProtocolVersions.MCP_2025_06_18, ProtocolVersions.MCP_2025_11_25);
161161
}
162162

163163
@Override

0 commit comments

Comments
 (0)