Skip to content

Commit 9b67375

Browse files
committed
func metadata: override input with json.loads only when value is valid
1 parent 9ae4df8 commit 9b67375

File tree

2 files changed

+23
-2
lines changed

2 files changed

+23
-2
lines changed

src/mcp/server/fastmcp/utilities/func_metadata.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,15 @@
77
ForwardRef,
88
)
99

10-
from pydantic import BaseModel, ConfigDict, Field, WithJsonSchema, create_model
10+
from pydantic import (
11+
BaseModel,
12+
ConfigDict,
13+
Field,
14+
TypeAdapter,
15+
ValidationError,
16+
WithJsonSchema,
17+
create_model,
18+
)
1119
from pydantic._internal._typing_extra import eval_type_backport
1220
from pydantic.fields import FieldInfo
1321
from pydantic_core import PydanticUndefined
@@ -93,7 +101,12 @@ def pre_parse_json(self, data: dict[str, Any]) -> dict[str, Any]:
93101
# Should really be parsed as '"hello"' in Python - but if we parse
94102
# it as JSON it'll turn into just 'hello'. So we skip it.
95103
continue
96-
new_data[field_name] = pre_parsed
104+
try:
105+
# Validate parsed value
106+
TypeAdapter(_field_info.annotation).validate_python(pre_parsed)
107+
new_data[field_name] = pre_parsed
108+
except ValidationError:
109+
continue # Parsed value is invalid - skip
97110
assert new_data.keys() == data.keys()
98111
return new_data
99112

tests/server/fastmcp/test_func_metadata.py

+8
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ def complex_arguments_fn(
5959
must_be_none_with_default: None = None,
6060
an_int_with_equals_field: int = Field(1, ge=0),
6161
int_annotated_with_default: Annotated[int, Field(description="hey")] = 5,
62+
num_annotated_with_str: str = "",
6263
) -> str:
6364
_ = (
6465
an_int,
@@ -81,6 +82,7 @@ def complex_arguments_fn(
8182
must_be_none_with_default,
8283
an_int_with_equals_field,
8384
int_annotated_with_default,
85+
num_annotated_with_str,
8486
)
8587
return "ok!"
8688

@@ -107,6 +109,7 @@ async def test_complex_function_runtime_arg_validation_non_json():
107109
"my_model_a": {},
108110
"my_model_a_forward_ref": {},
109111
"my_model_b": {"how_many_shrimp": 5, "ok": {"x": 1}, "y": None},
112+
"num_annotated_with_str": "5",
110113
},
111114
arguments_to_pass_directly=None,
112115
)
@@ -381,6 +384,11 @@ def test_complex_function_json_schema():
381384
"title": "Int Annotated With Default",
382385
"type": "integer",
383386
},
387+
"num_annotated_with_str": {
388+
"default": "",
389+
"title": "Num Annotated With Str",
390+
"type": "string",
391+
},
384392
},
385393
"required": [
386394
"an_int",

0 commit comments

Comments
 (0)