diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index afb6c11..ccf9c3f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -24,8 +24,9 @@ on:
jobs:
build:
strategy:
+ fail-fast: false
matrix:
- java: [ 8, 11, 17 ]
+ java: [ 8, 17, 21, 23, 25 ]
os: [ ubuntu-latest ]
runs-on: ${{ matrix.os }}
if: (github.repository == 'apache/shenyu-client-java')
@@ -33,7 +34,7 @@ jobs:
- name: Support longpaths
if: ${{ matrix.os == 'windows-latest'}}
run: git config --system core.longpaths true
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
submodules: true
# - uses: dorny/paths-filter@v2
@@ -41,40 +42,26 @@ jobs:
# with:
# filters: '.github/filters.yml'
# list-files: json
- - name: Restore ShenYu Maven Repos
-# if: steps.filter.outputs.changed == 'true'
- id: restore-maven-cache
- uses: actions/cache/restore@v3
- with:
- path: ~/.m2/repository
- key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- ${{ runner.os }}-maven-
- - uses: actions/setup-java@v1
+ - uses: actions/setup-java@v4
# if: steps.filter.outputs.changed == 'true'
with:
+ distribution: temurin
java-version: ${{ matrix.java }}
+ cache: maven
- name: Build with Maven
# if: steps.filter.outputs.changed == 'true'
- run: ./mvnw -B clean test -Prelease
- - uses: codecov/codecov-action@v1
+ run: ./mvnw -B clean test
+ - uses: codecov/codecov-action@v4
with:
token: 2760af6a-3405-4882-9e61-04c5176fecfa
# if: steps.filter.outputs.changed == 'true'
- - name: Save ShenYu Maven Repos
-# if: steps.filter.outputs.changed == 'true' && steps.restore-maven-cache.outputs.cache-hit != 'true'
- if: steps.restore-maven-cache.outputs.cache-hit != 'true'
- uses: actions/cache/save@v3
- with:
- path: ~/.m2/repository
- key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
check-license-header:
name: check-license-header
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
submodules: true
- name: Check License Header
@@ -92,4 +79,3 @@ jobs:
- name: checking job status
run: |
[[ "${{ needs.build.result }}" == "success" ]] || exit -1
-
diff --git a/pom.xml b/pom.xml
index de025b8..bbae5f8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,8 +29,10 @@
pom2.7.0.1-jdk8-SNAPSHOT
+ shenyu-registry-apishenyu-client-coreshenyu-client-http
+ shenyu-client-mcpshenyu-client-dubboshenyu-client-sofashenyu-client-tars
@@ -39,6 +41,8 @@
shenyu-client-websocketshenyu-client-api-docs-annotationsshenyu-client-autoconfig
+ shenyu-register-client-beat
+ shenyu-spring-boot-starter-client
@@ -76,7 +80,7 @@
false1.6.0
- 0.8.12
+ 0.8.151.6.31.63.0.1
@@ -116,7 +120,9 @@
4.9.31.783.5.15
+ 1.18.104.0.3
+ 2.2.21
@@ -197,6 +203,16 @@
bcprov-jdk18on${bcprov-jdk18on.version}
+
+ net.bytebuddy
+ byte-buddy
+ ${byte-buddy.version}
+
+
+ net.bytebuddy
+ byte-buddy-agent
+ ${byte-buddy.version}
+
diff --git a/progress.md b/progress.md
new file mode 100644
index 0000000..70e2f6b
--- /dev/null
+++ b/progress.md
@@ -0,0 +1,51 @@
+# Client Split Migration Progress
+
+## Baseline
+
+- Working branch: `split-client-jdk8-base`
+- Base branch: `origin/2.7.0.1-jdk8-release`
+- Goal: keep the split client repository JDK 8 compatible while preparing it to absorb client-only changes from the main ShenYu repository.
+
+## Done
+
+- Selected the existing `2.7.0.1-jdk8-release` branch as the migration base because it already preserves Java 8 source level and Apache release metadata.
+- Added the MCP client module from the main ShenYu repository.
+- Rebased MCP module POMs onto `shenyu-client-java` and removed Spring Boot 3 / springdoc starter coupling.
+- Added MCP registration DTO, disruptor subscriber, RPC/data types, and HTTP register path support in `shenyu-client-core`.
+- Added a Java 8 compatible `RequestMethodUtils` for MCP method and parameter discovery.
+- Added the Spring Boot starter client modules from the main ShenYu repository.
+- Rebased starter POMs onto this split client repo and rewired imports to `shenyu-client-core`.
+- Restored the beat register client and beat Spring Boot starter.
+- Restored the registry API module needed by instance registration.
+- Restored Spring MVC, gRPC, and Spring WebSocket discovery instance auto-configuration.
+- Added the minimal client-core DTO/config/registry support needed by beat and discovery without pulling in the full main-repo `shenyu-common` module.
+- Updated GitHub Actions CI to run the Maven build on JDK 8, 17, 21, 23, and 25.
+- Kept the SOFA starter test dependency on the JDK 8 compatible SOFA Boot 3.1.4 line.
+- Upgraded JaCoCo to 0.8.15 so coverage instrumentation recognizes JDK 25 class files.
+- Overrode Byte Buddy test dependencies to 1.18.10 so Mockito 3.x tests can run on JDK 21, 23, and 25 while preserving JDK 8 runtime compatibility.
+
+## Verification
+
+- `./mvnw -pl shenyu-client-mcp/shenyu-client-mcp-register -am -DskipTests compile` passed.
+- `./mvnw -pl shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-beat -am -DskipTests compile` passed.
+- `./mvnw -DskipTests compile` passed.
+- `./mvnw -DskipTests test` passed.
+- `./mvnw -pl shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc -am test` passed.
+- `./mvnw test` passed across the full 30-module reactor.
+- `JAVA_HOME=$(/usr/libexec/java_home -v 1.8) ./mvnw -B clean test -Prelease` passed across the full 30-module reactor.
+- `JAVA_HOME=$(/usr/libexec/java_home -v 17) ./mvnw -B clean test -Prelease` passed across the full 30-module reactor.
+- `JAVA_HOME=$(/usr/libexec/java_home -v 21) ./mvnw -B clean test -Prelease` passed across the full 30-module reactor.
+- `JAVA_HOME=$(/usr/libexec/java_home -v 25) ./mvnw -B clean test` passed across the full 30-module reactor after the JaCoCo and Byte Buddy upgrades.
+- Downloaded Temurin JDK 23.0.2+7 from Adoptium to `/tmp/shenyu-jdks`, verified SHA256 `749993e751f085c7ae713140066a90800075e4aeedfac50a5ed0c5457131c5a0`, and ran `JAVA_HOME=/tmp/shenyu-jdks/jdk-23.0.2+7/Contents/Home ./mvnw -B clean test` successfully across the full 30-module reactor.
+- Re-ran `JAVA_HOME=$(/usr/libexec/java_home -v 1.8) ./mvnw -B clean test` successfully after the JaCoCo and Byte Buddy upgrades.
+- The CI command now omits `-Prelease` because Maven reports that profile does not exist in this split repository.
+- `.github/workflows/ci.yml` YAML parsing passed.
+- Compatibility scan found no remaining old `org.apache.shenyu.common`, `org.apache.shenyu.register.common`, `org.apache.shenyu.register.client.http`, or `jakarta.*` imports in the restored beat/discovery code.
+- Java 8 syntax scan found no `List.of`, `Map.of`, `Set.of`, or pattern-matching `instanceof` usage in the migrated MCP/starter modules.
+- The full test run still logs existing background exceptions from original async tests under JDK 17, but Surefire reports reactor success.
+
+## Next Steps
+
+- Confirm the expanded GitHub Actions matrix on the remote runner after opening the pull request.
+- Verify beat and discovery instance registration against matching admin/bootstrap runtime.
+- Verify `/shenyu-client/register-mcp` against the matching admin side before treating MCP registration as end-to-end complete.
diff --git a/shenyu-client-core/pom.xml b/shenyu-client-core/pom.xml
index 0c008e9..e854e6a 100644
--- a/shenyu-client-core/pom.xml
+++ b/shenyu-client-core/pom.xml
@@ -31,11 +31,16 @@
shenyu-disruptor2.6.1
-
-
-
-
-
+
+ org.apache.shenyu
+ shenyu-spi
+ 2.6.1
+
+
+ org.apache.shenyu
+ shenyu-registry-api
+ ${project.version}
+ com.github.ben-manes.caffeine
diff --git a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/config/ShenyuConfig.java b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/config/ShenyuConfig.java
new file mode 100644
index 0000000..fb66e04
--- /dev/null
+++ b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/config/ShenyuConfig.java
@@ -0,0 +1,36 @@
+/*
+ * 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.shenyu.client.core.config;
+
+import org.apache.shenyu.client.core.constant.Constants;
+
+/**
+ * Minimal client-side ShenYu configuration used by heartbeat reporting.
+ */
+public class ShenyuConfig {
+
+ private String namespace = Constants.SYS_DEFAULT_NAMESPACE_ID;
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public void setNamespace(final String namespace) {
+ this.namespace = namespace;
+ }
+}
diff --git a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/constant/Constants.java b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/constant/Constants.java
index 8489011..d620c49 100644
--- a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/constant/Constants.java
+++ b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/constant/Constants.java
@@ -269,6 +269,11 @@ public interface Constants {
*/
String HEARTBEAT = "heartbeat";
+ /**
+ * The constant BEAT_URI_PATH.
+ */
+ String BEAT_URI_PATH = "/instance/beat";
+
/**
* The constant header key of sign plugin version-2.
*/
@@ -623,6 +628,16 @@ public interface Constants {
* When register by http, the uri path.
*/
String URI_PATH = "/shenyu-client/register-uri";
+
+ /**
+ * When register by http, the mcp tools path.
+ */
+ String MCP_TOOLS_PATH = "/shenyu-client/register-mcp";
+
+ /**
+ * When register by http, the mcp tools type.
+ */
+ String MCP_TOOLS_TYPE = "mcp";
/**
* When register by http, the offline path.
diff --git a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/constant/InstanceTypeConstants.java b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/constant/InstanceTypeConstants.java
new file mode 100644
index 0000000..d3c5d4a
--- /dev/null
+++ b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/constant/InstanceTypeConstants.java
@@ -0,0 +1,32 @@
+/*
+ * 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.shenyu.client.core.constant;
+
+/**
+ * Instance type constants.
+ */
+public final class InstanceTypeConstants {
+
+ /**
+ * Bootstrap instance info type.
+ */
+ public static final String BOOTSTRAP_INSTANCE_INFO = "bootstrapInstanceInfo";
+
+ private InstanceTypeConstants() {
+ }
+}
diff --git a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/disruptor/ShenyuClientRegisterEventPublisher.java b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/disruptor/ShenyuClientRegisterEventPublisher.java
index b5eaea0..8743900 100644
--- a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/disruptor/ShenyuClientRegisterEventPublisher.java
+++ b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/disruptor/ShenyuClientRegisterEventPublisher.java
@@ -19,6 +19,7 @@
import org.apache.shenyu.client.core.disruptor.executor.RegisterClientConsumerExecutor.RegisterClientExecutorFactory;
import org.apache.shenyu.client.core.disruptor.subcriber.ShenyuClientApiDocExecutorSubscriber;
+import org.apache.shenyu.client.core.disruptor.subcriber.ShenyuClientMcpExecutorSubscriber;
import org.apache.shenyu.client.core.disruptor.subcriber.ShenyuClientMetadataExecutorSubscriber;
import org.apache.shenyu.client.core.disruptor.subcriber.ShenyuClientURIExecutorSubscriber;
import org.apache.shenyu.client.core.register.ShenyuClientRegisterRepository;
@@ -54,6 +55,7 @@ public void start(final ShenyuClientRegisterRepository shenyuClientRegisterRepos
factory.addSubscribers(new ShenyuClientMetadataExecutorSubscriber(shenyuClientRegisterRepository));
factory.addSubscribers(new ShenyuClientURIExecutorSubscriber(shenyuClientRegisterRepository));
factory.addSubscribers(new ShenyuClientApiDocExecutorSubscriber(shenyuClientRegisterRepository));
+ factory.addSubscribers(new ShenyuClientMcpExecutorSubscriber(shenyuClientRegisterRepository));
providerManage = new DisruptorProviderManage<>(factory);
providerManage.startup();
}
diff --git a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/disruptor/subcriber/ShenyuClientMcpExecutorSubscriber.java b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/disruptor/subcriber/ShenyuClientMcpExecutorSubscriber.java
new file mode 100644
index 0000000..dd88746
--- /dev/null
+++ b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/disruptor/subcriber/ShenyuClientMcpExecutorSubscriber.java
@@ -0,0 +1,52 @@
+/*
+ * 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.shenyu.client.core.disruptor.subcriber;
+
+import org.apache.shenyu.client.core.dto.McpToolsRegisterDTO;
+import org.apache.shenyu.client.core.register.ShenyuClientRegisterRepository;
+import org.apache.shenyu.client.core.register.subsriber.ExecutorTypeSubscriber;
+import org.apache.shenyu.client.core.type.DataType;
+
+import java.util.Collection;
+
+/**
+ * The type Shenyu client mcp executor subscriber.
+ */
+public class ShenyuClientMcpExecutorSubscriber implements ExecutorTypeSubscriber {
+
+ private final ShenyuClientRegisterRepository shenyuClientRegisterRepository;
+
+ /**
+ * Instantiates a new Shenyu client mcp executor subscriber.
+ *
+ * @param shenyuClientRegisterRepository the shenyu client register repository
+ */
+ public ShenyuClientMcpExecutorSubscriber(final ShenyuClientRegisterRepository shenyuClientRegisterRepository) {
+ this.shenyuClientRegisterRepository = shenyuClientRegisterRepository;
+ }
+
+ @Override
+ public void executor(final Collection dataList) {
+ dataList.forEach(shenyuClientRegisterRepository::persistMcpTools);
+ }
+
+ @Override
+ public DataType getType() {
+ return DataType.MCP_TOOLS;
+ }
+}
diff --git a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/dto/DiscoveryUpstreamData.java b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/dto/DiscoveryUpstreamData.java
new file mode 100644
index 0000000..15ba3aa
--- /dev/null
+++ b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/dto/DiscoveryUpstreamData.java
@@ -0,0 +1,130 @@
+/*
+ * 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.shenyu.client.core.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+import java.sql.Timestamp;
+
+/**
+ * Discovery upstream data used by client-side instance discovery registration.
+ */
+public class DiscoveryUpstreamData {
+
+ private String id;
+
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Timestamp dateCreated;
+
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Timestamp dateUpdated;
+
+ private String discoveryHandlerId;
+
+ private String protocol;
+
+ private String url;
+
+ private int status;
+
+ private int weight;
+
+ private String props;
+
+ private String namespaceId;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(final String id) {
+ this.id = id;
+ }
+
+ public Timestamp getDateCreated() {
+ return dateCreated;
+ }
+
+ public void setDateCreated(final Timestamp dateCreated) {
+ this.dateCreated = dateCreated;
+ }
+
+ public Timestamp getDateUpdated() {
+ return dateUpdated;
+ }
+
+ public void setDateUpdated(final Timestamp dateUpdated) {
+ this.dateUpdated = dateUpdated;
+ }
+
+ public String getDiscoveryHandlerId() {
+ return discoveryHandlerId;
+ }
+
+ public void setDiscoveryHandlerId(final String discoveryHandlerId) {
+ this.discoveryHandlerId = discoveryHandlerId;
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(final String protocol) {
+ this.protocol = protocol;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(final String url) {
+ this.url = url;
+ }
+
+ public int getStatus() {
+ return status;
+ }
+
+ public void setStatus(final int status) {
+ this.status = status;
+ }
+
+ public int getWeight() {
+ return weight;
+ }
+
+ public void setWeight(final int weight) {
+ this.weight = weight;
+ }
+
+ public String getProps() {
+ return props;
+ }
+
+ public void setProps(final String props) {
+ this.props = props;
+ }
+
+ public String getNamespaceId() {
+ return namespaceId;
+ }
+
+ public void setNamespaceId(final String namespaceId) {
+ this.namespaceId = namespaceId;
+ }
+}
diff --git a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/dto/InstanceBeatInfoDTO.java b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/dto/InstanceBeatInfoDTO.java
new file mode 100644
index 0000000..1e6c11a
--- /dev/null
+++ b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/dto/InstanceBeatInfoDTO.java
@@ -0,0 +1,74 @@
+/*
+ * 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.shenyu.client.core.dto;
+
+/**
+ * Bootstrap heartbeat payload.
+ */
+public class InstanceBeatInfoDTO {
+
+ private String instanceIp;
+
+ private String instancePort;
+
+ private String instanceType;
+
+ private String instanceInfo;
+
+ private String namespaceId;
+
+ public String getInstanceIp() {
+ return instanceIp;
+ }
+
+ public void setInstanceIp(final String instanceIp) {
+ this.instanceIp = instanceIp;
+ }
+
+ public String getInstancePort() {
+ return instancePort;
+ }
+
+ public void setInstancePort(final String instancePort) {
+ this.instancePort = instancePort;
+ }
+
+ public String getInstanceType() {
+ return instanceType;
+ }
+
+ public void setInstanceType(final String instanceType) {
+ this.instanceType = instanceType;
+ }
+
+ public String getInstanceInfo() {
+ return instanceInfo;
+ }
+
+ public void setInstanceInfo(final String instanceInfo) {
+ this.instanceInfo = instanceInfo;
+ }
+
+ public String getNamespaceId() {
+ return namespaceId;
+ }
+
+ public void setNamespaceId(final String namespaceId) {
+ this.namespaceId = namespaceId;
+ }
+}
diff --git a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/dto/McpToolsRegisterDTO.java b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/dto/McpToolsRegisterDTO.java
new file mode 100644
index 0000000..56af176
--- /dev/null
+++ b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/dto/McpToolsRegisterDTO.java
@@ -0,0 +1,92 @@
+/*
+ * 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.shenyu.client.core.dto;
+
+import org.apache.shenyu.client.core.type.DataType;
+import org.apache.shenyu.client.core.type.DataTypeParent;
+
+/**
+ * Mcp tools register dto.
+ */
+public class McpToolsRegisterDTO implements DataTypeParent {
+
+ private MetaDataRegisterDTO metaDataRegisterDTO;
+
+ private String namespaceId;
+
+ private String mcpConfig;
+
+ /**
+ * get meta data register dto.
+ *
+ * @return metaDataRegisterDTO
+ */
+ public MetaDataRegisterDTO getMetaDataRegisterDTO() {
+ return metaDataRegisterDTO;
+ }
+
+ /**
+ * set meta data register dto.
+ *
+ * @param metaDataRegisterDTO metaDataRegisterDTO
+ */
+ public void setMetaDataRegisterDTO(final MetaDataRegisterDTO metaDataRegisterDTO) {
+ this.metaDataRegisterDTO = metaDataRegisterDTO;
+ }
+
+ /**
+ * get mcp config.
+ *
+ * @return mcpConfig
+ */
+ public String getMcpConfig() {
+ return mcpConfig;
+ }
+
+ /**
+ * set mcp config.
+ *
+ * @param mcpConfig mcpConfig
+ */
+ public void setMcpConfig(final String mcpConfig) {
+ this.mcpConfig = mcpConfig;
+ }
+
+ /**
+ * get namespace id.
+ *
+ * @return namespaceId
+ */
+ public String getNamespaceId() {
+ return namespaceId;
+ }
+
+ /**
+ * set namespace id.
+ *
+ * @param namespaceId namespaceId
+ */
+ public void setNamespaceId(final String namespaceId) {
+ this.namespaceId = namespaceId;
+ }
+
+ @Override
+ public DataType getType() {
+ return DataType.MCP_TOOLS;
+ }
+}
diff --git a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/enums/RpcTypeEnum.java b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/enums/RpcTypeEnum.java
index 3a44ac0..8fda93c 100644
--- a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/enums/RpcTypeEnum.java
+++ b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/enums/RpcTypeEnum.java
@@ -69,7 +69,12 @@ public enum RpcTypeEnum {
/**
* ai.
*/
- AI("ai", true);
+ AI("ai", true),
+
+ /**
+ * mcp.
+ */
+ MCP("mcp", true);
private final String name;
diff --git a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/FailbackRegistryRepository.java b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/FailbackRegistryRepository.java
index 28ee568..7301545 100644
--- a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/FailbackRegistryRepository.java
+++ b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/FailbackRegistryRepository.java
@@ -19,6 +19,7 @@
import org.apache.shenyu.client.core.constant.Constants;
import org.apache.shenyu.client.core.dto.ApiDocRegisterDTO;
+import org.apache.shenyu.client.core.dto.McpToolsRegisterDTO;
import org.apache.shenyu.client.core.dto.MetaDataRegisterDTO;
import org.apache.shenyu.client.core.dto.URIRegisterDTO;
import org.apache.shenyu.client.core.register.retry.FailureRegistryTask;
@@ -96,6 +97,21 @@ public void persistApiDoc(final ApiDocRegisterDTO registerDTO) {
}
}
+ /**
+ * Persist mcp tools.
+ *
+ * @param registerDTO registerDTO
+ */
+ @Override
+ public void persistMcpTools(final McpToolsRegisterDTO registerDTO) {
+ try {
+ this.doPersistMcpTools(registerDTO);
+ } catch (Exception ex) {
+ logger.warn("Failed to persistMcpTools {}, cause:{}", registerDTO, ex.getMessage());
+ this.addFailureMcpToolsRegister(registerDTO);
+ }
+ }
+
/**
* doPersistApiDoc.
*
@@ -103,6 +119,13 @@ public void persistApiDoc(final ApiDocRegisterDTO registerDTO) {
*/
protected abstract void doPersistApiDoc(ApiDocRegisterDTO apiDocRegisterDTO);
+ /**
+ * doPersistMcpTools.
+ *
+ * @param mcpToolsRegisterDTO mcpToolsRegisterDTO
+ */
+ protected abstract void doPersistMcpTools(McpToolsRegisterDTO mcpToolsRegisterDTO);
+
/**
* Add failure meta data register.
*
@@ -145,6 +168,20 @@ protected void addFailureApiDocRegister(final T t) {
}
}
+ /**
+ * Add failure mcp tools register.
+ *
+ * @param the type parameter
+ * @param t the t
+ */
+ protected void addFailureMcpToolsRegister(final T t) {
+ if (t instanceof McpToolsRegisterDTO) {
+ McpToolsRegisterDTO dto = (McpToolsRegisterDTO) t;
+ String address = String.join(":", dto.getNamespaceId(), dto.getMcpConfig());
+ addToFail(new Holder(t, address, Constants.MCP_TOOLS_TYPE));
+ }
+ }
+
private void addToFail(final Holder t) {
Holder oldObj = concurrentHashMap.get(t.getKey());
if (Objects.nonNull(oldObj)) {
@@ -186,6 +223,9 @@ public void accept(final String key) {
case Constants.API_DOC_TYPE:
this.doPersistApiDoc((ApiDocRegisterDTO) holder.getObj());
break;
+ case Constants.MCP_TOOLS_TYPE:
+ this.doPersistMcpTools((McpToolsRegisterDTO) holder.getObj());
+ break;
default:
break;
}
diff --git a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/HttpClientRegisterRepository.java b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/HttpClientRegisterRepository.java
index e6898ed..0431573 100644
--- a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/HttpClientRegisterRepository.java
+++ b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/HttpClientRegisterRepository.java
@@ -26,6 +26,7 @@
import org.apache.shenyu.client.core.constant.Constants;
import org.apache.shenyu.client.core.dto.ApiDocRegisterDTO;
import org.apache.shenyu.client.core.dto.DiscoveryConfigRegisterDTO;
+import org.apache.shenyu.client.core.dto.McpToolsRegisterDTO;
import org.apache.shenyu.client.core.dto.MetaDataRegisterDTO;
import org.apache.shenyu.client.core.dto.URIRegisterDTO;
import org.apache.shenyu.client.core.enums.EventType;
@@ -152,6 +153,11 @@ public void doPersistInterface(final MetaDataRegisterDTO metadata) {
doRegister(metadata, Constants.META_PATH, Constants.META_TYPE);
}
+ @Override
+ protected void doPersistMcpTools(final McpToolsRegisterDTO registerDTO) {
+ doRegister(registerDTO, Constants.MCP_TOOLS_PATH, Constants.MCP_TOOLS_TYPE);
+ }
+
@Override
public void closeRepository() {
if (Objects.nonNull(uriRegisterDTO)) {
diff --git a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/InstanceRegisterListener.java b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/InstanceRegisterListener.java
new file mode 100644
index 0000000..4ddfdcb
--- /dev/null
+++ b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/InstanceRegisterListener.java
@@ -0,0 +1,100 @@
+/*
+ * 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.shenyu.client.core.register;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shenyu.client.core.dto.DiscoveryUpstreamData;
+import org.apache.shenyu.client.core.register.config.ShenyuDiscoveryConfig;
+import org.apache.shenyu.registry.api.ShenyuInstanceRegisterRepository;
+import org.apache.shenyu.registry.api.config.RegisterConfig;
+import org.apache.shenyu.registry.api.entity.InstanceEntity;
+import org.apache.shenyu.spi.ExtensionLoader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.core.Ordered;
+
+import java.net.URI;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Properties;
+
+/**
+ * Instance register listener.
+ */
+public class InstanceRegisterListener implements ApplicationListener, Ordered {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(InstanceRegisterListener.class);
+
+ private final DiscoveryUpstreamData currentInstanceUpstream;
+
+ private final RegisterConfig discoveryConfig;
+
+ private final String path;
+
+ private ShenyuInstanceRegisterRepository discoveryService;
+
+ public InstanceRegisterListener(final DiscoveryUpstreamData discoveryUpstream, final ShenyuDiscoveryConfig shenyuDiscoveryConfig) {
+ this.currentInstanceUpstream = discoveryUpstream;
+ this.currentInstanceUpstream.setProps("{\"warmupTime\":\"10\"}");
+ this.discoveryConfig = new RegisterConfig();
+ this.discoveryConfig.setServerLists(shenyuDiscoveryConfig.getServerList());
+ this.discoveryConfig.setRegisterType(shenyuDiscoveryConfig.getType());
+ this.discoveryConfig.setProps(Optional.ofNullable(shenyuDiscoveryConfig.getProps()).orElse(new Properties()));
+ this.path = shenyuDiscoveryConfig.getRegisterPath();
+ Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+ @Override
+ public void run() {
+ LOGGER.info("unregister upstream server by jvm runtime hook");
+ if (Objects.nonNull(discoveryService)) {
+ discoveryService.close();
+ }
+ }
+ }));
+ }
+
+ @Override
+ public void onApplicationEvent(final ContextRefreshedEvent event) {
+ try {
+ if (StringUtils.isBlank(discoveryConfig.getRegisterType()) || StringUtils.equalsIgnoreCase(discoveryConfig.getRegisterType(), "local")) {
+ return;
+ }
+ this.discoveryService = ExtensionLoader.getExtensionLoader(ShenyuInstanceRegisterRepository.class).getJoin(discoveryConfig.getRegisterType());
+ discoveryConfig.getProps().put("watchPath", path);
+ discoveryService.init(discoveryConfig);
+ InstanceEntity instance = new InstanceEntity();
+ instance.setStatus(currentInstanceUpstream.getStatus());
+ instance.setWeight(currentInstanceUpstream.getWeight());
+ URI uri = URI.create(currentInstanceUpstream.getProtocol() + currentInstanceUpstream.getUrl());
+ instance.setPort(uri.getPort());
+ instance.setHost(uri.getHost());
+ instance.setAppName(discoveryConfig.getProps().getProperty("name"));
+ discoveryService.persistInstance(instance);
+ LOGGER.info("shenyu register into ShenyuDiscoveryService {} success", discoveryConfig.getRegisterType());
+ } catch (Exception e) {
+ LOGGER.error("shenyu register into ShenyuDiscoveryService {} type find error", discoveryConfig.getRegisterType(), e);
+ throw new IllegalStateException(String.format("shenyu register into ShenyuDiscoveryService %s type find error", discoveryConfig.getRegisterType()), e);
+ }
+ }
+
+ @Override
+ public int getOrder() {
+ return HIGHEST_PRECEDENCE;
+ }
+}
diff --git a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/ShenyuClientRegisterRepository.java b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/ShenyuClientRegisterRepository.java
index 449a423..aed76e4 100644
--- a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/ShenyuClientRegisterRepository.java
+++ b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/ShenyuClientRegisterRepository.java
@@ -19,6 +19,7 @@
import org.apache.shenyu.client.core.dto.ApiDocRegisterDTO;
+import org.apache.shenyu.client.core.dto.McpToolsRegisterDTO;
import org.apache.shenyu.client.core.dto.MetaDataRegisterDTO;
import org.apache.shenyu.client.core.dto.URIRegisterDTO;
import org.apache.shenyu.client.core.register.config.ShenyuRegisterCenterConfig;
@@ -73,6 +74,14 @@ default void sendHeartbeat(URIRegisterDTO heartbeatDTO) {
*/
default void persistApiDoc(ApiDocRegisterDTO apiDocRegisterDTO) {
}
+
+ /**
+ * persistMcpTools.
+ *
+ * @param mcpToolsRegisterDTO mcpToolsRegisterDTO
+ */
+ default void persistMcpTools(McpToolsRegisterDTO mcpToolsRegisterDTO) {
+ }
/**
* closeRepository.
diff --git a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/type/DataType.java b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/type/DataType.java
index f9eae28..3f64c61 100644
--- a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/type/DataType.java
+++ b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/type/DataType.java
@@ -46,4 +46,9 @@ public enum DataType {
* Discovery config type enum.
*/
DISCOVERY_CONFIG,
+
+ /**
+ * Mcp tools type enum.
+ */
+ MCP_TOOLS,
}
diff --git a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/utils/RequestMethodUtils.java b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/utils/RequestMethodUtils.java
new file mode 100644
index 0000000..be4426d
--- /dev/null
+++ b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/utils/RequestMethodUtils.java
@@ -0,0 +1,192 @@
+/*
+ * 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.shenyu.client.core.utils;
+
+import org.springframework.core.DefaultParameterNameDiscoverer;
+import org.springframework.core.ParameterNameDiscoverer;
+import org.springframework.web.bind.annotation.CookieValue;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PatchMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * Request method utils.
+ */
+public final class RequestMethodUtils {
+
+ private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
+
+ private RequestMethodUtils() {
+ }
+
+ /**
+ * Get supported method types from method.
+ *
+ * @param method method
+ * @return the list of method type
+ */
+ public static List getRequestMethodTypes(final Method method) {
+ if (method.isAnnotationPresent(GetMapping.class)) {
+ return Collections.singletonList("GET");
+ }
+ if (method.isAnnotationPresent(PostMapping.class)) {
+ return Collections.singletonList("POST");
+ }
+ if (method.isAnnotationPresent(PutMapping.class)) {
+ return Collections.singletonList("PUT");
+ }
+ if (method.isAnnotationPresent(DeleteMapping.class)) {
+ return Collections.singletonList("DELETE");
+ }
+ if (method.isAnnotationPresent(PatchMapping.class)) {
+ return Collections.singletonList("PATCH");
+ }
+ if (method.isAnnotationPresent(RequestMapping.class)) {
+ RequestMapping rm = method.getAnnotation(RequestMapping.class);
+ RequestMethod[] requestMethods = rm.method();
+ if (requestMethods.length == 0) {
+ return Arrays.asList("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD");
+ }
+ return Arrays.stream(requestMethods)
+ .map(Enum::name)
+ .collect(Collectors.toList());
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * Get the parameter position list.
+ *
+ * @param method method
+ * @return positionList
+ */
+ public static List getParameterPositions(final Method method) {
+ List positions = new ArrayList<>();
+ Annotation[][] parameterAnnotations = method.getParameterAnnotations();
+
+ for (Annotation[] annotations : parameterAnnotations) {
+ String position = "query";
+
+ for (Annotation annotation : annotations) {
+ if (annotation instanceof PathVariable) {
+ position = "path";
+ break;
+ } else if (annotation instanceof RequestParam) {
+ position = "query";
+ break;
+ } else if (annotation instanceof RequestBody) {
+ position = "body";
+ break;
+ } else if (annotation instanceof RequestHeader) {
+ position = "header";
+ break;
+ } else if (annotation instanceof CookieValue) {
+ position = "cookie";
+ break;
+ }
+ }
+
+ positions.add(position);
+ }
+
+ return positions;
+ }
+
+ /**
+ * Get the parameter names.
+ *
+ * @param method method
+ * @return the array of parameter names
+ */
+ public static String[] getParameterNames(final Method method) {
+ Parameter[] parameters = method.getParameters();
+ String[] namesFromDiscoverer = PARAMETER_NAME_DISCOVERER.getParameterNames(method);
+
+ String[] result = new String[parameters.length];
+
+ for (int i = 0; i < parameters.length; i++) {
+ String nameFromAnnotation = getNameFromSpringAnnotation(parameters[i].getAnnotations());
+
+ if (Objects.nonNull(nameFromAnnotation) && !nameFromAnnotation.isEmpty()) {
+ result[i] = nameFromAnnotation;
+ } else if (Objects.nonNull(namesFromDiscoverer) && namesFromDiscoverer.length > i) {
+ result[i] = namesFromDiscoverer[i];
+ } else {
+ result[i] = "arg" + i;
+ }
+ }
+ return result;
+ }
+
+ private static String getNameFromSpringAnnotation(final Annotation[] annotations) {
+ for (Annotation annotation : annotations) {
+ if (annotation instanceof RequestParam) {
+ RequestParam requestParam = (RequestParam) annotation;
+ if (!requestParam.name().isEmpty()) {
+ return requestParam.name();
+ }
+ if (!requestParam.value().isEmpty()) {
+ return requestParam.value();
+ }
+ } else if (annotation instanceof PathVariable) {
+ PathVariable pathVariable = (PathVariable) annotation;
+ if (!pathVariable.name().isEmpty()) {
+ return pathVariable.name();
+ }
+ if (!pathVariable.value().isEmpty()) {
+ return pathVariable.value();
+ }
+ } else if (annotation instanceof RequestHeader) {
+ RequestHeader requestHeader = (RequestHeader) annotation;
+ if (!requestHeader.name().isEmpty()) {
+ return requestHeader.name();
+ }
+ if (!requestHeader.value().isEmpty()) {
+ return requestHeader.value();
+ }
+ } else if (annotation instanceof CookieValue) {
+ CookieValue cookieValue = (CookieValue) annotation;
+ if (!cookieValue.name().isEmpty()) {
+ return cookieValue.name();
+ }
+ if (!cookieValue.value().isEmpty()) {
+ return cookieValue.value();
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/utils/SystemInfoUtils.java b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/utils/SystemInfoUtils.java
new file mode 100644
index 0000000..60ebfea
--- /dev/null
+++ b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/utils/SystemInfoUtils.java
@@ -0,0 +1,47 @@
+/*
+ * 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.shenyu.client.core.utils;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.OperatingSystemMXBean;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Java 8 compatible system information helper for heartbeat payloads.
+ */
+public final class SystemInfoUtils {
+
+ private SystemInfoUtils() {
+ }
+
+ /**
+ * Gets system info.
+ *
+ * @return system info json
+ */
+ public static String getSystemInfo() {
+ OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
+ Map hostInfo = new HashMap<>(4);
+ hostInfo.put("arch", osBean.getArch());
+ hostInfo.put("operatingSystem", osBean.getName());
+ hostInfo.put("availableProcessors", osBean.getAvailableProcessors());
+ hostInfo.put("systemLoadAverage", osBean.getSystemLoadAverage());
+ return GsonUtils.getInstance().toJson(hostInfo);
+ }
+}
diff --git a/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/utils/VersionUtils.java b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/utils/VersionUtils.java
new file mode 100644
index 0000000..09a9c36
--- /dev/null
+++ b/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/utils/VersionUtils.java
@@ -0,0 +1,127 @@
+/*
+ * 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.shenyu.client.core.utils;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.CodeSource;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Version utils.
+ */
+public final class VersionUtils {
+
+ private static final Logger LOG = LoggerFactory.getLogger(VersionUtils.class);
+
+ private static final String VERSION = getVersion(VersionUtils.class, "1.0.0");
+
+ private static final String JAR = ".jar";
+
+ private VersionUtils() {
+ }
+
+ /**
+ * Gets version.
+ *
+ * @return the version
+ */
+ public static String getVersion() {
+ return VERSION;
+ }
+
+ /**
+ * Gets version.
+ *
+ * @param cls the class
+ * @param defaultVersion the default version
+ * @return the version
+ */
+ public static String getVersion(final Class> cls, final String defaultVersion) {
+ String version = cls.getPackage().getImplementationVersion();
+ if (StringUtils.isBlank(version)) {
+ version = cls.getPackage().getSpecificationVersion();
+ }
+ if (StringUtils.isNoneBlank(version)) {
+ return version;
+ }
+ CodeSource codeSource = cls.getProtectionDomain().getCodeSource();
+ if (Objects.isNull(codeSource)) {
+ LOG.info("No codeSource for class {} when getVersion, use default version {}", cls.getName(), defaultVersion);
+ return defaultVersion;
+ }
+ String file = codeSource.getLocation().getFile();
+ if (Objects.nonNull(file) && file.endsWith(JAR)) {
+ file = file.substring(0, file.length() - 4);
+ int index = file.lastIndexOf('/');
+ if (index >= 0) {
+ file = file.substring(index + 1);
+ }
+ index = file.indexOf("-");
+ if (index >= 0) {
+ file = file.substring(index + 1);
+ }
+ while (StringUtils.isNoneBlank(file) && !Character.isDigit(file.charAt(0))) {
+ index = file.indexOf("-");
+ if (index < 0) {
+ break;
+ }
+ file = file.substring(index + 1);
+ }
+ version = file;
+ }
+ return StringUtils.isBlank(version) ? defaultVersion : version;
+ }
+
+ /**
+ * Check duplicate class resources.
+ *
+ * @param cls class
+ */
+ public static void checkDuplicate(final Class> cls) {
+ try {
+ String path = cls.getName().replace('.', '/') + ".class";
+ Set files = readResources(path, cls);
+ if (files.size() > 1) {
+ String error = "Duplicate class " + path + " in " + files.size() + " jar " + files;
+ LOG.error("checkDuplicate error,{}", error);
+ }
+ } catch (Throwable e) {
+ LOG.error("checkDuplicate error,msg :{}", e.getMessage(), e);
+ }
+ }
+
+ private static Set readResources(final String path, final Class> cls) throws IOException {
+ Enumeration urls = cls.getClassLoader().getResources(path);
+ Set files = new HashSet<>();
+ while (urls.hasMoreElements()) {
+ URL url = urls.nextElement();
+ if (Objects.nonNull(url) && StringUtils.isNotEmpty(url.getFile())) {
+ files.add(url.getFile());
+ }
+ }
+ return files;
+ }
+}
diff --git a/shenyu-client-mcp/pom.xml b/shenyu-client-mcp/pom.xml
new file mode 100644
index 0000000..a0bad07
--- /dev/null
+++ b/shenyu-client-mcp/pom.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+ org.apache.shenyu
+ shenyu-client-java
+ 2.7.0.1-jdk8-SNAPSHOT
+
+ 4.0.0
+ shenyu-client-mcp
+ pom
+
+
+ shenyu-client-mcp-common
+ shenyu-client-mcp-register
+
+
+
+
+ org.apache.shenyu
+ shenyu-client-core
+ ${project.version}
+
+
+
diff --git a/shenyu-client-mcp/shenyu-client-mcp-common/pom.xml b/shenyu-client-mcp/shenyu-client-mcp-common/pom.xml
new file mode 100644
index 0000000..ef3443a
--- /dev/null
+++ b/shenyu-client-mcp/shenyu-client-mcp-common/pom.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+ org.apache.shenyu
+ shenyu-client-mcp
+ 2.7.0.1-jdk8-SNAPSHOT
+
+ 4.0.0
+ shenyu-client-mcp-common
+
+
+
+
+ io.swagger.core.v3
+ swagger-annotations
+ ${swagger-core.version}
+
+
+
+ io.swagger.core.v3
+ swagger-models
+ ${swagger-core.version}
+
+
+
+ org.springframework
+ spring-context
+ provided
+
+
+
+ org.springframework
+ spring-web
+ provided
+
+
+
+
+
diff --git a/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/annotation/ShenyuMcpHeader.java b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/annotation/ShenyuMcpHeader.java
new file mode 100644
index 0000000..80e0a41
--- /dev/null
+++ b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/annotation/ShenyuMcpHeader.java
@@ -0,0 +1,38 @@
+/*
+ * 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.shenyu.client.mcp.common.annotation;
+
+/**
+ * the headersMap.
+ */
+public @interface ShenyuMcpHeader {
+
+ /**
+ * the key.
+ *
+ * @return key
+ */
+ String key() default "";
+
+ /**
+ * the value.
+ *
+ * @return value
+ */
+ String value() default "";
+}
diff --git a/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/annotation/ShenyuMcpRequestConfig.java b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/annotation/ShenyuMcpRequestConfig.java
new file mode 100644
index 0000000..9a93a88
--- /dev/null
+++ b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/annotation/ShenyuMcpRequestConfig.java
@@ -0,0 +1,38 @@
+/*
+ * 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.shenyu.client.mcp.common.annotation;
+
+/**
+ * the shenyuMcpRequestConfig.
+ */
+public @interface ShenyuMcpRequestConfig {
+
+ /**
+ * headers.
+ *
+ * @return the headers
+ */
+ ShenyuMcpHeader[] headers() default {};
+
+ /**
+ * bodyJson.
+ * @return the bodyJson
+ */
+ String bodyToJson() default "false";
+
+}
diff --git a/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/annotation/ShenyuMcpTool.java b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/annotation/ShenyuMcpTool.java
new file mode 100644
index 0000000..e43c8be
--- /dev/null
+++ b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/annotation/ShenyuMcpTool.java
@@ -0,0 +1,78 @@
+/*
+ * 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.shenyu.client.mcp.common.annotation;
+
+import io.swagger.v3.oas.annotations.OpenAPIDefinition;
+import io.swagger.v3.oas.annotations.Operation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The interface shenyu client.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface ShenyuMcpTool {
+
+
+ /**
+ * the openApi definition.
+ *
+ * @return definition
+ */
+ OpenAPIDefinition definition() default @OpenAPIDefinition;
+
+ /**
+ * the openApi operation.
+ *
+ * @return operation
+ */
+ Operation operation() default @Operation;
+
+ /**
+ * request config.
+ *
+ * @return the request config
+ */
+ ShenyuMcpRequestConfig requestConfig() default @ShenyuMcpRequestConfig;
+
+ /**
+ * Rule name string.
+ *
+ * @return the string
+ */
+ String toolName() default "";
+
+ /**
+ * Desc string.
+ *
+ * @return String string
+ */
+ String desc() default "";
+
+ /**
+ * Enabled boolean.
+ *
+ * @return the boolean
+ */
+ boolean enabled() default true;
+
+}
diff --git a/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/annotation/ShenyuMcpToolParam.java b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/annotation/ShenyuMcpToolParam.java
new file mode 100644
index 0000000..43ec715
--- /dev/null
+++ b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/annotation/ShenyuMcpToolParam.java
@@ -0,0 +1,41 @@
+/*
+ * 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.shenyu.client.mcp.common.annotation;
+
+import io.swagger.v3.oas.annotations.Parameter;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * ShenyuMcpToolParam.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface ShenyuMcpToolParam {
+
+ /**
+ * the parameter.
+ *
+ * @return parameter.
+ */
+ Parameter parameter() default @Parameter;
+
+}
diff --git a/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/constants/OpenApiConstants.java b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/constants/OpenApiConstants.java
new file mode 100644
index 0000000..2b8f7f0
--- /dev/null
+++ b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/constants/OpenApiConstants.java
@@ -0,0 +1,124 @@
+/*
+ * 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.shenyu.client.mcp.common.constants;
+
+/**
+ * openApi constants.
+ */
+public class OpenApiConstants {
+
+ /**
+ * the version of openApi key.
+ */
+ public static final String OPEN_API_VERSION_KEY = "openapi";
+
+ /**
+ * the info of openApi key.
+ */
+ public static final String OPEN_API_INFO_KEY = "info";
+
+ /**
+ * the title of info key.
+ */
+ public static final String OPEN_API_INFO_TITLE_KEY = "title";
+
+ /**
+ * the description of info key.
+ */
+ public static final String OPEN_API_INFO_DESCRIPTION_KEY = "description";
+
+ /**
+ * the version of info key.
+ */
+ public static final String OPEN_API_INFO_VERSION_KEY = "version";
+
+ /**
+ * the server of openApi key.
+ */
+ public static final String OPEN_API_SERVER_KEY = "server";
+
+ /**
+ * the url of openApi server.
+ */
+ public static final String OPEN_API_SERVER_URL_KEY = "url";
+
+ /**
+ * the path of openApi key.
+ */
+ public static final String OPEN_API_PATH_KEY = "paths";
+
+ /**
+ * the summary of method key.
+ */
+ public static final String OPEN_API_PATH_OPERATION_METHOD_SUMMARY_KEY = "summary";
+
+ /**
+ * the description of method key.
+ */
+ public static final String OPEN_API_PATH_OPERATION_METHOD_DESCRIPTION_KEY = "description";
+
+ /**
+ * the operationId of method key.
+ */
+ public static final String OPEN_API_PATH_OPERATION_METHOD_OPERATION_ID_KEY = "operationId";
+
+ /**
+ * the tag of method key.
+ */
+ public static final String OPEN_API_PATH_OPERATION_METHOD_TAG_KEY = "tag";
+
+ /**
+ * the parameters of method key.
+ */
+ public static final String OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_KEY = "parameters";
+
+ /**
+ * the name of parameter key.
+ */
+ public static final String OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_NAME_KEY = "name";
+
+ /**
+ * the location of parameter key.
+ */
+ public static final String OPEN_API_OPERATION_PATH_METHOD_PARAMETERS_IN_KEY = "in";
+
+ /**
+ * the description of parameter key.
+ */
+ public static final String OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_DESCRIPTION_KEY = "description";
+
+ /**
+ * the required of parameter key.
+ */
+ public static final String OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_REQUIRED_KEY = "required";
+
+ /**
+ * the schema of parameter key.
+ */
+ public static final String OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_SCHEMA_KEY = "schema";
+
+ /**
+ * the type of schema key.
+ */
+ public static final String OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_SCHEMA_TYPE_KEY = "type";
+
+ /**
+ * the defaultValue of schema key.
+ */
+ public static final String OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_SCHEMA_DEFAULT_VALUE_KEY = "defaultValue";
+}
diff --git a/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/constants/RequestTemplateConstants.java b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/constants/RequestTemplateConstants.java
new file mode 100644
index 0000000..86542f9
--- /dev/null
+++ b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/constants/RequestTemplateConstants.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.shenyu.client.mcp.common.constants;
+
+/**
+ * requestTemplate constants.
+ */
+public class RequestTemplateConstants {
+
+ /**
+ * the url key.
+ */
+ public static final String URL_KEY = "url";
+
+ /**
+ * the method key.
+ */
+ public static final String METHOD_KEY = "method";
+
+ /**
+ * the bodyJson key.
+ */
+ public static final String BODY_JSON_KEY = "argsToJsonBody";
+
+ /**
+ * the headers key.
+ */
+ public static final String HEADERS_KEY = "headers";
+
+ /**
+ * the argsPosition key.
+ */
+ public static final String ARGS_POSITION_KEY = "argsPosition";
+
+ /**
+ * the requestTemplate key.
+ */
+ public static final String REQUEST_TEMPLATE_KEY = "requestTemplate";
+
+}
diff --git a/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/constants/ShenyuToolConfigConstants.java b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/constants/ShenyuToolConfigConstants.java
new file mode 100644
index 0000000..6c20b52
--- /dev/null
+++ b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/constants/ShenyuToolConfigConstants.java
@@ -0,0 +1,47 @@
+/*
+ * 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.shenyu.client.mcp.common.constants;
+
+/**
+ * shenyu tool config constants.
+ */
+public class ShenyuToolConfigConstants {
+
+ /**
+ * the name key.
+ */
+ public static final String NAME_KEY = "name";
+
+ /**
+ * the parameters key.
+ */
+ public static final String PARAMETERS_KEY = "parameters";
+
+ /**
+ * the requestConfig key.
+ */
+ public static final String REQUEST_CONFIG_KEY = "requestConfig";
+
+ /**
+ * the description key.
+ */
+ public static final String DESCRIPTION_KEY = "description";
+
+
+
+}
diff --git a/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/dto/ShenyuMcpRequestConfig.java b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/dto/ShenyuMcpRequestConfig.java
new file mode 100644
index 0000000..63094ab
--- /dev/null
+++ b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/dto/ShenyuMcpRequestConfig.java
@@ -0,0 +1,70 @@
+/*
+ * 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.shenyu.client.mcp.common.dto;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * the shenyu mcp request config object.
+ */
+public class ShenyuMcpRequestConfig {
+
+ private Map headers = new HashMap<>();
+
+ private String bodyToJson;
+
+ public ShenyuMcpRequestConfig() {
+ }
+
+ /**
+ * get headers.
+ *
+ * @return headers
+ */
+ public Map getHeaders() {
+ return headers;
+ }
+
+ /**
+ * set headers.
+ *
+ * @param headers headers
+ */
+ public void setHeaders(final Map headers) {
+ this.headers = headers;
+ }
+
+ /**
+ * get bodyToJson.
+ *
+ * @return bodyToJson
+ */
+ public String getBodyToJson() {
+ return bodyToJson;
+ }
+
+ /**
+ * set bodyToJson.
+ *
+ * @param bodyToJson bodyToJson
+ */
+ public void setBodyToJson(final String bodyToJson) {
+ this.bodyToJson = bodyToJson;
+ }
+}
diff --git a/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/dto/ShenyuMcpTool.java b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/dto/ShenyuMcpTool.java
new file mode 100644
index 0000000..80ef8dd
--- /dev/null
+++ b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/dto/ShenyuMcpTool.java
@@ -0,0 +1,138 @@
+/*
+ * 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.shenyu.client.mcp.common.dto;
+
+import io.swagger.v3.oas.models.Operation;
+import org.apache.shenyu.client.mcp.utils.OpenApiConvertorUtil;
+
+/**
+ * the shenyu mcp tool object.
+ */
+public class ShenyuMcpTool {
+
+ private io.swagger.v3.oas.models.Operation operation;
+
+ private ShenyuMcpRequestConfig requestConfig;
+
+ private String toolName;
+
+ private Boolean enable = true;
+
+ private String method;
+
+ public ShenyuMcpTool() {
+ }
+
+ public ShenyuMcpTool(final org.apache.shenyu.client.mcp.common.annotation.ShenyuMcpTool shenyuMcpTool) {
+ this.operation = OpenApiConvertorUtil.convertOperation(shenyuMcpTool.operation());
+ this.requestConfig = OpenApiConvertorUtil.convertRequestConfig(shenyuMcpTool.requestConfig());
+ this.toolName = shenyuMcpTool.toolName();
+ this.enable = shenyuMcpTool.enabled();
+ }
+
+ /**
+ * get operation.
+ *
+ * @return operation;
+ */
+ public Operation getOperation() {
+ return operation;
+ }
+
+ /**
+ * set operation.
+ *
+ * @param operation operation
+ */
+ public void setOperation(final Operation operation) {
+ this.operation = operation;
+ }
+
+ /**
+ * get requestConfig.
+ *
+ * @return requestConfig;
+ */
+ public ShenyuMcpRequestConfig getRequestConfig() {
+ return requestConfig;
+ }
+
+ /**
+ * set requestConfig.
+ *
+ * @param requestConfig requestConfig
+ */
+ public void setRequestConfig(final ShenyuMcpRequestConfig requestConfig) {
+ this.requestConfig = requestConfig;
+ }
+
+ /**
+ * get toolName.
+ *
+ * @return toolName;
+ */
+ public String getToolName() {
+ return toolName;
+ }
+
+ /**
+ * set toolName.
+ *
+ * @param toolName toolName
+ */
+ public void setToolName(final String toolName) {
+ this.toolName = toolName;
+ }
+
+ /**
+ * get enable.
+ *
+ * @return enable;
+ */
+ public Boolean getEnable() {
+ return enable;
+ }
+
+ /**
+ * set enable.
+ *
+ * @param enable enable
+ */
+ public void setEnable(final Boolean enable) {
+ this.enable = enable;
+ }
+
+
+ /**
+ * get method.
+ *
+ * @return method;
+ */
+ public String getMethod() {
+ return method;
+ }
+
+ /**
+ * set method.
+ *
+ * @param method method
+ */
+ public void setMethod(final String method) {
+ this.method = method;
+ }
+}
diff --git a/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/eunm/McpParameterType.java b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/eunm/McpParameterType.java
new file mode 100644
index 0000000..caaadc0
--- /dev/null
+++ b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/common/eunm/McpParameterType.java
@@ -0,0 +1,75 @@
+/*
+ * 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.shenyu.client.mcp.common.eunm;
+
+import java.lang.reflect.Parameter;
+import java.util.Objects;
+
+public enum McpParameterType {
+ STRING("string"),
+ INTEGER("integer"),
+ LONG("long"),
+ DOUBLE("double"),
+ FLOAT("float"),
+ BOOLEAN("boolean"),
+ OBJECT("object"),
+ ARRAY("array");
+
+ private final String typeName;
+
+ McpParameterType(final String typeName) {
+ this.typeName = typeName;
+ }
+
+ public String getTypeName() {
+ return typeName;
+ }
+
+ /**
+ * get enum by java class.
+ *
+ * @param parameter parameter
+ * @return mcp parameter type
+ */
+ public static McpParameterType fromParameter(final Parameter parameter) {
+ if (Objects.isNull(parameter)) {
+ return null;
+ }
+ Class> clazz = parameter.getType();
+
+ if (clazz == String.class) {
+ return STRING;
+ } else if (clazz == Integer.class || clazz == int.class) {
+ return INTEGER;
+ } else if (clazz == Long.class || clazz == long.class) {
+ return LONG;
+ } else if (clazz == Double.class || clazz == double.class) {
+ return DOUBLE;
+ } else if (clazz == Float.class || clazz == float.class) {
+ return FLOAT;
+ } else if (clazz == Boolean.class || clazz == boolean.class) {
+ return BOOLEAN;
+ } else if (clazz.isArray() || java.util.Collection.class.isAssignableFrom(clazz)) {
+ return ARRAY;
+ } else if (clazz == Object.class) {
+ return OBJECT;
+ } else {
+ return OBJECT;
+ }
+ }
+}
\ No newline at end of file
diff --git a/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/generator/McpOpenApiGenerator.java b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/generator/McpOpenApiGenerator.java
new file mode 100644
index 0000000..1446fa5
--- /dev/null
+++ b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/generator/McpOpenApiGenerator.java
@@ -0,0 +1,95 @@
+/*
+ * 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.shenyu.client.mcp.generator;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import io.swagger.v3.oas.annotations.OpenAPIDefinition;
+import io.swagger.v3.oas.models.parameters.Parameter;
+import org.apache.shenyu.client.mcp.common.annotation.ShenyuMcpTool;
+import org.apache.shenyu.client.mcp.common.constants.OpenApiConstants;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * the openApi generator.
+ */
+public class McpOpenApiGenerator {
+ public static JsonObject generateOpenApiJson(final ShenyuMcpTool classMcpClient,
+ final org.apache.shenyu.client.mcp.common.dto.ShenyuMcpTool shenyuMcpTool,
+ final String url) {
+ JsonObject root = new JsonObject();
+ root.addProperty(OpenApiConstants.OPEN_API_VERSION_KEY, "3.0.0");
+
+ // Info
+ JsonObject info = new JsonObject();
+ OpenAPIDefinition definition = classMcpClient.definition();
+ info.addProperty(OpenApiConstants.OPEN_API_INFO_TITLE_KEY, definition.info().title());
+ info.addProperty(OpenApiConstants.OPEN_API_INFO_DESCRIPTION_KEY, definition.info().description());
+ info.addProperty(OpenApiConstants.OPEN_API_INFO_VERSION_KEY, "");
+ root.add(OpenApiConstants.OPEN_API_INFO_KEY, info);
+
+ // Servers
+ JsonObject server = new JsonObject();
+ root.add(OpenApiConstants.OPEN_API_SERVER_KEY, server);
+ server.addProperty(OpenApiConstants.OPEN_API_SERVER_URL_KEY, definition.servers()[0].url());
+
+ // Paths
+ JsonObject paths = new JsonObject();
+ root.add(OpenApiConstants.OPEN_API_PATH_KEY, paths);
+
+ String pathKey = url;
+ JsonObject pathMap = new JsonObject();
+ paths.add(pathKey, pathMap);
+
+ String methodType = shenyuMcpTool.getMethod();
+ JsonObject methodMap = new JsonObject();
+ pathMap.add(methodType, methodMap);
+
+ methodMap.addProperty(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_SUMMARY_KEY, "");
+ methodMap.addProperty(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_DESCRIPTION_KEY, "");
+ methodMap.addProperty(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_OPERATION_ID_KEY, "");
+ methodMap.add(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_TAG_KEY, new JsonArray());
+
+ JsonArray parameters = new JsonArray();
+ methodMap.add(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_KEY, parameters);
+
+ List parameterList = shenyuMcpTool.getOperation().getParameters();
+
+ if (!parameterList.isEmpty()) {
+
+ for (Parameter parameter : parameterList) {
+ JsonObject parameterObj = new JsonObject();
+ parameterObj.addProperty(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_NAME_KEY, parameter.getName());
+ parameterObj.addProperty(OpenApiConstants.OPEN_API_OPERATION_PATH_METHOD_PARAMETERS_IN_KEY, parameter.getIn());
+ parameterObj.addProperty(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_DESCRIPTION_KEY, parameter.getDescription());
+ parameterObj.addProperty(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_REQUIRED_KEY, parameter.getRequired());
+
+ if (Objects.nonNull(parameter.getSchema())) {
+ JsonObject schemaMap = new JsonObject();
+ schemaMap.addProperty(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_SCHEMA_TYPE_KEY, parameter.getSchema().getType());
+ parameterObj.add(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_SCHEMA_KEY, schemaMap);
+ }
+ parameters.add(parameterObj);
+ }
+ }
+
+ return root;
+ }
+}
\ No newline at end of file
diff --git a/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/generator/McpRequestConfigGenerator.java b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/generator/McpRequestConfigGenerator.java
new file mode 100644
index 0000000..feea24f
--- /dev/null
+++ b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/generator/McpRequestConfigGenerator.java
@@ -0,0 +1,100 @@
+/*
+ * 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.shenyu.client.mcp.generator;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import org.apache.shenyu.client.mcp.common.constants.OpenApiConstants;
+import org.apache.shenyu.client.mcp.common.constants.RequestTemplateConstants;
+import org.apache.shenyu.client.mcp.common.dto.ShenyuMcpRequestConfig;
+
+import java.util.Objects;
+
+
+/**
+ * the mcpRequestConfig generator.
+ */
+public class McpRequestConfigGenerator {
+
+ public static JsonObject generateRequestConfig(final JsonObject openApiJson, final ShenyuMcpRequestConfig shenyuMcpRequestConfig) {
+
+ // requestConfig
+ JsonObject root = new JsonObject();
+
+ // requestTemplate
+ JsonObject requestTemplate = new JsonObject();
+ root.add(RequestTemplateConstants.REQUEST_TEMPLATE_KEY, requestTemplate);
+
+ // url
+ JsonObject paths = openApiJson.get(OpenApiConstants.OPEN_API_PATH_KEY).getAsJsonObject();
+ String path = null;
+ for (String methodKey : paths.keySet()) {
+ path = methodKey;
+ break;
+ }
+ requestTemplate.addProperty(RequestTemplateConstants.URL_KEY, path);
+
+ // method
+ JsonObject method = paths.get(path).getAsJsonObject();
+ String methodType = null;
+ for (String methodKey : method.keySet()) {
+ methodType = methodKey;
+ break;
+ }
+ requestTemplate.addProperty(RequestTemplateConstants.METHOD_KEY, methodType);
+
+ // argsPosition
+ JsonObject argsPosition = new JsonObject();
+ JsonObject methodTypeJson = method.getAsJsonObject(methodType);
+ JsonArray parameters = methodTypeJson.getAsJsonArray(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_KEY);
+ if (Objects.nonNull(parameters)) {
+ for (JsonElement parameter : parameters) {
+ JsonObject paramObj = parameter.getAsJsonObject();
+
+ if (paramObj.has(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_NAME_KEY)
+ && paramObj.has(OpenApiConstants.OPEN_API_OPERATION_PATH_METHOD_PARAMETERS_IN_KEY)) {
+
+ String name = paramObj.get(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_NAME_KEY).getAsString();
+ String inValue = paramObj.get(OpenApiConstants.OPEN_API_OPERATION_PATH_METHOD_PARAMETERS_IN_KEY).getAsString();
+
+ argsPosition.addProperty(name, inValue);
+ }
+ }
+ }
+ // Keep root-level argsPosition as canonical format used by gateway parser.
+ root.add(RequestTemplateConstants.ARGS_POSITION_KEY, argsPosition.deepCopy());
+ // Keep requestTemplate-level argsPosition for backward compatibility.
+ requestTemplate.add(RequestTemplateConstants.ARGS_POSITION_KEY, argsPosition);
+
+ // argsToJsonBody
+ requestTemplate.addProperty(RequestTemplateConstants.BODY_JSON_KEY, shenyuMcpRequestConfig.getBodyToJson());
+
+ // header
+ JsonArray headers = new JsonArray();
+ shenyuMcpRequestConfig.getHeaders().forEach((key, value) -> {
+ JsonObject headerJson = new JsonObject();
+ headerJson.addProperty("key", key);
+ headerJson.addProperty("value", value);
+ headers.add(headerJson);
+ });
+ requestTemplate.add(RequestTemplateConstants.HEADERS_KEY, headers);
+
+ return root;
+ }
+}
diff --git a/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/generator/McpToolsRegisterDTOGenerator.java b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/generator/McpToolsRegisterDTOGenerator.java
new file mode 100644
index 0000000..3638029
--- /dev/null
+++ b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/generator/McpToolsRegisterDTOGenerator.java
@@ -0,0 +1,82 @@
+/*
+ * 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.shenyu.client.mcp.generator;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import org.apache.shenyu.client.mcp.common.constants.OpenApiConstants;
+import org.apache.shenyu.client.mcp.common.constants.ShenyuToolConfigConstants;
+import org.apache.shenyu.client.mcp.common.dto.ShenyuMcpTool;
+import org.apache.shenyu.client.core.dto.McpToolsRegisterDTO;
+
+import java.util.Objects;
+
+/**
+ * the mcpToolsRegisterDTO generator.
+ */
+public class McpToolsRegisterDTOGenerator {
+
+ public static McpToolsRegisterDTO generateRegisterDTO(final ShenyuMcpTool shenyuMcpTool, final JsonObject openApiJsonObject,
+ final String url, final String namespaceId) {
+ JsonObject root = new JsonObject();
+
+ JsonObject paths = openApiJsonObject.getAsJsonObject(OpenApiConstants.OPEN_API_PATH_KEY);
+ JsonObject path = paths.getAsJsonObject(url);
+ JsonObject method = path.getAsJsonObject(shenyuMcpTool.getMethod());
+ JsonArray parameters = method.getAsJsonArray(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_KEY);
+
+ JsonObject requestConfig = McpRequestConfigGenerator.generateRequestConfig(openApiJsonObject, shenyuMcpTool.getRequestConfig());
+ root.addProperty(ShenyuToolConfigConstants.REQUEST_CONFIG_KEY, requestConfig.toString());
+
+ root.addProperty(ShenyuToolConfigConstants.NAME_KEY, shenyuMcpTool.getToolName());
+ root.add(ShenyuToolConfigConstants.PARAMETERS_KEY, parameterFormatting(parameters));
+
+ root.addProperty(ShenyuToolConfigConstants.DESCRIPTION_KEY, shenyuMcpTool.getOperation().getDescription());
+
+ McpToolsRegisterDTO mcpToolsRegisterDTO = new McpToolsRegisterDTO();
+ mcpToolsRegisterDTO.setNamespaceId(namespaceId);
+ mcpToolsRegisterDTO.setMcpConfig(root.toString());
+ return mcpToolsRegisterDTO;
+ }
+
+ public static JsonArray parameterFormatting(final JsonArray parameters) {
+
+ parameters.forEach(parameter -> {
+ JsonObject paramObj = parameter.getAsJsonObject();
+
+ JsonElement schema = paramObj.remove(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_SCHEMA_KEY);
+ if (Objects.nonNull(schema) && schema.isJsonObject()) {
+ JsonObject schemaObj = schema.getAsJsonObject();
+ JsonElement typeElement = schemaObj.get(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_SCHEMA_TYPE_KEY);
+
+ if (Objects.nonNull(typeElement) && typeElement.isJsonPrimitive()) {
+ String type = typeElement.getAsString();
+ paramObj.addProperty(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_SCHEMA_TYPE_KEY, type);
+ }
+ }
+ if (!paramObj.has(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_DESCRIPTION_KEY)) {
+ paramObj.addProperty(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_DESCRIPTION_KEY,
+ paramObj.get(OpenApiConstants.OPEN_API_PATH_OPERATION_METHOD_PARAMETERS_NAME_KEY).getAsString());
+ }
+ paramObj.remove(OpenApiConstants.OPEN_API_OPERATION_PATH_METHOD_PARAMETERS_IN_KEY);
+ });
+ return parameters;
+
+ }
+}
diff --git a/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/utils/OpenApiConvertorUtil.java b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/utils/OpenApiConvertorUtil.java
new file mode 100644
index 0000000..fcbb5f1
--- /dev/null
+++ b/shenyu-client-mcp/shenyu-client-mcp-common/src/main/java/org/apache/shenyu/client/mcp/utils/OpenApiConvertorUtil.java
@@ -0,0 +1,101 @@
+/*
+ * 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.shenyu.client.mcp.utils;
+
+import io.swagger.v3.oas.models.Operation;
+import io.swagger.v3.oas.models.parameters.Parameter;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shenyu.client.mcp.common.annotation.ShenyuMcpHeader;
+import org.apache.shenyu.client.mcp.common.annotation.ShenyuMcpRequestConfig;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * OpenApiConvertorUtil.
+ */
+public class OpenApiConvertorUtil {
+
+ public static io.swagger.v3.oas.models.parameters.Parameter convertParameter(final io.swagger.v3.oas.annotations.Parameter annotation) {
+
+ io.swagger.v3.oas.models.parameters.Parameter parameter = new io.swagger.v3.oas.models.parameters.Parameter();
+
+ if (!Objects.nonNull(annotation)) {
+ return parameter;
+ }
+
+ parameter.setName(annotation.name());
+ parameter.setIn(annotation.in().toString().toLowerCase());
+ parameter.setDescription(annotation.description());
+ parameter.setRequired(annotation.required());
+
+ io.swagger.v3.oas.annotations.media.Schema schemaAnn = annotation.schema();
+ if (!schemaAnn.implementation().equals(Void.class)
+ || StringUtils.isNoneBlank(schemaAnn.type())
+ || StringUtils.isNoneBlank(schemaAnn.format())) {
+ io.swagger.v3.oas.models.media.Schema