Skip to content

Commit 6eb70cf

Browse files
authored
Make JSON Mapper SPI (#1)
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. ## Breaking changes - This pull request also removes the deprecated `Tool` constructors, and it updates every test to use the builder API to instantiate tools. - Several constructors taking an ObjectMapper or constructor which forced the instantiation of a generic `McpJsonMapper` have been removed.
1 parent 1396084 commit 6eb70cf

File tree

116 files changed

+1138
-1438
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+1138
-1438
lines changed

mcp-json-jackson/pom.xml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>io.modelcontextprotocol.sdk</groupId>
8+
<artifactId>mcp-parent</artifactId>
9+
<version>0.13.0-SNAPSHOT</version>
10+
</parent>
11+
<artifactId>mcp-json-jackson</artifactId>
12+
<packaging>jar</packaging>
13+
<name>Java MCP SDK JSON Jackson</name>
14+
<description>Java MCP SDK JSON implementation based on Jackson</description>
15+
<url>https://github.com/modelcontextprotocol/java-sdk</url>
16+
<scm>
17+
<url>https://github.com/modelcontextprotocol/java-sdk</url>
18+
<connection>git://github.com/modelcontextprotocol/java-sdk.git</connection>
19+
<developerConnection>[email protected]/modelcontextprotocol/java-sdk.git</developerConnection>
20+
</scm>
21+
<build>
22+
<plugins>
23+
<plugin>
24+
<groupId>org.apache.maven.plugins</groupId>
25+
<artifactId>maven-jar-plugin</artifactId>
26+
<configuration>
27+
<archive>
28+
<manifest>
29+
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
30+
</manifest>
31+
</archive>
32+
</configuration>
33+
</plugin>
34+
</plugins>
35+
</build>
36+
<dependencies>
37+
<dependency>
38+
<groupId>io.modelcontextprotocol.sdk</groupId>
39+
<artifactId>mcp-json</artifactId>
40+
<version>0.13.0-SNAPSHOT</version>
41+
</dependency>
42+
<dependency>
43+
<groupId>com.fasterxml.jackson.core</groupId>
44+
<artifactId>jackson-databind</artifactId>
45+
<version>${jackson.version}</version>
46+
</dependency>
47+
<dependency>
48+
<groupId>com.networknt</groupId>
49+
<artifactId>json-schema-validator</artifactId>
50+
<version>${json-schema-validator.version}</version>
51+
</dependency>
52+
</dependencies>
53+
</project>

mcp/src/main/java/io/modelcontextprotocol/spec/json/jackson/JacksonMcpJsonMapper.java renamed to mcp-json-jackson/src/main/java/io/modelcontextprotocol/json/jackson/JacksonMcpJsonMapper.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
package io.modelcontextprotocol.spec.json.jackson;
1+
package io.modelcontextprotocol.json.jackson;
22

33
import com.fasterxml.jackson.databind.JavaType;
44
import com.fasterxml.jackson.databind.ObjectMapper;
5-
import io.modelcontextprotocol.spec.json.McpJsonMapper;
6-
import io.modelcontextprotocol.spec.json.TypeRef;
5+
import io.modelcontextprotocol.json.McpJsonMapper;
6+
import io.modelcontextprotocol.json.TypeRef;
77

88
import java.io.IOException;
99

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.modelcontextprotocol.json.jackson;
2+
3+
import io.modelcontextprotocol.json.McpJsonMapper;
4+
import io.modelcontextprotocol.json.McpJsonMapperSupplier;
5+
6+
public class JacksonMcpJsonMapperSupplier implements McpJsonMapperSupplier {
7+
8+
@Override
9+
public McpJsonMapper get() {
10+
return new JacksonMcpJsonMapper(new com.fasterxml.jackson.databind.ObjectMapper());
11+
}
12+
13+
}

mcp/src/main/java/io/modelcontextprotocol/spec/DefaultJsonSchemaValidator.java renamed to mcp-json-jackson/src/main/java/io/modelcontextprotocol/json/schema/jackson/DefaultJsonSchemaValidator.java

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
* Copyright 2024-2024 the original author or authors.
33
*/
44

5-
package io.modelcontextprotocol.spec;
5+
package io.modelcontextprotocol.json.schema.jackson;
66

77
import java.util.Map;
88
import java.util.Set;
99
import java.util.concurrent.ConcurrentHashMap;
1010

11+
import io.modelcontextprotocol.json.schema.JsonSchemaValidator;
1112
import org.slf4j.Logger;
1213
import org.slf4j.LoggerFactory;
1314

@@ -20,10 +21,6 @@
2021
import com.networknt.schema.SpecVersion;
2122
import com.networknt.schema.ValidationMessage;
2223

23-
import io.modelcontextprotocol.util.Assert;
24-
import io.modelcontextprotocol.spec.json.McpJsonMapper;
25-
import io.modelcontextprotocol.spec.json.jackson.JacksonMcpJsonMapper;
26-
2724
/**
2825
* Default implementation of the {@link JsonSchemaValidator} interface. This class
2926
* provides methods to validate structured content against a JSON schema. It uses the
@@ -52,29 +49,14 @@ public DefaultJsonSchemaValidator(ObjectMapper objectMapper) {
5249
this.schemaCache = new ConcurrentHashMap<>();
5350
}
5451

55-
/**
56-
* Alternate constructor that accepts a JsonMapper. If the mapper is backed by
57-
* Jackson, the underlying ObjectMapper will be reused; otherwise a new ObjectMapper
58-
* will be created for schema validation only.
59-
*/
60-
public DefaultJsonSchemaValidator(McpJsonMapper jsonMapper) {
61-
ObjectMapper mapper;
62-
if (jsonMapper instanceof JacksonMcpJsonMapper jacksonJsonMapper) {
63-
mapper = jacksonJsonMapper.getObjectMapper();
64-
}
65-
else {
66-
mapper = new ObjectMapper();
67-
}
68-
this.objectMapper = mapper;
69-
this.schemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012);
70-
this.schemaCache = new ConcurrentHashMap<>();
71-
}
72-
7352
@Override
7453
public ValidationResponse validate(Map<String, Object> schema, Map<String, Object> structuredContent) {
75-
76-
Assert.notNull(schema, "Schema must not be null");
77-
Assert.notNull(structuredContent, "Structured content must not be null");
54+
if (schema == null) {
55+
throw new IllegalArgumentException("Schema must not be null");
56+
}
57+
if (structuredContent == null) {
58+
throw new IllegalArgumentException("Structured content must not be null");
59+
}
7860

7961
try {
8062

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.modelcontextprotocol.json.schema.jackson;
2+
3+
import io.modelcontextprotocol.json.schema.JsonSchemaValidator;
4+
import io.modelcontextprotocol.json.schema.JsonSchemaValidatorSupplier;
5+
6+
public class JacksonJsonSchemaValidatorSupplier implements JsonSchemaValidatorSupplier {
7+
8+
@Override
9+
public JsonSchemaValidator get() {
10+
return new DefaultJsonSchemaValidator();
11+
}
12+
13+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
io.modelcontextprotocol.json.jackson.JacksonMcpJsonMapperSupplier
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
io.modelcontextprotocol.json.schema.jackson.JacksonJsonSchemaValidatorSupplier

mcp-json/pom.xml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>io.modelcontextprotocol.sdk</groupId>
8+
<artifactId>mcp-parent</artifactId>
9+
<version>0.13.0-SNAPSHOT</version>
10+
</parent>
11+
<artifactId>mcp-json</artifactId>
12+
<packaging>jar</packaging>
13+
<name>Java MCP SDK JSON Schema</name>
14+
<description>Java MCP SDK JSON Schema API</description>
15+
<url>https://github.com/modelcontextprotocol/java-sdk</url>
16+
<scm>
17+
<url>https://github.com/modelcontextprotocol/java-sdk</url>
18+
<connection>git://github.com/modelcontextprotocol/java-sdk.git</connection>
19+
<developerConnection>[email protected]/modelcontextprotocol/java-sdk.git</developerConnection>
20+
</scm>
21+
<build>
22+
<plugins>
23+
<plugin>
24+
<groupId>org.apache.maven.plugins</groupId>
25+
<artifactId>maven-jar-plugin</artifactId>
26+
<configuration>
27+
<archive>
28+
<manifest>
29+
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
30+
</manifest>
31+
</archive>
32+
</configuration>
33+
</plugin>
34+
</plugins>
35+
</build>
36+
<dependencies>
37+
38+
</dependencies>
39+
</project>

mcp/src/main/java/io/modelcontextprotocol/spec/json/McpJsonMapper.java renamed to mcp-json/src/main/java/io/modelcontextprotocol/json/McpJsonMapper.java

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
package io.modelcontextprotocol.spec.json;
1+
package io.modelcontextprotocol.json;
22

33
import java.io.IOException;
4+
import java.util.ServiceLoader;
5+
import java.util.concurrent.atomic.AtomicReference;
6+
import java.util.stream.Stream;
47

58
/**
69
* Abstraction for JSON serialization/deserialization to decouple the SDK from any
@@ -83,4 +86,51 @@ public interface McpJsonMapper {
8386
*/
8487
byte[] writeValueAsBytes(Object value) throws IOException;
8588

89+
/**
90+
* Resolves the default {@link McpJsonMapper}.
91+
* @return The default {@link McpJsonMapper}
92+
* @throws IllegalStateException If no {@link McpJsonMapper} implementation exists on
93+
* the classpath.
94+
*/
95+
static McpJsonMapper createDefault() {
96+
AtomicReference<IllegalStateException> ex = new AtomicReference<>();
97+
return ServiceLoader.load(McpJsonMapperSupplier.class).stream().flatMap(p -> {
98+
try {
99+
McpJsonMapperSupplier supplier = p.get();
100+
return Stream.ofNullable(supplier);
101+
}
102+
catch (Exception e) {
103+
addException(ex, e);
104+
return Stream.empty();
105+
}
106+
}).flatMap(jsonMapperSupplier -> {
107+
try {
108+
return Stream.of(jsonMapperSupplier.get());
109+
}
110+
catch (Exception e) {
111+
addException(ex, e);
112+
return Stream.empty();
113+
}
114+
}).findFirst().orElseThrow(() -> {
115+
if (ex.get() != null) {
116+
return ex.get();
117+
}
118+
else {
119+
return new IllegalStateException("No default McpJsonMapper implementation found");
120+
}
121+
});
122+
}
123+
124+
private static void addException(AtomicReference<IllegalStateException> ref, Exception toAdd) {
125+
ref.updateAndGet(existing -> {
126+
if (existing == null) {
127+
return new IllegalStateException("Failed to initialize default McpJsonMapper", toAdd);
128+
}
129+
else {
130+
existing.addSuppressed(toAdd);
131+
return existing;
132+
}
133+
});
134+
}
135+
86136
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package io.modelcontextprotocol.json;
2+
3+
import java.util.function.Supplier;
4+
5+
/**
6+
* Strategy interface for resolving a {@link McpJsonMapper}.
7+
*/
8+
public interface McpJsonMapperSupplier extends Supplier<McpJsonMapper> {
9+
10+
}

0 commit comments

Comments
 (0)