feat: mount MCP server, add bearer auth, and guard outbound calls
The MCP server was created but never mounted — no client could reach
it. Mount it at /mcp/ over streamable HTTP with a combined lifespan,
resolving the gateway lazily so mounting happens at app construction.
Security and safety for the agent surface:
- One static API_TOKEN (SecretStr) enforced across REST (dependency),
WebSocket (query param/header before accept), and MCP
(StaticTokenVerifier). Startup refuses tokenless non-loopback binds.
- Emergency numbers (911/9911/112) always refused on make_call, plus a
MAX_CONCURRENT_CALLS cap; ValueError surfaces as 400/ToolError.
- Safe defaults: debug off, no credential in default DATABASE_URL,
SIP/LLM/TTS secrets as SecretStr.
Cleanups:
- Delete broken learn_call_flow tool (wrong ctor args, nonexistent
method) and the never-fed CallAnalytics service; keep
call_flow_learner for proper wiring later.
- Trim dial_plan to what is actually used (emergency guard, extension
allocation); delete the unreferenced matcher/normaliser.
- Register call_history before calls so /api/calls/history is no
longer shadowed by /api/calls/{call_id}.
- fastmcp pinned >=3.0 (http_app + StaticTokenVerifier).
New tests: MCP in-memory client (tool surface, lazy gateway, emergency
refusal, call cap) and API security (401 paths, route order, mount).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -26,8 +26,8 @@ class TTSService:
|
||||
async def _get_client(self) -> httpx.AsyncClient:
|
||||
if self._client is None or self._client.is_closed:
|
||||
headers = {}
|
||||
if self.settings.api_key:
|
||||
headers["Authorization"] = f"Bearer {self.settings.api_key}"
|
||||
if self.settings.api_key.get_secret_value():
|
||||
headers["Authorization"] = f"Bearer {self.settings.api_key.get_secret_value()}"
|
||||
self._client = httpx.AsyncClient(
|
||||
base_url=self.settings.base_url,
|
||||
timeout=httpx.Timeout(self.settings.timeout, connect=5.0),
|
||||
|
||||
Reference in New Issue
Block a user