Skip to content

JsonSchemaValidator.validate parses String input as serialized JSON; document this contract #1110

Description

@slachiewicz

Summary

The validate method of JsonSchemaValidator treats a String structuredContent argument as a serialized JSON document to parse before validation. It does not treat the argument as a literal JSON string value.
This behavior is intentional and consistent across both JSON modules, but it is undocumented, which makes the API easy to misuse when you validate schemas whose document root is a scalar.

Steps to reproduce

Suppose you validate a root-level string against an enum schema:

var validator = new DefaultJsonSchemaValidator();
var schema = Map.<String, Object>of("enum", List.of("red", "green"));
var response = validator.validate(schema, "red");

The validation fails even though red is a member of the enumeration:

Error parsing tool JSON Schema: Unrecognized token 'red': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')

Two things confuse readers here:

  1. The failure comes from JSON parsing, not from the enum keyword. NetworkNT evaluates root-level enums correctly when the instance reaches it as a parsed node.
  2. The message says "tool JSON Schema", although the schema parses fine and the content is what fails to parse.

Passing quoted JSON text produces the expected results:

validator.validate(schema, "\"red\"");   // valid=true
validator.validate(schema, "\"blue\"");  // valid=false

Non-string instances follow the other path. The following call validates
correctly because Jackson converts the value through valueToTree:

var numericSchema = Map.<String, Object>of("enum", List.of(1, 2));
validator.validate(numericSchema, 1);    // valid=true

Current implementation

Both default validators contain the same special case:

  • mcp-json-jackson2: io.modelcontextprotocol.json.schema.jackson2.DefaultJsonSchemaValidator,
    the instanceof String branch at line 70
  • mcp-json-jackson3: io.modelcontextprotocol.json.schema.jackson3.DefaultJsonSchemaValidator,
    the instanceof String branch at line 69

When the argument is a String, the validator calls jsonMapper.readTree on
it; otherwise it calls jsonMapper.valueToTree.

Suggested resolution

This is a documentation gap, not a behavioral bug. Add the contract to the
JsonSchemaValidator#validate Javadoc: if structuredContent is a String,
the method parses it as serialized JSON; quote embedded strings accordingly
(for example, "\"red\"" represents the JSON string value red). Any other
type is converted to its JSON representation directly.

A companion pull request with that Javadoc addition is ready if maintainers
want it; say so here and I will open it.

Context

Found during an audit of tool output-schema validation coverage against
SEP-2106 (#1000). Existing tests cover enum values nested inside object
properties; root-level scalar documents were the untested corner where the
undocumented contract surfaced.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Nice to haves, rare edge casesdocumentationImprovements or additions to documentation

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions