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 @@ -1298,27 +1298,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 @@ -1363,7 +1342,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 @@ -1380,7 +1359,7 @@ public static class Builder {

private String description;

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

private Map<String, Object> outputSchema;

Expand All @@ -1403,13 +1382,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 @@ -1450,15 +1429,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 @@ -25,7 +25,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 All @@ -41,11 +40,7 @@ class AsyncToolSpecificationBuilderTest {
@Test
void builderShouldCreateValidAsyncToolSpecification() {

Tool tool = McpSchema.Tool.builder()
.name("test-tool")
.title("A test tool")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
Tool tool = McpSchema.Tool.builder().name("test-tool").title("A test tool").inputSchema(Map.of()).build();

McpServerFeatures.AsyncToolSpecification specification = McpServerFeatures.AsyncToolSpecification.builder()
.tool(tool)
Expand All @@ -68,11 +63,7 @@ void builderShouldThrowExceptionWhenToolIsNull() {

@Test
void builderShouldThrowExceptionWhenCallToolIsNull() {
Tool tool = McpSchema.Tool.builder()
.name("test-tool")
.title("A test tool")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
Tool tool = McpSchema.Tool.builder().name("test-tool").title("A test tool").inputSchema(Map.of()).build();

assertThatThrownBy(() -> McpServerFeatures.AsyncToolSpecification.builder().tool(tool).build())
.isInstanceOf(IllegalArgumentException.class)
Expand All @@ -81,11 +72,7 @@ void builderShouldThrowExceptionWhenCallToolIsNull() {

@Test
void builderShouldAllowMethodChaining() {
Tool tool = McpSchema.Tool.builder()
.name("test-tool")
.title("A test tool")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
Tool tool = McpSchema.Tool.builder().name("test-tool").title("A test tool").inputSchema(Map.of()).build();
McpServerFeatures.AsyncToolSpecification.Builder builder = McpServerFeatures.AsyncToolSpecification.builder();

// Then - verify method chaining returns the same builder instance
Expand All @@ -100,7 +87,7 @@ void builtSpecificationShouldExecuteCallToolCorrectly() {
Tool tool = McpSchema.Tool.builder()
.name("calculator")
.title("Simple calculator")
.inputSchema(EMPTY_JSON_SCHEMA)
.inputSchema(Map.of())
.build();
String expectedResult = "42";

Expand All @@ -124,11 +111,7 @@ void builtSpecificationShouldExecuteCallToolCorrectly() {

@Test
void fromSyncShouldConvertSyncToolSpecificationCorrectly() {
Tool tool = McpSchema.Tool.builder()
.name("sync-tool")
.title("A sync tool")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
Tool tool = McpSchema.Tool.builder().name("sync-tool").title("A sync tool").inputSchema(Map.of()).build();
String expectedResult = "sync result";

// Create a sync tool specification
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import org.junit.jupiter.api.Test;
import org.slf4j.LoggerFactory;

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 All @@ -38,7 +37,7 @@ class SyncToolSpecificationBuilderTest {
@Test
void builderShouldCreateValidSyncToolSpecification() {

Tool tool = Tool.builder().name("test-tool").title("A test tool").inputSchema(EMPTY_JSON_SCHEMA).build();
Tool tool = Tool.builder().name("test-tool").title("A test tool").inputSchema(Map.of()).build();

McpServerFeatures.SyncToolSpecification specification = McpServerFeatures.SyncToolSpecification.builder()
.tool(tool)
Expand All @@ -62,7 +61,7 @@ void builderShouldThrowExceptionWhenToolIsNull() {

@Test
void builderShouldThrowExceptionWhenCallToolIsNull() {
Tool tool = Tool.builder().name("test-tool").description("A test tool").inputSchema(EMPTY_JSON_SCHEMA).build();
Tool tool = Tool.builder().name("test-tool").description("A test tool").inputSchema(Map.of()).build();

assertThatThrownBy(() -> McpServerFeatures.SyncToolSpecification.builder().tool(tool).build())
.isInstanceOf(IllegalArgumentException.class)
Expand All @@ -71,7 +70,7 @@ void builderShouldThrowExceptionWhenCallToolIsNull() {

@Test
void builderShouldAllowMethodChaining() {
Tool tool = Tool.builder().name("test-tool").description("A test tool").inputSchema(EMPTY_JSON_SCHEMA).build();
Tool tool = Tool.builder().name("test-tool").description("A test tool").inputSchema(Map.of()).build();
McpServerFeatures.SyncToolSpecification.Builder builder = McpServerFeatures.SyncToolSpecification.builder();

// Then - verify method chaining returns the same builder instance
Expand All @@ -83,11 +82,7 @@ void builderShouldAllowMethodChaining() {

@Test
void builtSpecificationShouldExecuteCallToolCorrectly() {
Tool tool = Tool.builder()
.name("calculator")
.description("Simple calculator")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
Tool tool = Tool.builder().name("calculator").description("Simple calculator").inputSchema(Map.of()).build();
String expectedResult = "42";

McpServerFeatures.SyncToolSpecification specification = McpServerFeatures.SyncToolSpecification.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,4 @@ public final class ToolsUtils {
private ToolsUtils() {
}

public static final McpSchema.JsonSchema EMPTY_JSON_SCHEMA = new McpSchema.JsonSchema("object",
Collections.emptyMap(), null, null, null, null);

}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
import org.junit.jupiter.params.provider.ValueSource;
import reactor.core.publisher.Mono;

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 @@ -106,7 +105,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 @@ -156,7 +155,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 @@ -235,7 +234,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 @@ -310,7 +309,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 @@ -360,7 +359,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 @@ -406,7 +405,7 @@ void testCreateElicitationSuccess(String clientType) {
AtomicReference<McpSchema.ElicitResult> elicitResultRef = 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 @@ -465,7 +464,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 @@ -536,7 +535,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 @@ -634,7 +633,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 @@ -776,7 +775,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 @@ -827,11 +826,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 @@ -869,7 +864,7 @@ void testToolCallSuccessWithTranportContextExtraction(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 @@ -925,7 +920,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 @@ -991,11 +986,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 @@ -1046,7 +1037,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 @@ -1163,7 +1154,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 @@ -1321,7 +1312,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