Repository navigation
Generated output schema uses Pydantic's validation shape while structured output uses its serialization shape #3100
Description
Activity
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.
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.
- addedbugSomething isn't workingSomething isn't workingP2Moderate issues affecting some users, edge cases, potentially valuable featureModerate issues affecting some users, edge cases, potentially valuable featureneeds confirmationNeeds confirmation that the PR is actually required or needed.Needs confirmation that the PR is actually required or needed.v1Affects the v1.x maintenance lineAffects the v1.x maintenance linev2Affects the v2 line (2.x on main)Affects the v2 line (2.x on main)
on Aug 14, 2026
Initial Checks
Description
For a Pydantic return model whose validation and serialization shapes differ, the generated
outputSchemadescribes the validation shape whilestructuredContentuses 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_aliasvalues still expose different validation and serialization shapes, and serialization-only fields such ascomputed_fieldreveal the same underlying mismatch.Example Code
Observed validator messages:
Expected behavior
The generated
outputSchemashould describe the serialized structured output. Generating the output model schema in Pydantic serialization mode makes both witnesses conform: the alias schema useswireOut, and the computed-field schema includesdoubled.Python & MCP Python SDK
main:2713b53b127afc094dc97d6067df9f69b647661c(2.0.0b2)