Skip to content

fix(server): surface structured validation errors in CallToolResult._meta (#3351) - #3383

Closed
linhongyu510 wants to merge 1 commit into
modelcontextprotocol:v1.xfrom
linhongyu510:fix/3351-structured-validation-error
Closed

fix(server): surface structured validation errors in CallToolResult._meta (#3351)#3383
linhongyu510 wants to merge 1 commit into
modelcontextprotocol:v1.xfrom
linhongyu510:fix/3351-structured-validation-error

Conversation

@linhongyu510

Copy link
Copy Markdown

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_result now takes an optional structured_data keyword and attaches it under the namespaced _meta key.
  • New _validation_error_data(...) staticmethod extracts the stable fields from jsonschema.ValidationError.
  • New module-level _jsonable(...) helper coerces non-JSON schema fragments (deques, sets, callables that live in validator_value for 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 (inputSchema and outputSchema validation) forward kind="input" / kind="output" accordingly.

Tests

  • test_input_validation_error_carries_structured_meta — asserts validator, validator_value, json_path for both missing-required and wrong-type failures.
  • test_enum_validation_error_carries_structured_meta — asserts enum classification exposes the allowed values in validator_value.
  • All existing test_lowlevel_input_validation.py and test_lowlevel_output_validation.py cases 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.

…_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
@github-actions github-actions Bot added the missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md) label Aug 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

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 missing-issue-link label, or adding bypass-issue-check bypasses the check.

@github-actions github-actions Bot closed this Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant