Skip to content

implement dedicated background event loop for MCP operations#4794

Open
lorenzejay wants to merge 3 commits intomainfrom
lorenze/fix/mcp-use-in-flows
Open

implement dedicated background event loop for MCP operations#4794
lorenzejay wants to merge 3 commits intomainfrom
lorenze/fix/mcp-use-in-flows

Conversation

@lorenzejay
Copy link
Copy Markdown
Collaborator

@lorenzejay lorenzejay commented Mar 9, 2026

  • Introduced a shared event loop running in a background thread to manage MCP connections efficiently.
  • Updated the MCPNativeTool class to ensure all async operations are dispatched to this dedicated loop, preventing cross-loop cancel-scope issues.
  • Enhanced error handling during tool execution to ensure clean disconnection and reconnection processes.

Note

Medium Risk
Changes core MCP tool execution to use a shared background event loop and cross-thread coroutine dispatch, which can impact concurrency behavior (deadlocks/hangs) and connection lifecycle handling. Scope is limited to MCPNativeTool but affects all native MCP tool invocations.

Overview
MCPNativeTool now uses a single shared asyncio event loop running in a background thread, submitting each invocation via run_coroutine_threadsafe instead of creating per-call event loops/threads.

Tool execution was adjusted to always run connect → call_tool → disconnect within the same task on that loop, with more defensive cleanup and one-time reconnect retry (disconnect failures are logged at debug rather than raising).

Written by Cursor Bugbot for commit 80f6c17. This will update automatically on new commits. Configure here.

- Introduced a shared event loop running in a background thread to manage MCP connections efficiently.
- Updated the MCPNativeTool class to ensure all async operations are dispatched to this dedicated loop, preventing cross-loop cancel-scope issues.
- Enhanced error handling during tool execution to ensure clean disconnection and reconnection processes.
Comment thread lib/crewai/src/crewai/tools/mcp_native_tool.py Fixed
Comment thread lib/crewai/src/crewai/tools/mcp_native_tool.py Fixed
Comment thread lib/crewai/src/crewai/tools/mcp_native_tool.py Fixed
Comment thread lib/crewai/src/crewai/tools/mcp_native_tool.py
Comment thread lib/crewai/src/crewai/tools/mcp_native_tool.py Outdated
Comment thread lib/crewai/src/crewai/tools/mcp_native_tool.py Fixed
Comment thread lib/crewai/src/crewai/tools/mcp_native_tool.py Fixed
Comment thread lib/crewai/src/crewai/tools/mcp_native_tool.py Fixed
…p code

- Eliminated the unused _mcp_shared_loop_thread variable to simplify the MCPNativeTool class.
- Updated the _ensure_loop method to remove redundant checks for the event loop's running state.
- Improved logging formatting for better readability during error handling.
name="mcp-native-tool-loop",
)
thread.start()
_mcp_shared_loop = loop
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

"""Submit *coro* to the dedicated loop and block until it completes."""
loop = self._ensure_loop()
future = asyncio.run_coroutine_threadsafe(coro, loop)
return future.result()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shared MCPClient races when tools run concurrently

High Severity

Multiple MCPNativeTool instances from the same MCP server share a single MCPClient (assigned in tool_resolver.py). The crew agent executor runs tools in parallel via ThreadPoolExecutor, and each tool now dispatches _run_async to the same dedicated event loop via run_coroutine_threadsafe. This creates concurrent async Tasks that interleave connect/disconnect/call_tool on the shared, unprotected MCPClient. One task can disconnect and replace the session while another task is mid-call_tool. Previously, each call used asyncio.run() on separate threads with isolated event loops, preventing this interleaving.

Additional Locations (1)

Fix in Cursor Fix in Web

@github-actions
Copy link
Copy Markdown
Contributor

This PR is stale because it has been open for 45 days with no activity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant