breathe-api
Version:
Model Context Protocol server for Breathe HR APIs with Swagger/OpenAPI support - also works with custom APIs
122 lines • 3.3 kB
JavaScript
import PQueue from 'p-queue';
const RATE_LIMITS = {
api_request: {
concurrency: 3,
interval: 1000,
intervalCap: 5,
timeout: 30000,
},
parse_swagger: {
concurrency: 5,
interval: 1000,
intervalCap: 10,
timeout: 60000,
},
explain_api_feature: {
concurrency: 4,
interval: 1000,
intervalCap: 8,
timeout: 45000,
},
generate_api_client: {
concurrency: 2,
interval: 1000,
intervalCap: 3,
timeout: 120000,
},
cache_stats: {
concurrency: 10,
interval: 1000,
intervalCap: 100,
},
clear_cache: {
concurrency: 10,
interval: 1000,
intervalCap: 20,
},
};
const rateLimiters = new Map();
export function getToolRateLimiter(toolName) {
if (!rateLimiters.has(toolName)) {
const config = RATE_LIMITS[toolName] || {
concurrency: 5,
interval: 1000,
intervalCap: 10,
};
const queue = new PQueue({
concurrency: config.concurrency,
interval: config.interval,
intervalCap: config.intervalCap,
timeout: config.timeout,
throwOnTimeout: true,
});
rateLimiters.set(toolName, queue);
}
return rateLimiters.get(toolName);
}
export function isToolRateLimited(toolName) {
const limiter = rateLimiters.get(toolName);
if (!limiter)
return false;
const config = RATE_LIMITS[toolName];
if (!config)
return false;
return limiter.pending >= config.concurrency || limiter.size > 0;
}
export function getToolRateLimitStatus(toolName) {
const limiter = rateLimiters.get(toolName);
const config = RATE_LIMITS[toolName] || {
concurrency: 5,
intervalCap: 10,
};
if (!limiter) {
return {
pending: 0,
queued: 0,
concurrency: config.concurrency,
intervalCap: config.intervalCap,
isLimited: false,
};
}
return {
pending: limiter.pending,
queued: limiter.size,
concurrency: config.concurrency,
intervalCap: config.intervalCap,
isLimited: isToolRateLimited(toolName),
};
}
export function getAllRateLimitStatus() {
const status = {};
for (const toolName of Object.keys(RATE_LIMITS)) {
status[toolName] = getToolRateLimitStatus(toolName);
}
for (const toolName of rateLimiters.keys()) {
if (!status[toolName]) {
status[toolName] = getToolRateLimitStatus(toolName);
}
}
return status;
}
export function resetToolRateLimiter(toolName) {
const limiter = rateLimiters.get(toolName);
if (limiter) {
limiter.clear();
}
}
export function resetAllRateLimiters() {
for (const limiter of rateLimiters.values()) {
limiter.clear();
}
}
export class RateLimitError extends Error {
toolName;
limitType;
constructor(toolName, limitType, message) {
super(message || `Rate limit exceeded for tool: ${toolName} (${limitType})`);
this.toolName = toolName;
this.limitType = limitType;
this.name = 'RateLimitError';
}
}
//# sourceMappingURL=rate-limiter.js.map