@gleanwork/mcp-server-tester 2.0.0-beta.3 → 2.0.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -285,6 +285,9 @@ interface HostEvent {
285
285
  isError?: boolean;
286
286
  rawName?: string;
287
287
  id?: string;
288
+ durationMs?: number;
289
+ startedAt?: string;
290
+ completedAt?: string;
288
291
  }
289
292
 
290
293
  /**
@@ -1874,7 +1874,7 @@ var debugHttp = createDebug(`${NAMESPACE}:http`);
1874
1874
 
1875
1875
  // package.json
1876
1876
  var package_default = {
1877
- version: "2.0.0-beta.3"};
1877
+ version: "2.0.0-beta.5"};
1878
1878
  var debug = createDebug("mcp-server-tester:oauth-flow");
1879
1879
  async function generatePKCE() {
1880
1880
  const codeVerifier = oauth.generateRandomCodeVerifier();
@@ -2038,8 +2038,55 @@ async function performClientCredentialsFlow(config) {
2038
2038
  };
2039
2039
  }
2040
2040
 
2041
+ // src/mcp/connectionDiagnostics.ts
2042
+ function classifyMCPConnectionFailure(error) {
2043
+ if (typeof error !== "object" || error === null) return "connection_failed";
2044
+ const details = error;
2045
+ const status = details.response?.status ?? details.status ?? details.code;
2046
+ if (typeof status === "number" && Number.isInteger(status) && status >= 400 && status <= 599) {
2047
+ return `http_${status}`;
2048
+ }
2049
+ const message = error instanceof Error ? error.message : "";
2050
+ const httpStatus = /\b(?:HTTP|status(?: code)?|code:)\s*[:=(]?\s*([45]\d{2})\b/i.exec(
2051
+ message
2052
+ )?.[1];
2053
+ if (httpStatus) return `http_${httpStatus}`;
2054
+ for (const code of ["ECONNRESET", "ECONNREFUSED", "ETIMEDOUT", "ENOTFOUND"]) {
2055
+ if (details.code === code || message.toUpperCase().includes(code)) {
2056
+ return code.toLowerCase();
2057
+ }
2058
+ }
2059
+ if (/timed out|timeout/i.test(message)) return "timeout";
2060
+ if (/network|socket hang up|fetch failed/i.test(message))
2061
+ return "network_error";
2062
+ return "connection_failed";
2063
+ }
2064
+ var MCPHttpConnectionError = class extends Error {
2065
+ constructor(streamableError, sseError, retryable, retryAfterMs) {
2066
+ const streamableHttpFailure = classifyMCPConnectionFailure(streamableError);
2067
+ const sseFailure = classifyMCPConnectionFailure(sseError);
2068
+ super(
2069
+ `MCP connection failed: streamableHttp=${streamableHttpFailure}; sse=${sseFailure}`
2070
+ );
2071
+ this.retryable = retryable;
2072
+ this.retryAfterMs = retryAfterMs;
2073
+ this.name = "MCPHttpConnectionError";
2074
+ this.streamableHttpFailure = streamableHttpFailure;
2075
+ this.sseFailure = sseFailure;
2076
+ }
2077
+ streamableHttpFailure;
2078
+ sseFailure;
2079
+ };
2080
+ function formatMCPConnectionFailure(error) {
2081
+ if (error instanceof MCPHttpConnectionError) {
2082
+ return `MCP connection failed: streamableHttp=${error.streamableHttpFailure}; sse=${error.sseFailure}`;
2083
+ }
2084
+ return classifyMCPConnectionFailure(error);
2085
+ }
2086
+
2041
2087
  // src/mcp/clientFactory.ts
2042
2088
  function getRetryAfterDelayMs(err) {
2089
+ if (err instanceof MCPHttpConnectionError) return err.retryAfterMs;
2043
2090
  const response = err?.response;
2044
2091
  const retryAfter = response?.headers?.get?.("Retry-After");
2045
2092
  if (retryAfter) {
@@ -2058,6 +2105,7 @@ function isTransientNetworkError(err) {
2058
2105
  return msg.includes("econnreset") || msg.includes("econnrefused") || msg.includes("etimedout") || msg.includes("enotfound") || msg.includes("network") || msg.includes("socket hang up") || msg.includes("fetch failed");
2059
2106
  }
2060
2107
  function isRetryableError(err) {
2108
+ if (err instanceof MCPHttpConnectionError) return err.retryable;
2061
2109
  return isTransientNetworkError(err) || isRateLimitError(err);
2062
2110
  }
2063
2111
  async function retryWithBackoff(fn, maxAttempts) {
@@ -2075,7 +2123,7 @@ async function retryWithBackoff(fn, maxAttempts) {
2075
2123
  attempt + 1,
2076
2124
  maxAttempts + 1,
2077
2125
  delayMs,
2078
- err.message
2126
+ formatMCPConnectionFailure(err)
2079
2127
  );
2080
2128
  await new Promise((resolve) => setTimeout(resolve, delayMs));
2081
2129
  } else {
@@ -2232,15 +2280,24 @@ async function createMCPClientForConfig(config, options) {
2232
2280
  } catch (err) {
2233
2281
  debugHttp(
2234
2282
  "streamableHttp failed (%s), falling back to SSE",
2235
- err.message
2283
+ formatMCPConnectionFailure(err)
2236
2284
  );
2237
2285
  debugClient("Streamable HTTP failed, falling back to SSE transport");
2238
2286
  debugHttp("Attempting transport: sse");
2239
- const sseTransport = new SSEClientTransport(url, {
2240
- requestInit,
2241
- ...options?.authProvider ? { authProvider: options.authProvider } : {}
2242
- });
2243
- await client.connect(sseTransport, connectOptions);
2287
+ try {
2288
+ const sseTransport = new SSEClientTransport(url, {
2289
+ requestInit,
2290
+ ...options?.authProvider ? { authProvider: options.authProvider } : {}
2291
+ });
2292
+ await client.connect(sseTransport, connectOptions);
2293
+ } catch (sseError) {
2294
+ throw new MCPHttpConnectionError(
2295
+ err,
2296
+ sseError,
2297
+ isRetryableError(err) || isRetryableError(sseError),
2298
+ getRetryAfterDelayMs(err) ?? getRetryAfterDelayMs(sseError)
2299
+ );
2300
+ }
2244
2301
  debugClient("Connected via SSE");
2245
2302
  debugHttp("Connection established via sse");
2246
2303
  }