Fix MCP client multiple connections issue
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
- Connection State Management: Added
ConnectionStateenum for proper state tracking - Task Isolation: Implemented connection locks to prevent race conditions between multiple clients
- Safe Cleanup: Enhanced cleanup process that won't interfere with other active clients
- Better Error Handling: Improved error handling that doesn't propagate to other clients
- Async Context Manager: Added proper async context manager support (
async with) - Connection Validation: Added checks to ensure operations only happen when connected
Changes Made
- Added
ConnectionStateenum to track client state - Implemented
_connection_lockusingasyncio.Lock()for thread-safe operations - Enhanced
close()method with proper isolation and error handling - Added
is_connectedproperty 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.