feat(schema): add support for JSON Schema $defs and definitions (#146)
Added support for $defs and definitions properties in JsonSchema record to handle JSON Schema references properly. Added tests to verify both formats work correctly. The JsonSchema test approach uses serialization/deserialization round-trip validation instead of property-by-property assertions. This makes tests more maintainable and less likely to break when new properties are added. 🤖 Generated with [Claude Code](https://claude.ai/code) --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
86e3e9048f
commit
f70b98b4b4
2 changed files with 132 additions and 1 deletions
|
|
@ -703,7 +703,9 @@ public final class McpSchema {
|
|||
@JsonProperty("type") String type,
|
||||
@JsonProperty("properties") Map<String, Object> properties,
|
||||
@JsonProperty("required") List<String> required,
|
||||
@JsonProperty("additionalProperties") Boolean additionalProperties) {
|
||||
@JsonProperty("additionalProperties") Boolean additionalProperties,
|
||||
@JsonProperty("$defs") Map<String, Object> defs,
|
||||
@JsonProperty("definitions") Map<String, Object> definitions) {
|
||||
} // @formatter:on
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
|
||||
import io.modelcontextprotocol.spec.McpSchema.TextResourceContents;
|
||||
|
|
@ -449,6 +450,92 @@ public class McpSchemaTests {
|
|||
|
||||
// Tool Tests
|
||||
|
||||
@Test
|
||||
void testJsonSchema() throws Exception {
|
||||
String schemaJson = """
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"address": {
|
||||
"$ref": "#/$defs/Address"
|
||||
}
|
||||
},
|
||||
"required": ["name"],
|
||||
"$defs": {
|
||||
"Address": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"street": {"type": "string"},
|
||||
"city": {"type": "string"}
|
||||
},
|
||||
"required": ["street", "city"]
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
// Deserialize the original string to a JsonSchema object
|
||||
McpSchema.JsonSchema schema = mapper.readValue(schemaJson, McpSchema.JsonSchema.class);
|
||||
|
||||
// Serialize the object back to a string
|
||||
String serialized = mapper.writeValueAsString(schema);
|
||||
|
||||
// Deserialize again
|
||||
McpSchema.JsonSchema deserialized = mapper.readValue(serialized, McpSchema.JsonSchema.class);
|
||||
|
||||
// Serialize one more time and compare with the first serialization
|
||||
String serializedAgain = mapper.writeValueAsString(deserialized);
|
||||
|
||||
// The two serialized strings should be the same
|
||||
assertThatJson(serializedAgain).when(Option.IGNORING_ARRAY_ORDER).isEqualTo(json(serialized));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testJsonSchemaWithDefinitions() throws Exception {
|
||||
String schemaJson = """
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"address": {
|
||||
"$ref": "#/definitions/Address"
|
||||
}
|
||||
},
|
||||
"required": ["name"],
|
||||
"definitions": {
|
||||
"Address": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"street": {"type": "string"},
|
||||
"city": {"type": "string"}
|
||||
},
|
||||
"required": ["street", "city"]
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
// Deserialize the original string to a JsonSchema object
|
||||
McpSchema.JsonSchema schema = mapper.readValue(schemaJson, McpSchema.JsonSchema.class);
|
||||
|
||||
// Serialize the object back to a string
|
||||
String serialized = mapper.writeValueAsString(schema);
|
||||
|
||||
// Deserialize again
|
||||
McpSchema.JsonSchema deserialized = mapper.readValue(serialized, McpSchema.JsonSchema.class);
|
||||
|
||||
// Serialize one more time and compare with the first serialization
|
||||
String serializedAgain = mapper.writeValueAsString(deserialized);
|
||||
|
||||
// The two serialized strings should be the same
|
||||
assertThatJson(serializedAgain).when(Option.IGNORING_ARRAY_ORDER).isEqualTo(json(serialized));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTool() throws Exception {
|
||||
String schemaJson = """
|
||||
|
|
@ -477,6 +564,48 @@ public class McpSchemaTests {
|
|||
{"name":"test-tool","description":"A test tool","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"number"}},"required":["name"]}}"""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToolWithComplexSchema() throws Exception {
|
||||
String complexSchemaJson = """
|
||||
{
|
||||
"type": "object",
|
||||
"$defs": {
|
||||
"Address": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"street": {"type": "string"},
|
||||
"city": {"type": "string"}
|
||||
},
|
||||
"required": ["street", "city"]
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"shippingAddress": {"$ref": "#/$defs/Address"}
|
||||
},
|
||||
"required": ["name", "shippingAddress"]
|
||||
}
|
||||
""";
|
||||
|
||||
McpSchema.Tool tool = new McpSchema.Tool("addressTool", "Handles addresses", complexSchemaJson);
|
||||
|
||||
// Serialize the tool to a string
|
||||
String serialized = mapper.writeValueAsString(tool);
|
||||
|
||||
// Deserialize back to a Tool object
|
||||
McpSchema.Tool deserializedTool = mapper.readValue(serialized, McpSchema.Tool.class);
|
||||
|
||||
// Serialize again and compare with first serialization
|
||||
String serializedAgain = mapper.writeValueAsString(deserializedTool);
|
||||
|
||||
// The two serialized strings should be the same
|
||||
assertThatJson(serializedAgain).when(Option.IGNORING_ARRAY_ORDER).isEqualTo(json(serialized));
|
||||
|
||||
// Just verify the basic structure was preserved
|
||||
assertThat(deserializedTool.inputSchema().defs()).isNotNull();
|
||||
assertThat(deserializedTool.inputSchema().defs()).containsKey("Address");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCallToolRequest() throws Exception {
|
||||
Map<String, Object> arguments = new HashMap<>();
|
||||
|
|
|
|||
Loading…
Reference in a new issue