diff --git a/src/fastmcp/tools/tool.py b/src/fastmcp/tools/tool.py index b039f2775..073496a90 100644 --- a/src/fastmcp/tools/tool.py +++ b/src/fastmcp/tools/tool.py @@ -125,18 +125,25 @@ class Tool(BaseModel): # which can be pre-parsed here. signature = inspect.signature(self.fn) for param_name in self.parameters["properties"]: + arg = parsed_args.get(param_name, None) + # if not in signature, we won't have annotations, so skip logic if param_name not in signature.parameters: continue - arg = parsed_args.get(param_name, None) - if isinstance(arg, str) and signature.parameters[ - param_name - ].annotation not in (int, float, bool): - # if arg.strip().startswith("{") or arg.strip().startswith("["): - try: - parsed_args[param_name] = json.loads(arg) + # if not a string, we won't have a JSON to parse, so skip logic + if not isinstance(arg, str): + continue + # skip if the type is a simple type (int, float, bool) + if signature.parameters[param_name].annotation in ( + int, + float, + bool, + ): + continue + try: + parsed_args[param_name] = json.loads(arg) - except json.JSONDecodeError: - pass + except json.JSONDecodeError: + pass type_adapter = get_cached_typeadapter(self.fn) result = type_adapter.validate_python(parsed_args | injected_args) diff --git a/src/fastmcp/utilities/types.py b/src/fastmcp/utilities/types.py index f21a013f7..0ad371db3 100644 --- a/src/fastmcp/utilities/types.py +++ b/src/fastmcp/utilities/types.py @@ -6,26 +6,23 @@ from collections.abc import Callable from functools import lru_cache from pathlib import Path from types import UnionType -from typing import Annotated, Any, TypeVar, Union, get_args, get_origin +from typing import Annotated, TypeVar, Union, get_args, get_origin from mcp.types import ImageContent -from pydantic import ConfigDict, TypeAdapter +from pydantic import TypeAdapter T = TypeVar("T") @lru_cache(maxsize=5000) -def get_cached_typeadapter( - cls: T, config: frozenset[tuple[str, Any]] | None = None -) -> TypeAdapter[T]: +def get_cached_typeadapter(cls: T) -> TypeAdapter[T]: """ TypeAdapters are heavy objects, and in an application context we'd typically create them once in a global scope and reuse them as often as possible. However, this isn't feasible for user-generated functions. Instead, we use a cache to minimize the cost of creating them as much as possible. """ - config_dict = dict(config or {}) - return TypeAdapter(cls, config=ConfigDict(**config_dict)) + return TypeAdapter(cls) def issubclass_safe(cls: type, base: type) -> bool: