Skip to content

Commit 689a87f

Browse files
committed
Add Support for Jackson 3 and use it by default
1 parent af65356 commit 689a87f

File tree

17 files changed

+1256
-17
lines changed

17 files changed

+1256
-17
lines changed

mcp-core/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
<dependency>
8181
<groupId>com.fasterxml.jackson.core</groupId>
8282
<artifactId>jackson-annotations</artifactId>
83-
<version>${jackson.version}</version>
83+
<version>${jackson-annotations.version}</version>
8484
</dependency>
8585

8686
<dependency>
@@ -100,7 +100,7 @@
100100
<!-- Test dependencies -->
101101
<dependency>
102102
<groupId>io.modelcontextprotocol.sdk</groupId>
103-
<artifactId>mcp-json-jackson2</artifactId>
103+
<artifactId>mcp-json-jackson3</artifactId>
104104
<version>0.18.0-SNAPSHOT</version>
105105
<scope>test</scope>
106106
</dependency>

mcp-core/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import static org.assertj.core.api.Assertions.assertThat;
1111
import static org.assertj.core.api.Assertions.assertThatThrownBy;
1212

13+
import java.io.IOException;
1314
import java.util.Arrays;
1415
import java.util.Collections;
1516
import java.util.HashMap;
@@ -18,7 +19,7 @@
1819

1920
import org.junit.jupiter.api.Test;
2021

21-
import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
22+
import tools.jackson.databind.exc.InvalidTypeIdException;
2223

2324
import io.modelcontextprotocol.spec.McpSchema.TextResourceContents;
2425
import net.javacrumbs.jsonunit.core.Option;
@@ -58,7 +59,9 @@ void testTextContentDeserialization() throws Exception {
5859
void testContentDeserializationWrongType() throws Exception {
5960

6061
assertThatThrownBy(() -> JSON_MAPPER.readValue("""
61-
{"type":"WRONG","text":"XXX"}""", McpSchema.TextContent.class))
62+
{"type":"WRONG","text":"XXX"}""", McpSchema.TextContent.class)).isInstanceOf(IOException.class)
63+
.hasMessage("Failed to read value")
64+
.cause()
6265
.isInstanceOf(InvalidTypeIdException.class)
6366
.hasMessageContaining(
6467
"Could not resolve type id 'WRONG' as a subtype of `io.modelcontextprotocol.spec.McpSchema$TextContent`: known type ids = [audio, image, resource, resource_link, text]");

mcp-json-jackson2/pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<artifactId>mcp-json-jackson2</artifactId>
1212
<packaging>jar</packaging>
1313
<name>Java MCP SDK JSON Jackson</name>
14-
<description>Java MCP SDK JSON implementation based on Jackson</description>
14+
<description>Java MCP SDK JSON implementation based on Jackson 2</description>
1515
<url>https://github.com/modelcontextprotocol/java-sdk</url>
1616
<scm>
1717
<url>https://github.com/modelcontextprotocol/java-sdk</url>
@@ -42,12 +42,12 @@
4242
<dependency>
4343
<groupId>com.fasterxml.jackson.core</groupId>
4444
<artifactId>jackson-databind</artifactId>
45-
<version>${jackson.version}</version>
45+
<version>${jackson2.version}</version>
4646
</dependency>
4747
<dependency>
4848
<groupId>com.networknt</groupId>
4949
<artifactId>json-schema-validator</artifactId>
50-
<version>${json-schema-validator.version}</version>
50+
<version>${json-schema-validator-jackson2.version}</version>
5151
</dependency>
5252

5353
<dependency>

mcp-json-jackson3/pom.xml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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.18.0-SNAPSHOT</version>
10+
</parent>
11+
<artifactId>mcp-json-jackson3</artifactId>
12+
<packaging>jar</packaging>
13+
<name>Java MCP SDK JSON Jackson</name>
14+
<description>Java MCP SDK JSON implementation based on Jackson 3</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>git@github.com/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.18.0-SNAPSHOT</version>
41+
</dependency>
42+
<dependency>
43+
<groupId>tools.jackson.core</groupId>
44+
<artifactId>jackson-databind</artifactId>
45+
<version>${jackson3.version}</version>
46+
</dependency>
47+
<dependency>
48+
<groupId>com.networknt</groupId>
49+
<artifactId>json-schema-validator</artifactId>
50+
<version>${json-schema-validator-jackson3.version}</version>
51+
</dependency>
52+
53+
<dependency>
54+
<groupId>org.assertj</groupId>
55+
<artifactId>assertj-core</artifactId>
56+
<version>${assert4j.version}</version>
57+
<scope>test</scope>
58+
</dependency>
59+
<dependency>
60+
<groupId>org.junit.jupiter</groupId>
61+
<artifactId>junit-jupiter-api</artifactId>
62+
<version>${junit.version}</version>
63+
<scope>test</scope>
64+
</dependency>
65+
<dependency>
66+
<groupId>org.junit.jupiter</groupId>
67+
<artifactId>junit-jupiter-params</artifactId>
68+
<version>${junit.version}</version>
69+
<scope>test</scope>
70+
</dependency>
71+
<dependency>
72+
<groupId>org.mockito</groupId>
73+
<artifactId>mockito-core</artifactId>
74+
<version>${mockito.version}</version>
75+
<scope>test</scope>
76+
</dependency>
77+
78+
</dependencies>
79+
</project>
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Copyright 2026 - 2026 the original author or authors.
3+
*/
4+
5+
package io.modelcontextprotocol.json.jackson3;
6+
7+
import java.io.IOException;
8+
9+
import io.modelcontextprotocol.json.McpJsonMapper;
10+
import io.modelcontextprotocol.json.TypeRef;
11+
12+
import tools.jackson.core.JacksonException;
13+
import tools.jackson.databind.JavaType;
14+
import tools.jackson.databind.json.JsonMapper;
15+
16+
/**
17+
* Jackson-based implementation of JsonMapper. Wraps a Jackson JsonMapper but keeps the
18+
* SDK decoupled from Jackson at the API level.
19+
*/
20+
public final class JacksonMcpJsonMapper implements McpJsonMapper {
21+
22+
private final JsonMapper jsonMapper;
23+
24+
/**
25+
* Constructs a new JacksonMcpJsonMapper instance with the given JsonMapper.
26+
* @param jsonMapper the JsonMapper to be used for JSON serialization and
27+
* deserialization. Must not be null.
28+
* @throws IllegalArgumentException if the provided JsonMapper is null.
29+
*/
30+
public JacksonMcpJsonMapper(JsonMapper jsonMapper) {
31+
if (jsonMapper == null) {
32+
throw new IllegalArgumentException("JsonMapper must not be null");
33+
}
34+
this.jsonMapper = jsonMapper;
35+
}
36+
37+
/**
38+
* Returns the underlying Jackson {@link JsonMapper} used for JSON serialization and
39+
* deserialization.
40+
* @return the JsonMapper instance
41+
*/
42+
public JsonMapper getJsonMapper() {
43+
return jsonMapper;
44+
}
45+
46+
@Override
47+
public <T> T readValue(String content, Class<T> type) throws IOException {
48+
try {
49+
return jsonMapper.readValue(content, type);
50+
}
51+
catch (JacksonException ex) {
52+
throw new IOException("Failed to read value", ex);
53+
}
54+
}
55+
56+
@Override
57+
public <T> T readValue(byte[] content, Class<T> type) throws IOException {
58+
try {
59+
return jsonMapper.readValue(content, type);
60+
}
61+
catch (JacksonException ex) {
62+
throw new IOException("Failed to read value", ex);
63+
}
64+
}
65+
66+
@Override
67+
public <T> T readValue(String content, TypeRef<T> type) throws IOException {
68+
JavaType javaType = jsonMapper.getTypeFactory().constructType(type.getType());
69+
try {
70+
return jsonMapper.readValue(content, javaType);
71+
}
72+
catch (JacksonException ex) {
73+
throw new IOException("Failed to read value", ex);
74+
}
75+
}
76+
77+
@Override
78+
public <T> T readValue(byte[] content, TypeRef<T> type) throws IOException {
79+
JavaType javaType = jsonMapper.getTypeFactory().constructType(type.getType());
80+
try {
81+
return jsonMapper.readValue(content, javaType);
82+
}
83+
catch (JacksonException ex) {
84+
throw new IOException("Failed to read value", ex);
85+
}
86+
}
87+
88+
@Override
89+
public <T> T convertValue(Object fromValue, Class<T> type) {
90+
return jsonMapper.convertValue(fromValue, type);
91+
}
92+
93+
@Override
94+
public <T> T convertValue(Object fromValue, TypeRef<T> type) {
95+
JavaType javaType = jsonMapper.getTypeFactory().constructType(type.getType());
96+
return jsonMapper.convertValue(fromValue, javaType);
97+
}
98+
99+
@Override
100+
public String writeValueAsString(Object value) throws IOException {
101+
try {
102+
return jsonMapper.writeValueAsString(value);
103+
}
104+
catch (JacksonException ex) {
105+
throw new IOException("Failed to write value as string", ex);
106+
}
107+
}
108+
109+
@Override
110+
public byte[] writeValueAsBytes(Object value) throws IOException {
111+
try {
112+
return jsonMapper.writeValueAsBytes(value);
113+
}
114+
catch (JacksonException ex) {
115+
throw new IOException("Failed to write value as bytes", ex);
116+
}
117+
}
118+
119+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2026 - 2026 the original author or authors.
3+
*/
4+
5+
package io.modelcontextprotocol.json.jackson3;
6+
7+
import tools.jackson.databind.json.JsonMapper;
8+
9+
import io.modelcontextprotocol.json.McpJsonMapper;
10+
import io.modelcontextprotocol.json.McpJsonMapperSupplier;
11+
12+
/**
13+
* A supplier of {@link McpJsonMapper} instances that uses the Jackson library for JSON
14+
* serialization and deserialization.
15+
* <p>
16+
* This implementation provides a {@link McpJsonMapper} backed by
17+
* {@link JsonMapper#shared() JsonMapper shared instance}.
18+
*/
19+
public class JacksonMcpJsonMapperSupplier implements McpJsonMapperSupplier {
20+
21+
/**
22+
* Returns a new instance of {@link McpJsonMapper} that uses the Jackson library for
23+
* JSON serialization and deserialization.
24+
* <p>
25+
* The returned {@link McpJsonMapper} is backed by {@link JsonMapper#shared()
26+
* JsonMapper shared instance}.
27+
* @return a new {@link McpJsonMapper} instance
28+
*/
29+
@Override
30+
public McpJsonMapper get() {
31+
return new JacksonMcpJsonMapper(JsonMapper.shared());
32+
}
33+
34+
}

0 commit comments

Comments
 (0)