Skip to content

Conversation

askdevai-bot
Copy link

Problem

When using multiple MCP clients in tinyagent, closing one client would cause an error in other active clients:

tinyagent.mcp_client - ERROR - Error during client cleanup: Attempted to exit a cancel scope that isn't the current task's current cancel scope

Root Cause

The issue was caused by improper isolation between multiple MCP client instances when using AsyncExitStack. When multiple clients shared the same event loop, closing one client would interfere with the context management of other active clients.

Solution

This PR implements a comprehensive fix for the MCP client to support multiple concurrent connections:

Key Improvements

  1. Connection State Management: Added ConnectionState enum for proper state tracking
  2. Task Isolation: Implemented connection locks to prevent race conditions between multiple clients
  3. Safe Cleanup: Enhanced cleanup process that won't interfere with other active clients
  4. Better Error Handling: Improved error handling that doesn't propagate to other clients
  5. Async Context Manager: Added proper async context manager support (async with)
  6. Connection Validation: Added checks to ensure operations only happen when connected

Changes Made

  • Added ConnectionState enum to track client state
  • Implemented _connection_lock using asyncio.Lock() for thread-safe operations
  • Enhanced close() method with proper isolation and error handling
  • Added is_connected property and connection state validation
  • Implemented __aenter__ and __aexit__ for async context manager support
  • Improved _cleanup_connection() method for safer resource cleanup
  • Enhanced logging and error messages
  • Added comprehensive examples showing multiple client usage

Backward Compatibility

Fully backward compatible - existing code will continue to work without changes
No additional dependencies - keeps tinyagent lightweight
Same API - all existing methods work as before

Testing

The implementation includes comprehensive examples demonstrating:

  • Multiple clients working concurrently
  • Safe cleanup when closing individual clients
  • Async context manager usage
  • Proper error isolation between clients

Example Usage

# Multiple clients working together
clients = []
for i in range(3):
    client = MCPClient()
    await client.connect("python", ["-m", "mcp.examples.echo_server"])
    clients.append(client)

# Close one client - others continue working
await clients[0].close()  # No longer affects other clients

# Async context manager support
async with MCPClient() as client:
    await client.connect("python", ["-m", "mcp.examples.echo_server"])
    result = await client.call_tool("echo", {"message": "Hello!"})
# Automatically cleaned up

This fix resolves the cancel scope error and enables robust multiple MCP client usage in tinyagent applications.

- Add ConnectionState enum for proper state tracking
- Add connection lock to prevent race conditions
- Improve error handling and isolation between clients
- Add async context manager support
- Implement safe cleanup that won't interfere with other clients
- Add connection state checks before operations
- Fix 'Attempted to exit a cancel scope' error when multiple clients are used
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant