Skip to main content
Glama
rate-limited-handler.ts4.99 kB
/** * Rate-limited handler functionality for Attio MCP server * Provides wrapper functions to apply rate limiting to API handlers */ import { checkFilterRateLimit } from '../utils/rate-limiter.js'; import { FilterValidationError } from '../errors/api-errors.js'; /** * Error response for rate limit exceeded */ interface RateLimitError { error: string; message: string; retryAfter: number; } /** * Generic API response interface */ export interface ApiResponse { content: { type: string; text: string; }[]; isError: boolean; } /** * Request object with response methods */ interface RequestWithResponse { res?: { setHeader: (key: string, value: unknown) => void; status: (code: number) => { json: (data: unknown) => void }; }; [key: string]: unknown; } /** * Apply rate limiting to a filter-based handler * * @param handler - The original handler function * @param endpointName - Name of the endpoint for rate limiting tracking * @returns Rate-limited handler function */ export function withRateLimiting<T extends readonly unknown[], R>( handler: (...args: T) => Promise<R>, endpointName: string ): (...args: T) => Promise<R | ApiResponse> { return async (...args: T) => { // First argument is typically the request object const req: RequestWithResponse = args[0] as RequestWithResponse; // Check rate limit const rateLimit = checkFilterRateLimit(req, endpointName); // If rate limit exceeded, return error if (!rateLimit.allowed) { const error: RateLimitError = { error: 'Rate limit exceeded', message: `Too many requests. Try again in ${Math.ceil( rateLimit.msUntilReset / 1000 )} seconds.`, retryAfter: Math.ceil(rateLimit.msUntilReset / 1000), }; // Format error for API response const response: ApiResponse = { content: [ { type: 'text', text: `Error: ${error.message}`, }, ], isError: true, }; return response; } try { // Call original handler return await handler(...args); } catch (error: unknown) { // Format error for API response const errorMessage = error instanceof Error ? error.message : String(error); const response: ApiResponse = { content: [ { type: 'text', text: `Error: ${errorMessage}`, }, ], isError: true, }; return response; } }; } /** * Apply rate limiting to a search handler with proper headers * * @param handler - The original handler function * @param endpointName - Name of the endpoint for rate limiting tracking * @returns Rate-limited handler function with headers */ export function withSearchRateLimiting<T extends readonly unknown[], R>( handler: (...args: T) => Promise<R>, endpointName: string ): (...args: T) => Promise<R | ApiResponse> { return async (...args: T) => { // First argument is typically the request object const req: RequestWithResponse = args[0] as RequestWithResponse; // Add response object if not present if (!req.res) { req.res = { setHeader: () => {}, status: () => ({ json: () => {} }), }; } // Check rate limit const rateLimit = checkFilterRateLimit(req, endpointName); // Add rate limit headers if (req.res) { req.res.setHeader('X-RateLimit-Limit', 60); req.res.setHeader('X-RateLimit-Remaining', rateLimit.remaining); req.res.setHeader('X-RateLimit-Reset', rateLimit.resetTime); } // If rate limit exceeded, return error if (!rateLimit.allowed) { if (req.res) { req.res.status(429).json({ error: 'Rate limit exceeded', message: `Too many requests. Try again in ${Math.ceil( rateLimit.msUntilReset / 1000 )} seconds.`, retryAfter: Math.ceil(rateLimit.msUntilReset / 1000), }); } // Format error for API response const response: ApiResponse = { content: [ { type: 'text', text: `Error: Rate limit exceeded. Try again in ${Math.ceil( rateLimit.msUntilReset / 1000 )} seconds.`, }, ], isError: true, }; return response; } try { // Call original handler return await handler(...args); } catch (error: unknown) { // Format error for API response const errorMessage = error instanceof FilterValidationError ? error.message : error instanceof Error ? `Unexpected error: ${error.message}` : `Unknown error: ${String(error)}`; const response: ApiResponse = { content: [ { type: 'text', text: `Error: ${errorMessage}`, }, ], isError: true, }; return response; } }; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kesslerio/attio-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server