Run synchronous tools in worker threads to avoid blocking the event loop #1902
+84
−1
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR ensures that synchronous tools are never executed inline on the event loop.
All sync functions invoked through call_fn_with_arg_validation are now offloaded to worker threads using anyio.to_thread.run_sync, preventing event loop starvation.
Motivation and Context
Previously, synchronous tool functions were executed directly when fn_is_async=False, which could block the event loop if the function performed blocking operations (e.g., time.sleep, I/O, CPU-heavy work).
This caused:
Event loop starvation
Delayed scheduling of other async tasks
Subtle concurrency bugs in FastMCP tool execution
This change guarantees that sync tools behave safely in async contexts, aligning with AnyIO best practices and expected MCP runtime behavior.
How Has This Been Tested?
Added regression tests to verify that:
A blocking synchronous tool does not block the event loop
Concurrent async tasks continue to run while a sync tool executes
Added a thread-identity test to assert that synchronous tools run in a worker thread rather than the event loop thread
All existing tests pass locally
Breaking Changes
No breaking changes.
Behavior is now correct and consistent with expectations for async execution environments.
Types of changes
Checklist
Additional context
This change brings FastMCP tool execution in line with AnyIO’s concurrency model.
Prevents a class of hard-to-debug latency and scheduling issues when tools perform blocking work.
The new tests act as regression guards against future accidental inline execution of sync tools.