From 003eb6e48d85ad77ebd94ec2858f033d0c36beac Mon Sep 17 00:00:00 2001 From: Scott Lewis Date: Wed, 28 Jan 2026 15:43:23 -0800 Subject: [PATCH 1/4] Rebasing on main after #742 merged. Moves mcp-json API back into mcp-core so that mcp-json API is no longer needed and mcp-core has fewer dependencies. --- mcp-bom/pom.xml | 7 -- mcp-core/pom.xml | 18 ++-- .../client/McpClient.java | 12 ++- .../HttpClientSseClientTransport.java | 3 +- .../HttpClientStreamableHttpTransport.java | 8 +- .../json/McpJsonDefaults.java | 50 +++++++++++ .../json/McpJsonMapper.java | 90 +++++++++++++++++++ .../json/McpJsonMapperSupplier.java | 14 +++ .../io/modelcontextprotocol/json/TypeRef.java | 44 +++++++++ .../DefaultMcpJsonMapperSupplier.java | 40 +++++++++ ...DefaultMcpJsonSchemaValidatorSupplier.java | 42 +++++++++ .../json/schema/JsonSchemaValidator.java | 44 +++++++++ .../schema/JsonSchemaValidatorSupplier.java | 19 ++++ .../server/McpServer.java | 40 +++++---- ...HttpServletSseServerTransportProvider.java | 5 +- .../HttpServletStatelessServerTransport.java | 6 +- ...vletStreamableServerTransportProvider.java | 5 +- .../util/McpServiceLoader.java | 50 +++++++++++ ...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 +- .../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 | 5 +- ...WebMvcSseServerTransportProviderTests.java | 3 +- .../MockMcpTransport.java | 4 +- .../util/McpJsonMapperUtils.java | 3 +- pom.xml | 1 - 46 files changed, 605 insertions(+), 99 deletions(-) create mode 100644 mcp-core/src/main/java/io/modelcontextprotocol/json/McpJsonDefaults.java create mode 100644 mcp-core/src/main/java/io/modelcontextprotocol/json/McpJsonMapper.java create mode 100644 mcp-core/src/main/java/io/modelcontextprotocol/json/McpJsonMapperSupplier.java create mode 100644 mcp-core/src/main/java/io/modelcontextprotocol/json/TypeRef.java create mode 100644 mcp-core/src/main/java/io/modelcontextprotocol/json/internal/DefaultMcpJsonMapperSupplier.java create mode 100644 mcp-core/src/main/java/io/modelcontextprotocol/json/internal/DefaultMcpJsonSchemaValidatorSupplier.java create mode 100644 mcp-core/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidator.java create mode 100644 mcp-core/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidatorSupplier.java 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 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..989764873 100644 --- a/mcp-core/pom.xml +++ b/mcp-core/pom.xml @@ -65,11 +65,6 @@ - - io.modelcontextprotocol.sdk - mcp-json - 0.18.0-SNAPSHOT - org.slf4j @@ -97,14 +92,6 @@ provided - - - io.modelcontextprotocol.sdk - mcp-json-jackson3 - 0.18.0-SNAPSHOT - test - - org.springframework spring-webmvc @@ -112,6 +99,11 @@ test + + tools.jackson.core + jackson-databind + ${jackson3.version} + 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 0a8dff363..20ad02691 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; @@ -822,9 +823,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..e0a5da370 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/json/McpJsonDefaults.java @@ -0,0 +1,50 @@ +package io.modelcontextprotocol.json; + +import io.modelcontextprotocol.json.schema.JsonSchemaValidator; +import io.modelcontextprotocol.json.schema.JsonSchemaValidatorSupplier; +import io.modelcontextprotocol.util.McpServiceLoader; + +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-core/src/main/java/io/modelcontextprotocol/json/McpJsonMapper.java b/mcp-core/src/main/java/io/modelcontextprotocol/json/McpJsonMapper.java new file mode 100644 index 000000000..8481d1703 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/json/McpJsonMapper.java @@ -0,0 +1,90 @@ +/* + * Copyright 2025 - 2025 the original author or authors. + */ + +package io.modelcontextprotocol.json; + +import java.io.IOException; + +/** + * Abstraction for JSON serialization/deserialization to decouple the SDK from any + * specific JSON library. A default implementation backed by Jackson is provided in + * io.modelcontextprotocol.spec.json.jackson.JacksonJsonMapper. + */ +public interface McpJsonMapper { + + /** + * Deserialize JSON string into a target type. + * @param content JSON as String + * @param type target class + * @return deserialized instance + * @param generic type + * @throws IOException on parse errors + */ + T readValue(String content, Class type) throws IOException; + + /** + * Deserialize JSON bytes into a target type. + * @param content JSON as bytes + * @param type target class + * @return deserialized instance + * @param generic type + * @throws IOException on parse errors + */ + T readValue(byte[] content, Class type) throws IOException; + + /** + * Deserialize JSON string into a parameterized target type. + * @param content JSON as String + * @param type parameterized type reference + * @return deserialized instance + * @param generic type + * @throws IOException on parse errors + */ + T readValue(String content, TypeRef type) throws IOException; + + /** + * Deserialize JSON bytes into a parameterized target type. + * @param content JSON as bytes + * @param type parameterized type reference + * @return deserialized instance + * @param generic type + * @throws IOException on parse errors + */ + T readValue(byte[] content, TypeRef type) throws IOException; + + /** + * Convert a value to a given type, useful for mapping nested JSON structures. + * @param fromValue source value + * @param type target class + * @return converted value + * @param generic type + */ + T convertValue(Object fromValue, Class type); + + /** + * Convert a value to a given parameterized type. + * @param fromValue source value + * @param type target type reference + * @return converted value + * @param generic type + */ + T convertValue(Object fromValue, TypeRef type); + + /** + * Serialize an object to JSON string. + * @param value object to serialize + * @return JSON as String + * @throws IOException on serialization errors + */ + String writeValueAsString(Object value) throws IOException; + + /** + * Serialize an object to JSON bytes. + * @param value object to serialize + * @return JSON as bytes + * @throws IOException on serialization errors + */ + byte[] writeValueAsBytes(Object value) throws IOException; + +} diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/json/McpJsonMapperSupplier.java b/mcp-core/src/main/java/io/modelcontextprotocol/json/McpJsonMapperSupplier.java new file mode 100644 index 000000000..619f96040 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/json/McpJsonMapperSupplier.java @@ -0,0 +1,14 @@ +/* + * Copyright 2025 - 2025 the original author or authors. + */ + +package io.modelcontextprotocol.json; + +import java.util.function.Supplier; + +/** + * Strategy interface for resolving a {@link McpJsonMapper}. + */ +public interface McpJsonMapperSupplier extends Supplier { + +} diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/json/TypeRef.java b/mcp-core/src/main/java/io/modelcontextprotocol/json/TypeRef.java new file mode 100644 index 000000000..725513c66 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/json/TypeRef.java @@ -0,0 +1,44 @@ +/* + * Copyright 2025 - 2025 the original author or authors. + */ + +package io.modelcontextprotocol.json; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +/** + * Captures generic type information at runtime for parameterized JSON (de)serialization. + * Usage: TypeRef> ref = new TypeRef<>(){}; + */ +public abstract class TypeRef { + + private final Type type; + + /** + * Constructs a new TypeRef instance, capturing the generic type information of the + * subclass. This constructor should be called from an anonymous subclass to capture + * the actual type arguments. For example:
+	 * TypeRef<List<Foo>> ref = new TypeRef<>(){};
+	 * 
+ * @throws IllegalStateException if TypeRef is not subclassed with actual type + * information + */ + protected TypeRef() { + Type superClass = getClass().getGenericSuperclass(); + if (superClass instanceof Class) { + throw new IllegalStateException("TypeRef constructed without actual type information"); + } + this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0]; + } + + /** + * Returns the captured type information. + * @return the Type representing the actual type argument captured by this TypeRef + * instance + */ + public Type getType() { + return type; + } + +} diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/json/internal/DefaultMcpJsonMapperSupplier.java b/mcp-core/src/main/java/io/modelcontextprotocol/json/internal/DefaultMcpJsonMapperSupplier.java new file mode 100644 index 000000000..b44cadc62 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/json/internal/DefaultMcpJsonMapperSupplier.java @@ -0,0 +1,40 @@ +package io.modelcontextprotocol.json.internal; + +import java.util.Optional; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; + +import io.modelcontextprotocol.json.McpJsonMapper; +import io.modelcontextprotocol.json.McpJsonMapperSupplier; + +public class DefaultMcpJsonMapperSupplier { + + private static McpJsonMapperSupplier jsonMapperSupplier; + + private static McpJsonMapper defaultJsonMapper; + + void setMcpJsonMapperSupplier(McpJsonMapperSupplier supplier) { + jsonMapperSupplier = supplier; + } + + void unsetMcpJsonMapperSupplier(McpJsonMapperSupplier supplier) { + jsonMapperSupplier = null; + defaultJsonMapper = null; + } + + public synchronized static McpJsonMapper getDefaultMcpJsonMapper() { + if (defaultJsonMapper == null) { + if (jsonMapperSupplier == null) { + // Use serviceloader + Optional sl = ServiceLoader.load(McpJsonMapperSupplier.class).findFirst(); + if (sl.isEmpty()) { + throw new ServiceConfigurationError("No JsonMapperSupplier available for creating McpJsonMapper"); + } + jsonMapperSupplier = sl.get(); + } + defaultJsonMapper = jsonMapperSupplier.get(); + } + return defaultJsonMapper; + } + +} diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/json/internal/DefaultMcpJsonSchemaValidatorSupplier.java b/mcp-core/src/main/java/io/modelcontextprotocol/json/internal/DefaultMcpJsonSchemaValidatorSupplier.java new file mode 100644 index 000000000..cb9d46029 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/json/internal/DefaultMcpJsonSchemaValidatorSupplier.java @@ -0,0 +1,42 @@ +package io.modelcontextprotocol.json.internal; + +import java.util.Optional; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; + +import io.modelcontextprotocol.json.schema.JsonSchemaValidator; +import io.modelcontextprotocol.json.schema.JsonSchemaValidatorSupplier; + +public class DefaultMcpJsonSchemaValidatorSupplier { + + private static JsonSchemaValidatorSupplier jsonSchemaValidatorSupplier; + + private static JsonSchemaValidator defaultJsonSchemaValidator; + + void setJsonSchemaValidatorSupplier(JsonSchemaValidatorSupplier supplier) { + jsonSchemaValidatorSupplier = supplier; + } + + void unsetJsonSchemaValidatorSupplier(JsonSchemaValidatorSupplier supplier) { + jsonSchemaValidatorSupplier = null; + defaultJsonSchemaValidator = null; + } + + public synchronized static JsonSchemaValidator getDefaultJsonSchemaValidator() { + if (defaultJsonSchemaValidator == null) { + if (jsonSchemaValidatorSupplier == null) { + // Use serviceloader + Optional sl = ServiceLoader.load(JsonSchemaValidatorSupplier.class) + .findFirst(); + if (sl.isEmpty()) { + throw new ServiceConfigurationError( + "No JsonSchemaValidatorSupplier available for creating JsonSchemaValidator"); + } + jsonSchemaValidatorSupplier = sl.get(); + } + defaultJsonSchemaValidator = jsonSchemaValidatorSupplier.get(); + } + return defaultJsonSchemaValidator; + } + +} diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidator.java b/mcp-core/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidator.java new file mode 100644 index 000000000..09fe604f4 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidator.java @@ -0,0 +1,44 @@ +/* + * Copyright 2024-2024 the original author or authors. + */ +package io.modelcontextprotocol.json.schema; + +import java.util.Map; + +/** + * Interface for validating structured content against a JSON schema. This interface + * defines a method to validate structured content based on the provided output schema. + * + * @author Christian Tzolov + */ +public interface JsonSchemaValidator { + + /** + * Represents the result of a validation operation. + * + * @param valid Indicates whether the validation was successful. + * @param errorMessage An error message if the validation failed, otherwise null. + * @param jsonStructuredOutput The text structured content in JSON format if the + * validation was successful, otherwise null. + */ + record ValidationResponse(boolean valid, String errorMessage, String jsonStructuredOutput) { + + public static ValidationResponse asValid(String jsonStructuredOutput) { + return new ValidationResponse(true, null, jsonStructuredOutput); + } + + public static ValidationResponse asInvalid(String message) { + return new ValidationResponse(false, message, null); + } + } + + /** + * Validates the structured content against the provided JSON schema. + * @param schema The JSON schema to validate against. + * @param structuredContent The structured content to validate. + * @return A ValidationResponse indicating whether the validation was successful or + * not. + */ + ValidationResponse validate(Map schema, Object structuredContent); + +} diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidatorSupplier.java b/mcp-core/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidatorSupplier.java new file mode 100644 index 000000000..6f69169a0 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/json/schema/JsonSchemaValidatorSupplier.java @@ -0,0 +1,19 @@ +/* + * Copyright 2025 - 2025 the original author or authors. + */ + +package io.modelcontextprotocol.json.schema; + +import java.util.function.Supplier; + +/** + * A supplier interface that provides a {@link JsonSchemaValidator} instance. + * Implementations of this interface are expected to return a new or cached instance of + * {@link JsonSchemaValidator} when {@link #get()} is invoked. + * + * @see JsonSchemaValidator + * @see Supplier + */ +public interface JsonSchemaValidatorSupplier extends Supplier { + +} 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 fe3125271..2e4026325 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServer.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServer.java @@ -14,6 +14,7 @@ import java.util.function.BiConsumer; import java.util.function.BiFunction; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.schema.JsonSchemaValidator; @@ -240,10 +241,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); } } @@ -267,9 +269,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); } } @@ -834,9 +837,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); } @@ -864,10 +867,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); } @@ -1871,9 +1874,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()); } } @@ -2351,9 +2355,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 96cebb74a..476aafafe 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 @@ -15,6 +15,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; @@ -632,8 +633,8 @@ public HttpServletSseServerTransportProvider build() { throw new IllegalStateException("MessageEndpoint must be set"); } return new HttpServletSseServerTransportProvider( - jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, baseUrl, messageEndpoint, sseEndpoint, - keepAliveInterval, contextExtractor); + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, baseUrl, + messageEndpoint, sseEndpoint, keepAliveInterval, contextExtractor); } } 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 40767f416..ae74cf839 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 @@ -11,6 +11,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.modelcontextprotocol.json.McpJsonDefaults; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.common.McpTransportContext; @@ -296,8 +297,9 @@ public Builder contextExtractor(McpTransportContextExtractor */ public HttpServletStatelessServerTransport build() { Assert.notNull(mcpEndpoint, "Message endpoint must be set"); - return new HttpServletStatelessServerTransport(jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, - mcpEndpoint, contextExtractor); + return new HttpServletStatelessServerTransport( + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, mcpEndpoint, + contextExtractor); } } 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 34671c105..5ed308b0b 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 @@ -28,6 +28,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; @@ -842,8 +843,8 @@ public Builder keepAliveInterval(Duration keepAliveInterval) { public HttpServletStreamableServerTransportProvider build() { Assert.notNull(this.mcpEndpoint, "MCP endpoint must be set"); return new HttpServletStreamableServerTransportProvider( - jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, mcpEndpoint, disallowDelete, - contextExtractor, keepAliveInterval); + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, mcpEndpoint, + disallowDelete, contextExtractor, keepAliveInterval); } } 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..e67ff46d3 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/util/McpServiceLoader.java @@ -0,0 +1,50 @@ +package io.modelcontextprotocol.util; + +import java.util.Optional; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; +import java.util.function.Supplier; + +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 JsonMapperSupplier available for creating McpJsonMapper"); + } + 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-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 5af98985d..f5b6ead89 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 0c80c5b8b..b7b4f22a6 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 @@ -10,6 +10,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; @@ -521,8 +522,9 @@ public Builder contextExtractor(McpTransportContextExtractor cont */ public WebFluxSseServerTransportProvider build() { Assert.notNull(messageEndpoint, "Message endpoint must be set"); - return new WebFluxSseServerTransportProvider(jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, - baseUrl, messageEndpoint, sseEndpoint, keepAliveInterval, contextExtractor); + return new WebFluxSseServerTransportProvider( + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, baseUrl, + messageEndpoint, sseEndpoint, keepAliveInterval, contextExtractor); } } 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 400be341e..78af45642 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; @@ -213,8 +214,9 @@ public Builder contextExtractor(McpTransportContextExtractor cont */ public WebFluxStatelessServerTransport build() { Assert.notNull(mcpEndpoint, "Message endpoint must be set"); - return new WebFluxStatelessServerTransport(jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, - mcpEndpoint, contextExtractor); + return new WebFluxStatelessServerTransport( + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, mcpEndpoint, + contextExtractor); } } 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 deebfc616..db171fe50 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; @@ -486,8 +487,8 @@ public Builder keepAliveInterval(Duration keepAliveInterval) { public WebFluxStreamableServerTransportProvider build() { Assert.notNull(mcpEndpoint, "Message endpoint must be set"); return new WebFluxStreamableServerTransportProvider( - jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, mcpEndpoint, contextExtractor, - disallowDelete, keepAliveInterval); + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, mcpEndpoint, + contextExtractor, disallowDelete, keepAliveInterval); } } 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 6c35de56d..2a29075ae 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 @@ -11,6 +11,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; @@ -559,8 +560,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); + return new WebMvcSseServerTransportProvider( + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, baseUrl, + messageEndpoint, sseEndpoint, keepAliveInterval, contextExtractor); } } 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 67b5f571c..244b17e59 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; @@ -232,8 +233,9 @@ public Builder contextExtractor(McpTransportContextExtractor cont */ public WebMvcStatelessServerTransport build() { Assert.notNull(mcpEndpoint, "Message endpoint must be set"); - return new WebMvcStatelessServerTransport(jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, - mcpEndpoint, contextExtractor); + return new WebMvcStatelessServerTransport( + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, mcpEndpoint, + contextExtractor); } } 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 f2a58d4d8..8d54fd25b 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 @@ -10,6 +10,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; @@ -681,8 +682,8 @@ public Builder keepAliveInterval(Duration keepAliveInterval) { public WebMvcStreamableServerTransportProvider build() { Assert.notNull(this.mcpEndpoint, "MCP endpoint must be set"); return new WebMvcStreamableServerTransportProvider( - jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, mcpEndpoint, disallowDelete, - contextExtractor, keepAliveInterval); + jsonMapper == null ? McpJsonDefaults.getDefaultMcpJsonMapper() : jsonMapper, mcpEndpoint, + disallowDelete, contextExtractor, keepAliveInterval); } } 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 faa2ad86e..f25f49275 100644 --- a/pom.xml +++ b/pom.xml @@ -109,7 +109,6 @@ mcp-core mcp-json-jackson2 mcp-json-jackson3 - mcp-json mcp-spring/mcp-spring-webflux mcp-spring/mcp-spring-webmvc mcp-test From bedf075f56c182477e4539753bfddc0c756a91fc Mon Sep 17 00:00:00 2001 From: Scott Lewis Date: Thu, 29 Jan 2026 13:43:00 -0800 Subject: [PATCH 2/4] Fix for missing test in mcp-core. Thanks to @lucabutboring for catch --- mcp-core/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/mcp-core/pom.xml b/mcp-core/pom.xml index 989764873..1db05f9e4 100644 --- a/mcp-core/pom.xml +++ b/mcp-core/pom.xml @@ -103,6 +103,7 @@ tools.jackson.core jackson-databind ${jackson3.version} + test From fbda61f3bc7cc39a25722d15ea95f534352298e4 Mon Sep 17 00:00:00 2001 From: Scott Lewis Date: Thu, 29 Jan 2026 13:46:35 -0800 Subject: [PATCH 3/4] Update mcp-core/src/main/java/io/modelcontextprotocol/util/McpServiceLoader.java Co-authored-by: Luca Chang <131398524+LucaButBoring@users.noreply.github.com> --- .../java/io/modelcontextprotocol/util/McpServiceLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/util/McpServiceLoader.java b/mcp-core/src/main/java/io/modelcontextprotocol/util/McpServiceLoader.java index e67ff46d3..fd8fd8a14 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/util/McpServiceLoader.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/util/McpServiceLoader.java @@ -38,7 +38,7 @@ public synchronized R getDefault() { // Use serviceloader Optional sl = serviceLoad(this.supplierType); if (sl.isEmpty()) { - throw new ServiceConfigurationError("No JsonMapperSupplier available for creating McpJsonMapper"); + throw new ServiceConfigurationError("No %s available for creating McpJsonMapper".format(this.supplierType.getSimpleName())); } this.supplier = (S) sl.get(); } From 76ce84df4e7fdf1548a701777e914897429e3742 Mon Sep 17 00:00:00 2001 From: Scott Lewis Date: Thu, 29 Jan 2026 14:14:56 -0800 Subject: [PATCH 4/4] Removed DefaultMcpJsonMapperSupplier and DefaultMcpJsonSchemaSupplier as they were originally committed accidently. Thanks to LucaButBoring for pointing out the error. --- .../DefaultMcpJsonMapperSupplier.java | 40 ------------------ ...DefaultMcpJsonSchemaValidatorSupplier.java | 42 ------------------- 2 files changed, 82 deletions(-) delete mode 100644 mcp-core/src/main/java/io/modelcontextprotocol/json/internal/DefaultMcpJsonMapperSupplier.java delete mode 100644 mcp-core/src/main/java/io/modelcontextprotocol/json/internal/DefaultMcpJsonSchemaValidatorSupplier.java diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/json/internal/DefaultMcpJsonMapperSupplier.java b/mcp-core/src/main/java/io/modelcontextprotocol/json/internal/DefaultMcpJsonMapperSupplier.java deleted file mode 100644 index b44cadc62..000000000 --- a/mcp-core/src/main/java/io/modelcontextprotocol/json/internal/DefaultMcpJsonMapperSupplier.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.modelcontextprotocol.json.internal; - -import java.util.Optional; -import java.util.ServiceConfigurationError; -import java.util.ServiceLoader; - -import io.modelcontextprotocol.json.McpJsonMapper; -import io.modelcontextprotocol.json.McpJsonMapperSupplier; - -public class DefaultMcpJsonMapperSupplier { - - private static McpJsonMapperSupplier jsonMapperSupplier; - - private static McpJsonMapper defaultJsonMapper; - - void setMcpJsonMapperSupplier(McpJsonMapperSupplier supplier) { - jsonMapperSupplier = supplier; - } - - void unsetMcpJsonMapperSupplier(McpJsonMapperSupplier supplier) { - jsonMapperSupplier = null; - defaultJsonMapper = null; - } - - public synchronized static McpJsonMapper getDefaultMcpJsonMapper() { - if (defaultJsonMapper == null) { - if (jsonMapperSupplier == null) { - // Use serviceloader - Optional sl = ServiceLoader.load(McpJsonMapperSupplier.class).findFirst(); - if (sl.isEmpty()) { - throw new ServiceConfigurationError("No JsonMapperSupplier available for creating McpJsonMapper"); - } - jsonMapperSupplier = sl.get(); - } - defaultJsonMapper = jsonMapperSupplier.get(); - } - return defaultJsonMapper; - } - -} diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/json/internal/DefaultMcpJsonSchemaValidatorSupplier.java b/mcp-core/src/main/java/io/modelcontextprotocol/json/internal/DefaultMcpJsonSchemaValidatorSupplier.java deleted file mode 100644 index cb9d46029..000000000 --- a/mcp-core/src/main/java/io/modelcontextprotocol/json/internal/DefaultMcpJsonSchemaValidatorSupplier.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.modelcontextprotocol.json.internal; - -import java.util.Optional; -import java.util.ServiceConfigurationError; -import java.util.ServiceLoader; - -import io.modelcontextprotocol.json.schema.JsonSchemaValidator; -import io.modelcontextprotocol.json.schema.JsonSchemaValidatorSupplier; - -public class DefaultMcpJsonSchemaValidatorSupplier { - - private static JsonSchemaValidatorSupplier jsonSchemaValidatorSupplier; - - private static JsonSchemaValidator defaultJsonSchemaValidator; - - void setJsonSchemaValidatorSupplier(JsonSchemaValidatorSupplier supplier) { - jsonSchemaValidatorSupplier = supplier; - } - - void unsetJsonSchemaValidatorSupplier(JsonSchemaValidatorSupplier supplier) { - jsonSchemaValidatorSupplier = null; - defaultJsonSchemaValidator = null; - } - - public synchronized static JsonSchemaValidator getDefaultJsonSchemaValidator() { - if (defaultJsonSchemaValidator == null) { - if (jsonSchemaValidatorSupplier == null) { - // Use serviceloader - Optional sl = ServiceLoader.load(JsonSchemaValidatorSupplier.class) - .findFirst(); - if (sl.isEmpty()) { - throw new ServiceConfigurationError( - "No JsonSchemaValidatorSupplier available for creating JsonSchemaValidator"); - } - jsonSchemaValidatorSupplier = sl.get(); - } - defaultJsonSchemaValidator = jsonSchemaValidatorSupplier.get(); - } - return defaultJsonSchemaValidator; - } - -}