merge: remote updates into main with tool-core schema updates
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 2m17s

This commit is contained in:
hjgram
2026-07-30 06:26:41 +09:00
11 changed files with 104 additions and 93 deletions

View File

@@ -24,7 +24,7 @@ dependencies {
api 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.17.1'
api 'com.fasterxml.jackson.core:jackson-databind:2.17.1'
api 'com.networknt:json-schema-validator:1.4.0'
api 'com.networknt:json-schema-validator:3.0.0'
api 'org.springframework.ai:spring-ai-starter-mcp-server-webmvc'
api 'org.springframework.kafka:spring-kafka:3.2.0'
api 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0'

View File

@@ -16,10 +16,7 @@ package io.shinhanlife.dap.mcc.presentation;
* </pre>
*/
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.JsonSchema;
import com.networknt.schema.JsonSchemaFactory;
import com.networknt.schema.SpecVersion;
import com.networknt.schema.ValidationMessage;
import com.networknt.schema.Error;
import io.shinhanlife.dap.lib.annotation.McpFunction;
import io.shinhanlife.dap.lib.annotation.McpTool;
import io.shinhanlife.dap.lib.config.McpProperties;
@@ -58,6 +55,7 @@ public class BusinessToolController {
private final ObjectMapper objectMapper;
private final McpProperties mcpProperties;
private final ToolRegistryHeartbeatSender toolRegistryHeartbeatSender;
private final ToolArgumentSchemaValidator toolArgumentSchemaValidator;
// 내부 조회용 로컬 Tool 목록 엔드포인트
@GetMapping("/mcp/api/v1/tools/local")
@@ -149,17 +147,12 @@ public class BusinessToolController {
if (!Map.class.isAssignableFrom(paramType)) {
try {
Map<String, Object> autoSchema = JsonSchemaGenerator.generateSchema(paramType);
String fullSchemaJson = objectMapper.writeValueAsString(autoSchema);
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);
JsonSchema schema = factory.getSchema(fullSchemaJson);
Set<ValidationMessage> errors = schema.validate(objectMapper.valueToTree(arguments));
List<Error> errors = toolArgumentSchemaValidator.validate(autoSchema, arguments);
if (!errors.isEmpty()) {
log.error("[Tool] 파라미터 유효성 검증 실패: {}", errors);
List<String> errorMessages = new ArrayList<>();
for (ValidationMessage vm : errors) {
errorMessages.add(vm.getMessage());
for (Error validationError : errors) {
errorMessages.add(validationError.getMessage());
}
Map<String, Object> errorDetails = new HashMap<>();
errorDetails.put("status", "422");
@@ -229,4 +222,4 @@ public class BusinessToolController {
return ResponseEntity.status(502).body(errorBody);
}
}
}
}

View File

@@ -1,12 +1,12 @@
package io.shinhanlife.dap.mcc.presentation;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.JsonSchema;
import com.networknt.schema.JsonSchemaFactory;
import com.networknt.schema.SpecVersion;
import com.networknt.schema.ValidationMessage;
import java.util.Set;
import com.networknt.schema.Error;
import com.networknt.schema.InputFormat;
import com.networknt.schema.Schema;
import com.networknt.schema.SchemaRegistry;
import com.networknt.schema.SpecificationVersion;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Component;
@@ -20,11 +20,9 @@ public class ToolArgumentSchemaValidator {
this.objectMapper = objectMapper;
}
public Set<ValidationMessage> validate(Map<String, Object> schemaDefinition, Map<String, Object> arguments) throws Exception {
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);
JsonNode schemaNode = objectMapper.valueToTree(schemaDefinition);
JsonSchema schema = factory.getSchema(schemaNode);
JsonNode argumentsNode = objectMapper.valueToTree(arguments);
return schema.validate(argumentsNode);
public List<Error> validate(Map<String, Object> schemaDefinition, Map<String, Object> arguments) throws Exception {
SchemaRegistry schemaRegistry = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_7);
Schema schema = schemaRegistry.getSchema(objectMapper.writeValueAsString(schemaDefinition));
return schema.validate(objectMapper.writeValueAsString(arguments), InputFormat.JSON);
}
}

View File

@@ -5,15 +5,15 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.JsonSchema;
import com.networknt.schema.JsonSchemaFactory;
import com.networknt.schema.SpecVersion;
import com.networknt.schema.ValidationMessage;
import com.networknt.schema.Error;
import com.networknt.schema.InputFormat;
import com.networknt.schema.Schema;
import com.networknt.schema.SchemaRegistry;
import com.networknt.schema.SpecificationVersion;
import io.shinhanlife.dap.lib.annotation.McpParameter;
import io.shinhanlife.dap.lib.annotation.McpValidation;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.jupiter.api.Test;
/**
@@ -65,10 +65,10 @@ class JsonSchemaGeneratorTest {
@Test
void validatorRejectsInvalidNestedValue() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
JsonSchema schema = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)
Schema schema = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_7)
.getSchema(objectMapper.writeValueAsString(JsonSchemaGenerator.generateSchema(NestedRequest.class)));
Set<ValidationMessage> errors = schema.validate(objectMapper.valueToTree(Map.of(
"child", Map.of("businessDate", "2026-07-28"))));
List<Error> errors = schema.validate(objectMapper.writeValueAsString(Map.of(
"child", Map.of("businessDate", "2026-07-28"))), InputFormat.JSON);
assertFalse(errors.isEmpty());
}