fix(server): surface structured validation errors in CallToolResult._meta (#3351) - #3383
Conversation
…_meta Fixes modelcontextprotocol#3351. When a tool call fails schema validation, the low-level Server previously returned only the interpolated free-text message, forcing clients to regex-match brittle wording to classify failures. This change forwards jsonschema.ValidationError's stable machine-readable fields (validator, validator_value, schema_path, json_path, message) into CallToolResult._meta under the MCP-namespaced key 'io.modelcontextprotocol/schema-validation-error', for both input- and output-schema failures. The human-readable message and isError=True stay unchanged, so existing clients keep working. - _make_error_result now accepts optional structured_data - new _validation_error_data helper extracts jsonschema fields - new _jsonable helper coerces non-JSON schema fragments (deques, sets, callables) to JSON-safe values before they cross the transport - added tests covering required/type/enum classification via _meta
|
Thanks for the contribution. This repository only keeps pull requests open when they're linked to an issue that a maintainer has assigned to the author — CONTRIBUTING.md explains why and how we work. This PR has been closed for now because you aren't currently assigned to #3351. If a maintainer would like this change as a PR from you, they'll assign you to #3351 and this PR will reopen automatically — there's nothing more you need to do. (If you opened the issue, this PR already shows up on its timeline.) There's no need to open a new PR — this one will be reopened. While it's closed, please push any updates as new commits rather than force-pushing, since GitHub can't reopen a PR whose branch has been rewritten. Maintainers: reopening this PR, removing the |
Summary
Fixes #3351.
When
Server.call_tool()(low-level) fails schema validation, it currently returns aCallToolResult(isError=True)whose content is only the interpolatedjsonschema.ValidationError.message, e.g."Input validation error: 'b' is a required property". As #3351 points out,jsonschema.ValidationErroralready 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 upstreamjsonschema) just to distinguishrequiredvstypevsenumfailures.Change
Forward the structured
jsonschemafields intoCallToolResult._metaunder the MCP-namespaced keyio.modelcontextprotocol/schema-validation-error, for both input- and output-schema failures.isError=Trueand the existing free-text message stay unchanged, so no existing client breaks.Example payload on
_metaafter 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._validation_error_data(...)staticmethod extracts the stable fields fromjsonschema.ValidationError._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.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.test_lowlevel_input_validation.pyandtest_lowlevel_output_validation.pycases keep passing.Backport target
Opened against
v1.xsince the issue is labeledv1+v2; happy to also cherry-pick tomain(v2) if maintainers prefer.