Skip to main content
Glama
error-handler.ts5.97 kB
/** * Global Error Handling Middleware * * Comprehensive error handling middleware for Express applications * with MCP protocol compliance and structured error responses */ import { Request, Response } from 'express'; import { ErrorCategory, StructuredError } from '../server'; /** * MCP Error Response format */ export interface MCPErrorResponse { error: { code: string; message: string; category: ErrorCategory; requestId?: string; timestamp: string; details?: Record<string, any>; }; } /** * Global error handling middleware for Express * Catches all unhandled errors and formats them according to MCP protocol */ export function globalErrorHandler( error: Error | StructuredError, req: Request, res: Response, _next: Function ): void { const requestId = (req as any).requestId || 'unknown'; const timestamp = new Date().toISOString(); // Default error values let statusCode = 500; let category = ErrorCategory.INTERNAL; let code = 'INTERNAL_ERROR'; // Handle structured errors if (isStructuredError(error)) { statusCode = error.statusCode; category = error.category; code = error.code; } else { // Categorize standard errors ({ statusCode, category, code } = categorizeError(error)); } // Create MCP-compliant error response const errorResponse: MCPErrorResponse = { error: { code, message: error.message || 'An unexpected error occurred', category, requestId, timestamp, }, }; // Add additional details for non-production environments if (process.env.NODE_ENV !== 'production') { errorResponse.error.details = { stack: error.stack, name: error.name, }; } // Set appropriate headers res.status(statusCode); res.setHeader('Content-Type', 'application/json'); // Send error response res.json(errorResponse); // Don't call next() - we're handling the error here } /** * Async error wrapper for Express route handlers * Catches promise rejections and passes them to error middleware */ export function asyncErrorHandler( fn: (req: Request, res: Response, next: Function) => Promise<any> ) { return (req: Request, res: Response, next: Function) => { Promise.resolve(fn(req, res, next)).catch((error) => next(error)); }; } /** * Not Found (404) middleware * Handles requests to non-existent routes */ export function notFoundHandler(req: Request, _res: Response, next: Function): void { const error = createNotFoundError(req.path, (req as any).requestId); next(error); } /** * Unhandled rejection handler for process-level errors */ export function setupProcessErrorHandlers(logger?: (level: string, message: string, error?: Error) => void): void { process.on('unhandledRejection', (reason: any, _promise: Promise<any>) => { const message = `Unhandled Promise Rejection: ${reason}`; if (logger) { logger('error', message, reason instanceof Error ? reason : new Error(String(reason))); } else { console.error(message, reason); } // In production, you might want to exit the process // process.exit(1); }); process.on('uncaughtException', (error: Error) => { const message = `Uncaught Exception: ${error.message}`; if (logger) { logger('fatal', message, error); } else { console.error(message, error); } // Exit the process for uncaught exceptions process.exit(1); }); } /** * Check if error is a structured error */ function isStructuredError(error: any): error is StructuredError { return error && typeof error.category === 'string' && typeof error.code === 'string' && typeof error.statusCode === 'number' && typeof error.isOperational === 'boolean'; } /** * Categorize standard JavaScript errors */ function categorizeError(error: Error): { statusCode: number; category: ErrorCategory; code: string } { const message = error.message.toLowerCase(); // Network-related errors if (message.includes('econnrefused') || message.includes('enotfound') || message.includes('timeout')) { return { statusCode: 503, category: ErrorCategory.NETWORK, code: 'NETWORK_ERROR', }; } // Validation errors if (message.includes('validation') || message.includes('invalid') || message.includes('required')) { return { statusCode: 400, category: ErrorCategory.VALIDATION, code: 'VALIDATION_ERROR', }; } // Authentication errors if (message.includes('unauthorized') || message.includes('authentication')) { return { statusCode: 401, category: ErrorCategory.AUTHENTICATION, code: 'AUTHENTICATION_ERROR', }; } // Authorization errors if (message.includes('forbidden') || message.includes('permission')) { return { statusCode: 403, category: ErrorCategory.AUTHORIZATION, code: 'AUTHORIZATION_ERROR', }; } // Rate limiting if (message.includes('rate limit') || message.includes('too many requests')) { return { statusCode: 429, category: ErrorCategory.RATE_LIMIT, code: 'RATE_LIMIT_ERROR', }; } // Timeout errors if (message.includes('timeout')) { return { statusCode: 408, category: ErrorCategory.TIMEOUT, code: 'TIMEOUT_ERROR', }; } // Default to internal error return { statusCode: 500, category: ErrorCategory.INTERNAL, code: 'INTERNAL_ERROR', }; } /** * Create a structured not found error */ function createNotFoundError(path: string, requestId?: string): StructuredError { const error = new Error(`Route not found: ${path}`) as StructuredError; error.category = ErrorCategory.VALIDATION; error.code = 'ROUTE_NOT_FOUND'; error.statusCode = 404; error.isOperational = true; if (requestId) { error.requestId = requestId; } error.context = { path }; return error; }

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/learnwithcc/tally-mcp'

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