feat: Introduce McpJsonMapper interface to decouple from Jackson (#543)

This pull request creates two modules, `mcp-json` and `mcp-json-jackson`. It removes the `com.fasterxml.jackson.core:jackson-databind` and `com.networknt:json-schema-validator` dependencies from the `mcp` module. The `mcp` module now only depends on `com.fasterxml.jackson.core:jackson-annotations`.

To use Jackson, you have to add `mcp-jackson` to your dependencies in addition to `mcp`. I added the dependency `mcp-jackson` to both `mcp-spring-mvc` and `mcp-spring-webflux` to avoid a breaking change in those modules.

It provides two [SPI](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html) `JsonSchemaValidatorSupplier` and `JacksonJsonSchemaValidatorSupplier` to allow easy replacement for consumers who don't want to use Jackson.

This pull request also ensures no `McpJsonMapper` is instantiated if one is provided via a builder method. Only if the builders don't receive a `McpJsonMapper` mapper, one is instantiated in the `build` method of the builder.
The logic behind this is to allow frameworks to provide a `McpJsonMapper` mapper singleton implementation and feed it to the builders without paying the price of instantiating `McpJsonMappers`, which will not be used. The goal is to be able to use the `ObjectMapper` singleton of an application also for the MCP code.

Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
This commit is contained in:
Graeme Rocher 2025-09-10 12:26:17 +02:00 • committed by Christian Tzolov
commit 80d0ad82a6
9 changed files with 2043 additions and 1421 deletions

View file

@ -8,7 +8,6 @@ import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
@ -40,7 +39,6 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.servlet.function.ServerResponse;
import static org.assertj.core.api.Assertions.assertThat;
/**
@ -223,10 +221,7 @@ public class McpTransportContextIntegrationTests {
@Bean
public WebMvcStatelessServerTransport webMvcStatelessServerTransport() {
return WebMvcStatelessServerTransport.builder()
.objectMapper(new ObjectMapper())
.contextExtractor(serverContextExtractor)
.build();
return WebMvcStatelessServerTransport.builder().contextExtractor(serverContextExtractor).build();
}
@Bean
@ -251,10 +246,7 @@ public class McpTransportContextIntegrationTests {
@Bean
public WebMvcStreamableServerTransportProvider webMvcStreamableServerTransport() {
return WebMvcStreamableServerTransportProvider.builder()
.objectMapper(new ObjectMapper())
.contextExtractor(serverContextExtractor)
.build();
return WebMvcStreamableServerTransportProvider.builder().contextExtractor(serverContextExtractor).build();
}
@Bean
@ -281,7 +273,6 @@ public class McpTransportContextIntegrationTests {
public WebMvcSseServerTransportProvider webMvcSseServerTransport() {
return WebMvcSseServerTransportProvider.builder()
.objectMapper(new ObjectMapper())
.contextExtractor(serverContextExtractor)
.messageEndpoint("/mcp/message")
.build();

View file

@ -16,8 +16,6 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.server.transport.WebMvcStreamableServerTransportProvider;
import io.modelcontextprotocol.spec.McpStreamableServerTransportProvider;
import reactor.netty.DisposableServer;
@ -48,10 +46,7 @@ class WebMcpStreamableAsyncServerTransportTests extends AbstractMcpAsyncServerTe
@Bean
public WebMvcStreamableServerTransportProvider webMvcSseServerTransportProvider() {
return WebMvcStreamableServerTransportProvider.builder()
.objectMapper(new ObjectMapper())
.mcpEndpoint(MCP_ENDPOINT)
.build();
return WebMvcStreamableServerTransportProvider.builder().mcpEndpoint(MCP_ENDPOINT).build();
}
@Bean

View file

@ -16,8 +16,6 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.server.transport.WebMvcStreamableServerTransportProvider;
import io.modelcontextprotocol.spec.McpStreamableServerTransportProvider;
import reactor.netty.DisposableServer;
@ -48,10 +46,7 @@ class WebMcpStreamableSyncServerTransportTests extends AbstractMcpSyncServerTest
@Bean
public WebMvcStreamableServerTransportProvider webMvcSseServerTransportProvider() {
return WebMvcStreamableServerTransportProvider.builder()
.objectMapper(new ObjectMapper())
.mcpEndpoint(MCP_ENDPOINT)
.build();
return WebMvcStreamableServerTransportProvider.builder().mcpEndpoint(MCP_ENDPOINT).build();
}
@Bean

View file

@ -4,7 +4,6 @@
package io.modelcontextprotocol.server;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.server.transport.WebMvcSseServerTransportProvider;
import io.modelcontextprotocol.spec.McpServerTransportProvider;
import org.apache.catalina.Context;
@ -37,7 +36,10 @@ class WebMvcSseAsyncServerTransportTests extends AbstractMcpAsyncServerTests {
@Bean
public WebMvcSseServerTransportProvider webMvcSseServerTransportProvider() {
return new WebMvcSseServerTransportProvider(new ObjectMapper(), MESSAGE_ENDPOINT);
return WebMvcSseServerTransportProvider.builder()
.messageEndpoint(MESSAGE_ENDPOINT)
.sseEndpoint(WebMvcSseServerTransportProvider.DEFAULT_SSE_ENDPOINT)
.build();
}
@Bean

View file

@ -3,7 +3,6 @@
*/
package io.modelcontextprotocol.server;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
import io.modelcontextprotocol.server.transport.WebMvcSseServerTransportProvider;
@ -92,7 +91,6 @@ class WebMvcSseCustomContextPathTests {
public WebMvcSseServerTransportProvider webMvcSseServerTransportProvider() {
return WebMvcSseServerTransportProvider.builder()
.objectMapper(new ObjectMapper())
.baseUrl(CUSTOM_CONTEXT_PATH)
.messageEndpoint(MESSAGE_ENDPOINT)
.sseEndpoint(WebMvcSseServerTransportProvider.DEFAULT_SSE_ENDPOINT)

View file

@ -21,8 +21,6 @@ import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.servlet.function.ServerResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.AbstractMcpClientServerIntegrationTests;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
@ -64,7 +62,6 @@ class WebMvcSseIntegrationTests extends AbstractMcpClientServerIntegrationTests
@Bean
public WebMvcSseServerTransportProvider webMvcSseServerTransportProvider() {
return WebMvcSseServerTransportProvider.builder()
.objectMapper(new ObjectMapper())
.messageEndpoint(MESSAGE_ENDPOINT)
.contextExtractor(TEST_CONTEXT_EXTRACTOR)
.build();

View file

@ -4,7 +4,6 @@
package io.modelcontextprotocol.server;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.server.transport.WebMvcSseServerTransportProvider;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
@ -36,10 +35,7 @@ class WebMvcSseSyncServerTransportTests extends AbstractMcpSyncServerTests {
@Bean
public WebMvcSseServerTransportProvider webMvcSseServerTransportProvider() {
return WebMvcSseServerTransportProvider.builder()
.objectMapper(new ObjectMapper())
.messageEndpoint(MESSAGE_ENDPOINT)
.build();
return WebMvcSseServerTransportProvider.builder().messageEndpoint(MESSAGE_ENDPOINT).build();
}
@Bean

View file

@ -19,8 +19,6 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.AbstractStatelessIntegrationTests;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport;
@ -46,10 +44,7 @@ class WebMvcStatelessIntegrationTests extends AbstractStatelessIntegrationTests
@Bean
public WebMvcStatelessServerTransport webMvcStatelessServerTransport() {
return WebMvcStatelessServerTransport.builder()
.objectMapper(new ObjectMapper())
.messageEndpoint(MESSAGE_ENDPOINT)
.build();
return WebMvcStatelessServerTransport.builder().messageEndpoint(MESSAGE_ENDPOINT).build();
}

View file

@ -21,8 +21,6 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.AbstractMcpClientServerIntegrationTests;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport;
@ -52,7 +50,6 @@ class WebMvcStreamableIntegrationTests extends AbstractMcpClientServerIntegratio
@Bean
public WebMvcStreamableServerTransportProvider webMvcStreamableServerTransportProvider() {
return WebMvcStreamableServerTransportProvider.builder()
.objectMapper(new ObjectMapper())
.contextExtractor(TEST_CONTEXT_EXTRACTOR)
.mcpEndpoint(MESSAGE_ENDPOINT)
.build();