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:
parent
d8959ef854
commit
80d0ad82a6
9 changed files with 2043 additions and 1421 deletions
|
|
@ -54,6 +54,7 @@ import org.junit.jupiter.params.provider.ValueSource;
|
|||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static io.modelcontextprotocol.utils.ToolsUtils.EMPTY_JSON_SCHEMA;
|
||||
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
|
||||
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.json;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
|
@ -81,7 +82,6 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
var server = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
|
||||
.requestTimeout(Duration.ofSeconds(1000))
|
||||
.build();
|
||||
|
||||
try (
|
||||
// Create client without sampling capabilities
|
||||
var client = clientBuilder.clientInfo(new McpSchema.Implementation("Sample " + "client", "0.0.0"))
|
||||
|
|
@ -106,7 +106,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
var clientBuilder = clientBuilders.get(clientType);
|
||||
|
||||
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(emptyJsonSchema).build())
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
|
||||
.callHandler((exchange, request) -> {
|
||||
return exchange.createMessage(mock(McpSchema.CreateMessageRequest.class))
|
||||
.then(Mono.just(mock(CallToolResult.class)));
|
||||
|
|
@ -155,7 +155,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
AtomicReference<CreateMessageResult> samplingResult = new AtomicReference<>();
|
||||
|
||||
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(emptyJsonSchema).build())
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
|
||||
.callHandler((exchange, request) -> {
|
||||
|
||||
var createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||
|
|
@ -233,7 +233,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
AtomicReference<CreateMessageResult> samplingResult = new AtomicReference<>();
|
||||
|
||||
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(emptyJsonSchema).build())
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
|
||||
.callHandler((exchange, request) -> {
|
||||
|
||||
var createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||
|
|
@ -307,7 +307,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
null);
|
||||
|
||||
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(emptyJsonSchema).build())
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
|
||||
.callHandler((exchange, request) -> {
|
||||
|
||||
var createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||
|
|
@ -357,7 +357,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
var clientBuilder = clientBuilders.get(clientType);
|
||||
|
||||
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(emptyJsonSchema).build())
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
|
||||
.callHandler((exchange, request) -> exchange.createElicitation(mock(ElicitRequest.class))
|
||||
.then(Mono.just(mock(CallToolResult.class))))
|
||||
.build();
|
||||
|
|
@ -400,7 +400,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
null);
|
||||
|
||||
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(emptyJsonSchema).build())
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
|
||||
.callHandler((exchange, request) -> {
|
||||
|
||||
var elicitationRequest = McpSchema.ElicitRequest.builder()
|
||||
|
|
@ -457,7 +457,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
AtomicReference<ElicitResult> resultRef = new AtomicReference<>();
|
||||
|
||||
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(emptyJsonSchema).build())
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
|
||||
.callHandler((exchange, request) -> {
|
||||
|
||||
var elicitationRequest = McpSchema.ElicitRequest.builder()
|
||||
|
|
@ -528,7 +528,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
AtomicReference<ElicitResult> resultRef = new AtomicReference<>();
|
||||
|
||||
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(emptyJsonSchema).build())
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
|
||||
.callHandler((exchange, request) -> {
|
||||
|
||||
var elicitationRequest = ElicitRequest.builder()
|
||||
|
|
@ -626,7 +626,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
var clientBuilder = clientBuilders.get(clientType);
|
||||
|
||||
McpServerFeatures.SyncToolSpecification tool = McpServerFeatures.SyncToolSpecification.builder()
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(emptyJsonSchema).build())
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
|
||||
.callHandler((exchange, request) -> {
|
||||
|
||||
exchange.listRoots(); // try to list roots
|
||||
|
|
@ -757,14 +757,6 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
// ---------------------------------------
|
||||
// Tools Tests
|
||||
// ---------------------------------------
|
||||
String emptyJsonSchema = """
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
""";
|
||||
|
||||
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||
@ValueSource(strings = { "httpclient", "webflux" })
|
||||
void testToolCallSuccess(String clientType) {
|
||||
|
|
@ -774,7 +766,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
var responseBodyIsNullOrBlank = new AtomicBoolean(false);
|
||||
var callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")), null);
|
||||
McpServerFeatures.SyncToolSpecification tool1 = McpServerFeatures.SyncToolSpecification.builder()
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(emptyJsonSchema).build())
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
|
||||
.callHandler((exchange, request) -> {
|
||||
|
||||
try {
|
||||
|
|
@ -828,7 +820,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
.tool(Tool.builder()
|
||||
.name("tool1")
|
||||
.description("tool1 description")
|
||||
.inputSchema(emptyJsonSchema)
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build())
|
||||
.callHandler((exchange, request) -> {
|
||||
// We trigger a timeout on blocking read, raising an exception
|
||||
|
|
@ -867,7 +859,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
var expectedCallResponse = new McpSchema.CallToolResult(
|
||||
List.of(new McpSchema.TextContent("CALL RESPONSE; ctx=value")), null);
|
||||
McpServerFeatures.SyncToolSpecification tool1 = McpServerFeatures.SyncToolSpecification.builder()
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(emptyJsonSchema).build())
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
|
||||
.callHandler((exchange, request) -> {
|
||||
|
||||
McpTransportContext transportContext = exchange.transportContext();
|
||||
|
|
@ -919,7 +911,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
|
||||
var callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")), null);
|
||||
McpServerFeatures.SyncToolSpecification tool1 = McpServerFeatures.SyncToolSpecification.builder()
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(emptyJsonSchema).build())
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
|
||||
.callHandler((exchange, request) -> {
|
||||
// perform a blocking call to a remote service
|
||||
try {
|
||||
|
|
@ -988,7 +980,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
.tool(Tool.builder()
|
||||
.name("tool2")
|
||||
.description("tool2 description")
|
||||
.inputSchema(emptyJsonSchema)
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build())
|
||||
.callHandler((exchange, request) -> callResponse)
|
||||
.build();
|
||||
|
|
@ -1040,7 +1032,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
.tool(Tool.builder()
|
||||
.name("logging-test")
|
||||
.description("Test logging notifications")
|
||||
.inputSchema(emptyJsonSchema)
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build())
|
||||
.callHandler((exchange, request) -> {
|
||||
|
||||
|
|
@ -1154,7 +1146,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
.tool(McpSchema.Tool.builder()
|
||||
.name("progress-test")
|
||||
.description("Test progress notifications")
|
||||
.inputSchema(emptyJsonSchema)
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build())
|
||||
.callHandler((exchange, request) -> {
|
||||
|
||||
|
|
@ -1308,7 +1300,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
|||
.tool(Tool.builder()
|
||||
.name("ping-async-test")
|
||||
.description("Test ping async behavior")
|
||||
.inputSchema(emptyJsonSchema)
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build())
|
||||
.callHandler((exchange, request) -> {
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import org.junit.jupiter.params.ParameterizedTest;
|
|||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import static io.modelcontextprotocol.utils.ToolsUtils.EMPTY_JSON_SCHEMA;
|
||||
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
|
||||
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.json;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
|
@ -74,15 +75,6 @@ public abstract class AbstractStatelessIntegrationTests {
|
|||
// ---------------------------------------
|
||||
// Tools Tests
|
||||
// ---------------------------------------
|
||||
|
||||
String emptyJsonSchema = """
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
""";
|
||||
|
||||
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||
@ValueSource(strings = { "httpclient", "webflux" })
|
||||
void testToolCallSuccess(String clientType) {
|
||||
|
|
@ -92,7 +84,7 @@ public abstract class AbstractStatelessIntegrationTests {
|
|||
var callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")), null);
|
||||
McpStatelessServerFeatures.SyncToolSpecification tool1 = McpStatelessServerFeatures.SyncToolSpecification
|
||||
.builder()
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(emptyJsonSchema).build())
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
|
||||
.callHandler((ctx, request) -> {
|
||||
|
||||
try {
|
||||
|
|
@ -145,7 +137,7 @@ public abstract class AbstractStatelessIntegrationTests {
|
|||
.tool(Tool.builder()
|
||||
.name("tool1")
|
||||
.description("tool1 description")
|
||||
.inputSchema(emptyJsonSchema)
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build())
|
||||
.callHandler((context, request) -> {
|
||||
// We trigger a timeout on blocking read, raising an exception
|
||||
|
|
@ -180,7 +172,7 @@ public abstract class AbstractStatelessIntegrationTests {
|
|||
var callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")), null);
|
||||
McpStatelessServerFeatures.SyncToolSpecification tool1 = McpStatelessServerFeatures.SyncToolSpecification
|
||||
.builder()
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(emptyJsonSchema).build())
|
||||
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
|
||||
.callHandler((ctx, request) -> {
|
||||
// perform a blocking call to a remote service
|
||||
try {
|
||||
|
|
@ -241,7 +233,7 @@ public abstract class AbstractStatelessIntegrationTests {
|
|||
.tool(Tool.builder()
|
||||
.name("tool2")
|
||||
.description("tool2 description")
|
||||
.inputSchema(emptyJsonSchema)
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build())
|
||||
.callHandler((exchange, request) -> callResponse)
|
||||
.build();
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import java.util.List;
|
|||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.modelcontextprotocol.json.McpJsonMapper;
|
||||
import io.modelcontextprotocol.json.TypeRef;
|
||||
import io.modelcontextprotocol.spec.McpClientTransport;
|
||||
import io.modelcontextprotocol.spec.McpSchema;
|
||||
import io.modelcontextprotocol.spec.McpSchema.JSONRPCNotification;
|
||||
|
|
@ -93,8 +93,8 @@ public class MockMcpTransport implements McpClientTransport, McpServerTransport
|
|||
}
|
||||
|
||||
@Override
|
||||
public <T> T unmarshalFrom(Object data, TypeReference<T> typeRef) {
|
||||
return new ObjectMapper().convertValue(data, typeRef);
|
||||
public <T> T unmarshalFrom(Object data, TypeRef<T> typeRef) {
|
||||
return McpJsonMapper.getDefault().convertValue(data, typeRef);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
package io.modelcontextprotocol.client;
|
||||
|
||||
import static io.modelcontextprotocol.utils.McpJsonMapperUtils.JSON_MAPPER;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
|
@ -22,8 +23,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import io.modelcontextprotocol.json.McpJsonMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
|
@ -176,7 +176,12 @@ public abstract class AbstractMcpAsyncClientTests {
|
|||
.consumeNextWith(result -> {
|
||||
assertThat(result.tools()).isNotNull();
|
||||
// Verify that the returned list is immutable
|
||||
assertThatThrownBy(() -> result.tools().add(new Tool("test", "test", "{\"type\":\"object\"}")))
|
||||
assertThatThrownBy(() -> result.tools()
|
||||
.add(Tool.builder()
|
||||
.name("test")
|
||||
.title("test")
|
||||
.inputSchema(JSON_MAPPER, "{\"type\":\"object\"}")
|
||||
.build()))
|
||||
.isInstanceOf(UnsupportedOperationException.class);
|
||||
})
|
||||
.verifyComplete();
|
||||
|
|
|
|||
|
|
@ -22,8 +22,6 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import org.junit.jupiter.params.provider.ValueSource;
|
|||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static io.modelcontextprotocol.utils.ToolsUtils.EMPTY_JSON_SCHEMA;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
|
@ -95,18 +96,10 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
// ---------------------------------------
|
||||
// Tools Tests
|
||||
// ---------------------------------------
|
||||
String emptyJsonSchema = """
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
""";
|
||||
|
||||
@Test
|
||||
@Deprecated
|
||||
void testAddTool() {
|
||||
Tool newTool = new McpSchema.Tool("new-tool", "New test tool", emptyJsonSchema);
|
||||
Tool newTool = Tool.builder().name("new-tool").title("New test tool").inputSchema(EMPTY_JSON_SCHEMA).build();
|
||||
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
.build();
|
||||
|
|
@ -120,7 +113,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
|
||||
@Test
|
||||
void testAddToolCall() {
|
||||
Tool newTool = new McpSchema.Tool("new-tool", "New test tool", emptyJsonSchema);
|
||||
Tool newTool = Tool.builder().name("new-tool").title("New test tool").inputSchema(EMPTY_JSON_SCHEMA).build();
|
||||
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
.build();
|
||||
|
|
@ -136,7 +129,11 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
@Test
|
||||
@Deprecated
|
||||
void testAddDuplicateTool() {
|
||||
Tool duplicateTool = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", emptyJsonSchema);
|
||||
Tool duplicateTool = McpSchema.Tool.builder()
|
||||
.name(TEST_TOOL_NAME)
|
||||
.title("Duplicate tool")
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build();
|
||||
|
||||
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
|
|
@ -156,7 +153,11 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
|
||||
@Test
|
||||
void testAddDuplicateToolCall() {
|
||||
Tool duplicateTool = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", emptyJsonSchema);
|
||||
Tool duplicateTool = McpSchema.Tool.builder()
|
||||
.name(TEST_TOOL_NAME)
|
||||
.title("Duplicate tool")
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build();
|
||||
|
||||
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
|
|
@ -176,8 +177,11 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
|
||||
@Test
|
||||
void testDuplicateToolCallDuringBuilding() {
|
||||
Tool duplicateTool = new Tool("duplicate-build-toolcall", "Duplicate toolcall during building",
|
||||
emptyJsonSchema);
|
||||
Tool duplicateTool = McpSchema.Tool.builder()
|
||||
.name("duplicate-build-toolcall")
|
||||
.title("Duplicate toolcall during building")
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
|
|
@ -189,7 +193,11 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
|
||||
@Test
|
||||
void testDuplicateToolsInBatchListRegistration() {
|
||||
Tool duplicateTool = new Tool("batch-list-tool", "Duplicate tool in batch list", emptyJsonSchema);
|
||||
Tool duplicateTool = McpSchema.Tool.builder()
|
||||
.name("batch-list-tool")
|
||||
.title("Duplicate tool in batch list")
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build();
|
||||
List<McpServerFeatures.AsyncToolSpecification> specs = List.of(
|
||||
McpServerFeatures.AsyncToolSpecification.builder()
|
||||
.tool(duplicateTool)
|
||||
|
|
@ -210,7 +218,11 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
|
||||
@Test
|
||||
void testDuplicateToolsInBatchVarargsRegistration() {
|
||||
Tool duplicateTool = new Tool("batch-varargs-tool", "Duplicate tool in batch varargs", emptyJsonSchema);
|
||||
Tool duplicateTool = McpSchema.Tool.builder()
|
||||
.name("batch-varargs-tool")
|
||||
.title("Duplicate tool in batch varargs")
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
|
|
@ -229,8 +241,11 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
|
||||
@Test
|
||||
void testRemoveTool() {
|
||||
Tool too = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", emptyJsonSchema);
|
||||
|
||||
Tool too = McpSchema.Tool.builder()
|
||||
.name(TEST_TOOL_NAME)
|
||||
.title("Duplicate tool")
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build();
|
||||
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
.toolCall(too, (exchange, request) -> Mono.just(new CallToolResult(List.of(), false)))
|
||||
|
|
@ -256,7 +271,11 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
|
||||
@Test
|
||||
void testNotifyToolsListChanged() {
|
||||
Tool too = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", emptyJsonSchema);
|
||||
Tool too = McpSchema.Tool.builder()
|
||||
.name(TEST_TOOL_NAME)
|
||||
.title("Duplicate tool")
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build();
|
||||
|
||||
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
package io.modelcontextprotocol.server;
|
||||
|
||||
import static io.modelcontextprotocol.utils.ToolsUtils.EMPTY_JSON_SCHEMA;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
|
@ -99,15 +100,6 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
// ---------------------------------------
|
||||
// Tools Tests
|
||||
// ---------------------------------------
|
||||
|
||||
String emptyJsonSchema = """
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
""";
|
||||
|
||||
@Test
|
||||
@Deprecated
|
||||
void testAddTool() {
|
||||
|
|
@ -115,7 +107,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
.build();
|
||||
|
||||
Tool newTool = new McpSchema.Tool("new-tool", "New test tool", emptyJsonSchema);
|
||||
Tool newTool = Tool.builder().name("new-tool").title("New test tool").inputSchema(EMPTY_JSON_SCHEMA).build();
|
||||
assertThatCode(() -> mcpSyncServer.addTool(new McpServerFeatures.SyncToolSpecification(newTool,
|
||||
(exchange, args) -> new CallToolResult(List.of(), false))))
|
||||
.doesNotThrowAnyException();
|
||||
|
|
@ -129,7 +121,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
.build();
|
||||
|
||||
Tool newTool = new McpSchema.Tool("new-tool", "New test tool", emptyJsonSchema);
|
||||
Tool newTool = Tool.builder().name("new-tool").title("New test tool").inputSchema(EMPTY_JSON_SCHEMA).build();
|
||||
assertThatCode(() -> mcpSyncServer.addTool(McpServerFeatures.SyncToolSpecification.builder()
|
||||
.tool(newTool)
|
||||
.callHandler((exchange, request) -> new CallToolResult(List.of(), false))
|
||||
|
|
@ -141,7 +133,11 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
@Test
|
||||
@Deprecated
|
||||
void testAddDuplicateTool() {
|
||||
Tool duplicateTool = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", emptyJsonSchema);
|
||||
Tool duplicateTool = Tool.builder()
|
||||
.name(TEST_TOOL_NAME)
|
||||
.title("Duplicate tool")
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build();
|
||||
|
||||
var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
|
|
@ -158,7 +154,11 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
|
||||
@Test
|
||||
void testAddDuplicateToolCall() {
|
||||
Tool duplicateTool = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", emptyJsonSchema);
|
||||
Tool duplicateTool = Tool.builder()
|
||||
.name(TEST_TOOL_NAME)
|
||||
.title("Duplicate tool")
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build();
|
||||
|
||||
var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
|
|
@ -176,8 +176,11 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
|
||||
@Test
|
||||
void testDuplicateToolCallDuringBuilding() {
|
||||
Tool duplicateTool = new Tool("duplicate-build-toolcall", "Duplicate toolcall during building",
|
||||
emptyJsonSchema);
|
||||
Tool duplicateTool = Tool.builder()
|
||||
.name("duplicate-build-toolcall")
|
||||
.title("Duplicate toolcall during building")
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> prepareSyncServerBuilder().serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
|
|
@ -189,7 +192,11 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
|
||||
@Test
|
||||
void testDuplicateToolsInBatchListRegistration() {
|
||||
Tool duplicateTool = new Tool("batch-list-tool", "Duplicate tool in batch list", emptyJsonSchema);
|
||||
Tool duplicateTool = Tool.builder()
|
||||
.name("batch-list-tool")
|
||||
.title("Duplicate tool in batch list")
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build();
|
||||
List<McpServerFeatures.SyncToolSpecification> specs = List.of(
|
||||
McpServerFeatures.SyncToolSpecification.builder()
|
||||
.tool(duplicateTool)
|
||||
|
|
@ -210,7 +217,11 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
|
||||
@Test
|
||||
void testDuplicateToolsInBatchVarargsRegistration() {
|
||||
Tool duplicateTool = new Tool("batch-varargs-tool", "Duplicate tool in batch varargs", emptyJsonSchema);
|
||||
Tool duplicateTool = Tool.builder()
|
||||
.name("batch-varargs-tool")
|
||||
.title("Duplicate tool in batch varargs")
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> prepareSyncServerBuilder().serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
|
|
@ -229,7 +240,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
|
||||
@Test
|
||||
void testRemoveTool() {
|
||||
Tool tool = new McpSchema.Tool(TEST_TOOL_NAME, "Test tool", emptyJsonSchema);
|
||||
Tool tool = Tool.builder().name(TEST_TOOL_NAME).title("Test tool").inputSchema(EMPTY_JSON_SCHEMA).build();
|
||||
|
||||
var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package io.modelcontextprotocol.utils;
|
||||
|
||||
import io.modelcontextprotocol.json.McpJsonMapper;
|
||||
|
||||
public final class McpJsonMapperUtils {
|
||||
|
||||
private McpJsonMapperUtils() {
|
||||
}
|
||||
|
||||
public static final McpJsonMapper JSON_MAPPER = McpJsonMapper.getDefault();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package io.modelcontextprotocol.utils;
|
||||
|
||||
import io.modelcontextprotocol.spec.McpSchema;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public final class ToolsUtils {
|
||||
|
||||
private ToolsUtils() {
|
||||
}
|
||||
|
||||
public static final McpSchema.JsonSchema EMPTY_JSON_SCHEMA = new McpSchema.JsonSchema("object",
|
||||
Collections.emptyMap(), null, null, null, null);
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue