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

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


The following commit(s) were added to refs/heads/master by this push:
     new d3f5ee9bbb fix(mcp): correctly merge fixed and dynamic query 
parameters (#7448)
d3f5ee9bbb is described below

commit d3f5ee9bbb1d1f4a7518260fccd36d422f605a6d
Author: lymerin <[email protected]>
AuthorDate: Thu Oct 8 14:28:15 2026 +0800

    fix(mcp): correctly merge fixed and dynamic query parameters (#7448)
---
 .../mcp/server/request/RequestConfigHelper.java    | 13 ++++-----
 .../server/callback/ShenyuToolCallbackTest.java    | 21 +++++++++++++++
 .../server/request/RequestConfigHelperTest.java    | 31 ++++++++++++++++++++++
 3 files changed, 57 insertions(+), 8 deletions(-)

diff --git 
a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/request/RequestConfigHelper.java
 
b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/request/RequestConfigHelper.java
index 07eb4e020e..2db1573dcd 100644
--- 
a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/request/RequestConfigHelper.java
+++ 
b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/request/RequestConfigHelper.java
@@ -140,7 +140,7 @@ public class RequestConfigHelper {
         String existingQuery = hasExistingQuery ? 
urlTemplate.substring(urlTemplate.indexOf("?") + 1) : "";
 
         // Handle new query parameters
-        basePath = processArguments(argsPosition, inputJson, basePath, 
queryBuilder);
+        basePath = processArguments(argsPosition, inputJson, basePath, 
queryBuilder, existingQuery);
 
         // Clear the template variables that have not been replaced
         basePath = basePath.replaceAll("\\{\\{\\.[^}]+}}", "");
@@ -202,10 +202,11 @@ public class RequestConfigHelper {
      * @param inputJson the input JSON object
      * @param basePath the base path to modify
      * @param queryBuilder the query builder to append to
+     * @param existingQuery the existing query string
      * @return the modified base path
      */
     private static String processArguments(final JsonObject argsPosition, 
final JsonObject inputJson,
-                                       final String basePath, final 
StringBuilder queryBuilder) {
+                                       final String basePath, final 
StringBuilder queryBuilder, final String existingQuery) {
         String modifiedBasePath = basePath;
         for (String key : argsPosition.keySet()) {
             String position = argsPosition.get(key).getAsString();
@@ -216,7 +217,7 @@ public class RequestConfigHelper {
                 modifiedBasePath = modifiedBasePath.replace("{{." + key + 
"}}", UriUtils.encodePathSegment(value, StandardCharsets.UTF_8));
             } else if ("query".equals(position) && inputJson.has(key)) {
                 // Handle query parameters
-                if (!modifiedBasePath.contains(key + "=")) {
+                if (!existingQuery.startsWith(key + "=") && 
!existingQuery.contains("&" + key + "=")) {
                     if (!queryBuilder.isEmpty()) {
                         queryBuilder.append("&");
                     }
@@ -244,11 +245,7 @@ public class RequestConfigHelper {
 
         // Add query parameters
         if (!queryBuilder.isEmpty()) {
-            if (hasExistingQuery) {
-                finalPath.append("&").append(queryBuilder);
-            } else {
-                finalPath.append("?").append(queryBuilder);
-            }
+            finalPath.append("?").append(queryBuilder);
         }
 
         // Add existing query parameters
diff --git 
a/shenyu-plugin/shenyu-plugin-mcp-server/src/test/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallbackTest.java
 
b/shenyu-plugin/shenyu-plugin-mcp-server/src/test/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallbackTest.java
index d2104e272c..60fa36bc84 100644
--- 
a/shenyu-plugin/shenyu-plugin-mcp-server/src/test/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallbackTest.java
+++ 
b/shenyu-plugin/shenyu-plugin-mcp-server/src/test/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallbackTest.java
@@ -45,6 +45,7 @@ import reactor.core.publisher.Mono;
 import java.net.URI;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -342,6 +343,26 @@ class ShenyuToolCallbackTest {
         assertEquals(URI.create("http://gateway.example:9195/api/order";), 
builder.build().getURI());
     }
 
+    @Test
+    void testDecoratedExchangePreservesFixedAndDynamicQueryParameters() {
+        shenyuToolCallback = new ShenyuToolCallback(toolDefinition);
+        MockServerWebExchange originExchange = MockServerWebExchange.from(
+                
MockServerHttpRequest.get("http://gateway.example:9195/mcp";).build());
+        ShenyuContext context = new ShenyuContext();
+        originExchange.getAttributes().put(Constants.CONTEXT, context);
+        String config = 
"{\"requestTemplate\":{\"url\":\"/users?fixed=true\",\"method\":\"GET\"},"
+                + "\"argsPosition\":{\"page\":\"query\"}}";
+
+        ServerWebExchange decoratedExchange = 
ReflectionTestUtils.invokeMethod(shenyuToolCallback, "buildDecoratedExchange",
+                originExchange, new CompletableFuture<String>(), "session123", 
config, "{\"page\":\"1\"}");
+
+        
assertEquals(URI.create("http://gateway.example:9195/users?page=1&fixed=true";), 
decoratedExchange.getRequest().getURI());
+        assertEquals("1", 
decoratedExchange.getRequest().getQueryParams().getFirst("page"));
+        assertEquals("true", 
decoratedExchange.getRequest().getQueryParams().getFirst("fixed"));
+        assertEquals("/users", context.getPath());
+        assertEquals("/users", context.getRealUrl());
+    }
+
     @Test
     void testConstructorWithValidToolDefinition() {
         ShenyuToolCallback callback = new ShenyuToolCallback(toolDefinition);
diff --git 
a/shenyu-plugin/shenyu-plugin-mcp-server/src/test/java/org/apache/shenyu/plugin/mcp/server/request/RequestConfigHelperTest.java
 
b/shenyu-plugin/shenyu-plugin-mcp-server/src/test/java/org/apache/shenyu/plugin/mcp/server/request/RequestConfigHelperTest.java
index 36fd938770..f1ae261150 100644
--- 
a/shenyu-plugin/shenyu-plugin-mcp-server/src/test/java/org/apache/shenyu/plugin/mcp/server/request/RequestConfigHelperTest.java
+++ 
b/shenyu-plugin/shenyu-plugin-mcp-server/src/test/java/org/apache/shenyu/plugin/mcp/server/request/RequestConfigHelperTest.java
@@ -19,6 +19,8 @@ package org.apache.shenyu.plugin.mcp.server.request;
 
 import com.google.gson.JsonObject;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -102,6 +104,35 @@ class RequestConfigHelperTest {
         assertTrue(result.contains("&"));
     }
 
+    @ParameterizedTest
+    @CsvSource({
+        "/users?fixed=true, /users?page=1&fixed=true",
+        "/users?page=10, /users?page=10",
+        "/users?homepage=10&note=page=10, 
/users?page=1&homepage=10&note=page=10"
+    })
+    void testQueryParameterMerging(final String template, final String 
expected) {
+        JsonObject argsPosition = new JsonObject();
+        argsPosition.addProperty("page", "query");
+        JsonObject inputJson = new JsonObject();
+        inputJson.addProperty("page", "1");
+
+        String result = RequestConfigHelper.buildPath(template, argsPosition, 
inputJson);
+
+        assertEquals(expected, result);
+    }
+
+    @Test
+    void testQueryMergingPreservesEncoding() {
+        JsonObject argsPosition = new JsonObject();
+        argsPosition.addProperty("query", "query");
+        JsonObject inputJson = new JsonObject();
+        inputJson.addProperty("query", "hello world & 50%");
+
+        String result = 
RequestConfigHelper.buildPath("/users?fixed=a%26b%3Dc%20d", argsPosition, 
inputJson);
+
+        
assertEquals("/users?query=hello%20world%20%26%2050%25&fixed=a%26b%3Dc%20d", 
result);
+    }
+
     @Test
     void testMixedPathAndQueryParameters() {
         JsonObject argsPosition = new JsonObject();

Reply via email to