diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpStreamableServerTransportProvider.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpStreamableServerTransportProvider.java index 09fe9fb0e..467a432c8 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpStreamableServerTransportProvider.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpStreamableServerTransportProvider.java @@ -4,6 +4,8 @@ package io.modelcontextprotocol.spec; +import java.util.List; + import reactor.core.publisher.Mono; /** @@ -66,4 +68,16 @@ default void close() { */ Mono closeGracefully(); + /** + * Streamable HTTP was introduced in protocol version {@code 2025-03-26}, so providers + * of this transport cannot serve {@code 2024-11-05} clients and must not advertise + * that version. + * @return the protocol versions supported by Streamable HTTP transport providers + */ + @Override + default List protocolVersions() { + return List.of(ProtocolVersions.MCP_2025_03_26, ProtocolVersions.MCP_2025_06_18, + ProtocolVersions.MCP_2025_11_25); + } + } diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/spec/ServerTransportProtocolVersionsTests.java b/mcp-core/src/test/java/io/modelcontextprotocol/spec/ServerTransportProtocolVersionsTests.java new file mode 100644 index 000000000..ec4fc96e1 --- /dev/null +++ b/mcp-core/src/test/java/io/modelcontextprotocol/spec/ServerTransportProtocolVersionsTests.java @@ -0,0 +1,81 @@ +/* + * Copyright 2024-2025 the original author or authors. + */ + +package io.modelcontextprotocol.spec; + +import java.util.List; + +import io.modelcontextprotocol.server.McpStatelessServerHandler; +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Mono; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Verifies that each server transport abstraction advertises only the protocol versions + * it can actually serve. + */ +class ServerTransportProtocolVersionsTests { + + private static final List STREAMABLE_HTTP_VERSIONS = List.of(ProtocolVersions.MCP_2025_03_26, + ProtocolVersions.MCP_2025_06_18, ProtocolVersions.MCP_2025_11_25); + + private static final McpStreamableServerTransportProvider STREAMABLE_PROVIDER = new McpStreamableServerTransportProvider() { + @Override + public void setSessionFactory(McpStreamableServerSession.Factory sessionFactory) { + } + + @Override + public Mono notifyClients(String method, Object params) { + return Mono.empty(); + } + + @Override + public Mono closeGracefully() { + return Mono.empty(); + } + }; + + private static final McpStatelessServerTransport STATELESS_TRANSPORT = new McpStatelessServerTransport() { + @Override + public void setMcpHandler(McpStatelessServerHandler mcpHandler) { + } + + @Override + public Mono closeGracefully() { + return Mono.empty(); + } + }; + + @Test + void streamableProviderDoesNotAdvertiseVersionsPredatingStreamableHttp() { + assertThat(STREAMABLE_PROVIDER.protocolVersions()).doesNotContain(ProtocolVersions.MCP_2024_11_05) + .containsExactlyElementsOf(STREAMABLE_HTTP_VERSIONS); + } + + @Test + void statelessTransportDoesNotAdvertiseVersionsPredatingStreamableHttp() { + assertThat(STATELESS_TRANSPORT.protocolVersions()).doesNotContain(ProtocolVersions.MCP_2024_11_05) + .containsExactlyElementsOf(STREAMABLE_HTTP_VERSIONS); + } + + @Test + void transportsWithoutStreamableHttpConstraintKeepTheFullRange() { + McpServerTransportProviderBase base = new McpServerTransportProviderBase() { + @Override + public Mono notifyClients(String method, Object params) { + return Mono.empty(); + } + + @Override + public Mono closeGracefully() { + return Mono.empty(); + } + }; + + assertThat(base.protocolVersions()).containsExactly(ProtocolVersions.MCP_2024_11_05, + ProtocolVersions.MCP_2025_03_26, ProtocolVersions.MCP_2025_06_18, ProtocolVersions.MCP_2025_11_25); + } + +}