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
This commit is contained in:
Luca Chang 2026-02-06 01:55:37 -08:00 • committed by GitHub
commit ef8399c1cb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 43 additions and 22 deletions

View file

@ -295,12 +295,23 @@ public class HttpClientStreamableHttpTransport implements McpClientTransport {
if (statusCode >= 200 && statusCode < 300) {
if (MESSAGE_EVENT_TYPE.equals(responseEvent.sseEvent().event())) {
String data = responseEvent.sseEvent().data();
// Per 2025-11-25 spec (SEP-1699), servers may
// send SSE events
// with empty data to prime the client for
// reconnection.
// Skip these events as they contain no JSON-RPC
// message.
if (data == null || data.isBlank()) {
logger.debug("Skipping SSE event with empty data (stream primer)");
return Flux.empty();
}
try {
// We don't support batching ATM and probably
// won't since the next version considers
// removing it.
McpSchema.JSONRPCMessage message = McpSchema.deserializeJsonRpcMessage(
this.jsonMapper, responseEvent.sseEvent().data());
McpSchema.JSONRPCMessage message = McpSchema
.deserializeJsonRpcMessage(this.jsonMapper, data);
Tuple2<Optional<String>, Iterable<McpSchema.JSONRPCMessage>> idWithMessages = Tuples
.of(Optional.ofNullable(responseEvent.sseEvent().id()),
@ -503,13 +514,22 @@ public class HttpClientStreamableHttpTransport implements McpClientTransport {
else if (contentType.contains(TEXT_EVENT_STREAM)) {
return Flux.just(((ResponseSubscribers.SseResponseEvent) responseEvent).sseEvent())
.flatMap(sseEvent -> {
String data = sseEvent.data();
// Per 2025-11-25 spec (SEP-1699), servers may send SSE
// events
// with empty data to prime the client for reconnection.
// Skip these events as they contain no JSON-RPC message.
if (data == null || data.isBlank()) {
logger.debug("Skipping SSE event with empty data (stream primer)");
return Flux.empty();
}
try {
// We don't support batching ATM and probably
// won't
// since the
// next version considers removing it.
McpSchema.JSONRPCMessage message = McpSchema
.deserializeJsonRpcMessage(this.jsonMapper, sseEvent.data());
.deserializeJsonRpcMessage(this.jsonMapper, data);
Tuple2<Optional<String>, Iterable<McpSchema.JSONRPCMessage>> idWithMessages = Tuples
.of(Optional.ofNullable(sseEvent.id()), List.of(message));
@ -641,7 +661,7 @@ public class HttpClientStreamableHttpTransport implements McpClientTransport {
private Duration connectTimeout = Duration.ofSeconds(10);
private List<String> supportedProtocolVersions = List.of(ProtocolVersions.MCP_2024_11_05,
ProtocolVersions.MCP_2025_03_26, ProtocolVersions.MCP_2025_06_18);
ProtocolVersions.MCP_2025_03_26, ProtocolVersions.MCP_2025_06_18, ProtocolVersions.MCP_2025_11_25);
/**
* Creates a new builder with the specified base URI.

View file

@ -171,7 +171,7 @@ public class HttpServletStreamableServerTransportProvider extends HttpServlet
@Override
public List<String> protocolVersions() {
return List.of(ProtocolVersions.MCP_2024_11_05, ProtocolVersions.MCP_2025_03_26,
ProtocolVersions.MCP_2025_06_18);
ProtocolVersions.MCP_2025_06_18, ProtocolVersions.MCP_2025_11_25);
}
@Override

View file

@ -42,7 +42,7 @@ public final class McpSchema {
}
@Deprecated
public static final String LATEST_PROTOCOL_VERSION = ProtocolVersions.MCP_2025_06_18;
public static final String LATEST_PROTOCOL_VERSION = ProtocolVersions.MCP_2025_11_25;
public static final String JSONRPC_VERSION = "2.0";

View file

@ -29,7 +29,8 @@ public interface McpStatelessServerTransport {
Mono<Void> closeGracefully();
default List<String> protocolVersions() {
return List.of(ProtocolVersions.MCP_2025_03_26, ProtocolVersions.MCP_2025_06_18);
return List.of(ProtocolVersions.MCP_2025_03_26, ProtocolVersions.MCP_2025_06_18,
ProtocolVersions.MCP_2025_11_25);
}
}