feat: validate MCP tool input constraints
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 1s
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 1s
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
package io.shinhanlife.dap.lib.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @package io.shinhanlife.dap.lib.annotation
|
||||
* @className McpValidation
|
||||
* @description Declares JSON Schema validation constraints for MCP tool input fields
|
||||
* @author 0986406
|
||||
* @create 2026.07.27
|
||||
* <pre>
|
||||
* ---------- revision history ----------
|
||||
* date author description
|
||||
* ---------- --------- ---------------------------
|
||||
* 2026.07.27 0986406 initial creation
|
||||
* </pre>
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface McpValidation {
|
||||
boolean required() default false;
|
||||
String pattern() default "";
|
||||
long minimum() default Long.MIN_VALUE;
|
||||
String[] allowedValues() default {};
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package io.shinhanlife.dap.lib.util;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
|
||||
import io.shinhanlife.dap.lib.annotation.McpParameter;
|
||||
import io.shinhanlife.dap.lib.annotation.McpValidation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -58,6 +59,20 @@ public class JsonSchemaGenerator {
|
||||
if ((jsonProp != null && jsonProp.required()) || (paramAnnotation != null && paramAnnotation.required())) {
|
||||
requiredList.add(field.getName());
|
||||
}
|
||||
|
||||
McpValidation validation = field.getAnnotation(McpValidation.class);
|
||||
if (validation != null && validation.required() && !requiredList.contains(field.getName())) {
|
||||
requiredList.add(field.getName());
|
||||
}
|
||||
if (validation != null && !validation.pattern().isEmpty()) {
|
||||
fieldSchema.put("pattern", validation.pattern());
|
||||
}
|
||||
if (validation != null && validation.minimum() != Long.MIN_VALUE) {
|
||||
fieldSchema.put("minimum", validation.minimum());
|
||||
}
|
||||
if (validation != null && validation.allowedValues().length > 0) {
|
||||
fieldSchema.put("enum", List.of(validation.allowedValues()));
|
||||
}
|
||||
|
||||
properties.put(field.getName(), fieldSchema);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package io.shinhanlife.dap.lib.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import io.shinhanlife.dap.lib.annotation.McpParameter;
|
||||
import io.shinhanlife.dap.lib.annotation.McpValidation;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* @package io.shinhanlife.dap.lib.util
|
||||
* @className JsonSchemaGeneratorTest
|
||||
* @description JSON Schema constraint generation test
|
||||
* @author 0986406
|
||||
* @create 2026.07.27
|
||||
* <pre>
|
||||
* ---------- revision history ----------
|
||||
* date author description
|
||||
* ---------- --------- ---------------------------
|
||||
* 2026.07.27 0986406 initial creation
|
||||
* </pre>
|
||||
*/
|
||||
class JsonSchemaGeneratorTest {
|
||||
|
||||
@Test
|
||||
void includesMcpParameterConstraintsInGeneratedSchema() {
|
||||
Map<String, Object> schema = JsonSchemaGenerator.generateSchema(ValidatedRequest.class);
|
||||
Map<String, Map<String, Object>> properties = properties(schema);
|
||||
|
||||
assertTrue(((List<String>) schema.get("required")).contains("phoneNumber"));
|
||||
assertTrue(((List<String>) schema.get("required")).contains("approvalStatus"));
|
||||
assertEquals("^01[0-9]{8,9}$", properties.get("phoneNumber").get("pattern"));
|
||||
assertEquals(1L, properties.get("amount").get("minimum"));
|
||||
assertEquals(List.of("APPROVE", "REJECT"), properties.get("approvalStatus").get("enum"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Map<String, Object>> properties(Map<String, Object> schema) {
|
||||
return (Map<String, Map<String, Object>>) schema.get("properties");
|
||||
}
|
||||
|
||||
private static class ValidatedRequest {
|
||||
@McpParameter(description = "recipient phone number", required = true)
|
||||
@McpValidation(pattern = "^01[0-9]{8,9}$")
|
||||
private String phoneNumber;
|
||||
|
||||
@McpParameter(description = "issue amount", required = true)
|
||||
@McpValidation(minimum = 1)
|
||||
private Long amount;
|
||||
|
||||
@McpParameter(description = "approval result")
|
||||
@McpValidation(required = true, allowedValues = {"APPROVE", "REJECT"})
|
||||
private String approvalStatus;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user