teams-mcp-server
Version:
Microsoft Teams MCP server with direct messaging support
75 lines (74 loc) • 2.49 kB
JavaScript
import PQueue from 'p-queue';
import { debugLog } from '../utils/logger.js';
export class RateLimiter {
queue;
requestCount = 0;
windowStart = Date.now();
windowMs = 600000; // 10 minutes
maxRequests = 10000; // Microsoft Graph limit per tenant
constructor() {
this.queue = new PQueue({
concurrency: 10,
interval: 1000,
intervalCap: 20
});
}
async execute(fn) {
return this.queue.add(async () => {
await this.checkRateLimit();
try {
const result = await fn();
this.requestCount++;
return result;
}
catch (error) {
if (error.statusCode === 429) {
const retryAfter = this.getRetryAfter(error);
debugLog(`Rate limited. Retrying after ${retryAfter}ms`);
await this.delay(retryAfter);
return this.execute(fn);
}
throw error;
}
});
}
async checkRateLimit() {
const now = Date.now();
if (now - this.windowStart > this.windowMs) {
this.requestCount = 0;
this.windowStart = now;
}
if (this.requestCount >= this.maxRequests) {
const waitTime = this.windowMs - (now - this.windowStart);
debugLog(`Rate limit reached. Waiting ${waitTime}ms`);
await this.delay(waitTime);
this.requestCount = 0;
this.windowStart = Date.now();
}
}
getRetryAfter(error) {
const retryAfter = error.headers?.['retry-after'];
if (retryAfter) {
const seconds = parseInt(retryAfter, 10);
if (!isNaN(seconds)) {
return seconds * 1000;
}
}
return Math.min(30000, Math.pow(2, Math.min(this.requestCount, 10)) * 1000);
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
getStats() {
const now = Date.now();
const windowRemaining = Math.max(0, this.windowMs - (now - this.windowStart));
const requestsRemaining = Math.max(0, this.maxRequests - this.requestCount);
return {
requestsUsed: this.requestCount,
requestsRemaining,
windowResetInMs: windowRemaining,
queueSize: this.queue.size,
queuePending: this.queue.pending
};
}
}