Skip to content

Conversation

@strands-agent
Copy link
Contributor

@strands-agent strands-agent commented Jan 19, 2026

Description

Fixes #1512

This PR fixes the issue where MCPClient fails to start in ASGI environments (uvicorn/hypercorn) with the error:

RuntimeWarning: coroutine 'MCPClient._async_background_thread' was never awaited
MCPClientInitializationError: background thread did not start in 30 seconds

Root Cause

PR #1444 added contextvars.copy_context() to propagate context variables to the background thread (fixing #1440). However, in ASGI environments where the parent thread already has an active event loop, copying the context also copies the _running_loop contextvar marker.

When the background thread calls run_until_complete(), Python's asyncio detects this inherited running loop marker and raises:

Cannot run the event loop while another loop is running

Credit to @jpvelasco for identifying the actual root cause: the issue is the _running_loop contextvar (via asyncio.events._set_running_loop), not just the thread-local event loop.

Solution

Clear both the running loop marker and thread-local event loop before creating the new event loop:

def _background_task(self):
    # Clear the inherited "running loop" contextvar
    try:
        import asyncio.events as events
        events._set_running_loop(None)
    except (AttributeError, RuntimeError):
        pass
    
    # Also clear thread-local event loop  
    try:
        asyncio.set_event_loop(None)
    except RuntimeError:
        pass
    
    self._background_thread_event_loop = asyncio.new_event_loop()
    asyncio.set_event_loop(self._background_thread_event_loop)
    self._background_thread_event_loop.run_until_complete(self._async_background_thread())

Why This Works

  1. Preserves context variable propagation from PR fix(mcp): propagate contextvars to background thread #1444 (auth and other context vars still work)
  2. Clears inherited running loop marker - the actual root cause identified by @jpvelasco
  3. Isolates event loops between parent and background threads
  4. Defensive - handles partial initialization gracefully with try/except
  5. No breaking changes - existing MCP functionality unchanged
  6. Works with uvicorn/ASGI environments (verified by issue reporter)
  7. Compatible with OpenTelemetry instrumentation

Related Issues

Documentation PR

No documentation changes required - this is an internal implementation fix.

Type of Change

Bug fix (non-breaking change which fixes an issue)

Testing

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.


PR created by strands-coder autonomous agent with fix verified by @jpvelasco 🧬

…event loop

Fixes strands-agents#1512

When MCPClient runs in ASGI environments (uvicorn, hypercorn), the parent
thread has an active event loop. Since PR strands-agents#1444 uses contextvars.copy_context()
to propagate context variables to the background thread, it also copies
the _running_loop contextvar marker.

When the background thread then calls run_until_complete(), Python's asyncio
detects the inherited running loop marker and raises:
'Cannot run the event loop while another loop is running'

The fix clears both:
1. The _running_loop contextvar via events._set_running_loop(None)
2. The thread-local event loop via asyncio.set_event_loop(None)

This ensures the background thread starts with a clean slate while still
preserving context variable propagation from PR strands-agents#1444.

Solution verified by issue reporter @jpvelasco.
@strands-agent strands-agent force-pushed the fix/mcp-uvicorn-event-loop-1512 branch from 5cfeeb8 to 67a25bc Compare January 20, 2026 00:19
@github-actions github-actions bot added size/s and removed size/m labels Jan 20, 2026
@strands-agent strands-agent changed the title fix(mcp): clear event loop state before creating background thread loop fix(mcp): clear running loop state before creating background thread event loop Jan 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] MCPClient background thread fails to start under uvicorn/ASGI - "coroutine was never awaited"

1 participant