fix(server): surface structured validation errors in CallToolResult._meta (#3351)
Summary
Fixes #3351.
When Server.call_tool() (low-level) fails schema validation, it currently returns a CallToolResult(isError=True) whose content is only the interpolated jsonschema.ValidationError.message, e.g. "Input validation error: 'b' is a required property". As #3351 points out, jsonschema.ValidationError already carries stable structured fields (validator, validator_value, schema_path, json_path) — but they are all discarded before crossing the transport, forcing clients to regex-match brittle wording (whose exact form is not guaranteed by upstream jsonschema) just to distinguish required vs type vs enum failures.
Change
Forward the structured jsonschema fields into CallToolResult._meta under the MCP-namespaced key io.modelcontextprotocol/schema-validation-error, for both input- and output-schema failures. isError=True and the existing free-text message stay unchanged, so no existing client breaks.
Example payload on _meta after a missing-required failure:
{
"io.modelcontextprotocol/schema-validation-error": {
"kind": "input",
"validator": "required",
"validator_value": ["a", "b"],
"schema_path": ["required"],
"json_path": "$",
"message": "'b' is a required property"
}
}
Implementation notes
_make_error_resultnow takes an optionalstructured_datakeyword and attaches it under the namespaced_metakey.- New
_validation_error_data(...)staticmethod extracts the stable fields fromjsonschema.ValidationError. - New module-level
_jsonable(...)helper coerces non-JSON schema fragments (deques, sets, callables that live invalidator_valuefor custom formats/keywords) into JSON-safe values before they cross the JSON-RPC transport, so the addition can't crash the response. - Both call sites (
inputSchemaandoutputSchemavalidation) forwardkind="input"/kind="output"accordingly.
Tests
test_input_validation_error_carries_structured_meta— assertsvalidator,validator_value,json_pathfor both missing-required and wrong-type failures.test_enum_validation_error_carries_structured_meta— asserts enum classification exposes the allowed values invalidator_value.- All existing
test_lowlevel_input_validation.pyandtest_lowlevel_output_validation.pycases keep passing.
Backport target
Opened against v1.x since the issue is labeled v1 + v2; happy to also cherry-pick to main (v2) if maintainers prefer.