Skip to main content
Glama
by Coder-RL
retry.ts5.04 kB
import { MCPError } from './errors'; export interface RetryOptions { maxAttempts: number; initialDelay: number; maxDelay: number; backoffFactor: number; retryCondition?: (error: Error) => boolean; onRetry?: (error: Error, attempt: number) => void; } export interface RetryPolicy { enabled: boolean; maxAttempts: number; baseDelay: number; maxDelay: number; backoffType: 'linear' | 'exponential' | 'fixed'; retryableErrors: string[]; } const DEFAULT_RETRY_OPTIONS: RetryOptions = { maxAttempts: 3, initialDelay: 1000, maxDelay: 10000, backoffFactor: 2, retryCondition: (error: Error) => { // Retry on network errors, timeouts, and 5xx HTTP errors if (error instanceof MCPError) { return error.statusCode >= 500 || error.code === 'NETWORK_ERROR' || error.code === 'TIMEOUT'; } return false; } }; export class RetryableOperation<T> { private options: RetryOptions; private operation: () => Promise<T>; constructor(operation: () => Promise<T>, options: Partial<RetryOptions> = {}) { this.operation = operation; this.options = { ...DEFAULT_RETRY_OPTIONS, ...options }; } async execute(): Promise<T> { let lastError: Error; let attempt = 0; while (attempt < this.options.maxAttempts) { try { return await this.operation(); } catch (error) { lastError = error as Error; attempt++; // Check if we should retry this error if (!this.shouldRetry(lastError) || attempt >= this.options.maxAttempts) { throw lastError; } // Calculate delay const delay = this.calculateDelay(attempt); // Call retry callback if provided if (this.options.onRetry) { this.options.onRetry(lastError, attempt); } // Wait before retrying await this.sleep(delay); } } throw lastError!; } private shouldRetry(error: Error): boolean { if (this.options.retryCondition) { return this.options.retryCondition(error); } return DEFAULT_RETRY_OPTIONS.retryCondition!(error); } private calculateDelay(attempt: number): number { const delay = this.options.initialDelay * Math.pow(this.options.backoffFactor, attempt - 1); return Math.min(delay, this.options.maxDelay); } private sleep(ms: number): Promise<void> { return new Promise(resolve => setTimeout(resolve, ms)); } } // Utility function to retry an operation export async function withRetry<T>( operation: () => Promise<T>, options: Partial<RetryOptions> = {} ): Promise<T> { const retryableOp = new RetryableOperation(operation, options); return retryableOp.execute(); } // Decorator for retrying method calls export function withRetryDecorator(options: Partial<RetryOptions> = {}) { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; descriptor.value = async function (...args: any[]) { return withRetry(() => originalMethod.apply(this, args), options); }; return descriptor; }; } // Circuit breaker implementation export class CircuitBreaker<T> { private failures: number = 0; private lastFailureTime: number = 0; private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED'; private successCount: number = 0; constructor( private operation: () => Promise<T>, private options: { failureThreshold: number; timeout: number; monitoringPeriod: number; } = { failureThreshold: 5, timeout: 60000, monitoringPeriod: 10000 } ) {} async execute(): Promise<T> { if (this.state === 'OPEN') { if (Date.now() - this.lastFailureTime < this.options.timeout) { throw new MCPError('Circuit breaker is OPEN', 'CIRCUIT_BREAKER_OPEN', 503); } else { this.state = 'HALF_OPEN'; this.successCount = 0; } } try { const result = await this.operation(); if (this.state === 'HALF_OPEN') { this.successCount++; if (this.successCount >= 3) { this.state = 'CLOSED'; this.failures = 0; } } else { this.failures = 0; } return result; } catch (error) { this.failures++; this.lastFailureTime = Date.now(); if (this.failures >= this.options.failureThreshold) { this.state = 'OPEN'; } throw error; } } getState(): 'CLOSED' | 'OPEN' | 'HALF_OPEN' { return this.state; } getFailureCount(): number { return this.failures; } reset(): void { this.state = 'CLOSED'; this.failures = 0; this.successCount = 0; } } // Utility function to create a circuit breaker export function createCircuitBreaker<T>( operation: () => Promise<T>, options?: { failureThreshold: number; timeout: number; monitoringPeriod: number } ): CircuitBreaker<T> { return new CircuitBreaker(operation, options); } export default withRetry;

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/Coder-RL/Claude_MCPServer_Dev1'

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