From d8ea3f06a72f1ff94f3222008bd6cfa64d2b1b26 Mon Sep 17 00:00:00 2001 From: Scott Lewis Date: Wed, 28 Jan 2026 15:43:23 -0800 Subject: [PATCH 1/4] Move mcp-json into mcp-core, add OSGi support Co-authored-by: Luca Chang <131398524+LucaButBoring@users.noreply.github.com> Signed-off-by: Daniel Garnier-Moiroux --- mcp-bom/pom.xml | 7 -- mcp-core/pom.xml | 20 ++--- .../client/McpClient.java | 12 ++- .../HttpClientSseClientTransport.java | 3 +- .../HttpClientStreamableHttpTransport.java | 8 +- .../json/McpJsonDefaults.java | 76 +++++++++++++++++ .../json/McpJsonMapper.java | 20 ----- .../json/McpJsonMapperSupplier.java | 0 .../io/modelcontextprotocol/json/TypeRef.java | 2 +- .../json/schema/JsonSchemaValidator.java | 20 ----- .../schema/JsonSchemaValidatorSupplier.java | 0 .../server/McpServer.java | 54 +++++++----- ...HttpServletSseServerTransportProvider.java | 5 +- .../HttpServletStatelessServerTransport.java | 6 +- ...vletStreamableServerTransportProvider.java | 5 +- .../util/McpServiceLoader.java | 68 +++++++++++++++ ...elcontextprotocol.json.McpJsonDefaults.xml | 9 ++ .../MockMcpClientTransport.java | 4 +- .../MockMcpServerTransport.java | 4 +- .../CompleteCompletionSerializationTest.java | 3 +- .../util/McpJsonMapperUtils.java | 3 +- mcp-json-jackson2/pom.xml | 47 +++++++++-- ....jackson2.JacksonMcpJsonMapperSupplier.xml | 7 ++ ...on2.JacksonJsonSchemaValidatorSupplier.xml | 7 ++ .../json/McpJsonMapperTest.java | 2 +- .../json/schema/JsonSchemaValidatorTest.java | 3 +- mcp-json-jackson3/pom.xml | 42 ++++++++-- ....jackson3.JacksonMcpJsonMapperSupplier.xml | 7 ++ ...on3.JacksonJsonSchemaValidatorSupplier.xml | 7 ++ .../json/McpJsonMapperTest.java | 2 +- .../json/schema/JsonSchemaValidatorTest.java | 3 +- mcp-json/pom.xml | 39 --------- .../json/McpJsonInternal.java | 84 ------------------- .../json/schema/JsonSchemaInternal.java | 83 ------------------ .../WebClientStreamableHttpTransport.java | 6 +- .../transport/WebFluxSseClientTransport.java | 3 +- .../WebFluxSseServerTransportProvider.java | 6 +- .../WebFluxStatelessServerTransport.java | 6 +- ...FluxStreamableServerTransportProvider.java | 5 +- .../utils/McpJsonMapperUtils.java | 3 +- .../WebMvcSseServerTransportProvider.java | 6 +- .../WebMvcStatelessServerTransport.java | 6 +- ...bMvcStreamableServerTransportProvider.java | 7 +- ...WebMvcSseServerTransportProviderTests.java | 3 +- .../MockMcpTransport.java | 4 +- .../util/McpJsonMapperUtils.java | 3 +- pom.xml | 1 - 47 files changed, 368 insertions(+), 353 deletions(-) create mode 100644 mcp-core/src/main/java/io/modelcontextprotocol/json/McpJsonDefaults.java rename {mcp-json => mcp-core}/src/main/java/io/modelcontextprotocol/json/McpJsonMapper.java (81%) rename {mcp-json => mcp-core}/src/main/java/io/modelcontextprotocol/json/McpJsonMapperSupplier.java (100%) rename {mcp-json => mcp-core}/src/main/java/io/modelcontextprotocol/json/TypeRef.java (94%) rename {mcp-json => mcp-core}/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidator.java (69%) rename {mcp-json => mcp-core}/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidatorSupplier.java (100%) create mode 100644 mcp-core/src/main/java/io/modelcontextprotocol/util/McpServiceLoader.java create mode 100644 mcp-core/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.McpJsonDefaults.xml create mode 100644 mcp-json-jackson2/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.jackson2.JacksonMcpJsonMapperSupplier.xml create mode 100644 mcp-json-jackson2/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.schema.jackson2.JacksonJsonSchemaValidatorSupplier.xml create mode 100644 mcp-json-jackson3/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.jackson3.JacksonMcpJsonMapperSupplier.xml create mode 100644 mcp-json-jackson3/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.schema.jackson3.JacksonJsonSchemaValidatorSupplier.xml delete mode 100644 mcp-json/pom.xml delete mode 100644 mcp-json/src/main/java/io/modelcontextprotocol/json/McpJsonInternal.java delete mode 100644 mcp-json/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaInternal.java diff --git a/mcp-bom/pom.xml b/mcp-bom/pom.xml index 447c9e0bd..f3d76d819 100644 --- a/mcp-bom/pom.xml +++ b/mcp-bom/pom.xml @@ -40,13 +40,6 @@ ${project.version} - - - io.modelcontextprotocol.sdk - mcp-json - ${project.version} - - io.modelcontextprotocol.sdk diff --git a/mcp-core/pom.xml b/mcp-core/pom.xml index 0c8650f46..e6eabff3d 100644 --- a/mcp-core/pom.xml +++ b/mcp-core/pom.xml @@ -41,6 +41,7 @@ Automatic-Module-Name: ${project.groupId}.${project.artifactId} Import-Package: jakarta.*;resolution:=optional, \ *; + Service-Component: OSGI-INF/io.modelcontextprotocol.json.McpJsonDefaults.xml Export-Package: io.modelcontextprotocol.*;version="${version}";-noimport:=true -noimportjava: true; -nouses: true; @@ -65,11 +66,6 @@ - - io.modelcontextprotocol.sdk - mcp-json - 0.18.0-SNAPSHOT - org.slf4j @@ -97,14 +93,6 @@ provided - - - io.modelcontextprotocol.sdk - mcp-json-jackson3 - 0.18.0-SNAPSHOT - test - - org.springframework spring-webmvc @@ -112,6 +100,12 @@ test + + tools.jackson.core + jackson-databind + ${jackson3.version} + test + io.projectreactor.netty diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClient.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClient.java index c9989f832..1210b9078 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClient.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClient.java @@ -5,6 +5,7 @@ package io.modelcontextprotocol.client; import io.modelcontextprotocol.common.McpTransportContext; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.schema.JsonSchemaValidator; import io.modelcontextprotocol.spec.McpClientTransport; import io.modelcontextprotocol.spec.McpSchema; @@ -491,9 +492,12 @@ public McpSyncClient build() { McpClientFeatures.Async asyncFeatures = McpClientFeatures.Async.fromSync(syncFeatures); - return new McpSyncClient(new McpAsyncClient(transport, this.requestTimeout, this.initializationTimeout, - jsonSchemaValidator != null ? jsonSchemaValidator : JsonSchemaValidator.getDefault(), - asyncFeatures), this.contextProvider); + return new McpSyncClient( + new McpAsyncClient(transport, this.requestTimeout, this.initializationTimeout, + jsonSchemaValidator != null ? jsonSchemaValidator + : McpJsonDefaults.getDefaultJsonSchemaValidator(), + asyncFeatures), + this.contextProvider); } } @@ -826,7 +830,7 @@ public AsyncSpec enableCallToolSchemaCaching(boolean enableCallToolSchemaCaching */ public McpAsyncClient build() { var jsonSchemaValidator = (this.jsonSchemaValidator != null) ? this.jsonSchemaValidator - : JsonSchemaValidator.getDefault(); + : McpJsonDefaults.getDefaultJsonSchemaValidator(); return new McpAsyncClient(this.transport, this.requestTimeout, this.initializationTimeout, jsonSchemaValidator, new McpClientFeatures.Async(this.clientInfo, this.capabilities, this.roots, diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransport.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransport.java index ae093316f..b9ed2711d 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransport.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransport.java @@ -22,6 +22,7 @@ import io.modelcontextprotocol.client.transport.customizer.McpAsyncHttpClientRequestCustomizer; import io.modelcontextprotocol.client.transport.customizer.McpSyncHttpClientRequestCustomizer; import io.modelcontextprotocol.common.McpTransportContext; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.spec.HttpHeaders; @@ -327,7 +328,7 @@ public Builder connectTimeout(Duration connectTimeout) { public HttpClientSseClientTransport build() { HttpClient httpClient = this.clientBuilder.connectTimeout(this.connectTimeout).build(); return new HttpClientSseClientTransport(httpClient, requestBuilder, baseUri, sseEndpoint, - jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, httpRequestCustomizer); + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, httpRequestCustomizer); } } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransport.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransport.java index 400a8a2fa..00b80f1d5 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransport.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransport.java @@ -25,6 +25,7 @@ import io.modelcontextprotocol.client.transport.customizer.McpAsyncHttpClientRequestCustomizer; import io.modelcontextprotocol.client.transport.customizer.McpSyncHttpClientRequestCustomizer; import io.modelcontextprotocol.common.McpTransportContext; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.spec.ClosedMcpTransportSession; @@ -842,9 +843,10 @@ public Builder supportedProtocolVersions(List supportedProtocolVersions) */ public HttpClientStreamableHttpTransport build() { HttpClient httpClient = this.clientBuilder.connectTimeout(this.connectTimeout).build(); - return new HttpClientStreamableHttpTransport(jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, - httpClient, requestBuilder, baseUri, endpoint, resumableStreams, openConnectionOnStartup, - httpRequestCustomizer, supportedProtocolVersions); + return new HttpClientStreamableHttpTransport( + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, httpClient, + requestBuilder, baseUri, endpoint, resumableStreams, openConnectionOnStartup, httpRequestCustomizer, + supportedProtocolVersions); } } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/json/McpJsonDefaults.java b/mcp-core/src/main/java/io/modelcontextprotocol/json/McpJsonDefaults.java new file mode 100644 index 000000000..b8bdd900f --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/json/McpJsonDefaults.java @@ -0,0 +1,76 @@ +/** + * Copyright 2026 - 2026 the original author or authors. + */ +package io.modelcontextprotocol.json; + +import io.modelcontextprotocol.json.schema.JsonSchemaValidator; +import io.modelcontextprotocol.json.schema.JsonSchemaValidatorSupplier; +import io.modelcontextprotocol.util.McpServiceLoader; + +/** + * This class is to be used to provide access to the default McpJsonMapper and to the + * default JsonSchemaValidator instances via the static methods: getDefaultMcpJsonMapper + * and getDefaultJsonSchemaValidator. + *

+ * The initialization of (singleton) instances of this class is different in non-OSGi + * environments and OSGi environments. Specifically, in non-OSGi environments The + * McpJsonDefaults class will be loaded by whatever classloader is used to call one of the + * existing static get methods for the first time. For servers, this will usually be in + * response to the creation of the first McpServer instance. At that first time, the + * mcpMapperServiceLoader and mcpValidatorServiceLoader will be null, and the + * McpJsonDefaults constructor will be called, creating/initializing the + * mcpMapperServiceLoader and the mcpValidatorServiceLoader...which will then be used to + * call the ServiceLoader.load method. + *

+ * In OSGi environments, upon bundle activation SCR will create a new (singleton) instance + * of McpJsonDefaults (via the constructor), and then inject suppliers via the + * setMcpJsonMapperSupplier and setJsonSchemaValidatorSupplier methods with the + * SCR-discovered instances of those services. This does depend upon the jars/bundles + * providing those suppliers to be started/activated. This SCR behavior is dictated by xml + * files in OSGi-INF directory of mcp-core (this project/jar/bundle), and the jsonmapper + * and jsonschemvalidator provider jars/bundles (e.g. mcp-json-jackson2, 3, or others). + */ +public class McpJsonDefaults { + + protected static McpServiceLoader mcpMapperServiceLoader; + + protected static McpServiceLoader mcpValidatorServiceLoader; + + public McpJsonDefaults() { + mcpMapperServiceLoader = new McpServiceLoader( + McpJsonMapperSupplier.class); + mcpValidatorServiceLoader = new McpServiceLoader( + JsonSchemaValidatorSupplier.class); + } + + void setMcpJsonMapperSupplier(McpJsonMapperSupplier supplier) { + mcpMapperServiceLoader.setSupplier(supplier); + } + + void unsetMcpJsonMapperSupplier(McpJsonMapperSupplier supplier) { + mcpMapperServiceLoader.unsetSupplier(supplier); + } + + public synchronized static McpJsonMapper getDefaultMcpJsonMapper() { + if (mcpMapperServiceLoader == null) { + new McpJsonDefaults(); + } + return mcpMapperServiceLoader.getDefault(); + } + + void setJsonSchemaValidatorSupplier(JsonSchemaValidatorSupplier supplier) { + mcpValidatorServiceLoader.setSupplier(supplier); + } + + void unsetJsonSchemaValidatorSupplier(JsonSchemaValidatorSupplier supplier) { + mcpValidatorServiceLoader.unsetSupplier(supplier); + } + + public synchronized static JsonSchemaValidator getDefaultJsonSchemaValidator() { + if (mcpValidatorServiceLoader == null) { + new McpJsonDefaults(); + } + return mcpValidatorServiceLoader.getDefault(); + } + +} diff --git a/mcp-json/src/main/java/io/modelcontextprotocol/json/McpJsonMapper.java b/mcp-core/src/main/java/io/modelcontextprotocol/json/McpJsonMapper.java similarity index 81% rename from mcp-json/src/main/java/io/modelcontextprotocol/json/McpJsonMapper.java rename to mcp-core/src/main/java/io/modelcontextprotocol/json/McpJsonMapper.java index 1e30cad16..8481d1703 100644 --- a/mcp-json/src/main/java/io/modelcontextprotocol/json/McpJsonMapper.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/json/McpJsonMapper.java @@ -87,24 +87,4 @@ public interface McpJsonMapper { */ byte[] writeValueAsBytes(Object value) throws IOException; - /** - * Returns the default {@link McpJsonMapper}. - * @return The default {@link McpJsonMapper} - * @throws IllegalStateException If no {@link McpJsonMapper} implementation exists on - * the classpath. - */ - static McpJsonMapper getDefault() { - return McpJsonInternal.getDefaultMapper(); - } - - /** - * Creates a new default {@link McpJsonMapper}. - * @return The default {@link McpJsonMapper} - * @throws IllegalStateException If no {@link McpJsonMapper} implementation exists on - * the classpath. - */ - static McpJsonMapper createDefault() { - return McpJsonInternal.createDefaultMapper(); - } - } diff --git a/mcp-json/src/main/java/io/modelcontextprotocol/json/McpJsonMapperSupplier.java b/mcp-core/src/main/java/io/modelcontextprotocol/json/McpJsonMapperSupplier.java similarity index 100% rename from mcp-json/src/main/java/io/modelcontextprotocol/json/McpJsonMapperSupplier.java rename to mcp-core/src/main/java/io/modelcontextprotocol/json/McpJsonMapperSupplier.java diff --git a/mcp-json/src/main/java/io/modelcontextprotocol/json/TypeRef.java b/mcp-core/src/main/java/io/modelcontextprotocol/json/TypeRef.java similarity index 94% rename from mcp-json/src/main/java/io/modelcontextprotocol/json/TypeRef.java rename to mcp-core/src/main/java/io/modelcontextprotocol/json/TypeRef.java index ab37b43f3..725513c66 100644 --- a/mcp-json/src/main/java/io/modelcontextprotocol/json/TypeRef.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/json/TypeRef.java @@ -9,7 +9,7 @@ /** * Captures generic type information at runtime for parameterized JSON (de)serialization. - * Usage: TypeRef<List<Foo>> ref = new TypeRef<>(){}; + * Usage: TypeRef> ref = new TypeRef<>(){}; */ public abstract class TypeRef { diff --git a/mcp-json/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidator.java b/mcp-core/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidator.java similarity index 69% rename from mcp-json/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidator.java rename to mcp-core/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidator.java index 8e35c0237..09fe604f4 100644 --- a/mcp-json/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidator.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidator.java @@ -41,24 +41,4 @@ public static ValidationResponse asInvalid(String message) { */ ValidationResponse validate(Map schema, Object structuredContent); - /** - * Creates the default {@link JsonSchemaValidator}. - * @return The default {@link JsonSchemaValidator} - * @throws IllegalStateException If no {@link JsonSchemaValidator} implementation - * exists on the classpath. - */ - static JsonSchemaValidator createDefault() { - return JsonSchemaInternal.createDefaultValidator(); - } - - /** - * Returns the default {@link JsonSchemaValidator}. - * @return The default {@link JsonSchemaValidator} - * @throws IllegalStateException If no {@link JsonSchemaValidator} implementation - * exists on the classpath. - */ - static JsonSchemaValidator getDefault() { - return JsonSchemaInternal.getDefaultValidator(); - } - } diff --git a/mcp-json/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidatorSupplier.java b/mcp-core/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidatorSupplier.java similarity index 100% rename from mcp-json/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidatorSupplier.java rename to mcp-core/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidatorSupplier.java diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServer.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServer.java index f4196c0bf..73a50162f 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServer.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServer.java @@ -4,7 +4,17 @@ package io.modelcontextprotocol.server; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; + import io.modelcontextprotocol.common.McpTransportContext; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.schema.JsonSchemaValidator; import io.modelcontextprotocol.spec.McpSchema; @@ -18,11 +28,6 @@ import io.modelcontextprotocol.util.ToolNameValidator; import reactor.core.publisher.Mono; -import java.time.Duration; -import java.util.*; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; - /** * Factory class for creating Model Context Protocol (MCP) servers. MCP servers expose * tools, resources, and prompts to AI models through a standardized interface. @@ -235,10 +240,11 @@ public McpAsyncServer build() { this.instructions); var jsonSchemaValidator = (this.jsonSchemaValidator != null) ? this.jsonSchemaValidator - : JsonSchemaValidator.getDefault(); + : McpJsonDefaults.getDefaultJsonSchemaValidator(); - return new McpAsyncServer(transportProvider, jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, - features, requestTimeout, uriTemplateManagerFactory, jsonSchemaValidator); + return new McpAsyncServer(transportProvider, + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, features, + requestTimeout, uriTemplateManagerFactory, jsonSchemaValidator); } } @@ -262,9 +268,10 @@ public McpAsyncServer build() { this.resources, this.resourceTemplates, this.prompts, this.completions, this.rootsChangeHandlers, this.instructions); var jsonSchemaValidator = this.jsonSchemaValidator != null ? this.jsonSchemaValidator - : JsonSchemaValidator.getDefault(); - return new McpAsyncServer(transportProvider, jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, - features, requestTimeout, uriTemplateManagerFactory, jsonSchemaValidator); + : McpJsonDefaults.getDefaultJsonSchemaValidator(); + return new McpAsyncServer(transportProvider, + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, features, + requestTimeout, uriTemplateManagerFactory, jsonSchemaValidator); } } @@ -851,9 +858,9 @@ public McpSyncServer build() { this.immediateExecution); var asyncServer = new McpAsyncServer(transportProvider, - jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, asyncFeatures, requestTimeout, - uriTemplateManagerFactory, - jsonSchemaValidator != null ? jsonSchemaValidator : JsonSchemaValidator.getDefault()); + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, asyncFeatures, + requestTimeout, uriTemplateManagerFactory, jsonSchemaValidator != null ? jsonSchemaValidator + : McpJsonDefaults.getDefaultJsonSchemaValidator()); return new McpSyncServer(asyncServer, this.immediateExecution); } @@ -881,10 +888,10 @@ public McpSyncServer build() { McpServerFeatures.Async asyncFeatures = McpServerFeatures.Async.fromSync(syncFeatures, this.immediateExecution); var jsonSchemaValidator = this.jsonSchemaValidator != null ? this.jsonSchemaValidator - : JsonSchemaValidator.getDefault(); + : McpJsonDefaults.getDefaultJsonSchemaValidator(); var asyncServer = new McpAsyncServer(transportProvider, - jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, asyncFeatures, this.requestTimeout, - this.uriTemplateManagerFactory, jsonSchemaValidator); + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, asyncFeatures, + this.requestTimeout, this.uriTemplateManagerFactory, jsonSchemaValidator); return new McpSyncServer(asyncServer, this.immediateExecution); } @@ -1931,9 +1938,10 @@ public StatelessAsyncSpecification jsonSchemaValidator(JsonSchemaValidator jsonS public McpStatelessAsyncServer build() { var features = new McpStatelessServerFeatures.Async(this.serverInfo, this.serverCapabilities, this.tools, this.resources, this.resourceTemplates, this.prompts, this.completions, this.instructions); - return new McpStatelessAsyncServer(transport, jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, - features, requestTimeout, uriTemplateManagerFactory, - jsonSchemaValidator != null ? jsonSchemaValidator : JsonSchemaValidator.getDefault()); + return new McpStatelessAsyncServer(transport, + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, features, + requestTimeout, uriTemplateManagerFactory, jsonSchemaValidator != null ? jsonSchemaValidator + : McpJsonDefaults.getDefaultJsonSchemaValidator()); } } @@ -2432,9 +2440,9 @@ public McpStatelessSyncServer build() { this.resources, this.resourceTemplates, this.prompts, this.completions, this.instructions); var asyncFeatures = McpStatelessServerFeatures.Async.fromSync(syncFeatures, this.immediateExecution); var asyncServer = new McpStatelessAsyncServer(transport, - jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, asyncFeatures, requestTimeout, - uriTemplateManagerFactory, - this.jsonSchemaValidator != null ? this.jsonSchemaValidator : JsonSchemaValidator.getDefault()); + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, asyncFeatures, + requestTimeout, uriTemplateManagerFactory, this.jsonSchemaValidator != null + ? this.jsonSchemaValidator : McpJsonDefaults.getDefaultJsonSchemaValidator()); return new McpStatelessSyncServer(asyncServer, this.immediateExecution); } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletSseServerTransportProvider.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletSseServerTransportProvider.java index d12fb8c9e..c07906b49 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletSseServerTransportProvider.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletSseServerTransportProvider.java @@ -18,6 +18,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import io.modelcontextprotocol.common.McpTransportContext; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.server.McpTransportContextExtractor; @@ -689,8 +690,8 @@ public HttpServletSseServerTransportProvider build() { throw new IllegalStateException("MessageEndpoint must be set"); } return new HttpServletSseServerTransportProvider( - jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, baseUrl, messageEndpoint, sseEndpoint, - keepAliveInterval, contextExtractor, securityValidator); + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, baseUrl, + messageEndpoint, sseEndpoint, keepAliveInterval, contextExtractor, securityValidator); } } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStatelessServerTransport.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStatelessServerTransport.java index 106f834f5..6431a2cd2 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStatelessServerTransport.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStatelessServerTransport.java @@ -16,6 +16,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.common.McpTransportContext; @@ -347,8 +348,9 @@ public Builder securityValidator(ServerTransportSecurityValidator securityValida */ public HttpServletStatelessServerTransport build() { Assert.notNull(mcpEndpoint, "Message endpoint must be set"); - return new HttpServletStatelessServerTransport(jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, - mcpEndpoint, contextExtractor, securityValidator); + return new HttpServletStatelessServerTransport( + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, mcpEndpoint, + contextExtractor, securityValidator); } } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStreamableServerTransportProvider.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStreamableServerTransportProvider.java index d9c0916af..18cdcff96 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStreamableServerTransportProvider.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletStreamableServerTransportProvider.java @@ -32,6 +32,7 @@ import io.modelcontextprotocol.spec.McpStreamableServerTransportProvider; import io.modelcontextprotocol.spec.ProtocolVersions; import io.modelcontextprotocol.util.Assert; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.util.KeepAliveScheduler; import jakarta.servlet.AsyncContext; @@ -912,8 +913,8 @@ public Builder securityValidator(ServerTransportSecurityValidator securityValida public HttpServletStreamableServerTransportProvider build() { Assert.notNull(this.mcpEndpoint, "MCP endpoint must be set"); return new HttpServletStreamableServerTransportProvider( - jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, mcpEndpoint, disallowDelete, - contextExtractor, keepAliveInterval, securityValidator); + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, mcpEndpoint, + disallowDelete, contextExtractor, keepAliveInterval, securityValidator); } } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/util/McpServiceLoader.java b/mcp-core/src/main/java/io/modelcontextprotocol/util/McpServiceLoader.java new file mode 100644 index 000000000..f1c73a07a --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/util/McpServiceLoader.java @@ -0,0 +1,68 @@ +/** + * Copyright 2026 - 2026 the original author or authors. + */ +package io.modelcontextprotocol.util; + +import java.util.Optional; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; +import java.util.function.Supplier; + +/** + * Instance of this class are intended to be used differently in OSGi and non-OSGi + * environments. In all non-OSGi environments the supplier member will be + * null and the serviceLoad method will be called to use the + * ServiceLoader.load to find the first instance of the supplier (assuming one is present + * in the runtime), cache it, and call the supplier's get method. + *

+ * In OSGi environments, the Service component runtime (scr) will call the setSupplier + * method upon bundle activation (assuming one is present in the runtime), and subsequent + * calls will use the given supplier instance rather than the ServiceLoader.load. + * + * @param the type of the supplier + * @param the type of the supplier result/returned value + */ +public class McpServiceLoader, R> { + + private Class supplierType; + + private S supplier; + + private R supplierResult; + + public void setSupplier(S supplier) { + this.supplier = supplier; + this.supplierResult = null; + } + + public void unsetSupplier(S supplier) { + this.supplier = null; + this.supplierResult = null; + } + + public McpServiceLoader(Class supplierType) { + this.supplierType = supplierType; + } + + protected Optional serviceLoad(Class type) { + return ServiceLoader.load(type).findFirst(); + } + + @SuppressWarnings("unchecked") + public synchronized R getDefault() { + if (this.supplierResult == null) { + if (this.supplier == null) { + // Use serviceloader + Optional sl = serviceLoad(this.supplierType); + if (sl.isEmpty()) { + throw new ServiceConfigurationError( + "No %s available for creating McpJsonMapper".formatted(this.supplierType.getSimpleName())); + } + this.supplier = (S) sl.get(); + } + this.supplierResult = this.supplier.get(); + } + return supplierResult; + } + +} diff --git a/mcp-core/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.McpJsonDefaults.xml b/mcp-core/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.McpJsonDefaults.xml new file mode 100644 index 000000000..1a10fdfb3 --- /dev/null +++ b/mcp-core/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.McpJsonDefaults.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java b/mcp-core/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java index 9854de210..04b058973 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java @@ -9,7 +9,7 @@ import java.util.function.BiConsumer; import java.util.function.Function; -import io.modelcontextprotocol.json.McpJsonMapper; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.spec.McpClientTransport; import io.modelcontextprotocol.spec.McpSchema; @@ -100,7 +100,7 @@ public Mono closeGracefully() { @Override public T unmarshalFrom(Object data, TypeRef typeRef) { - return McpJsonMapper.getDefault().convertValue(data, typeRef); + return McpJsonDefaults.getDefaultMcpJsonMapper().convertValue(data, typeRef); } } diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/MockMcpServerTransport.java b/mcp-core/src/test/java/io/modelcontextprotocol/MockMcpServerTransport.java index f3d6b77a7..5fefb892d 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/MockMcpServerTransport.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/MockMcpServerTransport.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.function.BiConsumer; -import io.modelcontextprotocol.json.McpJsonMapper; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpSchema.JSONRPCNotification; @@ -68,7 +68,7 @@ public Mono closeGracefully() { @Override public T unmarshalFrom(Object data, TypeRef typeRef) { - return McpJsonMapper.getDefault().convertValue(data, typeRef); + return McpJsonDefaults.getDefaultMcpJsonMapper().convertValue(data, typeRef); } } diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/spec/CompleteCompletionSerializationTest.java b/mcp-core/src/test/java/io/modelcontextprotocol/spec/CompleteCompletionSerializationTest.java index 55f71fea4..da5422e4e 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/spec/CompleteCompletionSerializationTest.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/spec/CompleteCompletionSerializationTest.java @@ -1,5 +1,6 @@ package io.modelcontextprotocol.spec; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -10,7 +11,7 @@ class CompleteCompletionSerializationTest { @Test void codeCompletionSerialization() throws IOException { - McpJsonMapper jsonMapper = McpJsonMapper.getDefault(); + McpJsonMapper jsonMapper = McpJsonDefaults.getDefaultMcpJsonMapper(); McpSchema.CompleteResult.CompleteCompletion codeComplete = new McpSchema.CompleteResult.CompleteCompletion( Collections.emptyList(), 0, false); String json = jsonMapper.writeValueAsString(codeComplete); diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/util/McpJsonMapperUtils.java b/mcp-core/src/test/java/io/modelcontextprotocol/util/McpJsonMapperUtils.java index 911506e01..0af4815c9 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/util/McpJsonMapperUtils.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/util/McpJsonMapperUtils.java @@ -1,5 +1,6 @@ package io.modelcontextprotocol.util; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; public final class McpJsonMapperUtils { @@ -7,6 +8,6 @@ public final class McpJsonMapperUtils { private McpJsonMapperUtils() { } - public static final McpJsonMapper JSON_MAPPER = McpJsonMapper.getDefault(); + public static final McpJsonMapper JSON_MAPPER = McpJsonDefaults.getDefaultMcpJsonMapper(); } diff --git a/mcp-json-jackson2/pom.xml b/mcp-json-jackson2/pom.xml index 956a72c23..37384fea9 100644 --- a/mcp-json-jackson2/pom.xml +++ b/mcp-json-jackson2/pom.xml @@ -10,7 +10,7 @@ mcp-json-jackson2 jar - Java MCP SDK JSON Jackson + Java MCP SDK JSON Jackson 2 Java MCP SDK JSON implementation based on Jackson 2 https://github.com/modelcontextprotocol/java-sdk @@ -20,30 +20,59 @@ + + biz.aQute.bnd + bnd-maven-plugin + ${bnd-maven-plugin.version} + + + bnd-process + + bnd-process + + + + + + + + + org.apache.maven.plugins maven-jar-plugin - - true - + ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - io.modelcontextprotocol.sdk - mcp-json - 0.18.0-SNAPSHOT - com.fasterxml.jackson.core jackson-databind ${jackson2.version} + + io.modelcontextprotocol.sdk + mcp-core + 0.18.0-SNAPSHOT + com.networknt json-schema-validator diff --git a/mcp-json-jackson2/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.jackson2.JacksonMcpJsonMapperSupplier.xml b/mcp-json-jackson2/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.jackson2.JacksonMcpJsonMapperSupplier.xml new file mode 100644 index 000000000..1d6705f56 --- /dev/null +++ b/mcp-json-jackson2/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.jackson2.JacksonMcpJsonMapperSupplier.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/mcp-json-jackson2/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.schema.jackson2.JacksonJsonSchemaValidatorSupplier.xml b/mcp-json-jackson2/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.schema.jackson2.JacksonJsonSchemaValidatorSupplier.xml new file mode 100644 index 000000000..ad628745f --- /dev/null +++ b/mcp-json-jackson2/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.schema.jackson2.JacksonJsonSchemaValidatorSupplier.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/mcp-json-jackson2/src/test/java/io/modelcontextprotocol/json/McpJsonMapperTest.java b/mcp-json-jackson2/src/test/java/io/modelcontextprotocol/json/McpJsonMapperTest.java index 062927587..bf865a087 100644 --- a/mcp-json-jackson2/src/test/java/io/modelcontextprotocol/json/McpJsonMapperTest.java +++ b/mcp-json-jackson2/src/test/java/io/modelcontextprotocol/json/McpJsonMapperTest.java @@ -14,7 +14,7 @@ class McpJsonMapperTest { @Test void shouldUseJackson2Mapper() { - assertThat(McpJsonMapper.getDefault()).isInstanceOf(JacksonMcpJsonMapper.class); + assertThat(McpJsonDefaults.getDefaultMcpJsonMapper()).isInstanceOf(JacksonMcpJsonMapper.class); } } diff --git a/mcp-json-jackson2/src/test/java/io/modelcontextprotocol/json/schema/JsonSchemaValidatorTest.java b/mcp-json-jackson2/src/test/java/io/modelcontextprotocol/json/schema/JsonSchemaValidatorTest.java index 7b92eb7ee..0c5864a2e 100644 --- a/mcp-json-jackson2/src/test/java/io/modelcontextprotocol/json/schema/JsonSchemaValidatorTest.java +++ b/mcp-json-jackson2/src/test/java/io/modelcontextprotocol/json/schema/JsonSchemaValidatorTest.java @@ -8,13 +8,14 @@ import org.junit.jupiter.api.Test; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.schema.jackson2.DefaultJsonSchemaValidator; class JsonSchemaValidatorTest { @Test void shouldUseJackson2Mapper() { - assertThat(JsonSchemaValidator.getDefault()).isInstanceOf(DefaultJsonSchemaValidator.class); + assertThat(McpJsonDefaults.getDefaultJsonSchemaValidator()).isInstanceOf(DefaultJsonSchemaValidator.class); } } diff --git a/mcp-json-jackson3/pom.xml b/mcp-json-jackson3/pom.xml index a3cc47048..c02bc375b 100644 --- a/mcp-json-jackson3/pom.xml +++ b/mcp-json-jackson3/pom.xml @@ -10,7 +10,7 @@ mcp-json-jackson3 jar - Java MCP SDK JSON Jackson + Java MCP SDK JSON Jackson 3 Java MCP SDK JSON implementation based on Jackson 3 https://github.com/modelcontextprotocol/java-sdk @@ -20,14 +20,42 @@ + + biz.aQute.bnd + bnd-maven-plugin + ${bnd-maven-plugin.version} + + + bnd-process + + bnd-process + + + + + + + + org.apache.maven.plugins maven-jar-plugin - - true - + ${project.build.outputDirectory}/META-INF/MANIFEST.MF @@ -35,9 +63,9 @@ - io.modelcontextprotocol.sdk - mcp-json - 0.18.0-SNAPSHOT + io.modelcontextprotocol.sdk + mcp-core + 0.18.0-SNAPSHOT tools.jackson.core diff --git a/mcp-json-jackson3/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.jackson3.JacksonMcpJsonMapperSupplier.xml b/mcp-json-jackson3/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.jackson3.JacksonMcpJsonMapperSupplier.xml new file mode 100644 index 000000000..0ad8a7b42 --- /dev/null +++ b/mcp-json-jackson3/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.jackson3.JacksonMcpJsonMapperSupplier.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/mcp-json-jackson3/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.schema.jackson3.JacksonJsonSchemaValidatorSupplier.xml b/mcp-json-jackson3/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.schema.jackson3.JacksonJsonSchemaValidatorSupplier.xml new file mode 100644 index 000000000..d14d8bea3 --- /dev/null +++ b/mcp-json-jackson3/src/main/resources/OSGI-INF/io.modelcontextprotocol.json.schema.jackson3.JacksonJsonSchemaValidatorSupplier.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/mcp-json-jackson3/src/test/java/io/modelcontextprotocol/json/McpJsonMapperTest.java b/mcp-json-jackson3/src/test/java/io/modelcontextprotocol/json/McpJsonMapperTest.java index e2d0a1d55..58f7e01dc 100644 --- a/mcp-json-jackson3/src/test/java/io/modelcontextprotocol/json/McpJsonMapperTest.java +++ b/mcp-json-jackson3/src/test/java/io/modelcontextprotocol/json/McpJsonMapperTest.java @@ -14,7 +14,7 @@ class McpJsonMapperTest { @Test void shouldUseJackson2Mapper() { - assertThat(McpJsonMapper.getDefault()).isInstanceOf(JacksonMcpJsonMapper.class); + assertThat(McpJsonDefaults.getDefaultMcpJsonMapper()).isInstanceOf(JacksonMcpJsonMapper.class); } } diff --git a/mcp-json-jackson3/src/test/java/io/modelcontextprotocol/json/schema/JsonSchemaValidatorTest.java b/mcp-json-jackson3/src/test/java/io/modelcontextprotocol/json/schema/JsonSchemaValidatorTest.java index 29c450d40..89197579b 100644 --- a/mcp-json-jackson3/src/test/java/io/modelcontextprotocol/json/schema/JsonSchemaValidatorTest.java +++ b/mcp-json-jackson3/src/test/java/io/modelcontextprotocol/json/schema/JsonSchemaValidatorTest.java @@ -8,13 +8,14 @@ import org.junit.jupiter.api.Test; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.schema.jackson3.DefaultJsonSchemaValidator; class JsonSchemaValidatorTest { @Test void shouldUseJackson2Mapper() { - assertThat(JsonSchemaValidator.getDefault()).isInstanceOf(DefaultJsonSchemaValidator.class); + assertThat(McpJsonDefaults.getDefaultJsonSchemaValidator()).isInstanceOf(DefaultJsonSchemaValidator.class); } } diff --git a/mcp-json/pom.xml b/mcp-json/pom.xml deleted file mode 100644 index 2cbcf3516..000000000 --- a/mcp-json/pom.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - 4.0.0 - - io.modelcontextprotocol.sdk - mcp-parent - 0.18.0-SNAPSHOT - - mcp-json - jar - Java MCP SDK JSON Support - Java MCP SDK JSON Support API - https://github.com/modelcontextprotocol/java-sdk - - https://github.com/modelcontextprotocol/java-sdk - git://github.com/modelcontextprotocol/java-sdk.git - git@github.com/modelcontextprotocol/java-sdk.git - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - true - - - - - - - - - - diff --git a/mcp-json/src/main/java/io/modelcontextprotocol/json/McpJsonInternal.java b/mcp-json/src/main/java/io/modelcontextprotocol/json/McpJsonInternal.java deleted file mode 100644 index 31930ab33..000000000 --- a/mcp-json/src/main/java/io/modelcontextprotocol/json/McpJsonInternal.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2025 - 2025 the original author or authors. - */ - -package io.modelcontextprotocol.json; - -import java.util.ServiceLoader; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Stream; - -/** - * Utility class for creating a default {@link McpJsonMapper} instance. This class - * provides a single method to create a default mapper using the {@link ServiceLoader} - * mechanism. - */ -final class McpJsonInternal { - - private static McpJsonMapper defaultJsonMapper = null; - - /** - * Returns the cached default {@link McpJsonMapper} instance. If the default mapper - * has not been created yet, it will be initialized using the - * {@link #createDefaultMapper()} method. - * @return the default {@link McpJsonMapper} instance - * @throws IllegalStateException if no default {@link McpJsonMapper} implementation is - * found - */ - static McpJsonMapper getDefaultMapper() { - if (defaultJsonMapper == null) { - defaultJsonMapper = McpJsonInternal.createDefaultMapper(); - } - return defaultJsonMapper; - } - - /** - * Creates a default {@link McpJsonMapper} instance using the {@link ServiceLoader} - * mechanism. The default mapper is resolved by loading the first available - * {@link McpJsonMapperSupplier} implementation on the classpath. - * @return the default {@link McpJsonMapper} instance - * @throws IllegalStateException if no default {@link McpJsonMapper} implementation is - * found - */ - static McpJsonMapper createDefaultMapper() { - AtomicReference ex = new AtomicReference<>(); - return ServiceLoader.load(McpJsonMapperSupplier.class).stream().flatMap(p -> { - try { - McpJsonMapperSupplier supplier = p.get(); - return Stream.ofNullable(supplier); - } - catch (Exception e) { - addException(ex, e); - return Stream.empty(); - } - }).flatMap(jsonMapperSupplier -> { - try { - return Stream.ofNullable(jsonMapperSupplier.get()); - } - catch (Exception e) { - addException(ex, e); - return Stream.empty(); - } - }).findFirst().orElseThrow(() -> { - if (ex.get() != null) { - return ex.get(); - } - else { - return new IllegalStateException("No default McpJsonMapper implementation found"); - } - }); - } - - private static void addException(AtomicReference ref, Exception toAdd) { - ref.updateAndGet(existing -> { - if (existing == null) { - return new IllegalStateException("Failed to initialize default McpJsonMapper", toAdd); - } - else { - existing.addSuppressed(toAdd); - return existing; - } - }); - } - -} diff --git a/mcp-json/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaInternal.java b/mcp-json/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaInternal.java deleted file mode 100644 index 2497e7f80..000000000 --- a/mcp-json/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaInternal.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2025 - 2025 the original author or authors. - */ - -package io.modelcontextprotocol.json.schema; - -import java.util.ServiceLoader; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Stream; - -/** - * Internal utility class for creating a default {@link JsonSchemaValidator} instance. - * This class uses the {@link ServiceLoader} to discover and instantiate a - * {@link JsonSchemaValidatorSupplier} implementation. - */ -final class JsonSchemaInternal { - - private static JsonSchemaValidator defaultValidator = null; - - /** - * Returns the default {@link JsonSchemaValidator} instance. If the default validator - * has not been initialized, it will be created using the {@link ServiceLoader} to - * discover and instantiate a {@link JsonSchemaValidatorSupplier} implementation. - * @return The default {@link JsonSchemaValidator} instance. - * @throws IllegalStateException If no {@link JsonSchemaValidatorSupplier} - * implementation exists on the classpath or if an error occurs during instantiation. - */ - static JsonSchemaValidator getDefaultValidator() { - if (defaultValidator == null) { - defaultValidator = JsonSchemaInternal.createDefaultValidator(); - } - return defaultValidator; - } - - /** - * Creates a default {@link JsonSchemaValidator} instance by loading a - * {@link JsonSchemaValidatorSupplier} implementation using the {@link ServiceLoader}. - * @return A default {@link JsonSchemaValidator} instance. - * @throws IllegalStateException If no {@link JsonSchemaValidatorSupplier} - * implementation is found or if an error occurs during instantiation. - */ - static JsonSchemaValidator createDefaultValidator() { - AtomicReference ex = new AtomicReference<>(); - return ServiceLoader.load(JsonSchemaValidatorSupplier.class).stream().flatMap(p -> { - try { - JsonSchemaValidatorSupplier supplier = p.get(); - return Stream.ofNullable(supplier); - } - catch (Exception e) { - addException(ex, e); - return Stream.empty(); - } - }).flatMap(jsonMapperSupplier -> { - try { - return Stream.of(jsonMapperSupplier.get()); - } - catch (Exception e) { - addException(ex, e); - return Stream.empty(); - } - }).findFirst().orElseThrow(() -> { - if (ex.get() != null) { - return ex.get(); - } - else { - return new IllegalStateException("No default JsonSchemaValidatorSupplier implementation found"); - } - }); - } - - private static void addException(AtomicReference ref, Exception toAdd) { - ref.updateAndGet(existing -> { - if (existing == null) { - return new IllegalStateException("Failed to initialize default JsonSchemaValidatorSupplier", toAdd); - } - else { - existing.addSuppressed(toAdd); - return existing; - } - }); - } - -} diff --git a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebClientStreamableHttpTransport.java b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebClientStreamableHttpTransport.java index 282745a78..e3a7091be 100644 --- a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebClientStreamableHttpTransport.java +++ b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebClientStreamableHttpTransport.java @@ -25,6 +25,7 @@ import org.springframework.web.reactive.function.client.WebClientResponseException; import io.modelcontextprotocol.client.McpAsyncClient; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.spec.ClosedMcpTransportSession; @@ -615,8 +616,9 @@ public Builder supportedProtocolVersions(List supportedProtocolVersions) * @return a new instance of {@link WebClientStreamableHttpTransport} */ public WebClientStreamableHttpTransport build() { - return new WebClientStreamableHttpTransport(jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, - webClientBuilder, endpoint, resumableStreams, openConnectionOnStartup, supportedProtocolVersions); + return new WebClientStreamableHttpTransport( + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, webClientBuilder, + endpoint, resumableStreams, openConnectionOnStartup, supportedProtocolVersions); } } diff --git a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebFluxSseClientTransport.java b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebFluxSseClientTransport.java index 91b89d6d2..3c3a008b1 100644 --- a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebFluxSseClientTransport.java +++ b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebFluxSseClientTransport.java @@ -9,6 +9,7 @@ import java.util.function.BiConsumer; import java.util.function.Function; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.TypeRef; @@ -404,7 +405,7 @@ public Builder jsonMapper(McpJsonMapper jsonMapper) { */ public WebFluxSseClientTransport build() { return new WebFluxSseClientTransport(webClientBuilder, - jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, sseEndpoint); + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, sseEndpoint); } } diff --git a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxSseServerTransportProvider.java b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxSseServerTransportProvider.java index 34d6e5085..de2b0e271 100644 --- a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxSseServerTransportProvider.java +++ b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxSseServerTransportProvider.java @@ -11,6 +11,7 @@ import java.util.concurrent.ConcurrentHashMap; import io.modelcontextprotocol.common.McpTransportContext; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.server.McpTransportContextExtractor; @@ -561,8 +562,9 @@ public Builder securityValidator(ServerTransportSecurityValidator securityValida */ public WebFluxSseServerTransportProvider build() { Assert.notNull(messageEndpoint, "Message endpoint must be set"); - return new WebFluxSseServerTransportProvider(jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, - baseUrl, messageEndpoint, sseEndpoint, keepAliveInterval, contextExtractor, securityValidator); + return new WebFluxSseServerTransportProvider( + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, baseUrl, + messageEndpoint, sseEndpoint, keepAliveInterval, contextExtractor, securityValidator); } } diff --git a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxStatelessServerTransport.java b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxStatelessServerTransport.java index b225ab61b..748821768 100644 --- a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxStatelessServerTransport.java +++ b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxStatelessServerTransport.java @@ -4,6 +4,7 @@ package io.modelcontextprotocol.server.transport; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.common.McpTransportContext; import io.modelcontextprotocol.server.McpStatelessServerHandler; @@ -244,8 +245,9 @@ public Builder securityValidator(ServerTransportSecurityValidator securityValida */ public WebFluxStatelessServerTransport build() { Assert.notNull(mcpEndpoint, "Message endpoint must be set"); - return new WebFluxStatelessServerTransport(jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, - mcpEndpoint, contextExtractor, securityValidator); + return new WebFluxStatelessServerTransport( + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, mcpEndpoint, + contextExtractor, securityValidator); } } diff --git a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxStreamableServerTransportProvider.java b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxStreamableServerTransportProvider.java index 0aed2443c..bb469b9df 100644 --- a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxStreamableServerTransportProvider.java +++ b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxStreamableServerTransportProvider.java @@ -4,6 +4,7 @@ package io.modelcontextprotocol.server.transport; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.common.McpTransportContext; @@ -532,8 +533,8 @@ public Builder securityValidator(ServerTransportSecurityValidator securityValida public WebFluxStreamableServerTransportProvider build() { Assert.notNull(mcpEndpoint, "Message endpoint must be set"); return new WebFluxStreamableServerTransportProvider( - jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, mcpEndpoint, contextExtractor, - disallowDelete, keepAliveInterval, securityValidator); + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, mcpEndpoint, + contextExtractor, disallowDelete, keepAliveInterval, securityValidator); } } diff --git a/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/utils/McpJsonMapperUtils.java b/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/utils/McpJsonMapperUtils.java index 67347573c..0177932cc 100644 --- a/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/utils/McpJsonMapperUtils.java +++ b/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/utils/McpJsonMapperUtils.java @@ -1,5 +1,6 @@ package io.modelcontextprotocol.utils; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; public final class McpJsonMapperUtils { @@ -7,6 +8,6 @@ public final class McpJsonMapperUtils { private McpJsonMapperUtils() { } - public static final McpJsonMapper JSON_MAPPER = McpJsonMapper.createDefault(); + public static final McpJsonMapper JSON_MAPPER = McpJsonDefaults.getDefaultMcpJsonMapper(); } \ No newline at end of file diff --git a/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcSseServerTransportProvider.java b/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcSseServerTransportProvider.java index 7e925a0af..bd6d75c36 100644 --- a/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcSseServerTransportProvider.java +++ b/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcSseServerTransportProvider.java @@ -12,6 +12,7 @@ import java.util.concurrent.locks.ReentrantLock; import io.modelcontextprotocol.common.McpTransportContext; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.server.McpTransportContextExtractor; @@ -599,8 +600,9 @@ public WebMvcSseServerTransportProvider build() { if (messageEndpoint == null) { throw new IllegalStateException("MessageEndpoint must be set"); } - return new WebMvcSseServerTransportProvider(jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, - baseUrl, messageEndpoint, sseEndpoint, keepAliveInterval, contextExtractor, securityValidator); + return new WebMvcSseServerTransportProvider( + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, baseUrl, + messageEndpoint, sseEndpoint, keepAliveInterval, contextExtractor, securityValidator); } } diff --git a/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcStatelessServerTransport.java b/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcStatelessServerTransport.java index 92a08a8f4..665d86ec8 100644 --- a/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcStatelessServerTransport.java +++ b/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcStatelessServerTransport.java @@ -5,6 +5,7 @@ package io.modelcontextprotocol.server.transport; import io.modelcontextprotocol.common.McpTransportContext; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.server.McpStatelessServerHandler; import io.modelcontextprotocol.server.McpTransportContextExtractor; @@ -263,8 +264,9 @@ public Builder securityValidator(ServerTransportSecurityValidator securityValida */ public WebMvcStatelessServerTransport build() { Assert.notNull(mcpEndpoint, "Message endpoint must be set"); - return new WebMvcStatelessServerTransport(jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, - mcpEndpoint, contextExtractor, securityValidator); + return new WebMvcStatelessServerTransport( + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, mcpEndpoint, + contextExtractor, securityValidator); } } diff --git a/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcStreamableServerTransportProvider.java b/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcStreamableServerTransportProvider.java index 411d9c292..4f8216c94 100644 --- a/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcStreamableServerTransportProvider.java +++ b/mcp-spring/mcp-spring-webmvc/src/main/java/io/modelcontextprotocol/server/transport/WebMvcStreamableServerTransportProvider.java @@ -11,6 +11,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantLock; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -112,8 +113,6 @@ public class WebMvcStreamableServerTransportProvider implements McpStreamableSer * Constructs a new WebMvcStreamableServerTransportProvider instance. * @param jsonMapper The McpJsonMapper to use for JSON serialization/deserialization * of messages. - * @param baseUrl The base URL for the message endpoint, used to construct the full - * endpoint URL for clients. * @param mcpEndpoint The endpoint URI where clients should send their JSON-RPC * messages via HTTP. This endpoint will handle GET, POST, and DELETE requests. * @param disallowDelete Whether to disallow DELETE requests on the endpoint. @@ -732,8 +731,8 @@ public Builder securityValidator(ServerTransportSecurityValidator securityValida public WebMvcStreamableServerTransportProvider build() { Assert.notNull(this.mcpEndpoint, "MCP endpoint must be set"); return new WebMvcStreamableServerTransportProvider( - jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, mcpEndpoint, disallowDelete, - contextExtractor, keepAliveInterval, securityValidator); + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, mcpEndpoint, + disallowDelete, contextExtractor, keepAliveInterval, securityValidator); } } diff --git a/mcp-spring/mcp-spring-webmvc/src/test/java/io/modelcontextprotocol/server/transport/WebMvcSseServerTransportProviderTests.java b/mcp-spring/mcp-spring-webmvc/src/test/java/io/modelcontextprotocol/server/transport/WebMvcSseServerTransportProviderTests.java index 1074e8a35..36ea2d354 100644 --- a/mcp-spring/mcp-spring-webmvc/src/test/java/io/modelcontextprotocol/server/transport/WebMvcSseServerTransportProviderTests.java +++ b/mcp-spring/mcp-spring-webmvc/src/test/java/io/modelcontextprotocol/server/transport/WebMvcSseServerTransportProviderTests.java @@ -7,6 +7,7 @@ import io.modelcontextprotocol.client.McpClient; import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport; import io.modelcontextprotocol.common.McpTransportContext; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.server.McpServer; import io.modelcontextprotocol.server.TestUtil; @@ -104,7 +105,7 @@ public WebMvcSseServerTransportProvider webMvcSseServerTransportProvider() { .baseUrl("http://localhost:" + PORT + "/") .messageEndpoint(MESSAGE_ENDPOINT) .sseEndpoint(WebMvcSseServerTransportProvider.DEFAULT_SSE_ENDPOINT) - .jsonMapper(McpJsonMapper.getDefault()) + .jsonMapper(McpJsonDefaults.getDefaultMcpJsonMapper()) .contextExtractor(req -> McpTransportContext.EMPTY) .build(); } diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/MockMcpTransport.java b/mcp-test/src/main/java/io/modelcontextprotocol/MockMcpTransport.java index cd8458311..7d71376b4 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/MockMcpTransport.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/MockMcpTransport.java @@ -9,7 +9,7 @@ import java.util.function.BiConsumer; import java.util.function.Function; -import io.modelcontextprotocol.json.McpJsonMapper; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.spec.McpClientTransport; import io.modelcontextprotocol.spec.McpSchema; @@ -94,7 +94,7 @@ public Mono closeGracefully() { @Override public T unmarshalFrom(Object data, TypeRef typeRef) { - return McpJsonMapper.getDefault().convertValue(data, typeRef); + return McpJsonDefaults.getDefaultMcpJsonMapper().convertValue(data, typeRef); } } diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/util/McpJsonMapperUtils.java b/mcp-test/src/main/java/io/modelcontextprotocol/util/McpJsonMapperUtils.java index 723965519..45e4a4e3c 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/util/McpJsonMapperUtils.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/util/McpJsonMapperUtils.java @@ -1,5 +1,6 @@ package io.modelcontextprotocol.util; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; public final class McpJsonMapperUtils { @@ -7,6 +8,6 @@ public final class McpJsonMapperUtils { private McpJsonMapperUtils() { } - public static final McpJsonMapper JSON_MAPPER = McpJsonMapper.getDefault(); + public static final McpJsonMapper JSON_MAPPER = McpJsonDefaults.getDefaultMcpJsonMapper(); } \ No newline at end of file diff --git a/pom.xml b/pom.xml index 3e36b084b..18b24ce2f 100644 --- a/pom.xml +++ b/pom.xml @@ -108,7 +108,6 @@ mcp-core mcp-json-jackson2 mcp-json-jackson3 - mcp-json mcp-spring/mcp-spring-webflux mcp-spring/mcp-spring-webmvc mcp-test From 28165ae9c085740354ca5e496bdf3a6296986402 Mon Sep 17 00:00:00 2001 From: Daniel Garnier-Moiroux Date: Wed, 11 Feb 2026 14:35:52 +0100 Subject: [PATCH 2/4] Move mcp-core integration tests to mcp-test package - The integration tests pass with both Jackson 2 and Jackson 3. - Modified McpSchemaTests.testContentDeserializationWrongType to work with both Jackson 2 and Jackson 3. Signed-off-by: Daniel Garnier-Moiroux --- mcp-core/pom.xml | 51 ---------- .../MockMcpClientTransport.java | 2 +- ...rverTransportSecurityIntegrationTests.java | 7 +- ...rverTransportSecurityIntegrationTests.java | 7 +- mcp-test/pom.xml | 93 +++++++++++++++++-- .../MockMcpClientTransport.java} | 26 ++++-- .../MockMcpServerTransport.java | 0 .../MockMcpServerTransportProvider.java | 0 ...eamableHttpAsyncClientResiliencyTests.java | 0 ...pClientStreamableHttpAsyncClientTests.java | 0 ...tpClientStreamableHttpSyncClientTests.java | 0 ...pSseMcpAsyncClientLostConnectionTests.java | 0 .../client/HttpSseMcpAsyncClientTests.java | 0 .../client/HttpSseMcpSyncClientTests.java | 0 .../McpAsyncClientResponseHandlerTests.java | 0 .../client/McpAsyncClientTests.java | 0 .../client/McpClientProtocolVersionTests.java | 0 .../client/ServerParameterUtils.java | 0 .../client/StdioMcpAsyncClientTests.java | 0 .../client/StdioMcpSyncClientTests.java | 0 .../HttpClientSseClientTransportTests.java | 0 ...bleHttpTransportEmptyJsonResponseTest.java | 0 ...eamableHttpTransportErrorHandlingTest.java | 0 ...HttpClientStreamableHttpTransportTest.java | 0 ...erMcpTransportContextIntegrationTests.java | 0 ...ttpVersionNegotiationIntegrationTests.java | 0 ...erMcpTransportContextIntegrationTests.java | 0 .../HttpServletSseIntegrationTests.java | 1 + .../HttpServletStatelessIntegrationTests.java | 0 ...HttpServletStreamableAsyncServerTests.java | 0 ...HttpServletStreamableIntegrationTests.java | 1 + .../HttpServletStreamableSyncServerTests.java | 0 .../server/McpCompletionTests.java | 0 .../server/McpServerProtocolVersionTests.java | 0 .../ResourceTemplateManagementTests.java | 0 .../server/ServletSseMcpAsyncServerTests.java | 0 .../server/ServletSseMcpSyncServerTests.java | 0 .../server/StdioMcpAsyncServerTests.java | 0 .../server/StdioMcpSyncServerTests.java | 0 ...ervletSseServerCustomContextPathTests.java | 0 .../McpTestRequestRecordingServletFilter.java | 0 ...rverTransportSecurityIntegrationTests.java | 8 +- .../StdioServerTransportProviderTests.java | 14 +-- .../server/transport/TomcatTestUtil.java | 0 .../CompleteCompletionSerializationTest.java | 0 .../spec/McpSchemaTests.java | 19 ++-- 46 files changed, 135 insertions(+), 94 deletions(-) rename mcp-test/src/{main/java/io/modelcontextprotocol/MockMcpTransport.java => test/java/io/modelcontextprotocol/MockMcpClientTransport.java} (79%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/MockMcpServerTransport.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/MockMcpServerTransportProvider.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpAsyncClientResiliencyTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpAsyncClientTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpSyncClientTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/client/HttpSseMcpAsyncClientLostConnectionTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/client/HttpSseMcpAsyncClientTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/client/HttpSseMcpSyncClientTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/client/McpAsyncClientResponseHandlerTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/client/McpAsyncClientTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/client/ServerParameterUtils.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/client/StdioMcpAsyncClientTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/client/StdioMcpSyncClientTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransportTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportEmptyJsonResponseTest.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportErrorHandlingTest.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportTest.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/common/AsyncServerMcpTransportContextIntegrationTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/common/HttpClientStreamableHttpVersionNegotiationIntegrationTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/common/SyncServerMcpTransportContextIntegrationTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/server/HttpServletSseIntegrationTests.java (97%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/server/HttpServletStatelessIntegrationTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/server/HttpServletStreamableAsyncServerTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/server/HttpServletStreamableIntegrationTests.java (97%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/server/HttpServletStreamableSyncServerTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/server/McpCompletionTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/server/McpServerProtocolVersionTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/server/ResourceTemplateManagementTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/server/ServletSseMcpAsyncServerTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/server/ServletSseMcpSyncServerTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/server/StdioMcpAsyncServerTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/server/StdioMcpSyncServerTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/server/transport/HttpServletSseServerCustomContextPathTests.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/server/transport/McpTestRequestRecordingServletFilter.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/server/transport/ServerTransportSecurityIntegrationTests.java (97%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/server/transport/StdioServerTransportProviderTests.java (92%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/server/transport/TomcatTestUtil.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/spec/CompleteCompletionSerializationTest.java (100%) rename {mcp-core => mcp-test}/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java (99%) diff --git a/mcp-core/pom.xml b/mcp-core/pom.xml index e6eabff3d..6dab41aff 100644 --- a/mcp-core/pom.xml +++ b/mcp-core/pom.xml @@ -93,43 +93,6 @@ provided - - org.springframework - spring-webmvc - ${springframework.version} - test - - - - tools.jackson.core - jackson-databind - ${jackson3.version} - test - - - - io.projectreactor.netty - reactor-netty-http - test - - - - - org.springframework - spring-context - ${springframework.version} - test - - - - org.springframework - spring-test - ${springframework.version} - test - - org.assertj assertj-core @@ -195,20 +158,6 @@ test - - - org.apache.tomcat.embed - tomcat-embed-core - ${tomcat.version} - test - - - org.apache.tomcat.embed - tomcat-embed-websocket - ${tomcat.version} - test - - org.testcontainers toxiproxy diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java b/mcp-core/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java index 04b058973..f9fc41b7a 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java @@ -100,7 +100,7 @@ public Mono closeGracefully() { @Override public T unmarshalFrom(Object data, TypeRef typeRef) { - return McpJsonDefaults.getDefaultMcpJsonMapper().convertValue(data, typeRef); + return (T) data; } } diff --git a/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/security/WebFluxServerTransportSecurityIntegrationTests.java b/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/security/WebFluxServerTransportSecurityIntegrationTests.java index 06e1286d2..6e231924f 100644 --- a/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/security/WebFluxServerTransportSecurityIntegrationTests.java +++ b/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/security/WebFluxServerTransportSecurityIntegrationTests.java @@ -11,6 +11,7 @@ import io.modelcontextprotocol.client.McpSyncClient; import io.modelcontextprotocol.client.transport.WebClientStreamableHttpTransport; import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.server.McpServer; import io.modelcontextprotocol.server.TestUtil; @@ -194,7 +195,7 @@ public McpSyncClient createMcpClient(String baseUrl, TestOriginHeaderExchangeFilterFunction exchangeFilterFunction) { var transport = WebFluxSseClientTransport .builder(WebClient.builder().baseUrl(baseUrl).filter(exchangeFilterFunction)) - .jsonMapper(McpJsonMapper.getDefault()) + .jsonMapper(McpJsonDefaults.getDefaultMcpJsonMapper()) .build(); return McpClient.sync(transport).initializationTimeout(Duration.ofMillis(500)).build(); } @@ -226,7 +227,7 @@ public McpSyncClient createMcpClient(String baseUrl, TestOriginHeaderExchangeFilterFunction exchangeFilterFunction) { var transport = WebClientStreamableHttpTransport .builder(WebClient.builder().baseUrl(baseUrl).filter(exchangeFilterFunction)) - .jsonMapper(McpJsonMapper.getDefault()) + .jsonMapper(McpJsonDefaults.getDefaultMcpJsonMapper()) .openConnectionOnStartup(true) .build(); return McpClient.sync(transport).initializationTimeout(Duration.ofMillis(500)).build(); @@ -259,7 +260,7 @@ public McpSyncClient createMcpClient(String baseUrl, TestOriginHeaderExchangeFilterFunction exchangeFilterFunction) { var transport = WebClientStreamableHttpTransport .builder(WebClient.builder().baseUrl(baseUrl).filter(exchangeFilterFunction)) - .jsonMapper(McpJsonMapper.getDefault()) + .jsonMapper(McpJsonDefaults.getDefaultMcpJsonMapper()) .openConnectionOnStartup(true) .build(); return McpClient.sync(transport).initializationTimeout(Duration.ofMillis(500)).build(); diff --git a/mcp-spring/mcp-spring-webmvc/src/test/java/io/modelcontextprotocol/security/ServerTransportSecurityIntegrationTests.java b/mcp-spring/mcp-spring-webmvc/src/test/java/io/modelcontextprotocol/security/ServerTransportSecurityIntegrationTests.java index 9615547d3..a5f3597d5 100644 --- a/mcp-spring/mcp-spring-webmvc/src/test/java/io/modelcontextprotocol/security/ServerTransportSecurityIntegrationTests.java +++ b/mcp-spring/mcp-spring-webmvc/src/test/java/io/modelcontextprotocol/security/ServerTransportSecurityIntegrationTests.java @@ -15,6 +15,7 @@ import io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport; import io.modelcontextprotocol.client.transport.customizer.McpSyncHttpClientRequestCustomizer; import io.modelcontextprotocol.common.McpTransportContext; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.server.McpServer; import io.modelcontextprotocol.server.McpStatelessSyncServer; @@ -209,7 +210,7 @@ static class SseConfig { McpSyncClient createMcpClient(McpSyncHttpClientRequestCustomizer requestCustomizer) { var transport = HttpClientSseClientTransport.builder(baseUrl) .httpRequestCustomizer(requestCustomizer) - .jsonMapper(McpJsonMapper.getDefault()) + .jsonMapper(McpJsonDefaults.getDefaultMcpJsonMapper()) .build(); return McpClient.sync(transport).initializationTimeout(Duration.ofMillis(500)).build(); } @@ -248,7 +249,7 @@ static class StreamableHttpConfig { McpSyncClient createMcpClient(McpSyncHttpClientRequestCustomizer requestCustomizer) { var transport = HttpClientStreamableHttpTransport.builder(baseUrl) .httpRequestCustomizer(requestCustomizer) - .jsonMapper(McpJsonMapper.getDefault()) + .jsonMapper(McpJsonDefaults.getDefaultMcpJsonMapper()) .openConnectionOnStartup(true) .build(); return McpClient.sync(transport).initializationTimeout(Duration.ofMillis(500)).build(); @@ -286,7 +287,7 @@ static class StatelessConfig { McpSyncClient createMcpClient(McpSyncHttpClientRequestCustomizer requestCustomizer) { var transport = HttpClientStreamableHttpTransport.builder(baseUrl) .httpRequestCustomizer(requestCustomizer) - .jsonMapper(McpJsonMapper.getDefault()) + .jsonMapper(McpJsonDefaults.getDefaultMcpJsonMapper()) .openConnectionOnStartup(true) .build(); return McpClient.sync(transport).initializationTimeout(Duration.ofMillis(500)).build(); diff --git a/mcp-test/pom.xml b/mcp-test/pom.xml index a6314e808..c77068f89 100644 --- a/mcp-test/pom.xml +++ b/mcp-test/pom.xml @@ -1,7 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 io.modelcontextprotocol.sdk @@ -33,12 +33,6 @@ ${slf4j-api.version} - - tools.jackson.core - jackson-databind - ${jackson3.version} - - io.projectreactor reactor-core @@ -97,8 +91,91 @@ ${json-unit-assertj.version} + + + org.springframework + spring-webmvc + ${springframework.version} + test + + + + org.springframework + spring-context + ${springframework.version} + test + + + + org.springframework + spring-test + ${springframework.version} + test + + + + io.projectreactor.netty + reactor-netty-http + test + + + + org.apache.tomcat.embed + tomcat-embed-core + ${tomcat.version} + test + + + + org.apache.tomcat.embed + tomcat-embed-websocket + ${tomcat.version} + test + + + + net.bytebuddy + byte-buddy + ${byte-buddy.version} + test + + + + jakarta.servlet + jakarta.servlet-api + ${jakarta.servlet.version} + test + + + + jackson3 + + true + + + + io.modelcontextprotocol.sdk + mcp-json-jackson3 + 0.18.0-SNAPSHOT + test + + + + + jackson2 + + + io.modelcontextprotocol.sdk + mcp-json-jackson2 + 0.18.0-SNAPSHOT + test + + + + + \ No newline at end of file diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/MockMcpTransport.java b/mcp-test/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java similarity index 79% rename from mcp-test/src/main/java/io/modelcontextprotocol/MockMcpTransport.java rename to mcp-test/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java index 7d71376b4..04b058973 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/MockMcpTransport.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/MockMcpClientTransport.java @@ -15,34 +15,40 @@ import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpSchema.JSONRPCNotification; import io.modelcontextprotocol.spec.McpSchema.JSONRPCRequest; -import io.modelcontextprotocol.spec.McpServerTransport; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; /** - * A mock implementation of the {@link McpClientTransport} and {@link McpServerTransport} - * interfaces. - * - * @deprecated not used. to be removed in the future. + * A mock implementation of the {@link McpClientTransport} interfaces. */ -@Deprecated -public class MockMcpTransport implements McpClientTransport, McpServerTransport { +public class MockMcpClientTransport implements McpClientTransport { private final Sinks.Many inbound = Sinks.many().unicast().onBackpressureBuffer(); private final List sent = new ArrayList<>(); - private final BiConsumer interceptor; + private final BiConsumer interceptor; - public MockMcpTransport() { + private String protocolVersion = McpSchema.LATEST_PROTOCOL_VERSION; + + public MockMcpClientTransport() { this((t, msg) -> { }); } - public MockMcpTransport(BiConsumer interceptor) { + public MockMcpClientTransport(BiConsumer interceptor) { this.interceptor = interceptor; } + public MockMcpClientTransport withProtocolVersion(String protocolVersion) { + return this; + } + + @Override + public List protocolVersions() { + return List.of(protocolVersion); + } + public void simulateIncomingMessage(McpSchema.JSONRPCMessage message) { if (inbound.tryEmitNext(message).isFailure()) { throw new RuntimeException("Failed to process incoming message " + message); diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/MockMcpServerTransport.java b/mcp-test/src/test/java/io/modelcontextprotocol/MockMcpServerTransport.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/MockMcpServerTransport.java rename to mcp-test/src/test/java/io/modelcontextprotocol/MockMcpServerTransport.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/MockMcpServerTransportProvider.java b/mcp-test/src/test/java/io/modelcontextprotocol/MockMcpServerTransportProvider.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/MockMcpServerTransportProvider.java rename to mcp-test/src/test/java/io/modelcontextprotocol/MockMcpServerTransportProvider.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpAsyncClientResiliencyTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpAsyncClientResiliencyTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpAsyncClientResiliencyTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpAsyncClientResiliencyTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpAsyncClientTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpAsyncClientTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpAsyncClientTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpAsyncClientTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpSyncClientTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpSyncClientTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpSyncClientTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpSyncClientTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/client/HttpSseMcpAsyncClientLostConnectionTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/HttpSseMcpAsyncClientLostConnectionTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/client/HttpSseMcpAsyncClientLostConnectionTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/client/HttpSseMcpAsyncClientLostConnectionTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/client/HttpSseMcpAsyncClientTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/HttpSseMcpAsyncClientTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/client/HttpSseMcpAsyncClientTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/client/HttpSseMcpAsyncClientTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/client/HttpSseMcpSyncClientTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/HttpSseMcpSyncClientTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/client/HttpSseMcpSyncClientTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/client/HttpSseMcpSyncClientTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/client/McpAsyncClientResponseHandlerTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/McpAsyncClientResponseHandlerTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/client/McpAsyncClientResponseHandlerTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/client/McpAsyncClientResponseHandlerTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/client/McpAsyncClientTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/McpAsyncClientTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/client/McpAsyncClientTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/client/McpAsyncClientTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/client/ServerParameterUtils.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/ServerParameterUtils.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/client/ServerParameterUtils.java rename to mcp-test/src/test/java/io/modelcontextprotocol/client/ServerParameterUtils.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/client/StdioMcpAsyncClientTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/StdioMcpAsyncClientTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/client/StdioMcpAsyncClientTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/client/StdioMcpAsyncClientTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/client/StdioMcpSyncClientTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/StdioMcpSyncClientTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/client/StdioMcpSyncClientTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/client/StdioMcpSyncClientTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransportTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransportTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransportTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientSseClientTransportTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportEmptyJsonResponseTest.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportEmptyJsonResponseTest.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportEmptyJsonResponseTest.java rename to mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportEmptyJsonResponseTest.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportErrorHandlingTest.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportErrorHandlingTest.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportErrorHandlingTest.java rename to mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportErrorHandlingTest.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportTest.java b/mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportTest.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportTest.java rename to mcp-test/src/test/java/io/modelcontextprotocol/client/transport/HttpClientStreamableHttpTransportTest.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/common/AsyncServerMcpTransportContextIntegrationTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/common/AsyncServerMcpTransportContextIntegrationTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/common/AsyncServerMcpTransportContextIntegrationTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/common/AsyncServerMcpTransportContextIntegrationTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/common/HttpClientStreamableHttpVersionNegotiationIntegrationTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/common/HttpClientStreamableHttpVersionNegotiationIntegrationTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/common/HttpClientStreamableHttpVersionNegotiationIntegrationTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/common/HttpClientStreamableHttpVersionNegotiationIntegrationTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/common/SyncServerMcpTransportContextIntegrationTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/common/SyncServerMcpTransportContextIntegrationTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/common/SyncServerMcpTransportContextIntegrationTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/common/SyncServerMcpTransportContextIntegrationTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/HttpServletSseIntegrationTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/HttpServletSseIntegrationTests.java similarity index 97% rename from mcp-core/src/test/java/io/modelcontextprotocol/server/HttpServletSseIntegrationTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/server/HttpServletSseIntegrationTests.java index d2b9d14d0..5841c13da 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/server/HttpServletSseIntegrationTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/server/HttpServletSseIntegrationTests.java @@ -8,6 +8,7 @@ import java.util.Map; import java.util.stream.Stream; +import io.modelcontextprotocol.AbstractMcpClientServerIntegrationTests; import io.modelcontextprotocol.client.McpClient; import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport; import io.modelcontextprotocol.common.McpTransportContext; diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/HttpServletStatelessIntegrationTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/HttpServletStatelessIntegrationTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/server/HttpServletStatelessIntegrationTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/server/HttpServletStatelessIntegrationTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/HttpServletStreamableAsyncServerTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/HttpServletStreamableAsyncServerTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/server/HttpServletStreamableAsyncServerTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/server/HttpServletStreamableAsyncServerTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/HttpServletStreamableIntegrationTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/HttpServletStreamableIntegrationTests.java similarity index 97% rename from mcp-core/src/test/java/io/modelcontextprotocol/server/HttpServletStreamableIntegrationTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/server/HttpServletStreamableIntegrationTests.java index 81423e0c5..5b934e4e9 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/server/HttpServletStreamableIntegrationTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/server/HttpServletStreamableIntegrationTests.java @@ -8,6 +8,7 @@ import java.util.Map; import java.util.stream.Stream; +import io.modelcontextprotocol.AbstractMcpClientServerIntegrationTests; import io.modelcontextprotocol.client.McpClient; import io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport; import io.modelcontextprotocol.common.McpTransportContext; diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/HttpServletStreamableSyncServerTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/HttpServletStreamableSyncServerTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/server/HttpServletStreamableSyncServerTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/server/HttpServletStreamableSyncServerTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/McpCompletionTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/McpCompletionTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/server/McpCompletionTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/server/McpCompletionTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/McpServerProtocolVersionTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/McpServerProtocolVersionTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/server/McpServerProtocolVersionTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/server/McpServerProtocolVersionTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/ResourceTemplateManagementTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/ResourceTemplateManagementTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/server/ResourceTemplateManagementTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/server/ResourceTemplateManagementTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/ServletSseMcpAsyncServerTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/ServletSseMcpAsyncServerTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/server/ServletSseMcpAsyncServerTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/server/ServletSseMcpAsyncServerTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/ServletSseMcpSyncServerTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/ServletSseMcpSyncServerTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/server/ServletSseMcpSyncServerTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/server/ServletSseMcpSyncServerTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/StdioMcpAsyncServerTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/StdioMcpAsyncServerTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/server/StdioMcpAsyncServerTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/server/StdioMcpAsyncServerTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/StdioMcpSyncServerTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/StdioMcpSyncServerTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/server/StdioMcpSyncServerTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/server/StdioMcpSyncServerTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/transport/HttpServletSseServerCustomContextPathTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/transport/HttpServletSseServerCustomContextPathTests.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/server/transport/HttpServletSseServerCustomContextPathTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/server/transport/HttpServletSseServerCustomContextPathTests.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/transport/McpTestRequestRecordingServletFilter.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/transport/McpTestRequestRecordingServletFilter.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/server/transport/McpTestRequestRecordingServletFilter.java rename to mcp-test/src/test/java/io/modelcontextprotocol/server/transport/McpTestRequestRecordingServletFilter.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/transport/ServerTransportSecurityIntegrationTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/transport/ServerTransportSecurityIntegrationTests.java similarity index 97% rename from mcp-core/src/test/java/io/modelcontextprotocol/server/transport/ServerTransportSecurityIntegrationTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/server/transport/ServerTransportSecurityIntegrationTests.java index e9e64c0d0..70df6557e 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/server/transport/ServerTransportSecurityIntegrationTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/server/transport/ServerTransportSecurityIntegrationTests.java @@ -15,7 +15,7 @@ import io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport; import io.modelcontextprotocol.client.transport.customizer.McpSyncHttpClientRequestCustomizer; import io.modelcontextprotocol.common.McpTransportContext; -import io.modelcontextprotocol.json.McpJsonMapper; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.server.McpServer; import io.modelcontextprotocol.spec.McpSchema; import jakarta.servlet.http.HttpServlet; @@ -195,7 +195,7 @@ public Sse() { public McpSyncClient createMcpClient(String baseUrl, TestRequestCustomizer requestCustomizer) { var transport = HttpClientSseClientTransport.builder(baseUrl) .httpRequestCustomizer(requestCustomizer) - .jsonMapper(McpJsonMapper.getDefault()) + .jsonMapper(McpJsonDefaults.getDefaultMcpJsonMapper()) .build(); return McpClient.sync(transport).initializationTimeout(Duration.ofMillis(500)).build(); } @@ -226,7 +226,7 @@ public StreamableHttp() { public McpSyncClient createMcpClient(String baseUrl, TestRequestCustomizer requestCustomizer) { var transport = HttpClientStreamableHttpTransport.builder(baseUrl) .httpRequestCustomizer(requestCustomizer) - .jsonMapper(McpJsonMapper.getDefault()) + .jsonMapper(McpJsonDefaults.getDefaultMcpJsonMapper()) .openConnectionOnStartup(true) .build(); return McpClient.sync(transport).initializationTimeout(Duration.ofMillis(500)).build(); @@ -258,7 +258,7 @@ public Stateless() { public McpSyncClient createMcpClient(String baseUrl, TestRequestCustomizer requestCustomizer) { var transport = HttpClientStreamableHttpTransport.builder(baseUrl) .httpRequestCustomizer(requestCustomizer) - .jsonMapper(McpJsonMapper.getDefault()) + .jsonMapper(McpJsonDefaults.getDefaultMcpJsonMapper()) .openConnectionOnStartup(true) .build(); return McpClient.sync(transport).initializationTimeout(Duration.ofMillis(500)).build(); diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/transport/StdioServerTransportProviderTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/transport/StdioServerTransportProviderTests.java similarity index 92% rename from mcp-core/src/test/java/io/modelcontextprotocol/server/transport/StdioServerTransportProviderTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/server/transport/StdioServerTransportProviderTests.java index 6a70af33d..996166fc9 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/server/transport/StdioServerTransportProviderTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/server/transport/StdioServerTransportProviderTests.java @@ -14,6 +14,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpServerSession; @@ -25,7 +26,6 @@ import reactor.core.publisher.Mono; import reactor.test.StepVerifier; -import static io.modelcontextprotocol.util.McpJsonMapperUtils.JSON_MAPPER; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -37,7 +37,6 @@ * * @author Christian Tzolov */ -@Disabled class StdioServerTransportProviderTests { private final PrintStream originalOut = System.out; @@ -71,7 +70,8 @@ void setUp() { when(mockSession.closeGracefully()).thenReturn(Mono.empty()); when(mockSession.sendNotification(any(), any())).thenReturn(Mono.empty()); - transportProvider = new StdioServerTransportProvider(JSON_MAPPER, System.in, testOutPrintStream); + transportProvider = new StdioServerTransportProvider(McpJsonDefaults.getDefaultMcpJsonMapper(), System.in, + testOutPrintStream); } @AfterEach @@ -101,7 +101,8 @@ void shouldHandleIncomingMessages() throws Exception { String jsonMessage = "{\"jsonrpc\":\"2.0\",\"method\":\"test\",\"params\":{},\"id\":1}\n"; InputStream stream = new ByteArrayInputStream(jsonMessage.getBytes(StandardCharsets.UTF_8)); - transportProvider = new StdioServerTransportProvider(JSON_MAPPER, stream, System.out); + transportProvider = new StdioServerTransportProvider(McpJsonDefaults.getDefaultMcpJsonMapper(), stream, + System.out); // Set up a real session to capture the message AtomicReference capturedMessage = new AtomicReference<>(); CountDownLatch messageLatch = new CountDownLatch(1); @@ -181,7 +182,7 @@ void shouldHandleMultipleCloseGracefullyCalls() { @Test void shouldHandleNotificationBeforeSessionFactoryIsSet() { - transportProvider = new StdioServerTransportProvider(JSON_MAPPER); + transportProvider = new StdioServerTransportProvider(McpJsonDefaults.getDefaultMcpJsonMapper()); // Send notification before setting session factory StepVerifier.create(transportProvider.notifyClients("testNotification", Map.of("key", "value"))) .verifyErrorSatisfies(error -> { @@ -196,7 +197,8 @@ void shouldHandleInvalidJsonMessage() throws Exception { String jsonMessage = "{invalid json}\n"; InputStream stream = new ByteArrayInputStream(jsonMessage.getBytes(StandardCharsets.UTF_8)); - transportProvider = new StdioServerTransportProvider(JSON_MAPPER, stream, testOutPrintStream); + transportProvider = new StdioServerTransportProvider(McpJsonDefaults.getDefaultMcpJsonMapper(), stream, + testOutPrintStream); // Set up a session factory transportProvider.setSessionFactory(sessionFactory); diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/server/transport/TomcatTestUtil.java b/mcp-test/src/test/java/io/modelcontextprotocol/server/transport/TomcatTestUtil.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/server/transport/TomcatTestUtil.java rename to mcp-test/src/test/java/io/modelcontextprotocol/server/transport/TomcatTestUtil.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/spec/CompleteCompletionSerializationTest.java b/mcp-test/src/test/java/io/modelcontextprotocol/spec/CompleteCompletionSerializationTest.java similarity index 100% rename from mcp-core/src/test/java/io/modelcontextprotocol/spec/CompleteCompletionSerializationTest.java rename to mcp-test/src/test/java/io/modelcontextprotocol/spec/CompleteCompletionSerializationTest.java diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java similarity index 99% rename from mcp-core/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java rename to mcp-test/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java index 82ffe9ede..c732b1cc1 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java @@ -17,10 +17,9 @@ import java.util.List; import java.util.Map; +import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; -import tools.jackson.databind.exc.InvalidTypeIdException; - import io.modelcontextprotocol.spec.McpSchema.TextResourceContents; import net.javacrumbs.jsonunit.core.Option; @@ -56,15 +55,19 @@ void testTextContentDeserialization() throws Exception { } @Test - void testContentDeserializationWrongType() throws Exception { - + void testContentDeserializationWrongType() { assertThatThrownBy(() -> JSON_MAPPER.readValue(""" {"type":"WRONG","text":"XXX"}""", McpSchema.TextContent.class)).isInstanceOf(IOException.class) - .hasMessage("Failed to read value") - .cause() - .isInstanceOf(InvalidTypeIdException.class) + // Jackson 2 throws the InvalidTypeException directly, but Jackson 3 wraps it. + // Try to unwrap in case it's Jackson 3. + .extracting(throwable -> throwable.getCause() != null ? throwable.getCause() : throwable) + .asInstanceOf(InstanceOfAssertFactories.THROWABLE) .hasMessageContaining( - "Could not resolve type id 'WRONG' as a subtype of `io.modelcontextprotocol.spec.McpSchema$TextContent`: known type ids = [audio, image, resource, resource_link, text]"); + "Could not resolve type id 'WRONG' as a subtype of `io.modelcontextprotocol.spec.McpSchema$TextContent`: known type ids = [audio, image, resource, resource_link, text]") + .extracting(Object::getClass) + .extracting(Class::getSimpleName) + // Class name is the same for both Jackson 2 and 3, only the package differs. + .isEqualTo("InvalidTypeIdException"); } @Test From d740686b34e37717377a56b6c209e34959ee7a02 Mon Sep 17 00:00:00 2001 From: Daniel Garnier-Moiroux Date: Wed, 11 Feb 2026 15:29:56 +0100 Subject: [PATCH 3/4] Run integration tests with Jackson 2 in CI Signed-off-by: Daniel Garnier-Moiroux --- .github/workflows/ci.yml | 19 ++++++++++++++++++- .github/workflows/maven-central-release.yml | 5 ++++- .github/workflows/publish-snapshot.yml | 3 +++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c73d9f38..0c79351a6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ on: jobs: build: - name: Build branch + name: Build and Test runs-on: ubuntu-latest steps: - name: Checkout source code @@ -20,3 +20,20 @@ jobs: - name: Build run: mvn verify + + jackson2-tests: + name: Jackson 2 Integration Tests + runs-on: ubuntu-latest + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: 'maven' + + - name: Jackson 2 Integration Tests + run: mvn -pl mcp-test -am -Pjackson2 test diff --git a/.github/workflows/maven-central-release.yml b/.github/workflows/maven-central-release.yml index c6c9d3ab6..8df337ec8 100644 --- a/.github/workflows/maven-central-release.yml +++ b/.github/workflows/maven-central-release.yml @@ -25,7 +25,10 @@ jobs: uses: actions/setup-node@v4 with: node-version: '20' - + + - name: Jackson 2 Integration Tests + run: mvn -pl mcp-test -am -Pjackson2 test + - name: Build and Test run: mvn clean verify diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml index 5d9b4aa39..1a61d336c 100644 --- a/.github/workflows/publish-snapshot.yml +++ b/.github/workflows/publish-snapshot.yml @@ -32,6 +32,9 @@ jobs: - name: Generate Java docs run: mvn -Pjavadoc -B javadoc:aggregate + - name: Jackson 2 Integration Tests + run: mvn -pl mcp-test -am -Pjackson2 test + - name: Build with Maven and deploy to Sonatype snapshot repository env: MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} From aa4a365e5d4e3f81985c283329a98231b96de239 Mon Sep 17 00:00:00 2001 From: Daniel Garnier-Moiroux Date: Wed, 11 Feb 2026 17:13:15 +0100 Subject: [PATCH 4/4] README: update module structure & key decisions with mcp-json removal Signed-off-by: Daniel Garnier-Moiroux --- README.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7bda15006..0cc417d05 100644 --- a/README.md +++ b/README.md @@ -83,11 +83,11 @@ The following sections explain what we chose, why it made sense, and how the cho ### 1. JSON Serialization -* **SDK Choice**: Jackson for JSON serialization and deserialization, behind an SDK abstraction (`mcp-json`) +* **SDK Choice**: Jackson for JSON serialization and deserialization, behind an SDK abstraction (package `io.modelcontextprotocol.json` in `mcp-core`) * **Why**: Jackson is widely adopted across the Java ecosystem, provides strong performance and a mature annotation model, and is familiar to the SDK team and many potential contributors. -* **How we expose it**: Public APIs use a zero-dependency abstraction (`mcp-json`). Jackson is shipped as the default implementation (`mcp-jackson2`), but alternatives can be plugged in. +* **How we expose it**: Public APIs use a bundled abstraction. Jackson is shipped as the default implementation (`mcp-json-jackson3`), but alternatives can be plugged in. * **How it fits the SDK**: This offers a pragmatic default while keeping flexibility for projects that prefer different JSON libraries. @@ -168,15 +168,26 @@ MCP supports both clients (applications consuming MCP servers) and servers (appl The SDK is organized into modules to separate concerns and allow adopters to bring in only what they need: * `mcp-bom` – Dependency versions -* `mcp-core` – Reference implementation (STDIO, JDK HttpClient, Servlet) -* `mcp-json` – JSON abstraction -* `mcp-jackson2` – Jackson implementation of JSON binding -* `mcp` – Convenience bundle (core + Jackson) +* `mcp-core` – Reference implementation (STDIO, JDK HttpClient, Servlet), JSON binding interface definitions +* `mcp-json-jackson2` – Jackson 2 implementation of JSON binding +* `mcp-json-jackson3` – Jackson 3 implementation of JSON binding +* `mcp` – Convenience bundle (core + Jackson 3) * `mcp-test` – Shared testing utilities * `mcp-spring` – Spring integrations (WebClient, WebFlux, WebMVC) For example, a minimal adopter may depend only on `mcp` (core + Jackson), while a Spring-based application can use `mcp-spring` for deeper framework integration. +Additionally, `mcp-test` contains integration tests for `mcp-core`. +`mcp-core` needs a JSON implementation to run full integration tests. +Implementations such as `mcp-json-jackson3`, depend on `mcp-core`, and therefore cannot be imported in `mcp-core` for tests. +Instead, all integration tests that need a JSON implementation are now in `mcp-test`, and use `jackson3` by default. +A `jackson2` maven profile allows to run integration tests with Jackson 2, like so: + + +```bash +./mvnw -pl mcp-test -am -Pjackson2 test +``` + ### Future Directions The SDK is designed to evolve with the Java ecosystem. Areas we are actively watching include: