Skip to content
Closed
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
14 changes: 14 additions & 0 deletions mcp-bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,20 @@
<version>${project.version}</version>
</dependency>

<!-- MCP JSON -->
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp-json</artifactId>
<version>${project.version}</version>
</dependency>

<!-- MCP JSON Jackson -->
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp-json-jackson2</artifactId>
<version>${project.version}</version>
</dependency>

<!-- MCP Test -->
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
Expand Down
53 changes: 53 additions & 0 deletions mcp-json-jackson2/pom.xml
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>
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);
}

}
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());
}

}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/*
* Copyright 2024-2024 the original author or authors.
*/

package io.modelcontextprotocol.spec;
package io.modelcontextprotocol.json.schema.jackson;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import io.modelcontextprotocol.json.schema.JsonSchemaValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -20,8 +20,6 @@
import com.networknt.schema.SpecVersion;
import com.networknt.schema.ValidationMessage;

import io.modelcontextprotocol.util.Assert;

/**
* Default implementation of the {@link JsonSchemaValidator} interface. This class
* provides methods to validate structured content against a JSON schema. It uses the
Expand Down Expand Up @@ -52,9 +50,12 @@ public DefaultJsonSchemaValidator(ObjectMapper objectMapper) {

@Override
public ValidationResponse validate(Map<String, Object> schema, Map<String, Object> structuredContent) {

Assert.notNull(schema, "Schema must not be null");
Assert.notNull(structuredContent, "Structured content must not be null");
if (schema == null) {
throw new IllegalArgumentException("Schema must not be null");
}
if (structuredContent == null) {
throw new IllegalArgumentException("Structured content must not be null");
}

try {

Expand Down
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();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.modelcontextprotocol.json.jackson.JacksonMcpJsonMapperSupplier
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.modelcontextprotocol.json.schema.jackson.JacksonJsonSchemaValidatorSupplier
39 changes: 39 additions & 0 deletions mcp-json/pom.xml
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>
Copy link
Contributor

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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<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>
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;
}
});
}

}
Loading