UNPKG

@cyanheads/toolkit-mcp-server

Version:

MCP server providing system, network, geolocation, generator, datetime, and security tools

82 lines (81 loc) 3.1 kB
import { McpToolError } from './errors.js'; import { ErrorCode } from '@modelcontextprotocol/sdk/types.js'; export class RateLimiter { requests = new Map(); maxRequests; windowMs; errorMessage; constructor(config) { this.maxRequests = config.maxRequests; this.windowMs = config.windowMs; this.errorMessage = config.errorMessage || 'Rate limit exceeded. Please try again later.'; } checkLimit(key) { const now = Date.now(); const timestamps = this.requests.get(key) || []; // Remove expired timestamps const validTimestamps = timestamps.filter(time => now - time < this.windowMs); if (validTimestamps.length >= this.maxRequests) { const oldestRequest = validTimestamps[0]; const resetTime = Math.ceil((oldestRequest + this.windowMs - now) / 1000); throw new McpToolError(ErrorCode.InternalError, this.errorMessage, { resetInSeconds: resetTime }); } // Add current request validTimestamps.push(now); this.requests.set(key, validTimestamps); } async withRateLimit(key, operation) { this.checkLimit(key); return operation(); } getRemainingRequests(key) { const now = Date.now(); const timestamps = this.requests.get(key) || []; const validTimestamps = timestamps.filter(time => now - time < this.windowMs); return Math.max(0, this.maxRequests - validTimestamps.length); } getTimeToReset(key) { const now = Date.now(); const timestamps = this.requests.get(key) || []; const validTimestamps = timestamps.filter(time => now - time < this.windowMs); if (validTimestamps.length === 0) { return 0; } return Math.max(0, validTimestamps[0] + this.windowMs - now); } // Clean up expired entries periodically cleanup() { const now = Date.now(); for (const [key, timestamps] of this.requests.entries()) { const validTimestamps = timestamps.filter(time => now - time < this.windowMs); if (validTimestamps.length === 0) { this.requests.delete(key); } else { this.requests.set(key, validTimestamps); } } } } // Create rate limiters for different tool categories export const systemRateLimiter = new RateLimiter({ maxRequests: 100, windowMs: 60 * 1000, // 1 minute errorMessage: 'Too many system operations. Please try again in a moment.' }); export const networkRateLimiter = new RateLimiter({ maxRequests: 50, windowMs: 60 * 1000, errorMessage: 'Too many network operations. Please try again in a moment.' }); export const geoRateLimiter = new RateLimiter({ maxRequests: 45, windowMs: 60 * 1000, errorMessage: 'IP-API rate limit exceeded. Please try again in a moment.' }); // Clean up expired rate limit entries every minute setInterval(() => { systemRateLimiter.cleanup(); networkRateLimiter.cleanup(); geoRateLimiter.cleanup(); }, 60 * 1000);