diff --git a/README.md b/README.md index 8d390a8..81c8b2e 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ and a Java MCP SDK dependency: io.modelcontextprotocol.sdk mcp - 0.12.0 + 0.13.0-SNAPSHOT ``` @@ -1999,7 +1999,7 @@ public class StatelessMcpServerFactory { - Java 17 or higher - Reactor Core (for async operations) -- MCP Java SDK 0.12.0 or higher +- MCP Java SDK 0.13.0-SNAPSHOT or higher ## Building from Source diff --git a/mcp-annotations/pom.xml b/mcp-annotations/pom.xml index 49620fc..e63e34b 100644 --- a/mcp-annotations/pom.xml +++ b/mcp-annotations/pom.xml @@ -56,11 +56,11 @@ ${swagger-annotations.version} - + com.fasterxml.jackson.datatype diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/AbstractMcpToolProvider.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/AbstractMcpToolProvider.java index d3921d7..f4877e4 100644 --- a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/AbstractMcpToolProvider.java +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/AbstractMcpToolProvider.java @@ -3,6 +3,7 @@ import java.lang.reflect.Method; import java.util.List; +import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.util.Assert; import org.springaicommunity.mcp.annotation.McpTool; @@ -10,6 +11,8 @@ public abstract class AbstractMcpToolProvider { protected final List toolObjects; + protected McpJsonMapper jsonMapper = McpJsonMapper.createDefault(); + public AbstractMcpToolProvider(List toolObjects) { Assert.notNull(toolObjects, "toolObjects cannot be null"); this.toolObjects = toolObjects; @@ -27,4 +30,12 @@ protected Class doGetToolCallException() { return Exception.class; } + public void setJsonMapper(McpJsonMapper jsonMapper) { + this.jsonMapper = jsonMapper; + } + + public McpJsonMapper getJsonMapper() { + return this.jsonMapper; + } + } diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/AsyncMcpToolProvider.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/AsyncMcpToolProvider.java index e93282e..72f35e6 100644 --- a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/AsyncMcpToolProvider.java +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/AsyncMcpToolProvider.java @@ -20,6 +20,7 @@ import java.util.function.BiFunction; import java.util.stream.Stream; +import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.server.McpAsyncServerExchange; import io.modelcontextprotocol.server.McpServerFeatures.AsyncToolSpecification; import io.modelcontextprotocol.spec.McpSchema; @@ -79,7 +80,7 @@ public List getToolSpecifications() { var toolBuilder = McpSchema.Tool.builder() .name(toolName) .description(toolDescrption) - .inputSchema(inputSchema); + .inputSchema(this.getJsonMapper(), inputSchema); var title = toolJavaAnnotation.title(); @@ -119,8 +120,8 @@ public List getToolSpecifications() { : null; if (!ClassUtils.isPrimitiveOrWrapper(methodReturnType) && !ClassUtils.isSimpleValueType(methodReturnType)) { - toolBuilder - .outputSchema(JsonSchemaGenerator.generateFromClass((Class) typeArgument)); + toolBuilder.outputSchema(this.getJsonMapper(), + JsonSchemaGenerator.generateFromClass((Class) typeArgument)); } }); } diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/AsyncStatelessMcpToolProvider.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/AsyncStatelessMcpToolProvider.java index e8fbf95..4841ca9 100644 --- a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/AsyncStatelessMcpToolProvider.java +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/AsyncStatelessMcpToolProvider.java @@ -21,6 +21,7 @@ import java.util.stream.Stream; import io.modelcontextprotocol.common.McpTransportContext; +import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.server.McpStatelessServerFeatures.AsyncToolSpecification; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpSchema.CallToolRequest; @@ -83,7 +84,7 @@ public List getToolSpecifications() { var toolBuilder = McpSchema.Tool.builder() .name(toolName) .description(toolDescrption) - .inputSchema(inputSchema); + .inputSchema(this.getJsonMapper(), inputSchema); var title = toolJavaAnnotation.title(); @@ -123,7 +124,8 @@ public List getToolSpecifications() { : null; if (!ClassUtils.isPrimitiveOrWrapper(methodReturnType) && !ClassUtils.isSimpleValueType(methodReturnType)) { - toolBuilder.outputSchema(JsonSchemaGenerator.generateFromType(typeArgument)); + toolBuilder.outputSchema(this.getJsonMapper(), + JsonSchemaGenerator.generateFromType(typeArgument)); } }); } diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/SyncMcpToolProvider.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/SyncMcpToolProvider.java index 7566955..e663e18 100644 --- a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/SyncMcpToolProvider.java +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/SyncMcpToolProvider.java @@ -20,6 +20,7 @@ import java.util.function.BiFunction; import java.util.stream.Stream; +import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.server.McpServerFeatures.SyncToolSpecification; import io.modelcontextprotocol.server.McpSyncServerExchange; import io.modelcontextprotocol.spec.McpSchema; @@ -74,10 +75,10 @@ public List getToolSpecifications() { String inputSchema = JsonSchemaGenerator.generateForMethodInput(mcpToolMethod); - var toolBuilder = McpSchema.Tool.builder() + McpSchema.Tool.Builder toolBuilder = McpSchema.Tool.builder() .name(toolName) .description(toolDescription) - .inputSchema(inputSchema); + .inputSchema(this.getJsonMapper(), inputSchema); var title = toolJavaAnnotation.title(); @@ -104,8 +105,6 @@ public List getToolSpecifications() { } toolBuilder.title(title); - // ReactiveUtils.isReactiveReturnTypeOfCallToolResult(mcpToolMethod); - // Generate Output Schema from the method return type. // Output schema is not generated for primitive types, void, // CallToolResult, simple value types (String, etc.) @@ -116,8 +115,8 @@ public List getToolSpecifications() { && methodReturnType != void.class && !ClassUtils.isPrimitiveOrWrapper(methodReturnType) && !ClassUtils.isSimpleValueType(methodReturnType)) { - toolBuilder - .outputSchema(JsonSchemaGenerator.generateFromType(mcpToolMethod.getGenericReturnType())); + toolBuilder.outputSchema(this.getJsonMapper(), + JsonSchemaGenerator.generateFromType(mcpToolMethod.getGenericReturnType())); } var tool = toolBuilder.build(); diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/SyncStatelessMcpToolProvider.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/SyncStatelessMcpToolProvider.java index 4953b2c..86495ec 100644 --- a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/SyncStatelessMcpToolProvider.java +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/tool/SyncStatelessMcpToolProvider.java @@ -21,6 +21,7 @@ import java.util.stream.Stream; import io.modelcontextprotocol.common.McpTransportContext; +import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.server.McpStatelessServerFeatures.SyncToolSpecification; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpSchema.CallToolRequest; @@ -80,7 +81,7 @@ public List getToolSpecifications() { var toolBuilder = McpSchema.Tool.builder() .name(toolName) .description(toolDescrption) - .inputSchema(inputSchema); + .inputSchema(this.getJsonMapper(), inputSchema); var title = toolJavaAnnotation.title(); @@ -119,8 +120,8 @@ public List getToolSpecifications() { && methodReturnType != void.class && !ClassUtils.isPrimitiveOrWrapper(methodReturnType) && !ClassUtils.isSimpleValueType(methodReturnType)) { - toolBuilder - .outputSchema(JsonSchemaGenerator.generateFromType(mcpToolMethod.getGenericReturnType())); + toolBuilder.outputSchema(this.getJsonMapper(), + JsonSchemaGenerator.generateFromType(mcpToolMethod.getGenericReturnType())); } var tool = toolBuilder.build(); diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/changed/tool/AsyncMcpToolListChangedMethodCallbackTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/changed/tool/AsyncMcpToolListChangedMethodCallbackTests.java index c9691e8..70778d9 100644 --- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/changed/tool/AsyncMcpToolListChangedMethodCallbackTests.java +++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/changed/tool/AsyncMcpToolListChangedMethodCallbackTests.java @@ -13,7 +13,7 @@ import org.junit.jupiter.api.Test; import org.springaicommunity.mcp.annotation.McpToolListChanged; - +import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.spec.McpSchema; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -26,8 +26,16 @@ public class AsyncMcpToolListChangedMethodCallbackTests { private static final List TEST_TOOLS = List.of( - McpSchema.Tool.builder().name("test-tool-1").description("Test Tool 1").inputSchema("{}").build(), - McpSchema.Tool.builder().name("test-tool-2").description("Test Tool 2").inputSchema("{}").build()); + McpSchema.Tool.builder() + .name("test-tool-1") + .description("Test Tool 1") + .inputSchema(McpJsonMapper.createDefault(), "{}") + .build(), + McpSchema.Tool.builder() + .name("test-tool-2") + .description("Test Tool 2") + .inputSchema(McpJsonMapper.createDefault(), "{}") + .build()); /** * Test class with valid methods. diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/changed/tool/SyncMcpToolListChangedMethodCallbackTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/changed/tool/SyncMcpToolListChangedMethodCallbackTests.java index a657c67..bfb6712 100644 --- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/changed/tool/SyncMcpToolListChangedMethodCallbackTests.java +++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/changed/tool/SyncMcpToolListChangedMethodCallbackTests.java @@ -13,7 +13,7 @@ import org.junit.jupiter.api.Test; import org.springaicommunity.mcp.annotation.McpToolListChanged; - +import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.spec.McpSchema; /** @@ -24,8 +24,16 @@ public class SyncMcpToolListChangedMethodCallbackTests { private static final List TEST_TOOLS = List.of( - McpSchema.Tool.builder().name("test-tool-1").description("Test Tool 1").inputSchema("{}").build(), - McpSchema.Tool.builder().name("test-tool-2").description("Test Tool 2").inputSchema("{}").build()); + McpSchema.Tool.builder() + .name("test-tool-1") + .description("Test Tool 1") + .inputSchema(McpJsonMapper.createDefault(), "{}") + .build(), + McpSchema.Tool.builder() + .name("test-tool-2") + .description("Test Tool 2") + .inputSchema(McpJsonMapper.createDefault(), "{}") + .build()); /** * Test class with valid methods. diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/AsyncCallToolRequestSupportTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/AsyncCallToolRequestSupportTests.java index 924069c..a3dd46b 100644 --- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/AsyncCallToolRequestSupportTests.java +++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/AsyncCallToolRequestSupportTests.java @@ -320,8 +320,8 @@ public void testAsyncStructuredOutputWithCallToolRequest() throws Exception { assertThat(result).isNotNull(); assertThat(result.isError()).isFalse(); assertThat(result.structuredContent()).isNotNull(); - assertThat(result.structuredContent()).containsEntry("message", "test-message"); - assertThat(result.structuredContent()).containsEntry("value", 42); + assertThat((Map) result.structuredContent()).containsEntry("message", "test-message"); + assertThat((Map) result.structuredContent()).containsEntry("value", 42); }).verifyComplete(); } diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/AsyncMcpToolMethodCallbackTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/AsyncMcpToolMethodCallbackTests.java index 2355a81..d65adbb 100644 --- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/AsyncMcpToolMethodCallbackTests.java +++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/AsyncMcpToolMethodCallbackTests.java @@ -570,8 +570,8 @@ public void testMonoToolReturningComplexObject() throws Exception { assertThat(result.isError()).isFalse(); assertThat(result.content()).isEmpty(); assertThat(result.structuredContent()).isNotNull(); - assertThat(result.structuredContent()).containsEntry("name", "test"); - assertThat(result.structuredContent()).containsEntry("value", 42); + assertThat((Map) result.structuredContent()).containsEntry("name", "test"); + assertThat((Map) result.structuredContent()).containsEntry("value", 42); }).verifyComplete(); } diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/AsyncStatelessMcpToolMethodCallbackTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/AsyncStatelessMcpToolMethodCallbackTests.java index 38bd5cf..5dccd71 100644 --- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/AsyncStatelessMcpToolMethodCallbackTests.java +++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/AsyncStatelessMcpToolMethodCallbackTests.java @@ -606,8 +606,8 @@ public void testMonoToolReturningComplexObject() throws Exception { assertThat(result.isError()).isFalse(); assertThat(result.content()).isEmpty(); assertThat(result.structuredContent()).isNotNull(); - assertThat(result.structuredContent()).containsEntry("name", "test"); - assertThat(result.structuredContent()).containsEntry("value", 42); + assertThat((Map) result.structuredContent()).containsEntry("name", "test"); + assertThat((Map) result.structuredContent()).containsEntry("value", 42); }).verifyComplete(); } diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/CallToolRequestSupportTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/CallToolRequestSupportTests.java index aaa5b42..6af8f0d 100644 --- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/CallToolRequestSupportTests.java +++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/CallToolRequestSupportTests.java @@ -446,8 +446,8 @@ public void testStructuredOutputWithCallToolRequest() throws Exception { assertThat(result).isNotNull(); assertThat(result.isError()).isFalse(); assertThat(result.structuredContent()).isNotNull(); - assertThat(result.structuredContent()).containsEntry("message", "test-message"); - assertThat(result.structuredContent()).containsEntry("value", 42); + assertThat((Map) result.structuredContent()).containsEntry("message", "test-message"); + assertThat((Map) result.structuredContent()).containsEntry("value", 42); } @Test diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/SyncMcpToolMethodCallbackTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/SyncMcpToolMethodCallbackTests.java index 5dd6634..724a254 100644 --- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/SyncMcpToolMethodCallbackTests.java +++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/SyncMcpToolMethodCallbackTests.java @@ -518,8 +518,8 @@ public void testToolReturningComplexObject() throws Exception { // the new implementation should return structured content assertThat(result.content()).isEmpty(); assertThat(result.structuredContent()).isNotNull(); - assertThat(result.structuredContent()).containsEntry("name", "test"); - assertThat(result.structuredContent()).containsEntry("value", 42); + assertThat((Map) result.structuredContent()).containsEntry("name", "test"); + assertThat((Map) result.structuredContent()).containsEntry("value", 42); } @Test diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/SyncStatelessMcpToolMethodCallbackTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/SyncStatelessMcpToolMethodCallbackTests.java index 77fcf6f..e1e066e 100644 --- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/SyncStatelessMcpToolMethodCallbackTests.java +++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/tool/SyncStatelessMcpToolMethodCallbackTests.java @@ -539,8 +539,8 @@ public void testToolReturningComplexObject() throws Exception { // the new implementation should return structured content assertThat(result.content()).isEmpty(); assertThat(result.structuredContent()).isNotNull(); - assertThat(result.structuredContent()).containsEntry("name", "test"); - assertThat(result.structuredContent()).containsEntry("value", 42); + assertThat((Map) result.structuredContent()).containsEntry("name", "test"); + assertThat((Map) result.structuredContent()).containsEntry("value", 42); } @Test diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/changed/tool/AsyncMcpToolListChangedProviderTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/changed/tool/AsyncMcpToolListChangedProviderTests.java index 939ef62..061f162 100644 --- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/changed/tool/AsyncMcpToolListChangedProviderTests.java +++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/changed/tool/AsyncMcpToolListChangedProviderTests.java @@ -13,7 +13,7 @@ import org.junit.jupiter.api.Test; import org.springaicommunity.mcp.annotation.McpToolListChanged; import org.springaicommunity.mcp.method.changed.tool.AsyncToolListChangedSpecification; - +import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.spec.McpSchema; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -26,8 +26,16 @@ public class AsyncMcpToolListChangedProviderTests { private static final List TEST_TOOLS = List.of( - McpSchema.Tool.builder().name("test-tool-1").description("Test Tool 1").inputSchema("{}").build(), - McpSchema.Tool.builder().name("test-tool-2").description("Test Tool 2").inputSchema("{}").build()); + McpSchema.Tool.builder() + .name("test-tool-1") + .description("Test Tool 1") + .inputSchema(McpJsonMapper.createDefault(), "{}") + .build(), + McpSchema.Tool.builder() + .name("test-tool-2") + .description("Test Tool 2") + .inputSchema(McpJsonMapper.createDefault(), "{}") + .build()); /** * Test class with tool list changed consumer methods. diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/changed/tool/SyncMcpToolListChangedProviderTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/changed/tool/SyncMcpToolListChangedProviderTests.java index f840122..427990f 100644 --- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/changed/tool/SyncMcpToolListChangedProviderTests.java +++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/changed/tool/SyncMcpToolListChangedProviderTests.java @@ -13,7 +13,7 @@ import org.junit.jupiter.api.Test; import org.springaicommunity.mcp.annotation.McpToolListChanged; import org.springaicommunity.mcp.method.changed.tool.SyncToolListChangedSpecification; - +import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.spec.McpSchema; /** @@ -24,8 +24,16 @@ public class SyncMcpToolListChangedProviderTests { private static final List TEST_TOOLS = List.of( - McpSchema.Tool.builder().name("test-tool-1").description("Test Tool 1").inputSchema("{}").build(), - McpSchema.Tool.builder().name("test-tool-2").description("Test Tool 2").inputSchema("{}").build()); + McpSchema.Tool.builder() + .name("test-tool-1") + .description("Test Tool 1") + .inputSchema(McpJsonMapper.createDefault(), "{}") + .build(), + McpSchema.Tool.builder() + .name("test-tool-2") + .description("Test Tool 2") + .inputSchema(McpJsonMapper.createDefault(), "{}") + .build()); /** * Test class with tool list changed consumer methods. diff --git a/pom.xml b/pom.xml index ffb1a12..4e06708 100644 --- a/pom.xml +++ b/pom.xml @@ -56,11 +56,13 @@ 17 17 - 0.12.1 + 0.13.0-SNAPSHOT 4.38.0 2.2.36 - 2.19.2 + + + 2.17.0 2.0.16 1.5.15