This is an automated email from the ASF dual-hosted git repository.
liuhongyu 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 164ad63538 feat:mcpServer support object and array param. (#6150)
164ad63538 is described below
commit 164ad635386e52f4edc1b2db398b44a9136932d7
Author: Wweiei <[email protected]>
AuthorDate: Mon Sep 15 15:11:40 2025 +0800
feat:mcpServer support object and array param. (#6150)
Co-authored-by: aias00 <[email protected]>
---
.../mcp/server/model/McpServerToolParameter.java | 35 +++++-
.../plugin/mcp/server/utils/JsonSchemaUtil.java | 34 +++++-
.../mcp/server/utils/JsonSchemaUtilTest.java | 124 +++++++++++++++++++++
3 files changed, 187 insertions(+), 6 deletions(-)
diff --git
a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/model/McpServerToolParameter.java
b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/model/McpServerToolParameter.java
index f6c85a2235..9b2054fe78 100644
---
a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/model/McpServerToolParameter.java
+++
b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/model/McpServerToolParameter.java
@@ -17,6 +17,8 @@
package org.apache.shenyu.plugin.mcp.server.model;
+import java.util.List;
+
/**
* McpParameter represents a parameter in the context of a tool description.
* It contains information about the parameter's name, type, description,
@@ -49,6 +51,11 @@ public class McpServerToolParameter {
*/
private String defaultValue;
+ /**
+ * the child parameters.
+ */
+ private List<McpServerToolParameter> parameters;
+
/**
* Constructor for McpParameter.
*/
@@ -63,13 +70,15 @@ public class McpServerToolParameter {
* @param description description
* @param required required
* @param defaultValue defaultValue
+ * @param parameters parameters
*/
- public McpServerToolParameter(final String name, final String type, final
String description, final boolean required, final String defaultValue) {
+ public McpServerToolParameter(final String name, final String type, final
String description, final boolean required, final String defaultValue, final
List<McpServerToolParameter> parameters) {
this.name = name;
this.type = type;
this.description = description;
this.required = required;
this.defaultValue = defaultValue;
+ this.parameters = parameters;
}
/**
@@ -145,20 +154,38 @@ public class McpServerToolParameter {
}
/**
- * Getter for mcpClass.
+ * Getter for defaultValue.
*
- * @return mcpClass
+ * @return defaultValue
*/
public String getDefaultValue() {
return defaultValue;
}
/**
- * Setter for mcpClass.
+ * Setter for defaultValue.
*
* @param defaultValue defaultValue
*/
public void setDefaultValue(final String defaultValue) {
this.defaultValue = defaultValue;
}
+
+ /**
+ * Getter for parameters.
+ *
+ * @return parameters
+ */
+ public List<McpServerToolParameter> getParameters() {
+ return parameters;
+ }
+
+ /**
+ * Setter for parameters.
+ *
+ * @param parameters parameters
+ */
+ public void setParameters(final List<McpServerToolParameter> parameters) {
+ this.parameters = parameters;
+ }
}
diff --git
a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/utils/JsonSchemaUtil.java
b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/utils/JsonSchemaUtil.java
index fee135e937..e15dc76209 100644
---
a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/utils/JsonSchemaUtil.java
+++
b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/utils/JsonSchemaUtil.java
@@ -20,6 +20,7 @@ package org.apache.shenyu.plugin.mcp.server.utils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.victools.jsonschema.generator.SchemaVersion;
+import io.micrometer.common.util.StringUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.shenyu.plugin.mcp.server.model.McpServerToolParameter;
import org.springframework.ai.util.json.JsonParser;
@@ -58,14 +59,43 @@ public final class JsonSchemaUtil {
ObjectNode properties = schema.putObject("properties");
for (McpServerToolParameter parameter : parameters) {
ObjectNode property = properties.putObject(parameter.getName());
- property.put("type", parameter.getType());
- property.put("description", parameter.getDescription());
+ recursionConstructPropertiesNodes(parameter, property);
}
processSchemaOptions(schemaOptions, schema);
return schema.toPrettyString();
}
+ /**
+ * Recursively construct the properties nodes for the parameter.
+ *
+ * @param parameter the parameter
+ * @param property the property
+ */
+ public static void recursionConstructPropertiesNodes(final
McpServerToolParameter parameter,
+ final ObjectNode
property) {
+ property.put("type", parameter.getType());
+ // if the parameter is the item parameter of array, the description is
null
+ if (StringUtils.isNotBlank(parameter.getDescription())) {
+ property.put("description", parameter.getDescription());
+ }
+ // add the properties schema for the object type parameter
+ List<McpServerToolParameter> parameters = parameter.getParameters();
+ if ("object".equals(parameter.getType()) &&
CollectionUtils.isNotEmpty(parameters)) {
+ ObjectNode properties = property.putObject("properties");
+ for (McpServerToolParameter itemParameter : parameters) {
+ ObjectNode property1 =
properties.putObject(itemParameter.getName());
+ recursionConstructPropertiesNodes(itemParameter, property1);
+ }
+ }
+ // add the items schema for the array type parameter
+ if ("array".equals(parameter.getType()) &&
CollectionUtils.isNotEmpty(parameters)) {
+ McpServerToolParameter itemParameter = parameters.get(0);
+ ObjectNode items = property.putObject("items");
+ recursionConstructPropertiesNodes(itemParameter, items);
+ }
+ }
+
private static void processSchemaOptions(final SchemaOption[]
schemaOptions, final ObjectNode schema) {
if (Stream.of(schemaOptions)
.noneMatch(option -> option ==
SchemaOption.ALLOW_ADDITIONAL_PROPERTIES_BY_DEFAULT)) {
diff --git
a/shenyu-plugin/shenyu-plugin-mcp-server/src/test/java/org/apache/shenyu/plugin/mcp/server/utils/JsonSchemaUtilTest.java
b/shenyu-plugin/shenyu-plugin-mcp-server/src/test/java/org/apache/shenyu/plugin/mcp/server/utils/JsonSchemaUtilTest.java
index bf6efb8275..8eeb22a246 100644
---
a/shenyu-plugin/shenyu-plugin-mcp-server/src/test/java/org/apache/shenyu/plugin/mcp/server/utils/JsonSchemaUtilTest.java
+++
b/shenyu-plugin/shenyu-plugin-mcp-server/src/test/java/org/apache/shenyu/plugin/mcp/server/utils/JsonSchemaUtilTest.java
@@ -19,6 +19,8 @@ package org.apache.shenyu.plugin.mcp.server.utils;
import org.apache.shenyu.plugin.mcp.server.model.McpServerToolParameter;
import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.Collections;
@@ -33,6 +35,8 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
*/
class JsonSchemaUtilTest {
+ private static final Logger log =
LoggerFactory.getLogger(JsonSchemaUtilTest.class);
+
@Test
void testCreateParameterSchemaWithEmptyParameters() {
String schema =
JsonSchemaUtil.createParameterSchema(Collections.emptyList());
@@ -175,4 +179,124 @@ class JsonSchemaUtilTest {
// Verify that special characters are properly escaped
assertTrue(schema.contains("\\\"quotes\\\""));
}
+
+ @Test
+ void testCreateParameterSchemaWithObjectType() {
+ McpServerToolParameter param1 = new McpServerToolParameter();
+ param1.setName("username");
+ param1.setType("string");
+ param1.setDescription("The username");
+ param1.setRequired(true);
+
+ McpServerToolParameter param2 = new McpServerToolParameter();
+ param2.setName("age");
+ param2.setType("integer");
+ param2.setDescription("The age");
+ param2.setRequired(false);
+
+ McpServerToolParameter param3 = new McpServerToolParameter();
+ param3.setName("class");
+ param3.setType("object");
+ param3.setDescription("The class info");
+ param3.setRequired(true);
+
+ McpServerToolParameter param31 = new McpServerToolParameter();
+ param31.setName("className");
+ param31.setType("string");
+ param31.setDescription("The class name");
+ param31.setRequired(true);
+
+ McpServerToolParameter param32 = new McpServerToolParameter();
+ param32.setName("course");
+ param32.setType("object");
+ param32.setDescription("The class course");
+ param32.setRequired(true);
+
+ param3.setParameters(Arrays.asList(param31, param32));
+
+ McpServerToolParameter param321 = new McpServerToolParameter();
+ param321.setName("courseName");
+ param321.setType("string");
+ param321.setDescription("The name of the course");
+ param321.setRequired(true);
+
+ McpServerToolParameter param322 = new McpServerToolParameter();
+ param322.setName("courseTime");
+ param322.setType("string");
+ param322.setDescription("The time of the course");
+ param322.setRequired(true);
+
+ param32.setParameters(Arrays.asList(param321, param322));
+
+ List<McpServerToolParameter> parameters = Arrays.asList(param1,
param2, param3);
+ String schema = JsonSchemaUtil.createParameterSchema(parameters);
+ log.info("schema: {}", schema);
+ assertNotNull(schema);
+ assertTrue(schema.contains("\"username\""));
+ assertTrue(schema.contains("\"class\""));
+ assertTrue(schema.contains("\"className\""));
+ assertTrue(schema.contains("\"course\""));
+ assertTrue(schema.contains("\"courseName\""));
+ assertTrue(schema.contains("\"The name of the course\""));
+ assertTrue(schema.contains("\"courseTime\""));
+ assertTrue(schema.contains("\"The time of the course\""));
+ }
+
+ @Test
+ void testCreateParameterSchemaWithArrayType() {
+ McpServerToolParameter param1 = new McpServerToolParameter();
+ param1.setName("className");
+ param1.setType("string");
+ param1.setDescription("The class info");
+ param1.setRequired(true);
+
+ McpServerToolParameter param2 = new McpServerToolParameter();
+ param2.setName("studentNames");
+ param2.setType("array");
+ param2.setDescription("The student names");
+ param2.setRequired(true);
+
+ McpServerToolParameter param21 = new McpServerToolParameter();
+ param21.setName("items");
+ param21.setType("string");
+ param21.setRequired(true);
+ param2.setParameters(Arrays.asList(param21));
+
+ McpServerToolParameter param3 = new McpServerToolParameter();
+ param3.setName("studentInfo");
+ param3.setType("array");
+ param3.setDescription("The student infos");
+ param3.setRequired(true);
+
+ McpServerToolParameter param31 = new McpServerToolParameter();
+ param31.setName("items");
+ param31.setType("object");
+ param31.setRequired(true);
+ param3.setParameters(Arrays.asList(param31));
+
+ McpServerToolParameter param331 = new McpServerToolParameter();
+ param331.setName("studentName");
+ param331.setType("String");
+ param331.setDescription("The student name");
+ param331.setRequired(true);
+
+ McpServerToolParameter param332 = new McpServerToolParameter();
+ param332.setName("studentAge");
+ param332.setType("integer");
+ param332.setDescription("The student age");
+ param332.setRequired(true);
+ param31.setParameters(Arrays.asList(param331, param332));
+
+ List<McpServerToolParameter> parameters = Arrays.asList(param1,
param2, param3);
+ String schema = JsonSchemaUtil.createParameterSchema(parameters);
+ log.info("schema: {}", schema);
+ assertNotNull(schema);
+ assertTrue(schema.contains("\"className\""));
+ assertTrue(schema.contains("\"studentNames\""));
+ assertTrue(schema.contains("\"items\""));
+ assertTrue(schema.contains("\"studentInfo\""));
+ assertTrue(schema.contains("\"studentName\""));
+ assertTrue(schema.contains("\"studentAge\""));
+ assertTrue(schema.contains("The student age"));
+ }
}