Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions mcp-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
Import-Package: jakarta.*;resolution:=optional, \
*;
Export-Package: io.modelcontextprotocol.*;version="${version}";-noimport:=true
Service-Component: OSGI-INF/io.modelcontextprotocol.json.internal.DefaultMcpJsonMapperSupplier.xml,OSGI-INF/io.modelcontextprotocol.json.internal.DefaultMcpJsonSchemaValidatorSupplier.xml
-noimportjava: true;
-nouses: true;
-removeheaders: Private-Package
Expand All @@ -65,12 +66,6 @@
</build>

<dependencies>
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp-json</artifactId>
<version>0.17.0-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
Expand All @@ -90,6 +85,11 @@


<!-- Used by the HttpServletSseServerTransport -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
Expand All @@ -104,7 +104,6 @@
<version>0.17.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.modelcontextprotocol.spec.McpClientSession;
import io.modelcontextprotocol.spec.McpError;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpTransportSessionNotFoundException;
import io.modelcontextprotocol.util.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Sinks;
import reactor.util.context.ContextView;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.modelcontextprotocol.client.LifecycleInitializer.Initialization;
import io.modelcontextprotocol.json.TypeRef;
import io.modelcontextprotocol.json.schema.JsonSchemaValidator;
Expand All @@ -37,8 +40,6 @@
import io.modelcontextprotocol.spec.McpSchema.Root;
import io.modelcontextprotocol.util.Assert;
import io.modelcontextprotocol.util.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
import java.util.function.Function;
import java.util.function.Supplier;

import io.modelcontextprotocol.json.schema.JsonSchemaValidator;
import io.modelcontextprotocol.common.McpTransportContext;
import io.modelcontextprotocol.json.internal.DefaultMcpJsonSchemaValidatorSupplier;
import io.modelcontextprotocol.json.schema.JsonSchemaValidator;
import io.modelcontextprotocol.spec.McpClientTransport;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.ClientCapabilities;
Expand Down Expand Up @@ -475,7 +476,8 @@ 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(),
jsonSchemaValidator != null ? jsonSchemaValidator
: DefaultMcpJsonSchemaValidatorSupplier.getDefaultJsonSchemaValidator(),
asyncFeatures), this.contextProvider);
}

Expand Down Expand Up @@ -809,7 +811,7 @@ public AsyncSpec enableCallToolSchemaCaching(boolean enableCallToolSchemaCaching
*/
public McpAsyncClient build() {
var jsonSchemaValidator = (this.jsonSchemaValidator != null) ? this.jsonSchemaValidator
: JsonSchemaValidator.getDefault();
: DefaultMcpJsonSchemaValidatorSupplier.getDefaultJsonSchemaValidator();
return new McpAsyncClient(this.transport, this.requestTimeout, this.initializationTimeout,
jsonSchemaValidator,
new McpClientFeatures.Async(this.clientInfo, this.capabilities, this.roots,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,17 @@

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.modelcontextprotocol.client.transport.ResponseSubscribers.ResponseEvent;
import io.modelcontextprotocol.client.transport.customizer.McpAsyncHttpClientRequestCustomizer;
import io.modelcontextprotocol.client.transport.customizer.McpSyncHttpClientRequestCustomizer;
import io.modelcontextprotocol.common.McpTransportContext;
import io.modelcontextprotocol.json.McpJsonMapper;
import io.modelcontextprotocol.json.TypeRef;
import io.modelcontextprotocol.json.internal.DefaultMcpJsonMapperSupplier;
import io.modelcontextprotocol.spec.HttpHeaders;
import io.modelcontextprotocol.spec.McpClientTransport;
import io.modelcontextprotocol.spec.McpError;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.JSONRPCMessage;
import io.modelcontextprotocol.spec.McpTransportException;
Expand Down Expand Up @@ -327,7 +330,8 @@ 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 ? DefaultMcpJsonMapperSupplier.getDefaultMcpJsonMapper() : jsonMapper,
httpRequestCustomizer);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,17 @@
import java.util.function.Consumer;
import java.util.function.Function;

import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.modelcontextprotocol.client.transport.ResponseSubscribers.ResponseEvent;
import io.modelcontextprotocol.client.transport.customizer.McpAsyncHttpClientRequestCustomizer;
import io.modelcontextprotocol.client.transport.customizer.McpSyncHttpClientRequestCustomizer;
import io.modelcontextprotocol.common.McpTransportContext;
import io.modelcontextprotocol.json.McpJsonMapper;
import io.modelcontextprotocol.json.TypeRef;
import io.modelcontextprotocol.json.internal.DefaultMcpJsonMapperSupplier;
import io.modelcontextprotocol.spec.ClosedMcpTransportSession;
import io.modelcontextprotocol.spec.DefaultMcpTransportSession;
import io.modelcontextprotocol.spec.DefaultMcpTransportStream;
Expand All @@ -39,9 +44,6 @@
import io.modelcontextprotocol.spec.ProtocolVersions;
import io.modelcontextprotocol.util.Assert;
import io.modelcontextprotocol.util.Utils;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.Disposable;
import reactor.core.publisher.Flux;
import reactor.core.publisher.FluxSink;
Expand Down Expand Up @@ -813,7 +815,8 @@ public Builder supportedProtocolVersions(List<String> supportedProtocolVersions)
*/
public HttpClientStreamableHttpTransport build() {
HttpClient httpClient = this.clientBuilder.connectTimeout(this.connectTimeout).build();
return new HttpClientStreamableHttpTransport(jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper,
return new HttpClientStreamableHttpTransport(
jsonMapper == null ? DefaultMcpJsonMapperSupplier.getDefaultMcpJsonMapper() : jsonMapper,
httpClient, requestBuilder, baseUri, endpoint, resumableStreams, openConnectionOnStartup,
httpRequestCustomizer, supportedProtocolVersions);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

import io.modelcontextprotocol.util.Assert;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@
import java.util.function.Consumer;
import java.util.function.Function;

import io.modelcontextprotocol.json.TypeRef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.modelcontextprotocol.json.McpJsonMapper;
import io.modelcontextprotocol.json.TypeRef;
import io.modelcontextprotocol.spec.McpClientTransport;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.JSONRPCMessage;
import io.modelcontextprotocol.util.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Sinks;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

import io.modelcontextprotocol.common.McpTransportContext;
import io.modelcontextprotocol.util.Assert;

import reactor.core.publisher.Mono;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
import java.net.http.HttpRequest;

import org.reactivestreams.Publisher;

import io.modelcontextprotocol.common.McpTransportContext;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import reactor.util.annotation.Nullable;

import io.modelcontextprotocol.common.McpTransportContext;

/**
* Customize {@link HttpRequest.Builder} before executing the request, in either SSE or
* Streamable HTTP transport.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@
import java.net.URI;
import java.net.http.HttpRequest;

import reactor.util.annotation.Nullable;

import io.modelcontextprotocol.client.McpClient.SyncSpec;
import io.modelcontextprotocol.common.McpTransportContext;
import reactor.util.annotation.Nullable;

/**
* Customize {@link HttpRequest.Builder} before executing the request, either in SSE or
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <T> generic type
* @throws IOException on parse errors
*/
<T> T readValue(String content, Class<T> type) throws IOException;

/**
* Deserialize JSON bytes into a target type.
* @param content JSON as bytes
* @param type target class
* @return deserialized instance
* @param <T> generic type
* @throws IOException on parse errors
*/
<T> T readValue(byte[] content, Class<T> 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 <T> generic type
* @throws IOException on parse errors
*/
<T> T readValue(String content, TypeRef<T> 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 <T> generic type
* @throws IOException on parse errors
*/
<T> T readValue(byte[] content, TypeRef<T> 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 <T> generic type
*/
<T> T convertValue(Object fromValue, Class<T> type);

/**
* Convert a value to a given parameterized type.
* @param fromValue source value
* @param type target type reference
* @return converted value
* @param <T> generic type
*/
<T> T convertValue(Object fromValue, TypeRef<T> 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;

}
Original file line number Diff line number Diff line change
@@ -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<McpJsonMapper> {

}
44 changes: 44 additions & 0 deletions mcp-core/src/main/java/io/modelcontextprotocol/json/TypeRef.java
Original file line number Diff line number Diff line change
@@ -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<List<Foo>> ref = new TypeRef<>(){};
*/
public abstract class TypeRef<T> {

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: <pre>
* TypeRef&lt;List&lt;Foo&gt;&gt; ref = new TypeRef&lt;&gt;(){};
* </pre>
* @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;
}

}
Loading