Skip to main content
Glama
retry.ts3.17 kB
/** * Retry utility with exponential backoff for API calls */ interface RetryOptions { maxRetries?: number; initialDelay?: number; maxDelay?: number; backoffMultiplier?: number; retryableErrors?: (error: unknown) => boolean; onRetry?: (attempt: number, error: unknown, delay: number) => void; } const DEFAULT_OPTIONS: Required<RetryOptions> = { maxRetries: 3, initialDelay: 1000, // 1 second maxDelay: 30000, // 30 seconds backoffMultiplier: 2, retryableErrors: (error: unknown) => { // Retry on network errors and 5xx status codes const axiosError = error as { response?: { status?: number } }; if (!axiosError.response) return true; // Network error const status = axiosError.response?.status; return (status !== undefined && (status >= 500 || status === 429)); // Server errors or rate limiting }, onRetry: () => {} // Default no-op }; /** * Execute a function with exponential backoff retry logic */ export async function withRetry<T>( fn: () => Promise<T>, options: RetryOptions = {} ): Promise<T> { const opts = { ...DEFAULT_OPTIONS, ...options }; let lastError: unknown; for (let attempt = 0; attempt <= opts.maxRetries; attempt++) { try { return await fn(); } catch (error) { lastError = error; // Check if we should retry if (attempt === opts.maxRetries || !opts.retryableErrors(error)) { throw error; } // Calculate delay with exponential backoff const delay = Math.min( opts.initialDelay * Math.pow(opts.backoffMultiplier, attempt), opts.maxDelay ); // Add jitter to prevent thundering herd const jitteredDelay = delay * (0.5 + Math.random() * 0.5); // Notify about retry opts.onRetry(attempt + 1, error, jitteredDelay); // Wait before retrying await sleep(jitteredDelay); } } throw lastError; } /** * Sleep for specified milliseconds */ function sleep(ms: number): Promise<void> { return new Promise(resolve => setTimeout(resolve, ms)); } /** * Create a retry wrapper with preset options */ export function createRetryWrapper(defaultOptions: RetryOptions) { return <T>(fn: () => Promise<T>, overrideOptions?: RetryOptions): Promise<T> => { return withRetry(fn, { ...defaultOptions, ...overrideOptions }); }; } /** * Check if an error is retryable based on common patterns */ export function isRetryableError(error: unknown): boolean { if (!error || typeof error !== 'object') return false; const axiosError = error as { response?: { status?: number }, request?: unknown }; // Network errors are always retryable if (!axiosError.response && axiosError.request) { return true; } // Check HTTP status codes const status = axiosError.response?.status; if (!status) return false; // Retry on server errors and rate limiting if (status >= 500 || status === 429) return true; // Retry on specific client errors that might be transient if (status === 408 || status === 409 || status === 423 || status === 425) { return true; } return false; } export default withRetry;

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/stier1ba/licensespring-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server