diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java index d0476e5f2..83aa8f5ba 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java @@ -26,7 +26,6 @@ import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.CompleteResult.CompleteCompletion; import io.modelcontextprotocol.spec.McpSchema.ErrorCodes; -import io.modelcontextprotocol.spec.McpSchema.LoggingLevel; import io.modelcontextprotocol.spec.McpSchema.PromptReference; import io.modelcontextprotocol.spec.McpSchema.ResourceReference; import io.modelcontextprotocol.spec.McpSchema.SetLevelRequest; @@ -433,9 +432,11 @@ public Mono apply(McpAsyncServerExchange exchange, McpSchema.Cal var validation = this.jsonSchemaValidator.validate(outputSchema, result.structuredContent()); if (!validation.valid()) { - logger.warn("Tool call result validation failed: {}", validation.errorMessage()); + String message = "Tool (" + request.name() + ") output validation failed: " + + validation.errorMessage(); + logger.warn(message); return CallToolResult.builder() - .content(List.of(McpSchema.TextContent.builder(validation.errorMessage()).build())) + .content(List.of(McpSchema.TextContent.builder(message).build())) .isError(true) .build(); } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java index 48b17ed2a..c6105267d 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java @@ -4,9 +4,19 @@ package io.modelcontextprotocol.server; -import io.modelcontextprotocol.json.TypeRef; -import io.modelcontextprotocol.json.McpJsonMapper; +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.BiFunction; + import io.modelcontextprotocol.common.McpTransportContext; +import io.modelcontextprotocol.json.McpJsonMapper; +import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.json.schema.JsonSchemaValidator; import io.modelcontextprotocol.server.McpStatelessServerFeatures.AsyncResourceTemplateSpecification; import io.modelcontextprotocol.spec.McpError; @@ -28,16 +38,6 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import java.time.Duration; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.function.BiFunction; - import static io.modelcontextprotocol.spec.McpError.RESOURCE_NOT_FOUND; /** @@ -293,9 +293,11 @@ public Mono apply(McpTransportContext transportContext, McpSchem var validation = this.jsonSchemaValidator.validate(outputSchema, result.structuredContent()); if (!validation.valid()) { - logger.warn("Tool call result validation failed: {}", validation.errorMessage()); + String message = "Tool (" + request.name() + ") output validation failed: " + + validation.errorMessage(); + logger.warn(message); return CallToolResult.builder() - .content(List.of(McpSchema.TextContent.builder(validation.errorMessage()).build())) + .content(List.of(McpSchema.TextContent.builder(message).build())) .isError(true) .build(); } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/util/ToolInputValidator.java b/mcp-core/src/main/java/io/modelcontextprotocol/util/ToolInputValidator.java index 17a313323..76f9390a8 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/util/ToolInputValidator.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/util/ToolInputValidator.java @@ -42,9 +42,10 @@ public static CallToolResult validate(McpSchema.Tool tool, Map a Map args = arguments != null ? arguments : Map.of(); var validation = validator.validate(tool.inputSchema(), args); if (!validation.valid()) { - logger.warn("Tool '{}' input validation failed: {}", tool.name(), validation.errorMessage()); + String message = "Tool (" + tool.name() + ") input validation failed: " + validation.errorMessage(); + logger.warn(message); return CallToolResult.builder() - .content(List.of(McpSchema.TextContent.builder(validation.errorMessage()).build())) + .content(List.of(McpSchema.TextContent.builder(message).build())) .isError(true) .build(); } diff --git a/mcp-json-jackson2/src/main/java/io/modelcontextprotocol/json/schema/jackson2/DefaultJsonSchemaValidator.java b/mcp-json-jackson2/src/main/java/io/modelcontextprotocol/json/schema/jackson2/DefaultJsonSchemaValidator.java index d632b2d16..09bf5b5b6 100644 --- a/mcp-json-jackson2/src/main/java/io/modelcontextprotocol/json/schema/jackson2/DefaultJsonSchemaValidator.java +++ b/mcp-json-jackson2/src/main/java/io/modelcontextprotocol/json/schema/jackson2/DefaultJsonSchemaValidator.java @@ -76,8 +76,7 @@ public ValidationResponse validate(Map schema, Object structured // Check if validation passed if (!validationResult.isEmpty()) { return ValidationResponse - .asInvalid("Validation failed: structuredContent does not match tool outputSchema. " - + "Validation errors: " + validationResult); + .asInvalid("Validation failed: JSON schema validation errors: " + validationResult); } return ValidationResponse.asValid(jsonStructuredOutput.toString()); diff --git a/mcp-json-jackson2/src/test/java/io/modelcontextprotocol/json/jackson2/DefaultJsonSchemaValidatorTests.java b/mcp-json-jackson2/src/test/java/io/modelcontextprotocol/json/jackson2/DefaultJsonSchemaValidatorTests.java index 3cf59aa3c..3707c0f7c 100644 --- a/mcp-json-jackson2/src/test/java/io/modelcontextprotocol/json/jackson2/DefaultJsonSchemaValidatorTests.java +++ b/mcp-json-jackson2/src/test/java/io/modelcontextprotocol/json/jackson2/DefaultJsonSchemaValidatorTests.java @@ -308,7 +308,7 @@ void testValidateWithInvalidTypeSchema() { assertFalse(response.valid()); assertNotNull(response.errorMessage()); assertTrue(response.errorMessage().contains("Validation failed")); - assertTrue(response.errorMessage().contains("structuredContent does not match tool outputSchema")); + assertTrue(response.errorMessage().contains("JSON schema validation errors")); } @Test diff --git a/mcp-json-jackson3/src/main/java/io/modelcontextprotocol/json/schema/jackson3/DefaultJsonSchemaValidator.java b/mcp-json-jackson3/src/main/java/io/modelcontextprotocol/json/schema/jackson3/DefaultJsonSchemaValidator.java index 284289895..9af17ebcd 100644 --- a/mcp-json-jackson3/src/main/java/io/modelcontextprotocol/json/schema/jackson3/DefaultJsonSchemaValidator.java +++ b/mcp-json-jackson3/src/main/java/io/modelcontextprotocol/json/schema/jackson3/DefaultJsonSchemaValidator.java @@ -75,8 +75,7 @@ public ValidationResponse validate(Map schema, Object structured // Check if validation passed if (!validationResult.isEmpty()) { return ValidationResponse - .asInvalid("Validation failed: structuredContent does not match tool outputSchema. " - + "Validation errors: " + validationResult); + .asInvalid("Validation failed: JSON schema validation errors: " + validationResult); } return ValidationResponse.asValid(jsonStructuredOutput.toString()); diff --git a/mcp-json-jackson3/src/test/java/io/modelcontextprotocol/json/DefaultJsonSchemaValidatorTests.java b/mcp-json-jackson3/src/test/java/io/modelcontextprotocol/json/DefaultJsonSchemaValidatorTests.java index be01eb23c..d56606a25 100644 --- a/mcp-json-jackson3/src/test/java/io/modelcontextprotocol/json/DefaultJsonSchemaValidatorTests.java +++ b/mcp-json-jackson3/src/test/java/io/modelcontextprotocol/json/DefaultJsonSchemaValidatorTests.java @@ -308,7 +308,7 @@ void testValidateWithInvalidTypeSchema() { assertFalse(response.valid()); assertNotNull(response.errorMessage()); assertTrue(response.errorMessage().contains("Validation failed")); - assertTrue(response.errorMessage().contains("structuredContent does not match tool outputSchema")); + assertTrue(response.errorMessage().contains("JSON schema validation errors")); } @Test diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/server/ToolInputValidationIntegrationTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/ToolInputValidationIntegrationTests.java index 5bd2a5dad..3e4f5fbd7 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/server/ToolInputValidationIntegrationTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/server/ToolInputValidationIntegrationTests.java @@ -182,6 +182,9 @@ void invalidInput_withDefaultValidation_shouldReturnToolError(String serverType, assertThat(result.isError()).isTrue(); String errorMessage = ((TextContent) result.content().get(0)).text(); + assertThat(errorMessage).startsWith("Tool (test-tool) input validation failed:"); + assertThat(errorMessage).containsIgnoringCase("Validation failed"); + assertThat(errorMessage).containsIgnoringCase("JSON schema validation errors"); assertThat(errorMessage).containsIgnoringCase(expectedErrorSubstring); } finally {