Initial Checks
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.
Example Code
from __future__ importannotationsimportasyncioimportjsonfromjsonschemaimportDraft202012ValidatorfrompydanticimportBaseModel, ConfigDict, Field, computed_fieldtry:
frommcp.server.mcpserver.tools.baseimportToolexceptImportError: # MCP Python SDK 1.xfrommcp.server.fastmcp.tools.baseimportToolclassAliasOutput(BaseModel):
model_config=ConfigDict(extra="forbid", populate_by_name=True)
value: int=Field(validation_alias="wireIn", serialization_alias="wireOut")
defalias_output() ->AliasOutput:
returnAliasOutput(value=1)
classComputedOutput(BaseModel):
model_config=ConfigDict(extra="forbid")
value: int@computed_field@propertydefdoubled(self) ->int:
returnself.value*2defcomputed_output() ->ComputedOutput:
returnComputedOutput(value=1)
asyncdefcheck(function: object) ->None:
tool=Tool.from_function(function)
converted=awaittool.run({}, None, convert_result=True)
structured=converted[1] ifisinstance(converted, tuple) elseconverted.structured_contenterrors= [
error.messageforerrorinDraft202012Validator(tool.output_schema).iter_errors(structured)
]
print(function.__name__)
print("schema:", json.dumps(tool.output_schema, sort_keys=True))
print("structured:", json.dumps(structured, sort_keys=True))
print("schema_errors:", errors)
asyncdefmain() ->None:
awaitcheck(alias_output)
awaitcheck(computed_output)
asyncio.run(main())Observed validator messages:
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) - Pydantic: 2.13.4
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)