-
Notifications
You must be signed in to change notification settings - Fork 649
Introduce McpJsonMapper interface to decouple from Jackson #543
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
graemerocher
wants to merge
7
commits into
modelcontextprotocol:main
from
graemerocher:jackson-decoupling
+1,988
−1,426
Closed
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
1396084
Introduce McpJsonMapper interface to decouple from Jackson
graemerocher 6eb70cf
Make JSON Mapper SPI (#1)
sdelamo f429a45
cleanup and cache default mapper/validator
graemerocher 82259bd
cleanup
graemerocher 81c2566
formatting
graemerocher aae7347
correct
graemerocher 06403ce
Address pr feedback (#2)
sdelamo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<parent> | ||
<groupId>io.modelcontextprotocol.sdk</groupId> | ||
<artifactId>mcp-parent</artifactId> | ||
<version>0.13.0-SNAPSHOT</version> | ||
</parent> | ||
<artifactId>mcp-json-jackson2</artifactId> | ||
<packaging>jar</packaging> | ||
<name>Java MCP SDK JSON Jackson</name> | ||
<description>Java MCP SDK JSON implementation based on Jackson</description> | ||
<url>https://github.com/modelcontextprotocol/java-sdk</url> | ||
<scm> | ||
<url>https://github.com/modelcontextprotocol/java-sdk</url> | ||
<connection>git://github.com/modelcontextprotocol/java-sdk.git</connection> | ||
<developerConnection>git@github.com/modelcontextprotocol/java-sdk.git</developerConnection> | ||
</scm> | ||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-jar-plugin</artifactId> | ||
<configuration> | ||
<archive> | ||
<manifest> | ||
<addDefaultImplementationEntries>true</addDefaultImplementationEntries> | ||
</manifest> | ||
</archive> | ||
</configuration> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
<dependencies> | ||
<dependency> | ||
<groupId>io.modelcontextprotocol.sdk</groupId> | ||
<artifactId>mcp-json</artifactId> | ||
<version>0.13.0-SNAPSHOT</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.fasterxml.jackson.core</groupId> | ||
<artifactId>jackson-databind</artifactId> | ||
<version>${jackson.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.networknt</groupId> | ||
<artifactId>json-schema-validator</artifactId> | ||
<version>${json-schema-validator.version}</version> | ||
</dependency> | ||
</dependencies> | ||
</project> |
86 changes: 86 additions & 0 deletions
86
...son-jackson2/src/main/java/io/modelcontextprotocol/json/jackson/JacksonMcpJsonMapper.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
/* | ||
* Copyright 2025 - 2025 the original author or authors. | ||
*/ | ||
package io.modelcontextprotocol.json.jackson; | ||
|
||
import com.fasterxml.jackson.databind.JavaType; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import io.modelcontextprotocol.json.McpJsonMapper; | ||
import io.modelcontextprotocol.json.TypeRef; | ||
|
||
import java.io.IOException; | ||
|
||
/** | ||
* Jackson-based implementation of JsonMapper. Wraps a Jackson ObjectMapper but keeps the | ||
* SDK decoupled from Jackson at the API level. | ||
*/ | ||
public final class JacksonMcpJsonMapper implements McpJsonMapper { | ||
|
||
private final ObjectMapper objectMapper; | ||
|
||
/** | ||
* Constructs a new JacksonMcpJsonMapper instance with the given ObjectMapper. | ||
* @param objectMapper the ObjectMapper to be used for JSON serialization and | ||
* deserialization. Must not be null. | ||
* @throws IllegalArgumentException if the provided ObjectMapper is null. | ||
*/ | ||
public JacksonMcpJsonMapper(ObjectMapper objectMapper) { | ||
if (objectMapper == null) { | ||
throw new IllegalArgumentException("ObjectMapper must not be null"); | ||
} | ||
this.objectMapper = objectMapper; | ||
} | ||
|
||
/** | ||
* Returns the underlying Jackson {@link ObjectMapper} used for JSON serialization and | ||
* deserialization. | ||
* @return the ObjectMapper instance | ||
*/ | ||
public ObjectMapper getObjectMapper() { | ||
return objectMapper; | ||
} | ||
|
||
@Override | ||
public <T> T readValue(String content, Class<T> type) throws IOException { | ||
return objectMapper.readValue(content, type); | ||
} | ||
|
||
@Override | ||
public <T> T readValue(byte[] content, Class<T> type) throws IOException { | ||
return objectMapper.readValue(content, type); | ||
} | ||
|
||
@Override | ||
public <T> T readValue(String content, TypeRef<T> type) throws IOException { | ||
JavaType javaType = objectMapper.getTypeFactory().constructType(type.getType()); | ||
return objectMapper.readValue(content, javaType); | ||
} | ||
|
||
@Override | ||
public <T> T readValue(byte[] content, TypeRef<T> type) throws IOException { | ||
JavaType javaType = objectMapper.getTypeFactory().constructType(type.getType()); | ||
return objectMapper.readValue(content, javaType); | ||
} | ||
|
||
@Override | ||
public <T> T convertValue(Object fromValue, Class<T> type) { | ||
return objectMapper.convertValue(fromValue, type); | ||
} | ||
|
||
@Override | ||
public <T> T convertValue(Object fromValue, TypeRef<T> type) { | ||
JavaType javaType = objectMapper.getTypeFactory().constructType(type.getType()); | ||
return objectMapper.convertValue(fromValue, javaType); | ||
} | ||
|
||
@Override | ||
public String writeValueAsString(Object value) throws IOException { | ||
return objectMapper.writeValueAsString(value); | ||
} | ||
|
||
@Override | ||
public byte[] writeValueAsBytes(Object value) throws IOException { | ||
return objectMapper.writeValueAsBytes(value); | ||
} | ||
|
||
} |
31 changes: 31 additions & 0 deletions
31
...son2/src/main/java/io/modelcontextprotocol/json/jackson/JacksonMcpJsonMapperSupplier.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/* | ||
* Copyright 2025 - 2025 the original author or authors. | ||
*/ | ||
package io.modelcontextprotocol.json.jackson; | ||
|
||
import io.modelcontextprotocol.json.McpJsonMapper; | ||
import io.modelcontextprotocol.json.McpJsonMapperSupplier; | ||
|
||
/** | ||
* A supplier of {@link McpJsonMapper} instances that uses the Jackson library for JSON | ||
* serialization and deserialization. | ||
* <p> | ||
* This implementation provides a {@link McpJsonMapper} backed by a Jackson | ||
* {@link com.fasterxml.jackson.databind.ObjectMapper}. | ||
*/ | ||
public class JacksonMcpJsonMapperSupplier implements McpJsonMapperSupplier { | ||
|
||
/** | ||
* Returns a new instance of {@link McpJsonMapper} that uses the Jackson library for | ||
* JSON serialization and deserialization. | ||
* <p> | ||
* The returned {@link McpJsonMapper} is backed by a new instance of | ||
* {@link com.fasterxml.jackson.databind.ObjectMapper}. | ||
* @return a new {@link McpJsonMapper} instance | ||
*/ | ||
@Override | ||
public McpJsonMapper get() { | ||
return new JacksonMcpJsonMapper(new com.fasterxml.jackson.databind.ObjectMapper()); | ||
} | ||
|
||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 28 additions & 0 deletions
28
.../java/io/modelcontextprotocol/json/schema/jackson/JacksonJsonSchemaValidatorSupplier.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* | ||
* Copyright 2025 - 2025 the original author or authors. | ||
*/ | ||
package io.modelcontextprotocol.json.schema.jackson; | ||
|
||
import io.modelcontextprotocol.json.schema.JsonSchemaValidator; | ||
import io.modelcontextprotocol.json.schema.JsonSchemaValidatorSupplier; | ||
|
||
/** | ||
* A concrete implementation of {@link JsonSchemaValidatorSupplier} that provides a | ||
* {@link JsonSchemaValidator} instance based on the Jackson library. | ||
* | ||
* @see JsonSchemaValidatorSupplier | ||
* @see JsonSchemaValidator | ||
*/ | ||
public class JacksonJsonSchemaValidatorSupplier implements JsonSchemaValidatorSupplier { | ||
|
||
/** | ||
* Returns a new instance of {@link JsonSchemaValidator} that uses the Jackson library | ||
* for JSON schema validation. | ||
* @return A {@link JsonSchemaValidator} instance. | ||
*/ | ||
@Override | ||
public JsonSchemaValidator get() { | ||
return new DefaultJsonSchemaValidator(); | ||
} | ||
|
||
} |
1 change: 1 addition & 0 deletions
1
...2/src/main/resources/META-INF/services/io.modelcontextprotocol.json.McpJsonMapperSupplier
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
io.modelcontextprotocol.json.jackson.JacksonMcpJsonMapperSupplier |
1 change: 1 addition & 0 deletions
1
...sources/META-INF/services/io.modelcontextprotocol.json.schema.JsonSchemaValidatorSupplier
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
io.modelcontextprotocol.json.schema.jackson.JacksonJsonSchemaValidatorSupplier |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<parent> | ||
<groupId>io.modelcontextprotocol.sdk</groupId> | ||
<artifactId>mcp-parent</artifactId> | ||
<version>0.13.0-SNAPSHOT</version> | ||
</parent> | ||
<artifactId>mcp-json</artifactId> | ||
<packaging>jar</packaging> | ||
<name>Java MCP SDK JSON Support</name> | ||
<description>Java MCP SDK JSON Support API</description> | ||
<url>https://github.com/modelcontextprotocol/java-sdk</url> | ||
<scm> | ||
<url>https://github.com/modelcontextprotocol/java-sdk</url> | ||
<connection>git://github.com/modelcontextprotocol/java-sdk.git</connection> | ||
<developerConnection>git@github.com/modelcontextprotocol/java-sdk.git</developerConnection> | ||
</scm> | ||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-jar-plugin</artifactId> | ||
<configuration> | ||
<archive> | ||
<manifest> | ||
<addDefaultImplementationEntries>true</addDefaultImplementationEntries> | ||
</manifest> | ||
</archive> | ||
</configuration> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
<dependencies> | ||
|
||
</dependencies> | ||
</project> |
83 changes: 83 additions & 0 deletions
83
mcp-json/src/main/java/io/modelcontextprotocol/json/McpJsonInternal.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/* | ||
* Copyright 2025 - 2025 the original author or authors. | ||
*/ | ||
package io.modelcontextprotocol.json; | ||
|
||
import java.util.ServiceLoader; | ||
import java.util.concurrent.atomic.AtomicReference; | ||
import java.util.stream.Stream; | ||
|
||
/** | ||
* Utility class for creating a default {@link McpJsonMapper} instance. This class | ||
* provides a single method to create a default mapper using the {@link ServiceLoader} | ||
* mechanism. | ||
*/ | ||
final class McpJsonInternal { | ||
|
||
private static McpJsonMapper defaultJsonMapper = null; | ||
|
||
/** | ||
* Returns the cached default {@link McpJsonMapper} instance. If the default mapper | ||
* has not been created yet, it will be initialized using the | ||
* {@link #createDefaultMapper()} method. | ||
* @return the default {@link McpJsonMapper} instance | ||
* @throws IllegalStateException if no default {@link McpJsonMapper} implementation is | ||
* found | ||
*/ | ||
static McpJsonMapper getDefaultMapper() { | ||
if (defaultJsonMapper == null) { | ||
defaultJsonMapper = McpJsonInternal.createDefaultMapper(); | ||
} | ||
return defaultJsonMapper; | ||
} | ||
|
||
/** | ||
* Creates a default {@link McpJsonMapper} instance using the {@link ServiceLoader} | ||
* mechanism. The default mapper is resolved by loading the first available | ||
* {@link McpJsonMapperSupplier} implementation on the classpath. | ||
* @return the default {@link McpJsonMapper} instance | ||
* @throws IllegalStateException if no default {@link McpJsonMapper} implementation is | ||
* found | ||
*/ | ||
static McpJsonMapper createDefaultMapper() { | ||
AtomicReference<IllegalStateException> ex = new AtomicReference<>(); | ||
return ServiceLoader.load(McpJsonMapperSupplier.class).stream().flatMap(p -> { | ||
try { | ||
McpJsonMapperSupplier supplier = p.get(); | ||
return Stream.ofNullable(supplier); | ||
} | ||
catch (Exception e) { | ||
addException(ex, e); | ||
return Stream.empty(); | ||
} | ||
}).flatMap(jsonMapperSupplier -> { | ||
try { | ||
return Stream.ofNullable(jsonMapperSupplier.get()); | ||
} | ||
catch (Exception e) { | ||
addException(ex, e); | ||
return Stream.empty(); | ||
} | ||
}).findFirst().orElseThrow(() -> { | ||
if (ex.get() != null) { | ||
return ex.get(); | ||
} | ||
else { | ||
return new IllegalStateException("No default McpJsonMapper implementation found"); | ||
} | ||
}); | ||
} | ||
|
||
private static void addException(AtomicReference<IllegalStateException> ref, Exception toAdd) { | ||
ref.updateAndGet(existing -> { | ||
if (existing == null) { | ||
return new IllegalStateException("Failed to initialize default McpJsonMapper", toAdd); | ||
} | ||
else { | ||
existing.addSuppressed(toAdd); | ||
return existing; | ||
} | ||
}); | ||
} | ||
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add to the mcp-bom
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see: graemerocher@1b4271b