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
An ordinary class's Annotated field constraints disappear when its return annotation is converted into a structured output model. A field declared Annotated[int, Field(gt=0)] produces a schema with only type: integer, and an instance containing zero is accepted as structured_content={"value": 0}.
I expected the generated property to retain exclusiveMinimum: 0 and output conversion to reject zero. The reproduction first proves that Pydantic validates the exact field annotation and rejects that value, then fails because SDK conversion does not raise ValidationError. A BaseModel with the same constraint rejects zero, and the existing ordinary-class output test still passes.
This is a class-field path, separate from the string-keyed dictionary return annotation metadata loss tracked in #2935. The relevant class path calls get_type_hints(type_annotation) without retaining extras before synthesizing the model. A tool author relying on the declared bound therefore gets neither the advertised constraint nor its validation.
Example Code
from typing import Annotated
import pytest
from pydantic import Field, TypeAdapter, ValidationError
from mcp.server.mcpserver.utilities.func_metadata import func_metadata
class Result:
value: Annotated[int, Field(gt=0)]
def __init__(self, value: int) -> None:
self.value = value
def produce() -> Result:
return Result(0)
with pytest.raises(ValidationError):
TypeAdapter(Annotated[int, Field(gt=0)]).validate_python(0)
with pytest.raises(ValidationError): # Fails: DID NOT RAISE
func_metadata(produce).convert_result(produce())The deterministic failing test is on a test-only branch.
uv run --frozen pytest tests/server/mcpserver/test_func_metadata.py::test_ordinary_class_output_rejects_values_outside_annotated_field_bounds -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
dhvani7014 commentedon Oct 15, 2026
Hi, I would like to work on this if it's not already taken.
sattyamjjain commentedon Oct 15, 2026
Ben-Home commentedon Oct 16, 2026
inanna-malick commentedon Oct 16, 2026
Thanks @sattyamjjain for confirming that dataclasses are affected too, and for the BaseModel clarification: normal construction validates, but an already-constructed instance isn't necessarily revalidated. The include_extras=True change matches the source-level cause.
@dhvani7014 I'm happy for someone else to take the fix forward, subject to maintainer assignment; I'm not reserving it for myself. Please coordinate here if either of you has a patch underway so work isn't duplicated. Regression coverage should check both the advertised constraint and rejection of invalid results for ordinary classes and dataclasses, with valid results still accepted.
AI-assisted follow-up.
dhvani7014 commentedon Oct 18, 2026
Thanks @inanna-malick and thanks @sattyamjjain for confirming the dataclass case. I reproduced this on main and confirmed the cause: L540 in func_metadata.py calls get_type_hints(type_annotation) without include_extras=True, so the Annotated constraint is stripped.
Passing include_extras=True fixes both ordinary classes and dataclasses. I added regression tests for each, checking that the schema keeps exclusiveMinimum: 0, that 0 is rejected with ValidationError, and that valid values are still accepted. The tests fail without the change and pass with it, and the full suite, ruff and pyright pass locally.
The patch is ready on my fork: https://github.com/dhvani7014/python-sdk/tree/fix-annotated-constraints-class-output. I’ll wait for maintainer assignment before opening a PR. Disclosure: AI-assisted investigation and test writing, with me running and reviewing everything.