Repository navigation
Description
Initial Checks
- I confirm that I'm using the newest release of my line (the latest 2.x, or the latest 1.x if I'm still on v1)
- I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue
Release line
2.x (current stable)
Description
Two standard dataclass configurations fail when used as structured tool outputs. With @dataclass(slots=True), schema derivation silently falls back to unstructured output: func_metadata(produce).output_schema is None. With an InitVar[str] constructor parameter, metadata construction instead raises PydanticForbiddenQualifier because the constructor-only annotation is treated as a model field.
I expected the schema and structured result to contain the dataclass's instance fields. A constructor-only InitVar should not become an output property. The ordinary dataclass control passes, and Pydantic's own TypeAdapter serializes both variants to {"value": "ok"} in the same environments. These cases matter when an existing dataclass is reused as a tool return value.
Example Code
from dataclasses import InitVar, dataclass
from mcp.server.mcpserver.utilities.func_metadata import func_metadata
@dataclass(slots=True)
class Slotted:
value: str
def slotted() -> Slotted:
return Slotted("ok")
print(func_metadata(slotted).output_schema) # None
@dataclass
class ConstructorOnly:
value: str
token: InitVar[str]
def constructor_only() -> ConstructorOnly:
return ConstructorOnly("ok", "constructor-only")
func_metadata(constructor_only) # PydanticForbiddenQualifierTwo deterministic, intentionally failing tests are on this test-only branch; no production changes. Run either case independently, for example:
uv run --frozen pytest tests/server/mcpserver/test_func_metadata.py::test_slotted_dataclass_structured_output_has_instance_fields -qPython & MCP Python SDK
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
Ben-Home commentedon Oct 16, 2026
nosmile99 commentedon Oct 16, 2026
Reproduced both cases on main and confirmed the failure modes, then verified a fix locally against your repro branch (
inanna-malick/python-sdk@repro/dataclass-output-fields):Root cause — both live in
_create_model_from_class(src/mcp/server/mcpserver/utilities/func_metadata.py):getattr(cls, field_name, _no_default)returns the slot member descriptor, which the code mistakes for a default value.create_modelthen fails and schema derivation silently falls back to unstructured output (output_schema=None).InitVar:get_type_hintskeepstoken: InitVar[str]in the hints and it is fed straight intocreate_model, which pydantic rejects withPydanticForbiddenQualifier(raised outside theFuncMetadatatry block, so it propagates).Fix (verified locally): in
_create_model_from_class,isinstance(field_type, dataclasses.InitVar)— constructor-only, not instance state, so never an output property;dataclasses.fields()instead of attribute access (slot descriptors are not defaults); non-dataclass classes keep the oldgetattrpath, with member descriptors additionally treated as "no default".Results: both repro tests now pass (
test_slotted_dataclass_structured_output_has_instance_fields,test_dataclass_initvar_is_excluded_from_structured_output— confirmed failing before the fix), the existingtest_structured_output_dataclasscontrol still passes, and the fulltests/server/mcpserver/test_func_metadata.pymodule is 60/60 green; broadertests/server/mcpserver/is 643 passed with 1 unrelated environmental failure (test_file_resources.py::test_permission_error, fails when run as root).ruff checkandruff format --checkare clean on the touched file.I have the patch ready as a branch. Happy to open a PR if the issue gets assigned (per the repo gate, unassigned PRs are auto-closed) — just say the word.
Disclosure: this investigation and fix verification were AI-assisted.
inanna-malick commentedon Oct 16, 2026
Thanks @nosmile99 for reproducing both cases and checking the broader suite. I'm happy for you to take the fix forward, subject to maintainer assignment; I'm not reserving it for myself.
Could you link your patch branch here? Reading dataclass fields instead of treating slot descriptors as defaults sounds like the right direction. Alongside the two original repros, inherited fields and default_factory fields would be useful checks.
AI-assisted follow-up.
nosmile99 commentedon Oct 16, 2026
Patch branch is up: https://github.com/nosmile99/python-sdk/tree/fix/dataclass-structured-output
The fix is in
_create_model_from_class(src/mcp/server/mcpserver/utilities/func_metadata.py). For dataclasses, defaults are now read fromdataclasses.fields()instead of attribute access — that fixes both reported cases at once: slot descriptors are no longer mistaken for defaults, andInitVar/ClassVarannotations are excluded since they never appear as dataclass fields. Non-dataclass slotted classes get the member-descriptor guard too, so they no longer silently fall back to unstructured output.Verification:
mainbefore the fixdefault_factorydefaults all land correctly in the schema (inherited fields required, defaults optional)tests/server/mcpserver/test_func_metadata.pymodule: 63/63 green; broadertests/server/mcpserver/: 646 passed, 1 unrelated environmental failure (test_file_resources.py::test_permission_error, fails as root on a clean tree too)ruff checkandruff format --checkclean on both touched filesHappy to open the PR whenever a maintainer assigns the issue — the repo gate auto-closes unassigned outside PRs, so I will hold it until then.
Disclosure: AI-assisted, as before.