diff --git a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebClientStreamableHttpTransport.java b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebClientStreamableHttpTransport.java index 6fa76cc..d7f7f9b 100644 --- a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebClientStreamableHttpTransport.java +++ b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebClientStreamableHttpTransport.java @@ -23,6 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.modelcontextprotocol.spec.DefaultMcpTransportSession; import io.modelcontextprotocol.spec.DefaultMcpTransportStream; +import io.modelcontextprotocol.spec.HttpHeaders; import io.modelcontextprotocol.spec.McpClientTransport; import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; @@ -66,6 +67,8 @@ public class WebClientStreamableHttpTransport implements McpClientTransport { private static final Logger logger = LoggerFactory.getLogger(WebClientStreamableHttpTransport.class); + private static final String MCP_PROTOCOL_VERSION = "2025-03-26"; + private static final String DEFAULT_ENDPOINT = "/mcp"; /** @@ -103,6 +106,11 @@ public class WebClientStreamableHttpTransport implements McpClientTransport { this.activeSession.set(createTransportSession()); } + @Override + public String protocolVersion() { + return MCP_PROTOCOL_VERSION; + } + /** * Create a stateful builder for creating {@link WebClientStreamableHttpTransport} * instances. @@ -128,12 +136,20 @@ public class WebClientStreamableHttpTransport implements McpClientTransport { private DefaultMcpTransportSession createTransportSession() { Function> onClose = sessionId -> sessionId == null ? Mono.empty() - : webClient.delete().uri(this.endpoint).headers(httpHeaders -> { - httpHeaders.add("mcp-session-id", sessionId); - }).retrieve().toBodilessEntity().onErrorComplete(e -> { - logger.warn("Got error when closing transport", e); - return true; - }).then(); + : webClient.delete() + .uri(this.endpoint) + .header(HttpHeaders.PROTOCOL_VERSION, MCP_PROTOCOL_VERSION) + .headers(httpHeaders -> { + httpHeaders.add(HttpHeaders.MCP_SESSION_ID, sessionId); + httpHeaders.add(HttpHeaders.PROTOCOL_VERSION, MCP_PROTOCOL_VERSION); + }) + .retrieve() + .toBodilessEntity() + .onErrorComplete(e -> { + logger.warn("Got error when closing transport", e); + return true; + }) + .then(); return new DefaultMcpTransportSession(onClose); } @@ -186,10 +202,11 @@ public class WebClientStreamableHttpTransport implements McpClientTransport { Disposable connection = webClient.get() .uri(this.endpoint) .accept(MediaType.TEXT_EVENT_STREAM) + .header(HttpHeaders.PROTOCOL_VERSION, MCP_PROTOCOL_VERSION) .headers(httpHeaders -> { - transportSession.sessionId().ifPresent(id -> httpHeaders.add("mcp-session-id", id)); + transportSession.sessionId().ifPresent(id -> httpHeaders.add(HttpHeaders.MCP_SESSION_ID, id)); if (stream != null) { - stream.lastId().ifPresent(id -> httpHeaders.add("last-event-id", id)); + stream.lastId().ifPresent(id -> httpHeaders.add(HttpHeaders.LAST_EVENT_ID, id)); } }) .exchangeToFlux(response -> { @@ -246,13 +263,14 @@ public class WebClientStreamableHttpTransport implements McpClientTransport { Disposable connection = webClient.post() .uri(this.endpoint) .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_EVENT_STREAM) + .header(HttpHeaders.PROTOCOL_VERSION, MCP_PROTOCOL_VERSION) .headers(httpHeaders -> { - transportSession.sessionId().ifPresent(id -> httpHeaders.add("mcp-session-id", id)); + transportSession.sessionId().ifPresent(id -> httpHeaders.add(HttpHeaders.MCP_SESSION_ID, id)); }) .bodyValue(message) .exchangeToFlux(response -> { if (transportSession - .markInitialized(response.headers().asHttpHeaders().getFirst("mcp-session-id"))) { + .markInitialized(response.headers().asHttpHeaders().getFirst(HttpHeaders.MCP_SESSION_ID))) { // Once we have a session, we try to open an async stream for // the server to send notifications and requests out-of-band. reconnect(null).contextWrite(sink.contextView()).subscribe(); diff --git a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebFluxSseClientTransport.java b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebFluxSseClientTransport.java index 59385b5..fe6b07a 100644 --- a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebFluxSseClientTransport.java +++ b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebFluxSseClientTransport.java @@ -9,6 +9,8 @@ import java.util.function.Function; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; + +import io.modelcontextprotocol.spec.HttpHeaders; import io.modelcontextprotocol.spec.McpClientTransport; import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; @@ -62,6 +64,8 @@ public class WebFluxSseClientTransport implements McpClientTransport { private static final Logger logger = LoggerFactory.getLogger(WebFluxSseClientTransport.class); + private static final String MCP_PROTOCOL_VERSION = "2024-11-05"; + /** * Event type for JSON-RPC messages received through the SSE connection. The server * sends messages with this event type to transmit JSON-RPC protocol data. @@ -166,6 +170,11 @@ public class WebFluxSseClientTransport implements McpClientTransport { this.sseEndpoint = sseEndpoint; } + @Override + public String protocolVersion() { + return MCP_PROTOCOL_VERSION; + } + /** * Establishes a connection to the MCP server using Server-Sent Events (SSE). This * method initiates the SSE connection and sets up the message processing pipeline. @@ -250,6 +259,7 @@ public class WebFluxSseClientTransport implements McpClientTransport { return webClient.post() .uri(messageEndpointUri) .contentType(MediaType.APPLICATION_JSON) + .header(HttpHeaders.PROTOCOL_VERSION, MCP_PROTOCOL_VERSION) .bodyValue(jsonText) .retrieve() .toBodilessEntity() @@ -282,6 +292,7 @@ public class WebFluxSseClientTransport implements McpClientTransport { .get() .uri(this.sseEndpoint) .accept(MediaType.TEXT_EVENT_STREAM) + .header(HttpHeaders.PROTOCOL_VERSION, MCP_PROTOCOL_VERSION) .retrieve() .bodyToFlux(SSE_TYPE) .retryWhen(Retry.from(retrySignal -> retrySignal.handle(inboundRetryHandler))); diff --git a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxSseServerTransportProvider.java b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxSseServerTransportProvider.java index b1b5246..67810fb 100644 --- a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxSseServerTransportProvider.java +++ b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxSseServerTransportProvider.java @@ -79,6 +79,8 @@ public class WebFluxSseServerTransportProvider implements McpServerTransportProv */ public static final String ENDPOINT_EVENT_TYPE = "endpoint"; + private static final String MCP_PROTOCOL_VERSION = "2025-06-18"; + /** * Default SSE endpoint path as specified by the MCP transport specification. */ @@ -212,6 +214,11 @@ public class WebFluxSseServerTransportProvider implements McpServerTransportProv } } + @Override + public String protocolVersion() { + return "2024-11-05"; + } + @Override public void setSessionFactory(McpServerSession.Factory sessionFactory) { this.sessionFactory = sessionFactory; diff --git a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxStatelessServerTransport.java b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxStatelessServerTransport.java index e75e926..c514f2d 100644 --- a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxStatelessServerTransport.java +++ b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxStatelessServerTransport.java @@ -21,7 +21,6 @@ import reactor.core.publisher.Mono; import java.io.IOException; import java.util.List; -import java.util.function.Function; /** * Implementation of a WebFlux based {@link McpStatelessServerTransport}. diff --git a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxStreamableServerTransportProvider.java b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxStreamableServerTransportProvider.java index 79224a5..00ec68c 100644 --- a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxStreamableServerTransportProvider.java +++ b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxStreamableServerTransportProvider.java @@ -95,6 +95,11 @@ public class WebFluxStreamableServerTransportProvider implements McpStreamableSe } + @Override + public String protocolVersion() { + return "2025-03-26"; + } + @Override public void setSessionFactory(McpStreamableServerSession.Factory sessionFactory) { this.sessionFactory = sessionFactory; diff --git a/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcSseServerTransportProvider.java b/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcSseServerTransportProvider.java index b90f9fb..a389800 100644 --- a/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcSseServerTransportProvider.java +++ b/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcSseServerTransportProvider.java @@ -209,6 +209,11 @@ public class WebMvcSseServerTransportProvider implements McpServerTransportProvi } } + @Override + public String protocolVersion() { + return "2024-11-05"; + } + @Override public void setSessionFactory(McpServerSession.Factory sessionFactory) { this.sessionFactory = sessionFactory; diff --git a/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcStreamableServerTransportProvider.java b/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcStreamableServerTransportProvider.java index 391aa3e..2f94d5c 100644 --- a/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcStreamableServerTransportProvider.java +++ b/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcStreamableServerTransportProvider.java @@ -147,6 +147,11 @@ public class WebMvcStreamableServerTransportProvider implements McpStreamableSer } } + @Override + public String protocolVersion() { + return "2025-03-26"; + } + @Override public void setSessionFactory(McpStreamableServerSession.Factory sessionFactory) { this.sessionFactory = sessionFactory; diff --git a/mcp-spring/mcp-spring-webmvc/src/test/java/io/modelcontextprotocol/server/WebMvcSseCustomContextPathTests.java b/mcp-spring/mcp-spring-webmvc/src/test/java/io/modelcontextprotocol/server/WebMvcSseCustomContextPathTests.java index 1b5218c..cce36d1 100644 --- a/mcp-spring/mcp-spring-webmvc/src/test/java/io/modelcontextprotocol/server/WebMvcSseCustomContextPathTests.java +++ b/mcp-spring/mcp-spring-webmvc/src/test/java/io/modelcontextprotocol/server/WebMvcSseCustomContextPathTests.java @@ -91,8 +91,15 @@ class WebMvcSseCustomContextPathTests { @Bean public WebMvcSseServerTransportProvider webMvcSseServerTransportProvider() { - return new WebMvcSseServerTransportProvider(new ObjectMapper(), CUSTOM_CONTEXT_PATH, MESSAGE_ENDPOINT, - WebMvcSseServerTransportProvider.DEFAULT_SSE_ENDPOINT); + return WebMvcSseServerTransportProvider.builder() + .objectMapper(new ObjectMapper()) + .baseUrl(CUSTOM_CONTEXT_PATH) + .messageEndpoint(MESSAGE_ENDPOINT) + .sseEndpoint(WebMvcSseServerTransportProvider.DEFAULT_SSE_ENDPOINT) + .build(); + // return new WebMvcSseServerTransportProvider(new ObjectMapper(), + // CUSTOM_CONTEXT_PATH, MESSAGE_ENDPOINT, + // WebMvcSseServerTransportProvider.DEFAULT_SSE_ENDPOINT); } @Bean diff --git a/mcp/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java b/mcp/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java index 22c0ee1..7376512 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java +++ b/mcp/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java @@ -272,9 +272,8 @@ public class McpAsyncClient { asyncProgressNotificationHandler(progressConsumersFinal)); this.initializer = new LifecycleInitializer(clientCapabilities, clientInfo, - List.of(McpSchema.LATEST_PROTOCOL_VERSION), initializationTimeout, - ctx -> new McpClientSession(requestTimeout, transport, requestHandlers, notificationHandlers, - con -> con.contextWrite(ctx))); + List.of(transport.protocolVersion()), initializationTimeout, ctx -> new McpClientSession(requestTimeout, + transport, requestHandlers, notificationHandlers, con -> con.contextWrite(ctx))); this.transport.setExceptionHandler(this.initializer::handleException); } diff --git a/mcp/src/main/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransport.java b/mcp/src/main/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransport.java index 39fb0d4..3fe88fe 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransport.java +++ b/mcp/src/main/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransport.java @@ -61,6 +61,10 @@ import reactor.core.publisher.Sinks; */ public class HttpClientSseClientTransport implements McpClientTransport { + private static final String MCP_PROTOCOL_VERSION = "2024-11-05"; + + private static final String MCP_PROTOCOL_VERSION_HEADER_NAME = "MCP-Protocol-Version"; + private static final Logger logger = LoggerFactory.getLogger(HttpClientSseClientTransport.class); /** SSE event type for JSON-RPC messages */ @@ -211,6 +215,11 @@ public class HttpClientSseClientTransport implements McpClientTransport { this.httpRequestCustomizer = httpRequestCustomizer; } + @Override + public String protocolVersion() { + return MCP_PROTOCOL_VERSION; + } + /** * Creates a new builder for {@link HttpClientSseClientTransport}. * @param baseUri the base URI of the MCP server @@ -391,6 +400,7 @@ public class HttpClientSseClientTransport implements McpClientTransport { .uri(uri) .header("Accept", "text/event-stream") .header("Cache-Control", "no-cache") + .header(MCP_PROTOCOL_VERSION_HEADER_NAME, MCP_PROTOCOL_VERSION) .GET(); return Mono.from(this.httpRequestCustomizer.customize(builder, "GET", uri, null)); }).flatMap(requestBuilder -> Mono.create(sink -> { @@ -516,7 +526,10 @@ public class HttpClientSseClientTransport implements McpClientTransport { private Mono> sendHttpPost(final String endpoint, final String body) { final URI requestUri = Utils.resolveUri(baseUri, endpoint); return Mono.defer(() -> { - var builder = this.requestBuilder.copy().uri(requestUri).POST(HttpRequest.BodyPublishers.ofString(body)); + var builder = this.requestBuilder.copy() + .uri(requestUri) + .header(MCP_PROTOCOL_VERSION_HEADER_NAME, MCP_PROTOCOL_VERSION) + .POST(HttpRequest.BodyPublishers.ofString(body)); return Mono.from(this.httpRequestCustomizer.customize(builder, "POST", requestUri, body)); }).flatMap(customizedBuilder -> { var request = customizedBuilder.build(); diff --git a/mcp/src/main/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransport.java b/mcp/src/main/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransport.java index 7997165..dadb09a 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransport.java +++ b/mcp/src/main/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransport.java @@ -28,6 +28,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.modelcontextprotocol.client.transport.ResponseSubscribers.ResponseEvent; import io.modelcontextprotocol.spec.DefaultMcpTransportSession; import io.modelcontextprotocol.spec.DefaultMcpTransportStream; +import io.modelcontextprotocol.spec.HttpHeaders; import io.modelcontextprotocol.spec.McpClientTransport; import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; @@ -72,6 +73,8 @@ public class HttpClientStreamableHttpTransport implements McpClientTransport { private static final Logger logger = LoggerFactory.getLogger(HttpClientStreamableHttpTransport.class); + private static final String MCP_PROTOCOL_VERSION = "2025-03-26"; + private static final String DEFAULT_ENDPOINT = "/mcp"; /** @@ -131,6 +134,11 @@ public class HttpClientStreamableHttpTransport implements McpClientTransport { this.httpRequestCustomizer = httpRequestCustomizer; } + @Override + public String protocolVersion() { + return MCP_PROTOCOL_VERSION; + } + public static Builder builder(String baseUri) { return new Builder(baseUri); } @@ -157,12 +165,14 @@ public class HttpClientStreamableHttpTransport implements McpClientTransport { } private Publisher createDelete(String sessionId) { + var uri = Utils.resolveUri(this.baseUri, this.endpoint); return Mono.defer(() -> { var builder = this.requestBuilder.copy() .uri(uri) .header("Cache-Control", "no-cache") - .header("mcp-session-id", sessionId) + .header(HttpHeaders.MCP_SESSION_ID, sessionId) + .header(HttpHeaders.PROTOCOL_VERSION, MCP_PROTOCOL_VERSION) .DELETE(); return Mono.from(this.httpRequestCustomizer.customize(builder, "DELETE", uri, null)); }).flatMap(requestBuilder -> { @@ -221,16 +231,18 @@ public class HttpClientStreamableHttpTransport implements McpClientTransport { HttpRequest.Builder requestBuilder = this.requestBuilder.copy(); if (transportSession != null && transportSession.sessionId().isPresent()) { - requestBuilder = requestBuilder.header("mcp-session-id", transportSession.sessionId().get()); + requestBuilder = requestBuilder.header(HttpHeaders.MCP_SESSION_ID, + transportSession.sessionId().get()); } if (stream != null && stream.lastId().isPresent()) { - requestBuilder = requestBuilder.header("last-event-id", stream.lastId().get()); + requestBuilder = requestBuilder.header(HttpHeaders.LAST_EVENT_ID, stream.lastId().get()); } var builder = requestBuilder.uri(uri) .header("Accept", TEXT_EVENT_STREAM) .header("Cache-Control", "no-cache") + .header(HttpHeaders.PROTOCOL_VERSION, MCP_PROTOCOL_VERSION) .GET(); return Mono.from(this.httpRequestCustomizer.customize(builder, "GET", uri, null)); }) @@ -377,13 +389,15 @@ public class HttpClientStreamableHttpTransport implements McpClientTransport { HttpRequest.Builder requestBuilder = this.requestBuilder.copy(); if (transportSession != null && transportSession.sessionId().isPresent()) { - requestBuilder = requestBuilder.header("mcp-session-id", transportSession.sessionId().get()); + requestBuilder = requestBuilder.header(HttpHeaders.MCP_SESSION_ID, + transportSession.sessionId().get()); } var builder = requestBuilder.uri(uri) .header("Accept", APPLICATION_JSON + ", " + TEXT_EVENT_STREAM) .header("Content-Type", APPLICATION_JSON) .header("Cache-Control", "no-cache") + .header(HttpHeaders.PROTOCOL_VERSION, MCP_PROTOCOL_VERSION) .POST(HttpRequest.BodyPublishers.ofString(jsonBody)); return Mono.from(this.httpRequestCustomizer.customize(builder, "GET", uri, jsonBody)); }).flatMapMany(requestBuilder -> Flux.create(responseEventSink -> { diff --git a/mcp/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java b/mcp/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java index 1b3eee3..9605fb3 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java +++ b/mcp/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java @@ -115,7 +115,7 @@ public class McpAsyncServer { private final ConcurrentHashMap completions = new ConcurrentHashMap<>(); - private List protocolVersions = List.of(McpSchema.LATEST_PROTOCOL_VERSION); + private List protocolVersions; private McpUriTemplateManagerFactory uriTemplateManagerFactory = new DeafaultMcpUriTemplateManagerFactory(); @@ -145,6 +145,8 @@ public class McpAsyncServer { Map> requestHandlers = prepareRequestHandlers(); Map notificationHandlers = prepareNotificationHandlers(features); + this.protocolVersions = List.of(mcpTransportProvider.protocolVersion()); + mcpTransportProvider.setSessionFactory(transport -> new McpServerSession(UUID.randomUUID().toString(), requestTimeout, transport, this::asyncInitializeRequestHandler, requestHandlers, notificationHandlers)); } @@ -168,6 +170,8 @@ public class McpAsyncServer { Map> requestHandlers = prepareRequestHandlers(); Map notificationHandlers = prepareNotificationHandlers(features); + this.protocolVersions = List.of(mcpTransportProvider.protocolVersion()); + mcpTransportProvider.setSessionFactory(new DefaultMcpStreamableServerSessionFactory(requestTimeout, this::asyncInitializeRequestHandler, requestHandlers, notificationHandlers)); } diff --git a/mcp/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java b/mcp/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java index 63fefa3..565c53f 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java +++ b/mcp/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java @@ -63,7 +63,7 @@ public class McpStatelessAsyncServer { private final ConcurrentHashMap completions = new ConcurrentHashMap<>(); - private List protocolVersions = List.of(McpSchema.LATEST_PROTOCOL_VERSION); + private List protocolVersions; private McpUriTemplateManagerFactory uriTemplateManagerFactory = new DeafaultMcpUriTemplateManagerFactory(); @@ -118,6 +118,8 @@ public class McpStatelessAsyncServer { requestHandlers.put(McpSchema.METHOD_COMPLETION_COMPLETE, completionCompleteRequestHandler()); } + this.protocolVersions = List.of(mcpTransport.protocolVersion()); + McpStatelessServerHandler handler = new DefaultMcpStatelessServerHandler(requestHandlers, Map.of()); mcpTransport.setMcpHandler(handler); } diff --git a/mcp/src/main/java/io/modelcontextprotocol/server/transport/HttpServletSseServerTransportProvider.java b/mcp/src/main/java/io/modelcontextprotocol/server/transport/HttpServletSseServerTransportProvider.java index 5c0b85f..24e749f 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/server/transport/HttpServletSseServerTransportProvider.java +++ b/mcp/src/main/java/io/modelcontextprotocol/server/transport/HttpServletSseServerTransportProvider.java @@ -178,6 +178,11 @@ public class HttpServletSseServerTransportProvider extends HttpServlet implement } } + @Override + public String protocolVersion() { + return "2024-11-05"; + } + /** * Creates a new HttpServletSseServerTransportProvider instance with the default SSE * endpoint. diff --git a/mcp/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStreamableServerTransportProvider.java b/mcp/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStreamableServerTransportProvider.java index 211a9c0..6805bf1 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStreamableServerTransportProvider.java +++ b/mcp/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStreamableServerTransportProvider.java @@ -154,6 +154,11 @@ public class HttpServletStreamableServerTransportProvider extends HttpServlet } + @Override + public String protocolVersion() { + return "2025-03-26"; + } + @Override public void setSessionFactory(McpStreamableServerSession.Factory sessionFactory) { this.sessionFactory = sessionFactory; diff --git a/mcp/src/main/java/io/modelcontextprotocol/server/transport/StdioServerTransportProvider.java b/mcp/src/main/java/io/modelcontextprotocol/server/transport/StdioServerTransportProvider.java index 9ef9c78..d2943b3 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/server/transport/StdioServerTransportProvider.java +++ b/mcp/src/main/java/io/modelcontextprotocol/server/transport/StdioServerTransportProvider.java @@ -88,6 +88,11 @@ public class StdioServerTransportProvider implements McpServerTransportProvider this.outputStream = outputStream; } + @Override + public String protocolVersion() { + return "2024-11-05"; + } + @Override public void setSessionFactory(McpServerSession.Factory sessionFactory) { // Create a single session for the stdio connection diff --git a/mcp/src/main/java/io/modelcontextprotocol/spec/HttpHeaders.java b/mcp/src/main/java/io/modelcontextprotocol/spec/HttpHeaders.java index 2e80849..c1c4c7a 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/spec/HttpHeaders.java +++ b/mcp/src/main/java/io/modelcontextprotocol/spec/HttpHeaders.java @@ -17,4 +17,9 @@ public interface HttpHeaders { */ String LAST_EVENT_ID = "last-event-id"; + /** + * Identifies the MCP protocol version. + */ + String PROTOCOL_VERSION = "MCP-Protocol-Version"; + } diff --git a/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java b/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java index a3812db..fb4baab 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -44,6 +44,7 @@ public final class McpSchema { private McpSchema() { } + @Deprecated public static final String LATEST_PROTOCOL_VERSION = "2025-03-26"; public static final String JSONRPC_VERSION = "2.0"; diff --git a/mcp/src/main/java/io/modelcontextprotocol/spec/McpServerTransportProviderBase.java b/mcp/src/main/java/io/modelcontextprotocol/spec/McpServerTransportProviderBase.java index 87e7d64..7985750 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/spec/McpServerTransportProviderBase.java +++ b/mcp/src/main/java/io/modelcontextprotocol/spec/McpServerTransportProviderBase.java @@ -55,4 +55,12 @@ public interface McpServerTransportProviderBase { */ Mono closeGracefully(); + /** + * Returns the protocol version supported by this transport provider. + * @return the protocol version as a string + */ + default String protocolVersion() { + return "2024-11-05"; + } + } diff --git a/mcp/src/main/java/io/modelcontextprotocol/spec/McpStatelessServerTransport.java b/mcp/src/main/java/io/modelcontextprotocol/spec/McpStatelessServerTransport.java index b6211fe..3299084 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/spec/McpStatelessServerTransport.java +++ b/mcp/src/main/java/io/modelcontextprotocol/spec/McpStatelessServerTransport.java @@ -22,4 +22,8 @@ public interface McpStatelessServerTransport { */ Mono closeGracefully(); + default String protocolVersion() { + return "2025-03-26"; + } + } diff --git a/mcp/src/main/java/io/modelcontextprotocol/spec/McpStreamableServerTransportProvider.java b/mcp/src/main/java/io/modelcontextprotocol/spec/McpStreamableServerTransportProvider.java index 87574e8..b750810 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/spec/McpStreamableServerTransportProvider.java +++ b/mcp/src/main/java/io/modelcontextprotocol/spec/McpStreamableServerTransportProvider.java @@ -2,8 +2,6 @@ package io.modelcontextprotocol.spec; import reactor.core.publisher.Mono; -import java.util.Map; - /** * The core building block providing the server-side MCP transport for Streamable HTTP * servers. Implement this interface to bridge between a particular server-side technology diff --git a/mcp/src/main/java/io/modelcontextprotocol/spec/McpTransport.java b/mcp/src/main/java/io/modelcontextprotocol/spec/McpTransport.java index 40d9ba7..49c4850 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/spec/McpTransport.java +++ b/mcp/src/main/java/io/modelcontextprotocol/spec/McpTransport.java @@ -77,4 +77,8 @@ public interface McpTransport { */ T unmarshalFrom(Object data, TypeReference typeRef); + default String protocolVersion() { + return "2024-11-05"; + } + } diff --git a/mcp/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java b/mcp/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java index 482d0aa..b531d57 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java +++ b/mcp/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java @@ -29,6 +29,8 @@ public class MockMcpClientTransport implements McpClientTransport { private final BiConsumer interceptor; + private String protocolVersion = McpSchema.LATEST_PROTOCOL_VERSION; + public MockMcpClientTransport() { this((t, msg) -> { }); @@ -38,6 +40,15 @@ public class MockMcpClientTransport implements McpClientTransport { this.interceptor = interceptor; } + public MockMcpClientTransport withProtocolVersion(String protocolVersion) { + return this; + } + + @Override + public String protocolVersion() { + return protocolVersion; + } + public void simulateIncomingMessage(McpSchema.JSONRPCMessage message) { if (inbound.tryEmitNext(message).isFailure()) { throw new RuntimeException("Failed to process incoming message " + message); diff --git a/mcp/src/test/java/io/modelcontextprotocol/client/McpAsyncClientResponseHandlerTests.java b/mcp/src/test/java/io/modelcontextprotocol/client/McpAsyncClientResponseHandlerTests.java index e773c83..11bd2e4 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/client/McpAsyncClientResponseHandlerTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/client/McpAsyncClientResponseHandlerTests.java @@ -52,7 +52,7 @@ class McpAsyncClientResponseHandlerTests { r.id(), mockInitResult, null); t.simulateIncomingMessage(initResponse); } - }); + }).withProtocolVersion(McpSchema.LATEST_PROTOCOL_VERSION); } @Test @@ -79,7 +79,7 @@ class McpAsyncClientResponseHandlerTests { // Verify initialization result assertThat(result).isNotNull(); - assertThat(result.protocolVersion()).isEqualTo(McpSchema.LATEST_PROTOCOL_VERSION); + assertThat(result.protocolVersion()).isEqualTo(transport.protocolVersion()); assertThat(result.capabilities()).isEqualTo(serverCapabilities); assertThat(result.serverInfo()).isEqualTo(serverInfo); assertThat(result.instructions()).isEqualTo("Test instructions"); diff --git a/mcp/src/test/java/io/modelcontextprotocol/client/McpAsyncClientTests.java b/mcp/src/test/java/io/modelcontextprotocol/client/McpAsyncClientTests.java index 14ca827..7b6777c 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/client/McpAsyncClientTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/client/McpAsyncClientTests.java @@ -20,8 +20,8 @@ class McpAsyncClientTests { public static final McpSchema.ServerCapabilities MOCK_SERVER_CAPABILITIES = McpSchema.ServerCapabilities.builder() .build(); - public static final McpSchema.InitializeResult MOCK_INIT_RESULT = new McpSchema.InitializeResult( - McpSchema.LATEST_PROTOCOL_VERSION, MOCK_SERVER_CAPABILITIES, MOCK_SERVER_INFO, "Test instructions"); + public static final McpSchema.InitializeResult MOCK_INIT_RESULT = new McpSchema.InitializeResult("2024-11-05", + MOCK_SERVER_CAPABILITIES, MOCK_SERVER_INFO, "Test instructions"); private static final String CONTEXT_KEY = "context.key"; diff --git a/mcp/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java b/mcp/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java index bf47384..2d41fc5 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java @@ -41,14 +41,14 @@ class McpClientProtocolVersionTests { McpSchema.JSONRPCRequest request = transport.getLastSentMessageAsRequest(); assertThat(request.params()).isInstanceOf(McpSchema.InitializeRequest.class); McpSchema.InitializeRequest initRequest = (McpSchema.InitializeRequest) request.params(); - assertThat(initRequest.protocolVersion()).isEqualTo(McpSchema.LATEST_PROTOCOL_VERSION); + assertThat(initRequest.protocolVersion()).isEqualTo(transport.protocolVersion()); transport.simulateIncomingMessage(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), - new McpSchema.InitializeResult(McpSchema.LATEST_PROTOCOL_VERSION, null, + new McpSchema.InitializeResult(transport.protocolVersion(), null, new McpSchema.Implementation("test-server", "1.0.0"), null), null)); }).assertNext(result -> { - assertThat(result.protocolVersion()).isEqualTo(McpSchema.LATEST_PROTOCOL_VERSION); + assertThat(result.protocolVersion()).isEqualTo(transport.protocolVersion()); }).verifyComplete(); } diff --git a/mcp/src/test/java/io/modelcontextprotocol/server/McpServerProtocolVersionTests.java b/mcp/src/test/java/io/modelcontextprotocol/server/McpServerProtocolVersionTests.java index f643f1b..95086ee 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/server/McpServerProtocolVersionTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/server/McpServerProtocolVersionTests.java @@ -45,7 +45,7 @@ class McpServerProtocolVersionTests { assertThat(jsonResponse.id()).isEqualTo(requestId); assertThat(jsonResponse.result()).isInstanceOf(McpSchema.InitializeResult.class); McpSchema.InitializeResult result = (McpSchema.InitializeResult) jsonResponse.result(); - assertThat(result.protocolVersion()).isEqualTo(McpSchema.LATEST_PROTOCOL_VERSION); + assertThat(result.protocolVersion()).isEqualTo(transportProvider.protocolVersion()); server.closeGracefully().subscribe(); } @@ -93,7 +93,7 @@ class McpServerProtocolVersionTests { assertThat(jsonResponse.id()).isEqualTo(requestId); assertThat(jsonResponse.result()).isInstanceOf(McpSchema.InitializeResult.class); McpSchema.InitializeResult result = (McpSchema.InitializeResult) jsonResponse.result(); - assertThat(result.protocolVersion()).isEqualTo(McpSchema.LATEST_PROTOCOL_VERSION); + assertThat(result.protocolVersion()).isEqualTo(transportProvider.protocolVersion()); server.closeGracefully().subscribe(); }