Skip to content
Open
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
38 changes: 4 additions & 34 deletions mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -1330,27 +1330,6 @@ public ListToolsResult(List<Tool> tools, String nextCursor) {
}
}

/**
* A JSON Schema object that describes the expected structure of arguments or output.
*
* @param type The type of the schema (e.g., "object")
* @param properties The properties of the schema object
* @param required List of required property names
* @param additionalProperties Whether additional properties are allowed
* @param defs Schema definitions using the newer $defs keyword
* @param definitions Schema definitions using the legacy definitions keyword
*/
@JsonInclude(JsonInclude.Include.NON_ABSENT)
@JsonIgnoreProperties(ignoreUnknown = true)
public record JsonSchema( // @formatter:off
@JsonProperty("type") String type,
@JsonProperty("properties") Map<String, Object> properties,
@JsonProperty("required") List<String> required,
@JsonProperty("additionalProperties") Boolean additionalProperties,
@JsonProperty("$defs") Map<String, Object> defs,
@JsonProperty("definitions") Map<String, Object> definitions) { // @formatter:on
}

/**
* Additional properties describing a Tool to clients.
*
Expand Down Expand Up @@ -1395,7 +1374,7 @@ public record Tool( // @formatter:off
@JsonProperty("name") String name,
@JsonProperty("title") String title,
@JsonProperty("description") String description,
@JsonProperty("inputSchema") JsonSchema inputSchema,
@JsonProperty("inputSchema") Map<String, Object> inputSchema,
@JsonProperty("outputSchema") Map<String, Object> outputSchema,
@JsonProperty("annotations") ToolAnnotations annotations,
@JsonProperty("_meta") Map<String, Object> meta) { // @formatter:on
Expand All @@ -1412,7 +1391,7 @@ public static class Builder {

private String description;

private JsonSchema inputSchema;
private Map<String, Object> inputSchema;

private Map<String, Object> outputSchema;

Expand All @@ -1435,13 +1414,13 @@ public Builder description(String description) {
return this;
}

public Builder inputSchema(JsonSchema inputSchema) {
public Builder inputSchema(Map<String, Object> inputSchema) {
this.inputSchema = inputSchema;
return this;
}

public Builder inputSchema(McpJsonMapper jsonMapper, String inputSchema) {
this.inputSchema = parseSchema(jsonMapper, inputSchema);
this.inputSchema = schemaToMap(jsonMapper, inputSchema);
return this;
}

Expand Down Expand Up @@ -1482,15 +1461,6 @@ private static Map<String, Object> schemaToMap(McpJsonMapper jsonMapper, String
}
}

private static JsonSchema parseSchema(McpJsonMapper jsonMapper, String schema) {
try {
return jsonMapper.readValue(schema, JsonSchema.class);
}
catch (IOException e) {
throw new IllegalArgumentException("Invalid schema: " + schema, e);
}
}

/**
* Used by the client to call a tool provided by the server.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,10 @@ private McpClientTransport createMockTransportForToolValidation(boolean hasOutpu
Map<String, Object> inputSchemaMap = Map.of("type", "object", "properties",
Map.of("expression", Map.of("type", "string")), "required", List.of("expression"));

McpSchema.JsonSchema inputSchema = new McpSchema.JsonSchema("object", inputSchemaMap, null, null, null, null);
McpSchema.Tool.Builder toolBuilder = McpSchema.Tool.builder()
.name("calculator")
.description("Performs mathematical calculations")
.inputSchema(inputSchema);
.inputSchema(inputSchemaMap);

if (hasOutputSchema) {
Map<String, Object> outputSchema = Map.of("type", "object", "properties",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import java.time.Duration;
import java.util.List;
import java.util.Map;

import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
Expand All @@ -23,7 +24,6 @@
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import static io.modelcontextprotocol.util.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;
Expand Down Expand Up @@ -94,11 +94,7 @@ void testImmediateClose() {
@Test
@Deprecated
void testAddTool() {
Tool newTool = McpSchema.Tool.builder()
.name("new-tool")
.title("New test tool")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
Tool newTool = McpSchema.Tool.builder().name("new-tool").title("New test tool").inputSchema(Map.of()).build();
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
.build();
Expand All @@ -113,11 +109,7 @@ void testAddTool() {

@Test
void testAddToolCall() {
Tool newTool = McpSchema.Tool.builder()
.name("new-tool")
.title("New test tool")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
Tool newTool = McpSchema.Tool.builder().name("new-tool").title("New test tool").inputSchema(Map.of()).build();

var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
Expand All @@ -138,7 +130,7 @@ void testAddDuplicateTool() {
Tool duplicateTool = McpSchema.Tool.builder()
.name(TEST_TOOL_NAME)
.title("Duplicate tool")
.inputSchema(EMPTY_JSON_SCHEMA)
.inputSchema(Map.of())
.build();

var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
Expand All @@ -160,7 +152,7 @@ void testAddDuplicateToolCall() {
Tool duplicateTool = McpSchema.Tool.builder()
.name(TEST_TOOL_NAME)
.title("Duplicate tool")
.inputSchema(EMPTY_JSON_SCHEMA)
.inputSchema(Map.of())
.build();

var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
Expand All @@ -184,7 +176,7 @@ void testDuplicateToolCallDuringBuilding() {
Tool duplicateTool = McpSchema.Tool.builder()
.name("duplicate-build-toolcall")
.title("Duplicate toolcall during building")
.inputSchema(EMPTY_JSON_SCHEMA)
.inputSchema(Map.of())
.build();

assertThatThrownBy(() -> prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
Expand All @@ -204,7 +196,7 @@ void testDuplicateToolsInBatchListRegistration() {
Tool duplicateTool = McpSchema.Tool.builder()
.name("batch-list-tool")
.title("Duplicate tool in batch list")
.inputSchema(EMPTY_JSON_SCHEMA)
.inputSchema(Map.of())
.build();

List<McpServerFeatures.AsyncToolSpecification> specs = List.of(
Expand Down Expand Up @@ -232,7 +224,7 @@ void testDuplicateToolsInBatchVarargsRegistration() {
Tool duplicateTool = McpSchema.Tool.builder()
.name("batch-varargs-tool")
.title("Duplicate tool in batch varargs")
.inputSchema(EMPTY_JSON_SCHEMA)
.inputSchema(Map.of())
.build();

assertThatThrownBy(() -> prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
Expand All @@ -254,11 +246,7 @@ void testDuplicateToolsInBatchVarargsRegistration() {

@Test
void testRemoveTool() {
Tool too = McpSchema.Tool.builder()
.name(TEST_TOOL_NAME)
.title("Duplicate tool")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
Tool too = McpSchema.Tool.builder().name(TEST_TOOL_NAME).title("Duplicate tool").inputSchema(Map.of()).build();

var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
Expand All @@ -285,11 +273,7 @@ void testRemoveNonexistentTool() {

@Test
void testNotifyToolsListChanged() {
Tool too = McpSchema.Tool.builder()
.name(TEST_TOOL_NAME)
.title("Duplicate tool")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
Tool too = McpSchema.Tool.builder().name(TEST_TOOL_NAME).title("Duplicate tool").inputSchema(Map.of()).build();

var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import static io.modelcontextprotocol.util.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;
Expand Down Expand Up @@ -103,7 +102,7 @@ void testCreateMessageWithoutSamplingCapabilities(String clientType) {
var clientBuilder = clientBuilders.get(clientType);

McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(Map.of()).build())
.callHandler((exchange, request) -> {
return exchange.createMessage(mock(McpSchema.CreateMessageRequest.class))
.then(Mono.just(mock(CallToolResult.class)));
Expand Down Expand Up @@ -153,7 +152,7 @@ void testCreateMessageSuccess(String clientType) {
AtomicReference<CreateMessageResult> samplingResult = new AtomicReference<>();

McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(Map.of()).build())
.callHandler((exchange, request) -> {

var createMessageRequest = McpSchema.CreateMessageRequest.builder()
Expand Down Expand Up @@ -232,7 +231,7 @@ void testCreateMessageWithRequestTimeoutSuccess(String clientType) throws Interr
AtomicReference<CreateMessageResult> samplingResult = new AtomicReference<>();

McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(Map.of()).build())
.callHandler((exchange, request) -> {

var createMessageRequest = McpSchema.CreateMessageRequest.builder()
Expand Down Expand Up @@ -307,7 +306,7 @@ void testCreateMessageWithRequestTimeoutFail(String clientType) throws Interrupt
.build();

McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(Map.of()).build())
.callHandler((exchange, request) -> {

var createMessageRequest = McpSchema.CreateMessageRequest.builder()
Expand Down Expand Up @@ -357,7 +356,7 @@ void testCreateElicitationWithoutElicitationCapabilities(String clientType) {
var clientBuilder = clientBuilders.get(clientType);

McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(Map.of()).build())
.callHandler((exchange, request) -> exchange.createElicitation(mock(ElicitRequest.class))
.then(Mono.just(mock(CallToolResult.class))))
.build();
Expand Down Expand Up @@ -401,7 +400,7 @@ void testCreateElicitationSuccess(String clientType) {
.build();

McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(Map.of()).build())
.callHandler((exchange, request) -> {

var elicitationRequest = McpSchema.ElicitRequest.builder()
Expand Down Expand Up @@ -459,7 +458,7 @@ void testCreateElicitationWithRequestTimeoutSuccess(String clientType) {
AtomicReference<ElicitResult> resultRef = new AtomicReference<>();

McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(Map.of()).build())
.callHandler((exchange, request) -> {

var elicitationRequest = McpSchema.ElicitRequest.builder()
Expand Down Expand Up @@ -530,7 +529,7 @@ void testCreateElicitationWithRequestTimeoutFail(String clientType) {
AtomicReference<ElicitResult> resultRef = new AtomicReference<>();

McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(Map.of()).build())
.callHandler((exchange, request) -> {

var elicitationRequest = ElicitRequest.builder()
Expand Down Expand Up @@ -628,7 +627,7 @@ void testRootsWithoutCapability(String clientType) {
var clientBuilder = clientBuilders.get(clientType);

McpServerFeatures.SyncToolSpecification tool = McpServerFeatures.SyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(Map.of()).build())
.callHandler((exchange, request) -> {

exchange.listRoots(); // try to list roots
Expand Down Expand Up @@ -770,7 +769,7 @@ void testToolCallSuccess(String clientType) {
.addContent(new McpSchema.TextContent("CALL RESPONSE; ctx=importantValue"))
.build();
McpServerFeatures.SyncToolSpecification tool1 = McpServerFeatures.SyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(Map.of()).build())
.callHandler((exchange, request) -> {

try {
Expand Down Expand Up @@ -821,11 +820,7 @@ void testThrowingToolCallIsCaughtBeforeTimeout(String clientType) {
McpSyncServer mcpServer = prepareSyncServerBuilder()
.capabilities(ServerCapabilities.builder().tools(true).build())
.tools(McpServerFeatures.SyncToolSpecification.builder()
.tool(Tool.builder()
.name("tool1")
.description("tool1 description")
.inputSchema(EMPTY_JSON_SCHEMA)
.build())
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(Map.of()).build())
.callHandler((exchange, request) -> {
// We trigger a timeout on blocking read, raising an exception
Mono.never().block(Duration.ofSeconds(1));
Expand Down Expand Up @@ -863,7 +858,7 @@ void testToolCallSuccessWithTransportContextExtraction(String clientType) {
.addContent(new McpSchema.TextContent("CALL RESPONSE; ctx=value"))
.build();
McpServerFeatures.SyncToolSpecification tool1 = McpServerFeatures.SyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(Map.of()).build())
.callHandler((exchange, request) -> {

McpTransportContext transportContext = exchange.transportContext();
Expand Down Expand Up @@ -919,7 +914,7 @@ void testToolListChangeHandlingSuccess(String clientType) {
.build();

McpServerFeatures.SyncToolSpecification tool1 = McpServerFeatures.SyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(Map.of()).build())
.callHandler((exchange, request) -> {
// perform a blocking call to a remote service
try {
Expand Down Expand Up @@ -985,11 +980,7 @@ void testToolListChangeHandlingSuccess(String clientType) {

// Add a new tool
McpServerFeatures.SyncToolSpecification tool2 = McpServerFeatures.SyncToolSpecification.builder()
.tool(Tool.builder()
.name("tool2")
.description("tool2 description")
.inputSchema(EMPTY_JSON_SCHEMA)
.build())
.tool(Tool.builder().name("tool2").description("tool2 description").inputSchema(Map.of()).build())
.callHandler((exchange, request) -> callResponse)
.build();

Expand Down Expand Up @@ -1040,7 +1031,7 @@ void testLoggingNotification(String clientType) throws InterruptedException {
.tool(Tool.builder()
.name("logging-test")
.description("Test logging notifications")
.inputSchema(EMPTY_JSON_SCHEMA)
.inputSchema(Map.of())
.build())
.callHandler((exchange, request) -> {

Expand Down Expand Up @@ -1157,7 +1148,7 @@ void testProgressNotification(String clientType) throws InterruptedException {
.tool(McpSchema.Tool.builder()
.name("progress-test")
.description("Test progress notifications")
.inputSchema(EMPTY_JSON_SCHEMA)
.inputSchema(Map.of())
.build())
.callHandler((exchange, request) -> {

Expand Down Expand Up @@ -1315,7 +1306,7 @@ void testPingSuccess(String clientType) {
.tool(Tool.builder()
.name("ping-async-test")
.description("Test ping async behavior")
.inputSchema(EMPTY_JSON_SCHEMA)
.inputSchema(Map.of())
.build())
.callHandler((exchange, request) -> {

Expand Down
Loading