From c4830552c29191f13fc399c90a605f7766b0c688 Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Sun, 18 Jan 2026 22:38:02 +0100 Subject: [PATCH] Don't block the event loop on non-async functions --- .../server/fastmcp/utilities/func_metadata.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/mcp/server/fastmcp/utilities/func_metadata.py b/src/mcp/server/fastmcp/utilities/func_metadata.py index be2296594..3fda88e9b 100644 --- a/src/mcp/server/fastmcp/utilities/func_metadata.py +++ b/src/mcp/server/fastmcp/utilities/func_metadata.py @@ -1,3 +1,4 @@ +import functools import inspect import json from collections.abc import Awaitable, Callable, Sequence @@ -5,15 +6,10 @@ from types import GenericAlias from typing import Annotated, Any, cast, get_args, get_origin, get_type_hints +import anyio +import anyio.to_thread import pydantic_core -from pydantic import ( - BaseModel, - ConfigDict, - Field, - RootModel, - WithJsonSchema, - create_model, -) +from pydantic import BaseModel, ConfigDict, Field, RootModel, WithJsonSchema, create_model from pydantic.fields import FieldInfo from pydantic.json_schema import GenerateJsonSchema, JsonSchemaWarningKind from typing_extensions import is_typeddict @@ -60,9 +56,7 @@ def model_dump_one_level(self) -> dict[str, Any]: kwargs[output_name] = value return kwargs - model_config = ConfigDict( - arbitrary_types_allowed=True, - ) + model_config = ConfigDict(arbitrary_types_allowed=True) class FuncMetadata(BaseModel): @@ -92,7 +86,7 @@ async def call_fn_with_arg_validation( if fn_is_async: return await fn(**arguments_parsed_dict) else: - return fn(**arguments_parsed_dict) + return await anyio.to_thread.run_sync(functools.partial(fn, **arguments_parsed_dict)) def convert_result(self, result: Any) -> Any: """Convert the result of a function call to the appropriate format for