diff --git a/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java b/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java index e21d53c80..18b4d7a7c 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -738,6 +738,17 @@ public record JsonSchema( // @formatter:off @JsonProperty("definitions") Map definitions) { } // @formatter:on + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonIgnoreProperties(ignoreUnknown = true) + public record ToolAnnotations( // @formatter:off + @JsonProperty("title") String title, + @JsonProperty("readOnlyHint") Boolean readOnlyHint, + @JsonProperty("destructiveHint") Boolean destructiveHint, + @JsonProperty("idempotentHint") Boolean idempotentHint, + @JsonProperty("openWorldHint") Boolean openWorldHint, + @JsonProperty("returnDirect") Boolean returnDirect) { + } // @formatter:on + /** * Represents a tool that the server provides. Tools enable servers to expose * executable functionality to the system. Through these tools, you can interact with @@ -749,17 +760,23 @@ public record JsonSchema( // @formatter:off * used by clients to improve the LLM's understanding of available tools. * @param inputSchema A JSON Schema object that describes the expected structure of * the arguments when calling this tool. This allows clients to validate tool + * @param annotations Additional properties describing a Tool to clients. * arguments before sending them to the server. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record Tool( // @formatter:off - @JsonProperty("name") String name, - @JsonProperty("description") String description, - @JsonProperty("inputSchema") JsonSchema inputSchema) { + @JsonProperty("name") String name, + @JsonProperty("description") String description, + @JsonProperty("inputSchema") JsonSchema inputSchema, + @JsonProperty("annotations") ToolAnnotations annotations) { public Tool(String name, String description, String schema) { - this(name, description, parseSchema(schema)); + this(name, description, parseSchema(schema), null); + } + + public Tool(String name, String description, String schema, ToolAnnotations annotations) { + this(name, description, parseSchema(schema), annotations); } } // @formatter:on diff --git a/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java b/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java index 99015d8c4..0c06b27f3 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java @@ -606,6 +606,43 @@ void testToolWithComplexSchema() throws Exception { assertThat(deserializedTool.inputSchema().defs()).containsKey("Address"); } + @Test + void testToolWithAnnotations() throws Exception { + String schemaJson = """ + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "number" + } + }, + "required": ["name"] + } + """; + McpSchema.ToolAnnotations annotations = new McpSchema.ToolAnnotations( + "A test tool", + false, + false, + false, + false, + false + ); + + McpSchema.Tool tool = new McpSchema.Tool("test-tool", "A test tool", schemaJson, annotations); + + String value = mapper.writeValueAsString(tool); + assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) + .when(Option.IGNORING_EXTRA_ARRAY_ITEMS) + .isObject() + .isEqualTo( + json(""" + {"name":"test-tool","description":"A test tool","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"number"}},"required":["name"]},"annotations":{"title":"A test tool","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false,"returnDirect":false}}""")); + } + + @Test void testCallToolRequest() throws Exception { Map arguments = new HashMap<>();