UNPKG

@glec/mcp-server

Version:

GLEC API MCP Server for Claude Desktop - Carbon emission calculation for logistics and transportation

131 lines 4.41 kB
import { createHash } from 'crypto'; // In-memory rate limiter (for single instance) class MemoryRateLimiter { limits = new Map(); config; constructor(config) { this.config = config; // Clean up expired entries every minute setInterval(() => { this.cleanup(); }, 60000); } cleanup() { const now = Date.now(); for (const [key, entry] of this.limits.entries()) { if (now > entry.resetTime) { this.limits.delete(key); } } } generateKey(identifier) { if (this.config.keyGenerator) { return this.config.keyGenerator(identifier); } return createHash('md5').update(identifier).digest('hex'); } async checkLimit(identifier) { const key = this.generateKey(identifier); const now = Date.now(); const entry = this.limits.get(key); if (!entry || now > entry.resetTime) { // Create new entry or reset expired entry this.limits.set(key, { count: 1, resetTime: now + this.config.windowMs }); return { allowed: true, remaining: this.config.maxRequests - 1, resetTime: now + this.config.windowMs, retryAfter: 0 }; } if (entry.count >= this.config.maxRequests) { return { allowed: false, remaining: 0, resetTime: entry.resetTime, retryAfter: Math.ceil((entry.resetTime - now) / 1000) }; } // Increment counter entry.count++; this.limits.set(key, entry); return { allowed: true, remaining: this.config.maxRequests - entry.count, resetTime: entry.resetTime, retryAfter: 0 }; } } // Rate limiter factory export class RateLimiterFactory { static instance; static getInstance() { if (!this.instance) { this.instance = new MemoryRateLimiter({ windowMs: 60000, // 1 minute maxRequests: 100, // 100 requests per minute keyGenerator: (identifier) => { // Include timestamp for better distribution const timestamp = Math.floor(Date.now() / 60000); // 1-minute buckets return createHash('md5').update(`${identifier}:${timestamp}`).digest('hex'); } }); } return this.instance; } static createCustomLimiter(config) { return new MemoryRateLimiter(config); } } // Rate limit decorator export function rateLimit(identifier, customConfig) { return function (target, propertyName, descriptor) { const method = descriptor.value; const limiter = customConfig ? RateLimiterFactory.createCustomLimiter({ windowMs: 60000, maxRequests: 100, ...customConfig }) : RateLimiterFactory.getInstance(); descriptor.value = async function (...args) { const result = await limiter.checkLimit(identifier); if (!result.allowed) { throw new Error(`Rate limit exceeded. Try again in ${result.retryAfter} seconds.`); } return method.apply(this, args); }; }; } // Rate limit middleware export const createRateLimitMiddleware = (config) => { const limiter = new MemoryRateLimiter(config); return async (identifier) => { return await limiter.checkLimit(identifier); }; }; // Default rate limiters export const defaultRateLimiters = { // General API calls api: RateLimiterFactory.createCustomLimiter({ windowMs: 60000, // 1 minute maxRequests: 100 }), // Emission calculations (more restrictive) calculation: RateLimiterFactory.createCustomLimiter({ windowMs: 60000, // 1 minute maxRequests: 50 }), // Report generation (most restrictive) report: RateLimiterFactory.createCustomLimiter({ windowMs: 300000, // 5 minutes maxRequests: 10 }), // Shipper management shipper: RateLimiterFactory.createCustomLimiter({ windowMs: 60000, // 1 minute maxRequests: 20 }) }; export default RateLimiterFactory; //# sourceMappingURL=rateLimiter.js.map