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

hengyuss 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 a89b6a2338 fix: make shenyu-examples-mcp work normally (#7033)
a89b6a2338 is described below

commit a89b6a2338c24e978428b731e8f1edce86022418
Author: hengyuss <[email protected]>
AuthorDate: Wed Oct 7 00:54:30 2026 +0800

    fix: make shenyu-examples-mcp work normally (#7033)
    
    * fix: make shenyu-examples-mcp work normally
    
    * fix: make client register event publisher startup idempotent
    
    * fix: update related build/documentation
    
    ---------
    
    Co-authored-by: aias00 <[email protected]>
---
 shenyu-examples/README.md                          | 63 ++++++++++++++++++----
 .../{ => examples}/ShenyuTestMcpApplication.java   |  2 +-
 .../{ => examples}/controller/OrderController.java |  4 +-
 .../apache/shenyu/{ => examples}/dto/OrderDTO.java |  2 +-
 .../src/main/resources/application.yml             |  2 -
 .../mcp/server/callback/ShenyuToolCallback.java    |  5 +-
 .../server/callback/ShenyuToolCallbackTest.java    | 23 ++++++--
 .../src/main/resources/META-INF/spring.provides    | 30 ++---------
 ...rk.boot.autoconfigure.AutoConfiguration.imports | 30 ++---------
 9 files changed, 85 insertions(+), 76 deletions(-)

diff --git a/shenyu-examples/README.md b/shenyu-examples/README.md
index 56b8312a92..cc26866c92 100644
--- a/shenyu-examples/README.md
+++ b/shenyu-examples/README.md
@@ -4,7 +4,7 @@ This module contains example applications that demonstrate how 
to integrate your
 
 ## Client Dependencies
 
-The example modules now consume **independent 
[shenyu-client-java](https://github.com/apache/shenyu-client-java) artifacts** 
from Maven Central, rather than depending on in-tree source modules from the 
main `apache/shenyu` repository.
+The example modules consume **independent 
[shenyu-client-java](https://github.com/apache/shenyu-client-java) artifacts** 
from Maven Central, rather than depending on in-tree source modules from the 
main `apache/shenyu` repository.
 
 This reflects the recommended approach for real users: add the ShenYu client 
library as an external Maven dependency.
 
@@ -18,6 +18,8 @@ The parent POM (`shenyu-examples/pom.xml`) defines a property:
 
 All `shenyu-client-*` and `shenyu-spring-boot-starter-client-*` dependencies 
in individual example POMs are managed by this property through the 
`<dependencyManagement>` section. The version is inherited automatically — 
individual examples do not specify a `<version>` for these artifacts.
 
+The released version remains the default so a clean standalone checkout can 
resolve the client artifacts from Maven Central. To test unreleased MCP fixes, 
explicitly override this property as described below.
+
 ### Which Artifacts Are Consumed Externally
 
 | Client Artifact | Used By |
@@ -42,7 +44,44 @@ The following dependencies remain internal to this 
repository (`${project.versio
 - `shenyu-sdk-*` — ShenYu SDK modules (not extracted to shenyu-client-java)
 - `shenyu-spring-boot-starter-sdk-*` — ShenYu SDK starters (not extracted to 
shenyu-client-java)
 
-## Testing with Unreleased Client Artifacts
+## Testing the MCP Example with 2.7.2-SNAPSHOT
+
+Use JDK 17 and run the following commands from the root of this 
`apache/shenyu` checkout. Use a checkout containing the MCP fixes: installing 
an unrelated snapshot from `shenyu-client-java` does not provide the changes in 
this repository.
+
+### Build and Run the MCP Example
+
+The MCP example uses both the MCP and Spring MVC client starters. Build both 
starters and their required reactor modules into the local Maven repository:
+
+```bash
+./mvnw install -pl 
shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-mcp,shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc
 -am -DskipTests
+```
+
+Build the MCP example and its shared example utilities using the separate 
examples reactor:
+
+```bash
+./mvnw -f shenyu-examples/pom.xml install -pl shenyu-examples-mcp -am \
+  -Dshenyu.client.java.version=2.7.2-SNAPSHOT -DskipTests
+```
+
+Before starting the example, configure 
`shenyu-examples/shenyu-examples-mcp/src/main/resources/application.yml` for 
your ShenYu Admin address, credentials, namespace, and reachable service 
address. Run ShenYu Admin and Bootstrap from a checkout containing the 
corresponding MCP gateway fixes.
+
+```bash
+./mvnw -f shenyu-examples/pom.xml spring-boot:run -pl shenyu-examples-mcp \
+  -Dshenyu.client.java.version=2.7.2-SNAPSHOT
+```
+
+Keep the same version override for build and run commands. This selects 
`2.7.2-SNAPSHOT` for both starters through the parent dependency management. 
Check the resolved ShenYu dependencies with:
+
+```bash
+./mvnw -f shenyu-examples/pom.xml dependency:tree -pl shenyu-examples-mcp \
+  -Dshenyu.client.java.version=2.7.2-SNAPSHOT '-Dincludes=org.apache.shenyu:*'
+```
+
+Confirm that the selected MCP and Spring MVC starters and their shared client 
dependencies use `2.7.2-SNAPSHOT`. Then verify that the example registers its 
upstream and MCP tools in ShenYu Admin and that a tool invocation through the 
gateway succeeds.
+
+Other examples continue to use the released `2.7.0.3` default and do not 
require this override.
+
+## Testing with Other Unreleased Client Artifacts
 
 When developing or testing changes to the `shenyu-client-java` library, you 
may need to build the client artifacts locally and use them in these examples.
 
@@ -59,8 +98,9 @@ mvn clean install -DskipTests
 This installs the artifacts with the project's snapshot version (e.g., 
`2.7.0.1-jdk8-SNAPSHOT`). Then, build the examples using that version:
 
 ```bash
-cd shenyu
-mvn clean install -pl shenyu-examples 
-Dshenyu.client.java.version=2.7.0.1-jdk8-SNAPSHOT
+# Run from the apache/shenyu repository root; select the example to test.
+mvn -f shenyu-examples/pom.xml clean install -pl shenyu-examples-springmvc -am 
\
+  -Dshenyu.client.java.version=2.7.0.1-jdk8-SNAPSHOT
 ```
 
 ### Option 2: Modify the Property in Parent POM
@@ -71,7 +111,7 @@ For a more permanent change during development, edit the 
`<shenyu.client.java.ve
 <shenyu.client.java.version>2.7.0.1-jdk8-SNAPSHOT</shenyu.client.java.version>
 ```
 
-> **Note**: When using `-SNAPSHOT` versions, you may need to configure the 
Apache Snapshot Repository in your `settings.xml` or POM:
+> **Note**: Locally installed snapshots do not require a remote snapshot 
repository. If the required snapshot has been published, you can configure the 
Apache Snapshot Repository in your POM or an active profile in `settings.xml` 
(inside `<repositories>`):
 > ```xml
 > <repository>
 >     <id>apache-snapshots</id>
@@ -87,10 +127,11 @@ For a more permanent change during development, edit the 
`<shenyu.client.java.ve
 If the snapshot has been published to the Apache snapshot repository:
 
 ```bash
-mvn clean install -pl shenyu-examples 
-Dshenyu.client.java.version=2.7.0.1-jdk8-SNAPSHOT -U
+mvn -f shenyu-examples/pom.xml clean install -pl shenyu-examples-springmvc -am 
\
+  -Dshenyu.client.java.version=2.7.0.1-jdk8-SNAPSHOT -U
 ```
 
-The `-U` flag forces Maven to check for updated snapshots.
+The `-U` flag forces Maven to check for updated snapshots. It does not publish 
missing artifacts or replace the local build required for unpublished fixes.
 
 ## Verification
 
@@ -98,16 +139,16 @@ After updating the client version, verify that the 
examples can register with a
 
 ### Prerequisites
 
-Before running any example, install the shared example utilities to your local 
Maven repository:
+After preparing the client dependencies as described above, install the shared 
example utilities and their parent POM to your local Maven repository:
 
 ```bash
 cd shenyu-examples
-mvn install -pl shenyu-examples-common -DskipTests
+mvn install -pl shenyu-examples-common -am -DskipTests
 ```
 
 ### Run Examples
 
-Run the example from the `shenyu-examples` reactor root using `-pl` so 
internal dependencies like `shenyu-examples-common` are resolved automatically:
+Run the example from the `shenyu-examples` reactor root using `-pl`. The run 
command uses the internal dependencies installed in the previous step:
 
 ```bash
 cd shenyu-examples
@@ -122,7 +163,7 @@ mvn spring-boot:run -pl shenyu-examples-springmvc
 # mvn spring-boot:run -pl shenyu-examples-mcp
 ```
 
-> **Note**: Running `mvn spring-boot:run` directly from within an individual 
example directory (e.g., `cd shenyu-examples-springmvc && mvn spring-boot:run`) 
will fail because `shenyu-examples-common` and the parent POM cannot be 
resolved outside the reactor. Always use the `-pl` approach from the 
`shenyu-examples/` root.
+> **Note**: `-pl` selects an example but does not build its dependencies. 
Install the required internal modules before running it. If you used a client 
version override when building, pass the same override to `spring-boot:run`.
 
 ### Confirm Registration
 
diff --git 
a/shenyu-examples/shenyu-examples-mcp/src/main/java/org/apache/shenyu/ShenyuTestMcpApplication.java
 
b/shenyu-examples/shenyu-examples-mcp/src/main/java/org/apache/shenyu/examples/ShenyuTestMcpApplication.java
similarity index 97%
rename from 
shenyu-examples/shenyu-examples-mcp/src/main/java/org/apache/shenyu/ShenyuTestMcpApplication.java
rename to 
shenyu-examples/shenyu-examples-mcp/src/main/java/org/apache/shenyu/examples/ShenyuTestMcpApplication.java
index 9950241f11..e5a383c4f7 100644
--- 
a/shenyu-examples/shenyu-examples-mcp/src/main/java/org/apache/shenyu/ShenyuTestMcpApplication.java
+++ 
b/shenyu-examples/shenyu-examples-mcp/src/main/java/org/apache/shenyu/examples/ShenyuTestMcpApplication.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.shenyu;
+package org.apache.shenyu.examples;
 
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
diff --git 
a/shenyu-examples/shenyu-examples-mcp/src/main/java/org/apache/shenyu/controller/OrderController.java
 
b/shenyu-examples/shenyu-examples-mcp/src/main/java/org/apache/shenyu/examples/controller/OrderController.java
similarity index 97%
rename from 
shenyu-examples/shenyu-examples-mcp/src/main/java/org/apache/shenyu/controller/OrderController.java
rename to 
shenyu-examples/shenyu-examples-mcp/src/main/java/org/apache/shenyu/examples/controller/OrderController.java
index e09f3c7d51..1c3f4af24b 100644
--- 
a/shenyu-examples/shenyu-examples-mcp/src/main/java/org/apache/shenyu/controller/OrderController.java
+++ 
b/shenyu-examples/shenyu-examples-mcp/src/main/java/org/apache/shenyu/examples/controller/OrderController.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.shenyu.controller;
+package org.apache.shenyu.examples.controller;
 
 import io.swagger.v3.oas.annotations.OpenAPIDefinition;
 import io.swagger.v3.oas.annotations.Operation;
@@ -30,7 +30,7 @@ import 
org.apache.shenyu.client.mcp.common.annotation.ShenyuMcpRequestConfig;
 import org.apache.shenyu.client.mcp.common.annotation.ShenyuMcpTool;
 import org.apache.shenyu.client.mcp.common.annotation.ShenyuMcpToolParam;
 import org.apache.shenyu.client.springmvc.annotation.ShenyuSpringMvcClient;
-import org.apache.shenyu.dto.OrderDTO;
+import org.apache.shenyu.examples.dto.OrderDTO;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
diff --git 
a/shenyu-examples/shenyu-examples-mcp/src/main/java/org/apache/shenyu/dto/OrderDTO.java
 
b/shenyu-examples/shenyu-examples-mcp/src/main/java/org/apache/shenyu/examples/dto/OrderDTO.java
similarity index 97%
rename from 
shenyu-examples/shenyu-examples-mcp/src/main/java/org/apache/shenyu/dto/OrderDTO.java
rename to 
shenyu-examples/shenyu-examples-mcp/src/main/java/org/apache/shenyu/examples/dto/OrderDTO.java
index c97c963ed9..79aa0da5e4 100644
--- 
a/shenyu-examples/shenyu-examples-mcp/src/main/java/org/apache/shenyu/dto/OrderDTO.java
+++ 
b/shenyu-examples/shenyu-examples-mcp/src/main/java/org/apache/shenyu/examples/dto/OrderDTO.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.shenyu.dto;
+package org.apache.shenyu.examples.dto;
 
 import java.io.Serializable;
 import java.util.StringJoiner;
diff --git 
a/shenyu-examples/shenyu-examples-mcp/src/main/resources/application.yml 
b/shenyu-examples/shenyu-examples-mcp/src/main/resources/application.yml
index accdbbb2ef..38dbf7fc03 100644
--- a/shenyu-examples/shenyu-examples-mcp/src/main/resources/application.yml
+++ b/shenyu-examples/shenyu-examples-mcp/src/main/resources/application.yml
@@ -18,8 +18,6 @@ server:
   address: 0.0.0.0
   tomcat:
     max-http-form-post-size: 100MB
-  servlet:
-    context-path: /mcp
 shenyu:
   namespace: 649330b6-c2d7-4edc-be8e-8a54df9eb385
   register:
diff --git 
a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallback.java
 
b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallback.java
index aa1bd65707..061bc7a564 100644
--- 
a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallback.java
+++ 
b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallback.java
@@ -24,6 +24,7 @@ import com.google.gson.JsonObject;
 import io.modelcontextprotocol.server.McpSyncServerExchange;
 import org.apache.shenyu.common.constant.Constants;
 import org.apache.shenyu.common.dto.MetaData;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
 import org.apache.shenyu.common.utils.GsonUtils;
 import org.apache.shenyu.plugin.api.ShenyuPluginChain;
 import org.apache.shenyu.plugin.api.context.ShenyuContext;
@@ -681,9 +682,11 @@ public class ShenyuToolCallback implements ToolCallback {
             MetaData metaData = 
MetaDataCache.getInstance().obtain(configHelper.getUrlTemplate());
             if (Objects.nonNull(metaData) && 
Boolean.TRUE.equals(metaData.getEnabled())) {
                 decoratedExchange.getAttributes().put(Constants.META_DATA, 
metaData);
-                shenyuContext.setRpcType(metaData.getRpcType());
             }
 
+            // MCP tools are invoked through an internal HTTP request. The 
response plugin
+            // must therefore select an HTTP MessageWriter rather than an MCP 
writer.
+            shenyuContext.setRpcType(RpcTypeEnum.HTTP.getName());
             shenyuContext.setPath(extractRawPath(decoratedPath));
 
             final Map<String, Object> attributes = 
decoratedExchange.getAttributes();
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 856caa704c..d2104e272c 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
@@ -17,12 +17,10 @@
 
 package org.apache.shenyu.plugin.mcp.server.callback;
 
-import io.modelcontextprotocol.server.McpSyncServerExchange;
 import io.modelcontextprotocol.common.McpTransportContext;
-import org.springframework.test.util.ReflectionTestUtils;
-import java.util.Map;
-import static org.junit.jupiter.api.Assertions.assertSame;
+import io.modelcontextprotocol.server.McpSyncServerExchange;
 import org.apache.shenyu.common.constant.Constants;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
 import org.apache.shenyu.plugin.api.ShenyuPluginChain;
 import org.apache.shenyu.plugin.api.context.ShenyuContext;
 import org.apache.shenyu.plugin.mcp.server.definition.ShenyuToolDefinition;
@@ -40,20 +38,24 @@ import org.springframework.ai.chat.model.ToolContext;
 import org.springframework.http.server.reactive.ServerHttpRequest;
 import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
 import org.springframework.mock.web.server.MockServerWebExchange;
+import org.springframework.test.util.ReflectionTestUtils;
 import org.springframework.web.server.ServerWebExchange;
 import reactor.core.publisher.Mono;
 
 import java.net.URI;
 import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 /**
@@ -261,6 +263,19 @@ class ShenyuToolCallbackTest {
         });
     }
 
+    @Test
+    void testToolCallUsesHttpRpcType() {
+        shenyuToolCallback = new ShenyuToolCallback(toolDefinition);
+        
when(exchange.getAttribute(Constants.CONTEXT)).thenReturn(shenyuContext);
+        when(exchange.getAttributes()).thenReturn(new HashMap<>());
+
+        ReflectionTestUtils.invokeMethod(shenyuToolCallback, 
"configureShenyuContext", exchange,
+                "session123", "/mcp/order/findAll",
+                
"{\"requestTemplate\":{\"url\":\"/mcp/order/findAll\",\"method\":\"GET\"},\"argsPosition\":{}}");
+
+        verify(shenyuContext).setRpcType(RpcTypeEnum.HTTP.getName());
+    }
+
     @Test
     void testExecuteToolCallDisposesPluginChainOnTimeout() {
         shenyuToolCallback = new ShenyuToolCallback(toolDefinition);
diff --git 
a/shenyu-examples/shenyu-examples-mcp/src/main/resources/application.yml 
b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-mcp/src/main/resources/META-INF/spring.provides
similarity index 60%
copy from shenyu-examples/shenyu-examples-mcp/src/main/resources/application.yml
copy to 
shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-mcp/src/main/resources/META-INF/spring.provides
index accdbbb2ef..43e1f868c1 100644
--- a/shenyu-examples/shenyu-examples-mcp/src/main/resources/application.yml
+++ 
b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-mcp/src/main/resources/META-INF/spring.provides
@@ -1,3 +1,4 @@
+#
 # 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.
@@ -12,31 +13,6 @@
 # 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.
+#
 
-server:
-  port: 8150
-  address: 0.0.0.0
-  tomcat:
-    max-http-form-post-size: 100MB
-  servlet:
-    context-path: /mcp
-shenyu:
-  namespace: 649330b6-c2d7-4edc-be8e-8a54df9eb385
-  register:
-    enable: true
-    registerType: http
-    serverLists: http://localhost:9095
-    props:
-      username: admin
-      password: 123456
-  client:
-    mcp:
-      props:
-        contextPath: /mcp
-        appName: mcp
-    http:
-      props:
-        contextPath: /mcp
-        appName: mcp
-        host: 127.0.0.1
-        port: 8150
+provides: shenyu-spring-boot-starter-client-mcp
diff --git 
a/shenyu-examples/shenyu-examples-mcp/src/main/resources/application.yml 
b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-mcp/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
similarity index 60%
copy from shenyu-examples/shenyu-examples-mcp/src/main/resources/application.yml
copy to 
shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-mcp/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index accdbbb2ef..1e553ca260 100644
--- a/shenyu-examples/shenyu-examples-mcp/src/main/resources/application.yml
+++ 
b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-mcp/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1,3 +1,4 @@
+#
 # 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.
@@ -12,31 +13,6 @@
 # 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.
+#
 
-server:
-  port: 8150
-  address: 0.0.0.0
-  tomcat:
-    max-http-form-post-size: 100MB
-  servlet:
-    context-path: /mcp
-shenyu:
-  namespace: 649330b6-c2d7-4edc-be8e-8a54df9eb385
-  register:
-    enable: true
-    registerType: http
-    serverLists: http://localhost:9095
-    props:
-      username: admin
-      password: 123456
-  client:
-    mcp:
-      props:
-        contextPath: /mcp
-        appName: mcp
-    http:
-      props:
-        contextPath: /mcp
-        appName: mcp
-        host: 127.0.0.1
-        port: 8150
+org.apache.shenyu.springboot.starter.client.mcp.ShenyuMcpClientConfiguration

Reply via email to