Skip to content

Enhance McpSchema.JsonSchema to Support Top-Level $defs for Tool Input Schemas #145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
arcaputo3 opened this issue Apr 11, 2025 · 4 comments

Comments

@arcaputo3
Copy link
Contributor

Expected Behavior

The McpSchema.Tool record should be able to represent the full structure of a JSON Schema provided for inputSchema, including top-level definitions specified using the $defs (or older definitions) keyword. This would allow server implementers to define complex, reusable types within the schema itself and use $ref within the properties to refer to them.

Ideally, the McpSchema.JsonSchema record would be enhanced, or an alternative mechanism provided, to retain this information.

Example of desired outcome:

// Hypothetical enhanced JsonSchema record
public record JsonSchema(
    @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 // Added field for definitions
) { }

// Or potentially keep JsonSchema simple and parse into a more generic Map
// Tool constructor might change:
public Tool(String name, String description, Map<String, Object> fullInputSchema) {
    // ... internal handling ...
}

// Example usage when creating a Tool:
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"]
}
""";

// Current SDK constructor loses $defs:
// McpSchema.Tool tool = new McpSchema.Tool("addressTool", "Handles addresses", complexSchemaJson);
// tool.inputSchema() would NOT contain the $defs section.

// Desired behavior:
// Somehow create a Tool object where the full structure, including $defs, is preserved
// and potentially accessible, allowing for more robust schema validation or generation
// on the client or server side based on the full definition.

Current Behavior

Currently, the McpSchema.JsonSchema record only defines fields for type, properties, required, and additionalProperties.

@JsonInclude(JsonInclude.Include.NON_ABSENT)
@JsonIgnoreProperties(ignoreUnknown = true)
public record JsonSchema(
    @JsonProperty("type") String type,
    @JsonProperty("properties") Map<String, Object> properties,
    @JsonProperty("required") List<String> required,
    @JsonProperty("additionalProperties") Boolean additionalProperties
) { }

When a JSON schema string is provided to the McpSchema.Tool(String name, String description, String schema) constructor, the internal parseSchema method deserializes this string directly into the McpSchema.JsonSchema record using Jackson (OBJECT_MAPPER.readValue(schema, JsonSchema.class)).

Due to the limited fields in the JsonSchema record and the @JsonIgnoreProperties(ignoreUnknown = true) annotation, any top-level properties in the JSON string that do not match the record's fields, specifically $defs (or definitions), are ignored and discarded during deserialization.

As a result, it's impossible to represent or convey tool input schemas that rely on internal definitions using $defs through the current McpSchema.Tool structure. The structural information about reusable types defined in $defs is lost.

Context

We are developing an MCP server using the Java SDK where our tools accept complex input structures. To promote consistency and avoid repetition, we want to define reusable data types within the tool's inputSchema using the standard JSON Schema $defs keyword and reference them using $ref within the properties.

The current limitation prevents us from accurately representing these schemas within the McpSchema.Tool object. This hinders:

  1. Client-side Validation: Clients receiving the Tool definition cannot perform complete validation if the schema relies on $defs that are missing.
  2. Schema Clarity: The schema conveyed via MCP doesn't fully represent the intended structure.
  3. Code Generation: Tooling that might generate client-side code or documentation based on the inputSchema will lack the necessary definitions.

Alternatives Considered:

  • External References: Using $ref to point to external URIs where the full schema (including definitions) is hosted. This adds complexity (hosting schemas) and potential latency.
  • Inlining Definitions: Repeating the full definition of a reusable type everywhere it's used within the properties. This violates the DRY principle and makes schemas verbose and hard to maintain.
  • Flattening Schemas: Pre-processing schemas to resolve all $refs and remove $defs before passing the string to the Tool constructor. This requires an external JSON Schema processing library and adds build complexity.

Workarounds:

  • The primary workaround is schema flattening/pre-processing before creating the Tool object, which is cumbersome.

Adding support for $defs directly within the SDK's schema representation would significantly improve the ability to define and communicate complex tool interfaces according to standard JSON Schema practices.

@arcaputo3
Copy link
Contributor Author

Proposed feature: #146

@tzolov
Copy link
Contributor

tzolov commented Apr 15, 2025

Thanks for the input @arcaputo3,

Thinking about it wouldn't it be simpler to replace the JsonSchema record by plain a Map<String, Object>?

@arcaputo3
Copy link
Contributor Author

Thanks for the reply! I would lean in the direction of explicitness / type safety but perhaps we allow additional fields to be supplied. Supporting all of JSON schema seems infeasible but $defs feels like a core functionality that should be supported. Open to suggestions.

@chemicL
Copy link
Member

chemicL commented Apr 23, 2025

@tzolov I decided to merge the proposed PR. Feel free to make changes if you see a better path forward, but it looks like a backwards-compatible improvement to the status quo.

@chemicL chemicL closed this as completed Apr 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants