This is an automated email from the ASF dual-hosted git repository.

terrymanu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new 9d9ae38e5db Restrict MCP protocol support to 2025-11-25 (#39022)
9d9ae38e5db is described below

commit 9d9ae38e5db275360177b9d150ccfabe35c1c6c3
Author: Liang Zhang <[email protected]>
AuthorDate: Mon Jul 6 20:30:12 2026 +0800

    Restrict MCP protocol support to 2025-11-25 (#39022)
    
    * Restrict MCP protocol support to 2025-11-25
    
    Limit MCP transport protocol negotiation to the 2025-11-25 protocol version 
and treat 2025-06-18 as an unsupported legacy version for follow-up requests. 
Simplify HTTP transport protocol header handling now that only one protocol can 
be negotiated, and update MCP E2E/client tests to assert the new contract.
    
    * Restrict MCP protocol support to 2025-11-25
    
    Limit MCP transport protocol negotiation to the 2025-11-25 protocol version 
and treat 2025-06-18 as an unsupported legacy version for follow-up requests. 
Simplify HTTP transport protocol header handling now that only one protocol can 
be negotiated, and update MCP E2E/client tests to assert the new contract.
---
 .../bootstrap/transport/MCPTransportConstants.java |  2 +-
 .../server/http/StreamableHttpMCPServlet.java      | 12 +----
 .../server/http/StreamableHttpMCPServletTest.java  | 30 +++---------
 .../ProtocolVersionHeaderConstraintTest.java       |  7 ++-
 .../ProductionMCPClientTransportFactory.java       |  6 ++-
 .../ProductionMCPClientTransportFactoryTest.java   | 55 ++++++++++++++++++++++
 .../HttpTransportProtocolContractE2ETest.java      |  4 +-
 7 files changed, 74 insertions(+), 42 deletions(-)

diff --git 
a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/MCPTransportConstants.java
 
b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/MCPTransportConstants.java
index ccd9a443748..862e4b38e8a 100644
--- 
a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/MCPTransportConstants.java
+++ 
b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/MCPTransportConstants.java
@@ -31,7 +31,7 @@ public final class MCPTransportConstants {
     
     public static final String PROTOCOL_VERSION = 
ProtocolVersions.MCP_2025_11_25;
     
-    public static final List<String> SUPPORTED_PROTOCOL_VERSIONS = 
List.of(PROTOCOL_VERSION, ProtocolVersions.MCP_2025_06_18);
+    public static final List<String> SUPPORTED_PROTOCOL_VERSIONS = 
List.of(PROTOCOL_VERSION);
     
     public static final String SERVER_NAME = "apache-shardingsphere-mcp";
     
diff --git 
a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServlet.java
 
b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServlet.java
index fc849f2c811..465baa0c673 100644
--- 
a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServlet.java
+++ 
b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServlet.java
@@ -48,7 +48,6 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 final class StreamableHttpMCPServlet extends HttpServlet implements 
McpStreamableServerTransportProvider {
@@ -75,8 +74,6 @@ final class StreamableHttpMCPServlet extends HttpServlet 
implements McpStreamabl
     
     private final SessionAttributionResolver sessionAttributionResolver;
     
-    private final Map<String, String> sessionProtocolVersions;
-    
     private final AtomicBoolean closed;
     
     StreamableHttpMCPServlet(final MCPSessionManager sessionManager, final 
McpJsonMapper jsonMapper, final HttpTransportConfiguration config) {
@@ -86,8 +83,6 @@ final class StreamableHttpMCPServlet extends HttpServlet 
implements McpStreamabl
         this.jsonMapper = jsonMapper;
         this.sessionManager = sessionManager;
         sessionExecutionCoordinator = new 
MCPSessionExecutionCoordinator(sessionManager);
-        sessionProtocolVersions = new ConcurrentHashMap<>();
-        
sessionManager.addSessionCloseListener(sessionProtocolVersions::remove);
         closed = new AtomicBoolean();
     }
     
@@ -109,7 +104,6 @@ final class StreamableHttpMCPServlet extends HttpServlet 
implements McpStreamabl
             McpStreamableServerSession.McpStreamableServerSessionInit result = 
sessionFactory.startSession(actualInitializeRequest);
             String sessionId = result.session().getId();
             sessionManager.createSession(sessionId);
-            sessionProtocolVersions.put(sessionId, 
actualInitializeRequest.protocolVersion());
             return result;
         });
     }
@@ -281,14 +275,10 @@ final class StreamableHttpMCPServlet extends HttpServlet 
implements McpStreamabl
     private void addNegotiatedProtocolHeader(final 
SessionAwareHttpServletResponse response, final String name, final String 
sessionId) {
         if (SESSION_HEADER.equalsIgnoreCase(name)) {
             response.setSessionId(sessionId);
-            response.setHeader(PROTOCOL_HEADER, 
findNegotiatedProtocolVersion(sessionId));
+            response.setHeader(PROTOCOL_HEADER, 
MCPTransportConstants.PROTOCOL_VERSION);
         }
     }
     
-    private String findNegotiatedProtocolVersion(final String sessionId) {
-        return 
sessionProtocolVersions.getOrDefault(Objects.toString(sessionId, ""), 
MCPTransportConstants.PROTOCOL_VERSION);
-    }
-    
     private abstract static class SessionAwareHttpServletResponse extends 
HttpServletResponseWrapper {
         
         private String sessionId = "";
diff --git 
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServletTest.java
 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServletTest.java
index 1c8b82859a7..22fa133af8b 100644
--- 
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServletTest.java
+++ 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServletTest.java
@@ -69,9 +69,8 @@ class StreamableHttpMCPServletTest {
         assertThat(actual.protocolVersions(), 
is(MCPTransportConstants.SUPPORTED_PROTOCOL_VERSIONS));
     }
     
-    @ParameterizedTest(name = "{0}")
-    @MethodSource("supportedProtocolVersions")
-    void assertSetSessionFactoryWithSupportedProtocolVersion(final String 
name, final String requestedProtocolVersion) throws 
ReflectiveOperationException {
+    @Test
+    void assertSetSessionFactoryWithSupportedProtocolVersion() throws 
ReflectiveOperationException {
         HttpServletStreamableServerTransportProvider delegate = 
mock(HttpServletStreamableServerTransportProvider.class);
         MCPSessionManager sessionManager = mock(MCPSessionManager.class);
         McpStreamableServerSession.Factory sessionFactory = 
mock(McpStreamableServerSession.Factory.class);
@@ -85,19 +84,13 @@ class StreamableHttpMCPServletTest {
         actual.setSessionFactory(sessionFactory);
         ArgumentCaptor<McpStreamableServerSession.Factory> actualFactory = 
ArgumentCaptor.forClass(McpStreamableServerSession.Factory.class);
         verify(delegate).setSessionFactory(actualFactory.capture());
-        McpSchema.InitializeRequest expectedInitializeRequest = new 
McpSchema.InitializeRequest(requestedProtocolVersion,
+        McpSchema.InitializeRequest expectedInitializeRequest = new 
McpSchema.InitializeRequest(MCPTransportConstants.PROTOCOL_VERSION,
                 new McpSchema.ClientCapabilities(Map.of(), null, null, null), 
new McpSchema.Implementation("foo_client", "1.0.0"));
         
assertThat(actualFactory.getValue().startSession(expectedInitializeRequest), 
is(expectedInit));
         verify(sessionFactory).startSession(expectedInitializeRequest);
         verify(sessionManager).createSession("session-id");
     }
     
-    private static Stream<Arguments> supportedProtocolVersions() {
-        return Stream.of(
-                Arguments.of("latest protocol version", 
MCPTransportConstants.PROTOCOL_VERSION),
-                Arguments.of("compatible protocol version", 
ProtocolVersions.MCP_2025_06_18));
-    }
-    
     @ParameterizedTest(name = "{0}")
     @MethodSource("unsupportedProtocolVersions")
     void assertSetSessionFactoryWithNegotiatedProtocolVersion(final String 
name, final String requestedProtocolVersion) throws 
ReflectiveOperationException {
@@ -130,6 +123,7 @@ class StreamableHttpMCPServletTest {
         return Stream.of(
                 Arguments.of("null protocol version", null),
                 Arguments.of("blank protocol version", "  "),
+                Arguments.of("legacy protocol version", 
ProtocolVersions.MCP_2025_06_18),
                 Arguments.of("unsupported protocol version", "2024-11-05"));
     }
     
@@ -278,19 +272,6 @@ class StreamableHttpMCPServletTest {
     @Test
     void assertServicePostWithNegotiatedProtocolHeader() throws 
ServletException, IOException, ReflectiveOperationException {
         HttpServletStreamableServerTransportProvider delegate = 
mock(HttpServletStreamableServerTransportProvider.class);
-        MCPSessionManager sessionManager = mock(MCPSessionManager.class);
-        McpStreamableServerSession.Factory sessionFactory = 
mock(McpStreamableServerSession.Factory.class);
-        McpStreamableServerSession session = 
mock(McpStreamableServerSession.class);
-        when(session.getId()).thenReturn("session-id");
-        
when(sessionFactory.startSession(any(McpSchema.InitializeRequest.class))).thenReturn(new
 McpStreamableServerSession.McpStreamableServerSessionInit(session,
-                Mono.just(new 
InitializeResult(ProtocolVersions.MCP_2025_06_18, 
McpSchema.ServerCapabilities.builder().tools(Boolean.FALSE).build(),
-                        new 
McpSchema.Implementation(MCPTransportConstants.SERVER_NAME, "development"), 
"runtime"))));
-        StreamableHttpMCPServlet actual = createServlet(delegate, 
sessionManager, mock(MCPSessionExecutionCoordinator.class));
-        actual.setSessionFactory(sessionFactory);
-        ArgumentCaptor<McpStreamableServerSession.Factory> actualFactory = 
ArgumentCaptor.forClass(McpStreamableServerSession.Factory.class);
-        verify(delegate).setSessionFactory(actualFactory.capture());
-        actualFactory.getValue().startSession(new 
McpSchema.InitializeRequest(ProtocolVersions.MCP_2025_06_18,
-                new McpSchema.ClientCapabilities(Map.of(), null, null, null), 
new McpSchema.Implementation("foo_client", "1.0.0")));
         HttpServletRequest request = mock(HttpServletRequest.class);
         when(request.getMethod()).thenReturn("POST");
         when(request.getHeader(HttpHeaders.ACCEPT)).thenReturn(ACCEPT);
@@ -300,8 +281,9 @@ class StreamableHttpMCPServletTest {
             ((HttpServletResponse) 
invocation.getArgument(1)).setHeader(HttpHeaders.MCP_SESSION_ID, "session-id");
             return null;
         }).when(delegate).service(any(HttpServletRequest.class), 
any(HttpServletResponse.class));
+        StreamableHttpMCPServlet actual = createServlet(delegate, 
mock(MCPSessionManager.class), mock(MCPSessionExecutionCoordinator.class));
         actual.service(request, response);
-        verify(response).setHeader(HttpHeaders.PROTOCOL_VERSION, 
ProtocolVersions.MCP_2025_06_18);
+        verify(response).setHeader(HttpHeaders.PROTOCOL_VERSION, 
MCPTransportConstants.PROTOCOL_VERSION);
     }
     
     @Test
diff --git 
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/validator/constraint/ProtocolVersionHeaderConstraintTest.java
 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/validator/constraint/ProtocolVersionHeaderConstraintTest.java
index 339d1cee0ea..bfa9f67febf 100644
--- 
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/validator/constraint/ProtocolVersionHeaderConstraintTest.java
+++ 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/validator/constraint/ProtocolVersionHeaderConstraintTest.java
@@ -58,7 +58,10 @@ class ProtocolVersionHeaderConstraintTest {
     }
     
     @Test
-    void assertValidateWithCompatibleProtocolVersion() {
-        assertDoesNotThrow(() -> new 
ProtocolVersionHeaderConstraint().validate(ProtocolVersions.MCP_2025_06_18));
+    void assertValidateWithLegacyProtocolVersion() {
+        ServerTransportSecurityException ex = 
assertThrows(ServerTransportSecurityException.class, () -> new 
ProtocolVersionHeaderConstraint().validate(ProtocolVersions.MCP_2025_06_18));
+        assertThat(ex.getStatusCode(), is(400));
+        assertThat(ex.getMessage(), is(String.format("Unsupported MCP protocol 
version `%s`. Supported versions are %s.", ProtocolVersions.MCP_2025_06_18,
+                MCPTransportConstants.SUPPORTED_PROTOCOL_VERSIONS)));
     }
 }
diff --git 
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMCPClientTransportFactory.java
 
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMCPClientTransportFactory.java
index 3a55210f428..3c5ae4ce315 100644
--- 
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMCPClientTransportFactory.java
+++ 
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMCPClientTransportFactory.java
@@ -51,6 +51,8 @@ final class ProductionMCPClientTransportFactory {
     
     private static final String MASK_PLAN_TOOL_NAME = 
"database_gateway_plan_mask_rule";
     
+    private static final List<String> SUPPORTED_PROTOCOL_VERSIONS = 
List.of(ProtocolVersions.MCP_2025_11_25);
+    
     private ProductionMCPClientTransportFactory() {
     }
     
@@ -67,7 +69,7 @@ final class ProductionMCPClientTransportFactory {
     
     static McpClientTransport createHttpClientTransport(final URI endpointUri) 
{
         return 
HttpClientStreamableHttpTransport.builder(String.format("%s://%s:%d", 
endpointUri.getScheme(), endpointUri.getHost(), endpointUri.getPort()))
-                .endpoint(endpointUri.getPath()).build();
+                
.endpoint(endpointUri.getPath()).supportedProtocolVersions(SUPPORTED_PROTOCOL_VERSIONS).build();
     }
     
     static StdioClientTransport createStdioClientTransport(final Path 
configFile) throws IOException {
@@ -111,7 +113,7 @@ final class ProductionMCPClientTransportFactory {
         
         @Override
         public List<String> protocolVersions() {
-            return List.of(ProtocolVersions.MCP_2025_06_18, 
ProtocolVersions.MCP_2025_11_25);
+            return SUPPORTED_PROTOCOL_VERSIONS;
         }
     }
 }
diff --git 
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMCPClientTransportFactoryTest.java
 
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMCPClientTransportFactoryTest.java
new file mode 100644
index 00000000000..2560f39006a
--- /dev/null
+++ 
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMCPClientTransportFactoryTest.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.test.e2e.mcp.runtime.production;
+
+import io.modelcontextprotocol.client.transport.StdioClientTransport;
+import io.modelcontextprotocol.spec.McpClientTransport;
+import io.modelcontextprotocol.spec.ProtocolVersions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Path;
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+class ProductionMCPClientTransportFactoryTest {
+    
+    @Test
+    void assertCreateHttpClientTransport() {
+        McpClientTransport actual = 
ProductionMCPClientTransportFactory.createHttpClientTransport(URI.create("http://127.0.0.1:8080/mcp";));
+        try {
+            assertThat(actual.protocolVersions(), 
is(List.of(ProtocolVersions.MCP_2025_11_25)));
+        } finally {
+            actual.closeGracefully().block();
+        }
+    }
+    
+    @Test
+    void assertCreateStdioClientTransport(@TempDir final Path tempDir) throws 
IOException {
+        StdioClientTransport actual = 
ProductionMCPClientTransportFactory.createStdioClientTransport(tempDir.resolve("mcp.yaml"));
+        try {
+            assertThat(actual.protocolVersions(), 
is(List.of(ProtocolVersions.MCP_2025_11_25)));
+        } finally {
+            actual.closeGracefully().block();
+        }
+    }
+}
diff --git 
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportProtocolContractE2ETest.java
 
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportProtocolContractE2ETest.java
index e50fe15b5f7..576124974e0 100644
--- 
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportProtocolContractE2ETest.java
+++ 
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportProtocolContractE2ETest.java
@@ -150,7 +150,7 @@ class HttpTransportProtocolContractE2ETest extends 
AbstractHttpProtocolOnlyE2ETe
         launchHttpTransport();
         HttpClient httpClient = HttpClient.newHttpClient();
         Map<String, Object> initializeRequestParams = new 
LinkedHashMap<>(MCPHttpTransportTestSupport.createInitializeRequestParams("mcp-e2e-programmatic"));
-        initializeRequestParams.put("protocolVersion", "2024-11-05");
+        initializeRequestParams.put("protocolVersion", "2025-06-18");
         HttpResponse<String> actual = sendInitializeRequest(httpClient, 
initializeRequestParams);
         assertThat(actual.statusCode(), is(200));
         
assertThat(actual.headers().firstValue("MCP-Protocol-Version").orElse(""), 
is(getProtocolVersion()));
@@ -163,7 +163,7 @@ class HttpTransportProtocolContractE2ETest extends 
AbstractHttpProtocolOnlyE2ETe
         launchHttpTransport();
         HttpClient httpClient = HttpClient.newHttpClient();
         String sessionId = initializeSession(httpClient);
-        HttpResponse<String> actual = sendCapabilitiesRequest(httpClient, 
Map.of("MCP-Session-Id", sessionId, "MCP-Protocol-Version", "2024-11-05"));
+        HttpResponse<String> actual = sendCapabilitiesRequest(httpClient, 
Map.of("MCP-Session-Id", sessionId, "MCP-Protocol-Version", "2025-06-18"));
         assertThat(actual.statusCode(), is(400));
     }
     

Reply via email to