Skip to main content
Glama

n8n-MCP

by 88-888
telemetry-error.tsβ€’6.09 kB
/** * Telemetry Error Classes * Custom error types for telemetry system with enhanced tracking */ import { TelemetryErrorType, TelemetryErrorContext } from './telemetry-types'; import { logger } from '../utils/logger'; // Re-export types for convenience export { TelemetryErrorType, TelemetryErrorContext } from './telemetry-types'; export class TelemetryError extends Error { public readonly type: TelemetryErrorType; public readonly context?: Record<string, any>; public readonly timestamp: number; public readonly retryable: boolean; constructor( type: TelemetryErrorType, message: string, context?: Record<string, any>, retryable: boolean = false ) { super(message); this.name = 'TelemetryError'; this.type = type; this.context = context; this.timestamp = Date.now(); this.retryable = retryable; // Ensure proper prototype chain Object.setPrototypeOf(this, TelemetryError.prototype); } /** * Convert error to context object */ toContext(): TelemetryErrorContext { return { type: this.type, message: this.message, context: this.context, timestamp: this.timestamp, retryable: this.retryable }; } /** * Log the error with appropriate level */ log(): void { const logContext = { type: this.type, message: this.message, ...this.context }; if (this.retryable) { logger.debug('Retryable telemetry error:', logContext); } else { logger.debug('Non-retryable telemetry error:', logContext); } } } /** * Circuit Breaker for handling repeated failures */ export class TelemetryCircuitBreaker { private failureCount: number = 0; private lastFailureTime: number = 0; private state: 'closed' | 'open' | 'half-open' = 'closed'; private readonly failureThreshold: number; private readonly resetTimeout: number; private readonly halfOpenRequests: number; private halfOpenCount: number = 0; constructor( failureThreshold: number = 5, resetTimeout: number = 60000, // 1 minute halfOpenRequests: number = 3 ) { this.failureThreshold = failureThreshold; this.resetTimeout = resetTimeout; this.halfOpenRequests = halfOpenRequests; } /** * Check if requests should be allowed */ shouldAllow(): boolean { const now = Date.now(); switch (this.state) { case 'closed': return true; case 'open': // Check if enough time has passed to try half-open if (now - this.lastFailureTime > this.resetTimeout) { this.state = 'half-open'; this.halfOpenCount = 0; logger.debug('Circuit breaker transitioning to half-open'); return true; } return false; case 'half-open': // Allow limited requests in half-open state if (this.halfOpenCount < this.halfOpenRequests) { this.halfOpenCount++; return true; } return false; default: return false; } } /** * Record a success */ recordSuccess(): void { if (this.state === 'half-open') { // If we've had enough successful requests, close the circuit if (this.halfOpenCount >= this.halfOpenRequests) { this.state = 'closed'; this.failureCount = 0; logger.debug('Circuit breaker closed after successful recovery'); } } else if (this.state === 'closed') { // Reset failure count on success this.failureCount = 0; } } /** * Record a failure */ recordFailure(error?: Error): void { this.failureCount++; this.lastFailureTime = Date.now(); if (this.state === 'half-open') { // Immediately open on failure in half-open state this.state = 'open'; logger.debug('Circuit breaker opened from half-open state', { error: error?.message }); } else if (this.state === 'closed' && this.failureCount >= this.failureThreshold) { // Open circuit after threshold reached this.state = 'open'; logger.debug( `Circuit breaker opened after ${this.failureCount} failures`, { error: error?.message } ); } } /** * Get current state */ getState(): { state: string; failureCount: number; canRetry: boolean } { return { state: this.state, failureCount: this.failureCount, canRetry: this.shouldAllow() }; } /** * Force reset the circuit breaker */ reset(): void { this.state = 'closed'; this.failureCount = 0; this.lastFailureTime = 0; this.halfOpenCount = 0; } } /** * Error aggregator for tracking error patterns */ export class TelemetryErrorAggregator { private errors: Map<TelemetryErrorType, number> = new Map(); private errorDetails: TelemetryErrorContext[] = []; private readonly maxDetails: number = 100; /** * Record an error */ record(error: TelemetryError): void { // Increment counter for this error type const count = this.errors.get(error.type) || 0; this.errors.set(error.type, count + 1); // Store error details (limited) this.errorDetails.push(error.toContext()); if (this.errorDetails.length > this.maxDetails) { this.errorDetails.shift(); } } /** * Get error statistics */ getStats(): { totalErrors: number; errorsByType: Record<string, number>; mostCommonError?: string; recentErrors: TelemetryErrorContext[]; } { const errorsByType: Record<string, number> = {}; let totalErrors = 0; let mostCommonError: string | undefined; let maxCount = 0; for (const [type, count] of this.errors.entries()) { errorsByType[type] = count; totalErrors += count; if (count > maxCount) { maxCount = count; mostCommonError = type; } } return { totalErrors, errorsByType, mostCommonError, recentErrors: this.errorDetails.slice(-10) // Last 10 errors }; } /** * Clear error history */ reset(): void { this.errors.clear(); this.errorDetails = []; } }

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/88-888/n8n-mcp'

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