UNPKG

teams-mcp-server

Version:

Microsoft Teams MCP server with direct messaging support

75 lines (74 loc) 2.49 kB
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 }; } }