diff --git a/src/main/services/mcpToolProber.ts b/src/main/services/mcpToolProber.ts index ab80063..3f2f069 100644 --- a/src/main/services/mcpToolProber.ts +++ b/src/main/services/mcpToolProber.ts @@ -73,7 +73,7 @@ export async function probeServer( status: 'success', }; } catch (error) { - const message = error instanceof Error ? error.message : String(error); + const message = formatProbeError(error); console.warn(`[aryx mcp-probe] ${server.name}: failed — ${message}`); return { serverId: server.id, @@ -102,10 +102,17 @@ async function probeServerCore( return await probeWithTransport( new StreamableHTTPClientTransport(new URL(server.url), headerOpts), ); - } catch { - return probeWithTransport( - new SSEClientTransport(new URL(server.url), headerOpts), - ); + } catch (streamableError) { + try { + return await probeWithTransport( + new SSEClientTransport(new URL(server.url), headerOpts), + ); + } catch (sseError) { + // SSE 405 means the server IS Streamable HTTP — surface the original error. + const sseCode = (sseError as { code?: number }).code; + if (sseCode === 405) throw streamableError; + throw sseError; + } } } @@ -177,6 +184,19 @@ function buildHeaders( }; } +function formatProbeError(error: unknown): string { + if (!(error instanceof Error)) return String(error); + + const httpCode = (error as { code?: number }).code; + const base = error.message; + + if (typeof httpCode === 'number' && httpCode >= 100) { + return `HTTP ${httpCode}: ${base}`; + } + + return base; +} + function withTimeout(promise: Promise, ms: number, message: string): Promise { return new Promise((resolve, reject) => { const timer = setTimeout(() => reject(new Error(message)), ms);