Generated output schema uses Pydantic's validation shape while structured output uses its serialization shape
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 52/100
Research direction
Start at Tool.from_function in the MCP server tools implementation, using the import paths shown for the SDK versions, and trace how output_schema and structured_content are produced. Reproduce the alias_output and computed_output examples with Draft202012Validator. Done means the generated schema accepts both serialized structured results, including wireOut and doubled.
Written by the indexing model from the issue text.
Description
Initial Checks
- 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.
Example Code
from __future__ import annotations
import asyncio
import json
from jsonschema import Draft202012Validator
from pydantic import BaseModel, ConfigDict, Field, computed_field
try:
from mcp.server.mcpserver.tools.base import Tool
except ImportError: # MCP Python SDK 1.x
from mcp.server.fastmcp.tools.base import Tool
class AliasOutput(BaseModel):
model_config = ConfigDict(extra="forbid", populate_by_name=True)
value: int = Field(validation_alias="wireIn", serialization_alias="wireOut")
def alias_output() -> AliasOutput:
return AliasOutput(value=1)
class ComputedOutput(BaseModel):
model_config = ConfigDict(extra="forbid")
value: int
@computed_field
@property
def doubled(self) -> int:
return self.value * 2
def computed_output() -> ComputedOutput:
return ComputedOutput(value=1)
async def check(function: object) -> None:
tool = Tool.from_function(function)
converted = await tool.run({}, None, convert_result=True)
structured = converted[1] if isinstance(converted, tuple) else converted.structured_content
errors = [
error.message
for error in Draft202012Validator(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)
async def main() -> None:
await check(alias_output)
await check(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
- Dominant language
- Python
- Stars
- 24.3k
- Forks
- 4k
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 30
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from modelcontextprotocol/python-sdk
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
modelcontextprotocol/python-sdk#3566 ·
-
v1 v2
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
modelcontextprotocol/python-sdk#3546 · 5 comments ·
-
v1 v2
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
modelcontextprotocol/python-sdk#3545 · 1 comment ·
-
v1 v2
Difficulty 1/5 Under an hour Newbie friendliness 91/100
modelcontextprotocol/python-sdk#3508 · 2 comments ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 64/100
modelcontextprotocol/python-sdk#3504 ·
All issues in modelcontextprotocol/python-sdk
Similar issues
-
essnmx good first issue
Difficulty 1/5 Under an hour Newbie friendliness 95/100
-
[Feature] 奇物选择添加优先级 Open
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
syfoud/Simulated_Scepter#174 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Giskard-AI/giskard-oss#2840 · 1 comment ·
-
A claim comment carrying the issue number is silently declined while the workflow reports success Openarea: repo bug perceived difficulty: 2
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
yeti-platform/yeti#1380 ·