1111import java .net .http .HttpResponse ;
1212import java .net .http .HttpResponse .BodyHandler ;
1313import java .time .Duration ;
14+ import java .util .Collections ;
15+ import java .util .Comparator ;
1416import java .util .List ;
1517import java .util .Optional ;
1618import java .util .concurrent .CompletionException ;
2123import java .util .function .Consumer ;
2224import java .util .function .Function ;
2325
24- import org .reactivestreams .Publisher ;
25- import org .slf4j .Logger ;
26- import org .slf4j .LoggerFactory ;
27-
28- import io .modelcontextprotocol .json .TypeRef ;
29- import io .modelcontextprotocol .json .McpJsonMapper ;
30-
26+ import io .modelcontextprotocol .client .McpAsyncClient ;
27+ import io .modelcontextprotocol .client .transport .ResponseSubscribers .ResponseEvent ;
3128import io .modelcontextprotocol .client .transport .customizer .McpAsyncHttpClientRequestCustomizer ;
3229import io .modelcontextprotocol .client .transport .customizer .McpSyncHttpClientRequestCustomizer ;
33- import io .modelcontextprotocol .client .transport .ResponseSubscribers .ResponseEvent ;
3430import io .modelcontextprotocol .common .McpTransportContext ;
31+ import io .modelcontextprotocol .json .McpJsonMapper ;
32+ import io .modelcontextprotocol .json .TypeRef ;
3533import io .modelcontextprotocol .spec .ClosedMcpTransportSession ;
3634import io .modelcontextprotocol .spec .DefaultMcpTransportSession ;
3735import io .modelcontextprotocol .spec .DefaultMcpTransportStream ;
4543import io .modelcontextprotocol .spec .ProtocolVersions ;
4644import io .modelcontextprotocol .util .Assert ;
4745import io .modelcontextprotocol .util .Utils ;
46+ import org .reactivestreams .Publisher ;
47+ import org .slf4j .Logger ;
48+ import org .slf4j .LoggerFactory ;
4849import reactor .core .Disposable ;
4950import reactor .core .publisher .Flux ;
5051import reactor .core .publisher .FluxSink ;
@@ -81,8 +82,6 @@ public class HttpClientStreamableHttpTransport implements McpClientTransport {
8182
8283 private static final Logger logger = LoggerFactory .getLogger (HttpClientStreamableHttpTransport .class );
8384
84- private static final String MCP_PROTOCOL_VERSION = ProtocolVersions .MCP_2025_06_18 ;
85-
8685 private static final String DEFAULT_ENDPOINT = "/mcp" ;
8786
8887 /**
@@ -128,6 +127,10 @@ public class HttpClientStreamableHttpTransport implements McpClientTransport {
128127
129128 private final AtomicReference <Consumer <Throwable >> exceptionHandler = new AtomicReference <>();
130129
130+ private final List <String > supportedProtocolVersions ;
131+
132+ private final String latestSupportedProtocolVersion ;
133+
131134 /**
132135 * Consumer to handle HttpClient closure. If null, no cleanup is performed (external
133136 * HttpClient).
@@ -137,7 +140,7 @@ public class HttpClientStreamableHttpTransport implements McpClientTransport {
137140 private HttpClientStreamableHttpTransport (McpJsonMapper jsonMapper , HttpClient httpClient ,
138141 HttpRequest .Builder requestBuilder , String baseUri , String endpoint , boolean resumableStreams ,
139142 boolean openConnectionOnStartup , McpAsyncHttpClientRequestCustomizer httpRequestCustomizer ,
140- Consumer <HttpClient > onCloseClient ) {
143+ List < String > supportedProtocolVersions , Consumer <HttpClient > onCloseClient ) {
141144 Assert .notNull (onCloseClient , "onCloseClient must not be null" );
142145 this .jsonMapper = jsonMapper ;
143146 this .httpClient = httpClient ;
@@ -149,12 +152,16 @@ private HttpClientStreamableHttpTransport(McpJsonMapper jsonMapper, HttpClient h
149152 this .activeSession .set (createTransportSession ());
150153 this .httpRequestCustomizer = httpRequestCustomizer ;
151154 this .onCloseClient = onCloseClient ;
155+ this .supportedProtocolVersions = Collections .unmodifiableList (supportedProtocolVersions );
156+ this .latestSupportedProtocolVersion = this .supportedProtocolVersions .stream ()
157+ .sorted (Comparator .reverseOrder ())
158+ .findFirst ()
159+ .get ();
152160 }
153161
154162 @ Override
155163 public List <String > protocolVersions () {
156- return List .of (ProtocolVersions .MCP_2024_11_05 , ProtocolVersions .MCP_2025_03_26 ,
157- ProtocolVersions .MCP_2025_06_18 );
164+ return supportedProtocolVersions ;
158165 }
159166
160167 public static Builder builder (String baseUri ) {
@@ -198,7 +205,9 @@ private Publisher<Void> createDelete(String sessionId) {
198205 .uri (uri )
199206 .header ("Cache-Control" , "no-cache" )
200207 .header (HttpHeaders .MCP_SESSION_ID , sessionId )
201- .header (HttpHeaders .PROTOCOL_VERSION , MCP_PROTOCOL_VERSION )
208+ .header (HttpHeaders .PROTOCOL_VERSION ,
209+ ctx .getOrDefault (McpAsyncClient .NEGOTIATED_PROTOCOL_VERSION ,
210+ this .latestSupportedProtocolVersion ))
202211 .DELETE ();
203212 var transportContext = ctx .getOrDefault (McpTransportContext .KEY , McpTransportContext .EMPTY );
204213 return Mono .from (this .httpRequestCustomizer .customize (builder , "DELETE" , uri , null , transportContext ));
@@ -314,7 +323,9 @@ private Mono<Disposable> reconnect(McpTransportStream<Disposable> stream) {
314323 var builder = requestBuilder .uri (uri )
315324 .header (HttpHeaders .ACCEPT , TEXT_EVENT_STREAM )
316325 .header ("Cache-Control" , "no-cache" )
317- .header (HttpHeaders .PROTOCOL_VERSION , MCP_PROTOCOL_VERSION )
326+ .header (HttpHeaders .PROTOCOL_VERSION ,
327+ connectionCtx .getOrDefault (McpAsyncClient .NEGOTIATED_PROTOCOL_VERSION ,
328+ this .latestSupportedProtocolVersion ))
318329 .GET ();
319330 var transportContext = connectionCtx .getOrDefault (McpTransportContext .KEY , McpTransportContext .EMPTY );
320331 return Mono .from (this .httpRequestCustomizer .customize (builder , "GET" , uri , null , transportContext ));
@@ -489,7 +500,9 @@ public Mono<Void> sendMessage(McpSchema.JSONRPCMessage sentMessage) {
489500 .header (HttpHeaders .ACCEPT , APPLICATION_JSON + ", " + TEXT_EVENT_STREAM )
490501 .header (HttpHeaders .CONTENT_TYPE , APPLICATION_JSON )
491502 .header (HttpHeaders .CACHE_CONTROL , "no-cache" )
492- .header (HttpHeaders .PROTOCOL_VERSION , MCP_PROTOCOL_VERSION )
503+ .header (HttpHeaders .PROTOCOL_VERSION ,
504+ ctx .getOrDefault (McpAsyncClient .NEGOTIATED_PROTOCOL_VERSION ,
505+ this .latestSupportedProtocolVersion ))
493506 .POST (HttpRequest .BodyPublishers .ofString (jsonBody ));
494507 var transportContext = ctx .getOrDefault (McpTransportContext .KEY , McpTransportContext .EMPTY );
495508 return Mono
@@ -684,6 +697,9 @@ public static class Builder {
684697 private Consumer <HttpClient > onCloseClient = (HttpClient client ) -> {
685698 };
686699
700+ private List <String > supportedProtocolVersions = List .of (ProtocolVersions .MCP_2024_11_05 ,
701+ ProtocolVersions .MCP_2025_03_26 , ProtocolVersions .MCP_2025_06_18 );
702+
687703 /**
688704 * Creates a new builder with the specified base URI.
689705 * @param baseUri the base URI of the MCP server
@@ -831,6 +847,30 @@ public Builder onHttpClientClose(Consumer<HttpClient> onCloseClient) {
831847 return this ;
832848 }
833849
850+ /**
851+ * Sets the list of supported protocol versions used in version negotiation. By
852+ * default, the client will send the latest of those versions in the
853+ * {@code MCP-Protocol-Version} header.
854+ * <p>
855+ * Setting this value only updates the values used in version negotiation, and
856+ * does NOT impact the actual capabilities of the transport. It should only be
857+ * used for compatibility with servers having strict requirements around the
858+ * {@code MCP-Protocol-Version} header.
859+ * @param supportedProtocolVersions protocol versions supported by this transport
860+ * @return this builder
861+ * @see <a href=
862+ * "https://modelcontextprotocol.io/specification/2024-11-05/basic/lifecycle#version-negotiation">version
863+ * negotiation specification</a>
864+ * @see <a href=
865+ * "https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#protocol-version-header">Protocol
866+ * Version Header</a>
867+ */
868+ public Builder supportedProtocolVersions (List <String > supportedProtocolVersions ) {
869+ Assert .notEmpty (supportedProtocolVersions , "supportedProtocolVersions must not be empty" );
870+ this .supportedProtocolVersions = Collections .unmodifiableList (supportedProtocolVersions );
871+ return this ;
872+ }
873+
834874 /**
835875 * Construct a fresh instance of {@link HttpClientStreamableHttpTransport} using
836876 * the current builder configuration.
@@ -869,7 +909,7 @@ public HttpClientStreamableHttpTransport build() {
869909
870910 return new HttpClientStreamableHttpTransport (jsonMapper == null ? McpJsonMapper .getDefault () : jsonMapper ,
871911 httpClient , requestBuilder , baseUri , endpoint , resumableStreams , openConnectionOnStartup ,
872- httpRequestCustomizer , closeHandler );
912+ httpRequestCustomizer , supportedProtocolVersions , closeHandler );
873913 }
874914
875915 }
0 commit comments