Skip to main content
Glama

n8n-MCP

by 88-888
rate-limiter.tsβ€’5.08 kB
/** * Rate Limiter for Telemetry * Implements sliding window rate limiting to prevent excessive telemetry events */ import { TELEMETRY_CONFIG } from './telemetry-types'; import { logger } from '../utils/logger'; export class TelemetryRateLimiter { private eventTimestamps: number[] = []; private windowMs: number; private maxEvents: number; private droppedEventsCount: number = 0; private lastWarningTime: number = 0; private readonly WARNING_INTERVAL = 60000; // Warn at most once per minute private readonly MAX_ARRAY_SIZE = 1000; // Prevent memory leaks by limiting array size constructor( windowMs: number = TELEMETRY_CONFIG.RATE_LIMIT_WINDOW, maxEvents: number = TELEMETRY_CONFIG.RATE_LIMIT_MAX_EVENTS ) { this.windowMs = windowMs; this.maxEvents = maxEvents; } /** * Check if an event can be tracked based on rate limits * Returns true if event can proceed, false if rate limited */ allow(): boolean { const now = Date.now(); // Clean up old timestamps outside the window this.cleanupOldTimestamps(now); // Check if we've hit the rate limit if (this.eventTimestamps.length >= this.maxEvents) { this.handleRateLimitHit(now); return false; } // Add current timestamp and allow event this.eventTimestamps.push(now); return true; } /** * Check if rate limiting would occur without actually blocking * Useful for pre-flight checks */ wouldAllow(): boolean { const now = Date.now(); this.cleanupOldTimestamps(now); return this.eventTimestamps.length < this.maxEvents; } /** * Get current usage statistics */ getStats() { const now = Date.now(); this.cleanupOldTimestamps(now); return { currentEvents: this.eventTimestamps.length, maxEvents: this.maxEvents, windowMs: this.windowMs, droppedEvents: this.droppedEventsCount, utilizationPercent: Math.round((this.eventTimestamps.length / this.maxEvents) * 100), remainingCapacity: Math.max(0, this.maxEvents - this.eventTimestamps.length), arraySize: this.eventTimestamps.length, maxArraySize: this.MAX_ARRAY_SIZE, memoryUsagePercent: Math.round((this.eventTimestamps.length / this.MAX_ARRAY_SIZE) * 100) }; } /** * Reset the rate limiter (useful for testing) */ reset(): void { this.eventTimestamps = []; this.droppedEventsCount = 0; this.lastWarningTime = 0; } /** * Clean up timestamps outside the current window and enforce array size limit */ private cleanupOldTimestamps(now: number): void { const windowStart = now - this.windowMs; // Remove all timestamps before the window start let i = 0; while (i < this.eventTimestamps.length && this.eventTimestamps[i] < windowStart) { i++; } if (i > 0) { this.eventTimestamps.splice(0, i); } // Enforce maximum array size to prevent memory leaks if (this.eventTimestamps.length > this.MAX_ARRAY_SIZE) { const excess = this.eventTimestamps.length - this.MAX_ARRAY_SIZE; this.eventTimestamps.splice(0, excess); if (now - this.lastWarningTime > this.WARNING_INTERVAL) { logger.debug( `Telemetry rate limiter array trimmed: removed ${excess} oldest timestamps to prevent memory leak. ` + `Array size: ${this.eventTimestamps.length}/${this.MAX_ARRAY_SIZE}` ); this.lastWarningTime = now; } } } /** * Handle rate limit hit */ private handleRateLimitHit(now: number): void { this.droppedEventsCount++; // Log warning if enough time has passed since last warning if (now - this.lastWarningTime > this.WARNING_INTERVAL) { const stats = this.getStats(); logger.debug( `Telemetry rate limit reached: ${stats.currentEvents}/${stats.maxEvents} events in ${stats.windowMs}ms window. ` + `Total dropped: ${stats.droppedEvents}` ); this.lastWarningTime = now; } } /** * Get the number of dropped events */ getDroppedEventsCount(): number { return this.droppedEventsCount; } /** * Estimate time until capacity is available (in ms) * Returns 0 if capacity is available now */ getTimeUntilCapacity(): number { const now = Date.now(); this.cleanupOldTimestamps(now); if (this.eventTimestamps.length < this.maxEvents) { return 0; } // Find the oldest timestamp that would need to expire const oldestRelevant = this.eventTimestamps[this.eventTimestamps.length - this.maxEvents]; const timeUntilExpiry = Math.max(0, (oldestRelevant + this.windowMs) - now); return timeUntilExpiry; } /** * Update rate limit configuration dynamically */ updateLimits(windowMs?: number, maxEvents?: number): void { if (windowMs !== undefined && windowMs > 0) { this.windowMs = windowMs; } if (maxEvents !== undefined && maxEvents > 0) { this.maxEvents = maxEvents; } logger.debug(`Rate limiter updated: ${this.maxEvents} events per ${this.windowMs}ms`); } }

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