All files / scripts/mcp-client/transport retry.ts

100% Statements 3/3
85.71% Branches 6/7
100% Functions 2/2
100% Lines 3/3

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49                                                  16x                           28x 28x                
/**
 * @module mcp-client/transport/retry
 * @description Exponential-backoff retry primitives shared by the MCPClient
 * JSON-RPC request stack.
 *
 * NOTE: The retry policy mirrors `scripts/imf-client.ts` (see
 * Hack23/riksdagsmonitor#2580). Convergence into a single, shared retry
 * library is intentionally deferred: this issue (#2578) keeps the
 * boundaries disjoint to avoid a merge surface with the IMF refactor
 * (#2580). Once both ship, the two retry stacks should be unified under
 * `scripts/lib/retry.ts`.
 *
 * @author Hack23 AB
 * @license Apache-2.0
 */
 
import { RETRY_DELAY } from '../config/defaults.js';
 
/**
 * Compute the delay (ms) before the Nth retry, using a 2^N exponential
 * backoff anchored on {@link RETRY_DELAY}.
 *
 * `retryCount` is the zero-indexed attempt counter (0 = first retry).
 */
export function calculateRetryDelay(retryCount: number): number {
  return RETRY_DELAY * Math.pow(2, Math.max(0, retryCount));
}
 
/**
 * Error signatures the MCPClient retries on. Used by the JSON-RPC
 * dispatcher to decide whether to re-issue a failed call.
 *
 * Retried causes:
 *   - `AbortError` — timeout-induced AbortController abort.
 *   - `network` / `econnrefused` / `connection closed` — transient TCP/IP
 *     or sandbox-firewall hiccup.
 *   - `too many requests` — upstream rate-limit; bounded retry.
 */
export function isRetryableNetworkError(err: Error): boolean {
  const errorMsg = (err.message ?? '').toLowerCase();
  return (
    err.name === 'AbortError' ||
    errorMsg.includes('network') ||
    errorMsg.includes('econnrefused') ||
    errorMsg.includes('connection closed') ||
    errorMsg.includes('too many requests')
  );
}