victor HF Staff commited on
Commit
20982ca
·
1 Parent(s): 1b26311

Improve error handling for MCP transport failures

Browse files

Enhances error reporting by preferring the original HTTP transport error when both HTTP and SSE transports fail, ensuring the UI displays the primary failure reason. Updates ServerCard to better display long error messages.

src/lib/components/mcp/ServerCard.svelte CHANGED
@@ -139,7 +139,7 @@
139
  {#if server.errorMessage}
140
  <div class="mb-2 flex items-center gap-2">
141
  <div
142
- class="rounded bg-red-50 px-2 py-1 text-xs text-red-800 dark:bg-red-900/20 dark:text-red-200"
143
  >
144
  {server.errorMessage}
145
  </div>
 
139
  {#if server.errorMessage}
140
  <div class="mb-2 flex items-center gap-2">
141
  <div
142
+ class="line-clamp-6 break-words rounded bg-red-50 px-2 py-1 text-xs text-red-800 dark:bg-red-900/20 dark:text-red-200"
143
  >
144
  {server.errorMessage}
145
  </div>
src/lib/server/mcp/clientPool.ts CHANGED
@@ -18,19 +18,32 @@ export async function getClient(server: McpServerConfig, signal?: AbortSignal):
18
  const existing = pool.get(key);
19
  if (existing) return existing;
20
 
 
21
  const client = new Client({ name: "chat-ui-mcp", version: "0.1.0" });
22
  const url = new URL(server.url);
23
  const requestInit: RequestInit = { headers: server.headers, signal };
24
  try {
25
  try {
26
  await client.connect(new StreamableHTTPClientTransport(url, { requestInit }));
27
- } catch {
 
 
 
28
  await client.connect(new SSEClientTransport(url, { requestInit }));
29
  }
30
  } catch (err) {
31
  try {
32
  await client.close?.();
33
  } catch {}
 
 
 
 
 
 
 
 
 
34
  throw err;
35
  }
36
 
 
18
  const existing = pool.get(key);
19
  if (existing) return existing;
20
 
21
+ let firstError: unknown;
22
  const client = new Client({ name: "chat-ui-mcp", version: "0.1.0" });
23
  const url = new URL(server.url);
24
  const requestInit: RequestInit = { headers: server.headers, signal };
25
  try {
26
  try {
27
  await client.connect(new StreamableHTTPClientTransport(url, { requestInit }));
28
+ } catch (httpErr) {
29
+ // Remember the original HTTP transport error so we can surface it if the fallback also fails.
30
+ // Today we always show the SSE message, which is misleading when the real failure was HTTP (e.g. 500).
31
+ firstError = httpErr;
32
  await client.connect(new SSEClientTransport(url, { requestInit }));
33
  }
34
  } catch (err) {
35
  try {
36
  await client.close?.();
37
  } catch {}
38
+ // Prefer the HTTP error if both transports fail; otherwise fall back to the last error.
39
+ if (firstError) {
40
+ const message =
41
+ "HTTP transport failed: " +
42
+ String(firstError instanceof Error ? firstError.message : firstError) +
43
+ "; SSE fallback failed: " +
44
+ String(err instanceof Error ? err.message : err);
45
+ throw new Error(message, { cause: err instanceof Error ? err : undefined });
46
+ }
47
  throw err;
48
  }
49
 
src/routes/api/mcp/health/+server.ts CHANGED
@@ -83,6 +83,7 @@ export const POST: RequestHandler = async ({ request, locals }) => {
83
  signal,
84
  };
85
 
 
86
  let lastError: Error | undefined;
87
 
88
  // Try Streamable HTTP transport first
@@ -137,7 +138,8 @@ export const POST: RequestHandler = async ({ request, locals }) => {
137
  return res;
138
  }
139
  } catch (error) {
140
- lastError = error instanceof Error ? error : new Error(String(error));
 
141
  console.log("Streamable HTTP failed, trying SSE transport...", lastError.message);
142
 
143
  // Close failed client
@@ -200,6 +202,14 @@ export const POST: RequestHandler = async ({ request, locals }) => {
200
  }
201
  } catch (sseError) {
202
  lastError = sseError instanceof Error ? sseError : new Error(String(sseError));
 
 
 
 
 
 
 
 
203
  console.error("Both transports failed. Last error:", lastError);
204
  }
205
  }
 
83
  signal,
84
  };
85
 
86
+ let httpError: Error | undefined;
87
  let lastError: Error | undefined;
88
 
89
  // Try Streamable HTTP transport first
 
138
  return res;
139
  }
140
  } catch (error) {
141
+ httpError = error instanceof Error ? error : new Error(String(error));
142
+ lastError = httpError;
143
  console.log("Streamable HTTP failed, trying SSE transport...", lastError.message);
144
 
145
  // Close failed client
 
202
  }
203
  } catch (sseError) {
204
  lastError = sseError instanceof Error ? sseError : new Error(String(sseError));
205
+ // Prefer the HTTP error when both failed so UI shows the primary failure (e.g., HTTP 500) instead
206
+ // of the fallback SSE message.
207
+ if (httpError) {
208
+ lastError = new Error(
209
+ `HTTP transport failed: ${httpError.message}; SSE fallback failed: ${lastError.message}`,
210
+ { cause: sseError instanceof Error ? sseError : undefined }
211
+ );
212
+ }
213
  console.error("Both transports failed. Last error:", lastError);
214
  }
215
  }