@cyanheads/toolkit-mcp-server
Version:
MCP server providing system, network, geolocation, generator, datetime, and security tools
82 lines (81 loc) • 3.1 kB
JavaScript
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);