| |
| """ |
| Shared HTTP client with connection pooling for better performance. |
| All MCP servers should use this instead of creating new clients for each request. |
| """ |
|
|
| import httpx |
| from typing import Optional |
|
|
| |
| |
| _http_client: Optional[httpx.AsyncClient] = None |
|
|
| def get_http_client(timeout: float = 30.0) -> httpx.AsyncClient: |
| """ |
| Get the shared HTTP client with connection pooling. |
| |
| NOTE: For different timeout values, use CustomHTTPClient context manager |
| instead to avoid conflicts between servers. |
| |
| Args: |
| timeout: Request timeout in seconds (default 30) |
| |
| Returns: |
| Shared httpx.AsyncClient instance |
| """ |
| global _http_client |
|
|
| if _http_client is None or _http_client.is_closed: |
| _http_client = httpx.AsyncClient( |
| timeout=httpx.Timeout(timeout), |
| limits=httpx.Limits( |
| max_connections=100, |
| max_keepalive_connections=20, |
| keepalive_expiry=300 |
| ), |
| |
| follow_redirects=True |
| ) |
|
|
| return _http_client |
|
|
| async def close_http_client(): |
| """Close the shared HTTP client (call on shutdown).""" |
| global _http_client |
| if _http_client and not _http_client.is_closed: |
| await _http_client.aclose() |
| _http_client = None |
|
|
| |
| class CustomHTTPClient: |
| """Context manager for creating temporary HTTP clients with custom settings.""" |
|
|
| def __init__(self, timeout: float = 30.0, **kwargs): |
| self.timeout = timeout |
| self.kwargs = kwargs |
| self.client = None |
|
|
| async def __aenter__(self): |
| self.client = httpx.AsyncClient( |
| timeout=httpx.Timeout(self.timeout), |
| **self.kwargs |
| ) |
| return self.client |
|
|
| async def __aexit__(self, exc_type, exc_val, exc_tb): |
| if self.client: |
| await self.client.aclose() |