# mcp/clients.py import os, httpx, asyncio from functools import lru_cache from typing import Any, Dict class APIError(Exception): pass class BaseClient: def __init__(self, base: str, api_key_env: str = None): self.base = base self.key = os.getenv(api_key_env) if api_key_env else None self._client = httpx.AsyncClient(timeout=10) async def request(self, method: str, path: str, **kwargs) -> Any: # automatically attach API key if present if self.key: if "headers" not in kwargs: kwargs["headers"] = {} kwargs["headers"]["Authorization"] = f"Bearer {self.key}" url = f"{self.base.rstrip('/')}/{path.lstrip('/')}" resp = await self._client.request(method, url, **kwargs) try: resp.raise_for_status() except httpx.HTTPStatusError as e: raise APIError(f"{resp.status_code} @ {url}") from e # auto-parse JSON or text ct = resp.headers.get("Content-Type","") return resp.json() if ct.startswith("application/json") else resp.text async def close(self): await self._client.aclose()