From 426f3bbda7bb5a3f4c2cb0bc54bb9a7652e0990c Mon Sep 17 00:00:00 2001 From: rUv Date: Sat, 21 Mar 2026 22:54:39 +0000 Subject: [PATCH] fix(ruvocal): MCP SSE auto-reconnect on stale session (404/connection errors) - Widen isConnectionClosedError to catch 404, fetch failed, ECONNRESET - Add transport readyState check in clientPool for dead connections - Retry logic now triggers reconnection on stale SSE sessions Co-Authored-By: claude-flow --- ui/ruvocal/src/lib/server/mcp/clientPool.ts | 14 +++++++++++++- ui/ruvocal/src/lib/server/mcp/httpClient.ts | 12 +++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/ui/ruvocal/src/lib/server/mcp/clientPool.ts b/ui/ruvocal/src/lib/server/mcp/clientPool.ts index 2f78ddd9..becb2a32 100644 --- a/ui/ruvocal/src/lib/server/mcp/clientPool.ts +++ b/ui/ruvocal/src/lib/server/mcp/clientPool.ts @@ -16,7 +16,19 @@ function keyOf(server: McpServerConfig) { export async function getClient(server: McpServerConfig, signal?: AbortSignal): Promise { const key = keyOf(server); const existing = pool.get(key); - if (existing) return existing; + if (existing) { + // Verify the cached client is still alive by checking transport state + try { + // If the transport is closed/errored, evict and reconnect + if ((existing as unknown as { _transport?: { readyState?: number } })._transport?.readyState === 2) { + pool.delete(key); + } else { + return existing; + } + } catch { + return existing; + } + } let firstError: unknown; const client = new Client({ name: "chat-ui-mcp", version: "0.1.0" }); diff --git a/ui/ruvocal/src/lib/server/mcp/httpClient.ts b/ui/ruvocal/src/lib/server/mcp/httpClient.ts index eb862157..de629c69 100644 --- a/ui/ruvocal/src/lib/server/mcp/httpClient.ts +++ b/ui/ruvocal/src/lib/server/mcp/httpClient.ts @@ -4,7 +4,17 @@ import { config } from "$lib/server/config"; function isConnectionClosedError(err: unknown): boolean { const message = err instanceof Error ? err.message : String(err); - return message.includes("-32000") || message.toLowerCase().includes("connection closed"); + const lower = message.toLowerCase(); + return ( + message.includes("-32000") || + lower.includes("connection closed") || + lower.includes("404") || + lower.includes("not found") || + lower.includes("session") || + lower.includes("fetch failed") || + lower.includes("econnreset") || + lower.includes("econnrefused") + ); } export interface McpServerConfig {