diff --git a/src/mcp/server/fastmcp/utilities/func_metadata.py b/src/mcp/server/fastmcp/utilities/func_metadata.py index 25832620..6435eaa7 100644 --- a/src/mcp/server/fastmcp/utilities/func_metadata.py +++ b/src/mcp/server/fastmcp/utilities/func_metadata.py @@ -7,7 +7,15 @@ ForwardRef, ) -from pydantic import BaseModel, ConfigDict, Field, WithJsonSchema, create_model +from pydantic import ( + BaseModel, + ConfigDict, + Field, + TypeAdapter, + ValidationError, + WithJsonSchema, + create_model, +) from pydantic._internal._typing_extra import eval_type_backport from pydantic.fields import FieldInfo from pydantic_core import PydanticUndefined @@ -93,6 +101,12 @@ def pre_parse_json(self, data: dict[str, Any]) -> dict[str, Any]: # Should really be parsed as '"hello"' in Python - but if we parse # it as JSON it'll turn into just 'hello'. So we skip it. continue + try: + # Validate parsed value + TypeAdapter(_field_info.annotation).validate_python(pre_parsed) + except ValidationError: + continue # Parsed value is invalid - skip + new_data[field_name] = pre_parsed assert new_data.keys() == data.keys() return new_data diff --git a/tests/server/fastmcp/test_func_metadata.py b/tests/server/fastmcp/test_func_metadata.py index 6461648e..5120636c 100644 --- a/tests/server/fastmcp/test_func_metadata.py +++ b/tests/server/fastmcp/test_func_metadata.py @@ -59,6 +59,7 @@ def complex_arguments_fn( must_be_none_with_default: None = None, an_int_with_equals_field: int = Field(1, ge=0), int_annotated_with_default: Annotated[int, Field(description="hey")] = 5, + num_annotated_with_str: str = "", ) -> str: _ = ( an_int, @@ -81,6 +82,7 @@ def complex_arguments_fn( must_be_none_with_default, an_int_with_equals_field, int_annotated_with_default, + num_annotated_with_str, ) return "ok!" @@ -107,6 +109,7 @@ async def test_complex_function_runtime_arg_validation_non_json(): "my_model_a": {}, "my_model_a_forward_ref": {}, "my_model_b": {"how_many_shrimp": 5, "ok": {"x": 1}, "y": None}, + "num_annotated_with_str": "5", }, arguments_to_pass_directly=None, ) @@ -381,6 +384,11 @@ def test_complex_function_json_schema(): "title": "Int Annotated With Default", "type": "integer", }, + "num_annotated_with_str": { + "default": "", + "title": "Num Annotated With Str", + "type": "string", + }, }, "required": [ "an_int",