You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Generated output schema uses Pydantic's validation shape while structured output uses its serialization shape #3100
I confirm that I'm using the latest version of MCP Python SDK.
I confirm that I searched existing issues and pull requests before opening this issue.
Description
For a Pydantic return model whose validation and serialization shapes differ, the generated outputSchema describes the validation shape while structuredContent uses the serialization shape. The SDK therefore publishes an output schema that rejects its own generated structured result.
This is related to, but not a duplicate of, #1073 / #1099. That change aligned ordinary field aliases by serializing structured output with aliases. Split validation_alias / serialization_alias values still expose different validation and serialization shapes, and serialization-only fields such as computed_field reveal the same underlying mismatch.
alias_output
schema: ... "wireIn" ...
structured: {"wireOut": 1}
schema_errors: ["Additional properties are not allowed ('wireOut' was unexpected)", "'wireIn' is a required property"]
computed_output
schema: ... "value" ...
structured: {"doubled": 2, "value": 1}
schema_errors: ["Additional properties are not allowed ('doubled' was unexpected)"]
Expected behavior
The generated outputSchema should describe the serialized structured output. Generating the output model schema in Pydantic serialization mode makes both witnesses conform: the alias schema uses wireOut, and the computed-field schema includes doubled.
Python & MCP Python SDK
Python: 3.13.13
MCP Python SDK: 1.28.1 (latest stable)
Also reproduced on current main: 2713b53b127afc094dc97d6067df9f69b647661c (2.0.0b2)
I'd like to work on this if you'll assign it / mark it ready for work.
The mismatch is between how the two shapes are produced in src/mcp/server/fastmcp/utilities/func_metadata.py: outputSchema is generated with model.model_json_schema(...) (default mode="validation", so it reflects validation_alias and omits computed_fields), while the structured content is produced with validated.model_dump(mode="json", by_alias=True) (the serialization shape, using serialization_alias and including computed fields). When a model splits validation_alias/serialization_alias, or has a computed_field under extra="forbid", the published outputSchema then rejects the SDK's own structuredContent.
Proposed fix: generate outputSchema from the serialization shape (model_json_schema(mode="serialization", ...)) so the schema and model_dump(by_alias=True) agree, including computed fields — while making sure this doesn't regress the earlier alias handling (#1073/#1099). I'd extend tests/server/fastmcp/test_func_metadata.py with cases for split validation/serialization aliases and a computed field under extra="forbid".
This is pure-Python and I can develop/test it CPU-only. Disclosure: I'll use AI assistance in preparing the change; there's a human (me) who understands the fix and will own it through review. Let me know if you'd prefer a different approach before I start.
This reads as a schema contract mismatch, not a model quality issue.
When “generate schema” uses Pydantic’s validation shape but structured output uses the serialization shape, the agent/runtime can believe it has a valid schema while the actual tool/output path rejects or mis-parses fields (aliases, defaults, computed fields, exclude, etc.).
Useful isolation before swapping models/prompts:
Expected vs actual: dump both JSON Schemas side-by-side (generate vs what structured-output actually enforces) for one failing tool/output.
Outside the agent: call the same structured-output path with a fixed payload — does the tool/API succeed without the agent loop?
Artifact: one redacted failing run (request schema + error + response body). That usually pins “contract” vs “transport/retry/state.”
If you can share that artifact (even redacted), the failure branch is usually obvious in one pass.
#3644 was closed as a duplicate of this, so moving my offer here. This still reproduces on v2: the advertised outputSchema is generated in validation mode, so a model with a serialization alias or a computed field returns structured content that does not match its own schema. The fix I'd make is small: generate the output schema in serialization mode in FuncMetadata, and dump a tool-returned CallToolResult's validated structured content with by_alias=True so both sides agree, with a regression test covering a serialization alias and a computed field. Could a maintainer assign this to me?
Adding a small deterministic reproduction for the validation/serialization shape mismatch already described here. It reproduces with a serialization alias alone; no split validation alias or computed property is needed.
The SDK successfully emits structured_content={"wire": 7}, but its published schema has property value and requires value. The test explicitly checks the emitted content before comparing the schema, so the observed failure is the advertised schema.
uv run --frozen pytest tests/server/mcpserver/test_func_metadata.py::test_structured_output_serialization_alias_matches_schema -q
Python 3.14.7; Pydantic 2.12.5; MCP 2.3.0 (tag v2.3.0, 2118f14f8a19bc158d8a1cf90af58d85d187f849). Also reproduced using main source at 754254c08fe1c3eda18b69da3d336106fe8d72d6.
AI-assisted investigation and test reproduction, with a human in the loop.
Activity
Kaif10 commentedon Jul 16, 2026
I'd like to work on this if you'll assign it / mark it ready for work.
The mismatch is between how the two shapes are produced in
src/mcp/server/fastmcp/utilities/func_metadata.py:outputSchemais generated withmodel.model_json_schema(...)(defaultmode="validation", so it reflectsvalidation_aliasand omitscomputed_fields), while the structured content is produced withvalidated.model_dump(mode="json", by_alias=True)(the serialization shape, usingserialization_aliasand including computed fields). When a model splitsvalidation_alias/serialization_alias, or has acomputed_fieldunderextra="forbid", the publishedoutputSchemathen rejects the SDK's ownstructuredContent.Proposed fix: generate
outputSchemafrom the serialization shape (model_json_schema(mode="serialization", ...)) so the schema andmodel_dump(by_alias=True)agree, including computed fields — while making sure this doesn't regress the earlier alias handling (#1073/#1099). I'd extendtests/server/fastmcp/test_func_metadata.pywith cases for split validation/serialization aliases and a computed field underextra="forbid".This is pure-Python and I can develop/test it CPU-only. Disclosure: I'll use AI assistance in preparing the change; there's a human (me) who understands the fix and will own it through review. Let me know if you'd prefer a different approach before I start.
IgorGanapolsky commentedon Jul 16, 2026
This reads as a schema contract mismatch, not a model quality issue.
When “generate schema” uses Pydantic’s validation shape but structured output uses the serialization shape, the agent/runtime can believe it has a valid schema while the actual tool/output path rejects or mis-parses fields (aliases, defaults, computed fields,
exclude, etc.).Useful isolation before swapping models/prompts:
If you can share that artifact (even redacted), the failure branch is usually obvious in one pass.
jayzuccarelli commentedon Oct 10, 2026
#3644 was closed as a duplicate of this, so moving my offer here. This still reproduces on v2: the advertised
outputSchemais generated in validation mode, so a model with a serialization alias or a computed field returns structured content that does not match its own schema. The fix I'd make is small: generate the output schema in serialization mode inFuncMetadata, and dump a tool-returnedCallToolResult's validated structured content withby_alias=Trueso both sides agree, with a regression test covering a serialization alias and a computed field. Could a maintainer assign this to me?inanna-malick commentedon Oct 14, 2026
Adding a small deterministic reproduction for the validation/serialization shape mismatch already described here. It reproduces with a serialization alias alone; no split validation alias or computed property is needed.
The SDK successfully emits
structured_content={"wire": 7}, but its published schema has propertyvalueand requiresvalue. The test explicitly checks the emitted content before comparing the schema, so the observed failure is the advertised schema.The intentionally failing test is available on a test-only branch. No production code was changed. Existing ordinary alias output tests pass.
Python 3.14.7; Pydantic 2.12.5; MCP 2.3.0 (tag v2.3.0,
2118f14f8a19bc158d8a1cf90af58d85d187f849). Also reproduced using main source at754254c08fe1c3eda18b69da3d336106fe8d72d6.AI-assisted investigation and test reproduction, with a human in the loop.