From c3b9ab09ea8145fb3d31456287cbe4c38b2d03aa Mon Sep 17 00:00:00 2001 From: Scott Lewis Date: Wed, 28 Jan 2026 15:43:23 -0800 Subject: [PATCH] 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 447c9e0..f3d76d8 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 0c8650f..e6eabff 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 c9989f8..1210b90 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 interface McpClient { 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 interface McpClient { */ 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 ae09331..b9ed271 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.ResponseSubscribers.ResponseEven 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 class HttpClientSseClientTransport implements McpClientTransport { 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 400a8a2..00b80f1 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.ResponseSubscribers.ResponseEven 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 class HttpClientStreamableHttpTransport implements McpClientTransport { */ 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 0000000..b8bdd90 --- /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 1e30cad..8481d17 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 ab37b43..725513c 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 @@ import java.lang.reflect.Type; /** * 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 8e35c02..09fe604 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 interface JsonSchemaValidator { */ 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 f4196c0..73a5016 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.McpUriTemplateManagerFactory; 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 interface McpServer { 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 interface McpServer { 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 interface McpServer { 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 interface McpServer { 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 interface McpServer { 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 interface McpServer { 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 d12fb8c..c07906b 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.ConcurrentHashMap; 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 class HttpServletSseServerTransportProvider extends HttpServlet implement 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 106f834..6431a2c 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 java.util.Map; 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 class HttpServletStatelessServerTransport extends HttpServlet implements */ 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 d9c0916..18cdcff 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.McpStreamableServerTransport; 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 class HttpServletStreamableServerTransportProvider extends HttpServlet 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 0000000..f1c73a0 --- /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 0000000..1a10fdf --- /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 9854de2..04b0589 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.List; 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 class MockMcpClientTransport implements McpClientTransport { @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 f3d6b77..5fefb89 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.ArrayList; 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 class MockMcpServerTransport implements McpServerTransport { @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 55f71fe..da5422e 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 911506e..0af4815 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 956a72c..37384fe 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 0000000..1d6705f --- /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 0000000..ad62874 --- /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 0629275..bf865a0 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 7b92eb7..0c5864a 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 static org.assertj.core.api.Assertions.assertThat; 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 a3cc470..c02bc37 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 0000000..0ad8a7b --- /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 0000000..d14d8be --- /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 e2d0a1d..58f7e01 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 29c450d..8919757 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 static org.assertj.core.api.Assertions.assertThat; 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 2cbcf35..0000000 --- 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 31930ab..0000000 --- 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 2497e7f..0000000 --- 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 282745a..e3a7091 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.WebClient; 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 class WebClientStreamableHttpTransport implements McpClientTransport { * @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 91b89d6..3c3a008 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.List; 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 class WebFluxSseClientTransport implements McpClientTransport { */ 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 34d6e50..de2b0e2 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.Map; 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 class WebFluxSseServerTransportProvider implements McpServerTransportProv */ 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 b225ab6..7488217 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 class WebFluxStatelessServerTransport implements McpStatelessServerTransp */ 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 0aed244..bb469b9 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 class WebFluxStreamableServerTransportProvider implements McpStreamableSe 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 6734757..0177932 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 7e925a0..bd6d75c 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.ConcurrentHashMap; 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 class WebMvcSseServerTransportProvider implements McpServerTransportProvi 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 92a08a8..665d86e 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 class WebMvcStatelessServerTransport implements McpStatelessServerTranspo */ 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 411d9c2..4f8216c 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.Map; 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 class WebMvcStreamableServerTransportProvider implements McpStreamableSer 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 1074e8a..36ea2d3 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 @@ package io.modelcontextprotocol.server.transport; 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 @@ class WebMvcSseServerTransportProviderTests { .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 cd84583..7d71376 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.List; 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 class MockMcpTransport implements McpClientTransport, McpServerTransport @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 7239655..45e4a4e 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 3e36b08..18b24ce 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