From 8a54daf3c9838038c87d2f4c6023d8c24e8fa7d0 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Thu, 2 Apr 2026 14:46:39 -0400 Subject: [PATCH] fix(fable-mcp): fix streaming error handling and bump timeout for SSE - stream_get now reads error responses before calling _raise_for_status (httpx raises on .json()/.text access inside an unread stream context) - Raise read timeout to 120s (was 30s) so generation streams don't timeout - Document "generation" as a valid category for fable_get_app_logs Co-Authored-By: Claude Sonnet 4.6 --- fable-mcp/fable_mcp/client.py | 6 ++++-- fable-mcp/fable_mcp/server.py | 2 +- fable-mcp/fable_mcp/tools/admin.py | 2 +- fable-mcp/pyproject.toml | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/fable-mcp/fable_mcp/client.py b/fable-mcp/fable_mcp/client.py index feca003..645eeea 100644 --- a/fable-mcp/fable_mcp/client.py +++ b/fable-mcp/fable_mcp/client.py @@ -36,7 +36,7 @@ class FableClient: self._client = httpx.AsyncClient( base_url=self.base_url, headers=self._headers, - timeout=30.0, + timeout=httpx.Timeout(connect=10.0, read=120.0, write=30.0, pool=10.0), ) return self @@ -91,7 +91,9 @@ class FableClient: async def stream_get(self, path: str, **kwargs: Any) -> AsyncIterator[str]: """Yield non-empty lines from a streaming GET response (SSE).""" async with self._http().stream("GET", path, **kwargs) as response: - self._raise_for_status(response) + if response.is_error: + await response.aread() + self._raise_for_status(response) async for line in response.aiter_lines(): if line: yield line diff --git a/fable-mcp/fable_mcp/server.py b/fable-mcp/fable_mcp/server.py index 2db77c0..ca46da9 100644 --- a/fable-mcp/fable_mcp/server.py +++ b/fable-mcp/fable_mcp/server.py @@ -565,7 +565,7 @@ async def fable_get_app_logs( """Fetch Fable application logs. Requires an admin-scoped API key. Args: - category: Log category — "error" (default), "audit", or "usage". + category: Log category — "error" (default), "audit", "usage", or "generation". limit: Maximum number of log entries to return. search: Optional keyword filter matched against action, endpoint, username, details. diff --git a/fable-mcp/fable_mcp/tools/admin.py b/fable-mcp/fable_mcp/tools/admin.py index 4b2aa5e..60111c3 100644 --- a/fable-mcp/fable_mcp/tools/admin.py +++ b/fable-mcp/fable_mcp/tools/admin.py @@ -12,7 +12,7 @@ async def get_app_logs( ) -> dict: """Fetch application logs from Fable. Requires an admin-scoped API key. - category: "error" | "audit" | "usage" (default: "error") + category: "error" | "audit" | "usage" | "generation" (default: "error") """ params: dict = {"category": category, "limit": limit} if search: diff --git a/fable-mcp/pyproject.toml b/fable-mcp/pyproject.toml index 7ca4929..963e89d 100644 --- a/fable-mcp/pyproject.toml +++ b/fable-mcp/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "fable-mcp" -version = "0.2.0" +version = "0.2.1" description = "MCP server for Fabled Assistant" requires-python = ">=3.12" dependencies = [