Skip to main content
Glama
error-handling.ts8.57 kB
/** * Error Handling Utilities * Provides standardized error handling and logging for the Figma MCP Server */ export enum ErrorType { VALIDATION_ERROR = "VALIDATION_ERROR", API_ERROR = "API_ERROR", NETWORK_ERROR = "NETWORK_ERROR", PROCESSING_ERROR = "PROCESSING_ERROR", AUTHENTICATION_ERROR = "AUTHENTICATION_ERROR", RATE_LIMIT_ERROR = "RATE_LIMIT_ERROR", NOT_FOUND = "NOT_FOUND", UNKNOWN_ERROR = "UNKNOWN_ERROR", } export interface ErrorContext { operation: string; input?: any; timestamp: string; requestId?: string; fileKey?: string; nodeId?: string; } /** * Log levels for controlling output verbosity */ export enum LogLevel { DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3, } /** * Logger configuration */ interface LoggerConfig { level: LogLevel; enableColors: boolean; enableTimestamps: boolean; } /** * Enhanced logger interface with proper formatting and log levels */ class Logger { private config: LoggerConfig; constructor() { this.config = { level: process.env.LOG_LEVEL ? parseInt(process.env.LOG_LEVEL) : process.env.NODE_ENV === "development" ? LogLevel.DEBUG : LogLevel.INFO, enableColors: process.env.NO_COLOR !== "true", enableTimestamps: true, }; } private shouldLog(level: LogLevel): boolean { return level >= this.config.level; } private formatMessage(level: string, message: string, context?: any): string { const timestamp = this.config.enableTimestamps ? new Date().toISOString() : ""; const contextStr = context ? ` | Context: ${JSON.stringify(context, null, 2)}` : ""; return `[${level}] ${timestamp}: ${message}${contextStr}`; } private getColorCode(level: string): string { if (!this.config.enableColors) return ""; switch (level) { case "DEBUG": return "\x1b[36m"; // Cyan case "INFO": return "\x1b[32m"; // Green case "WARN": return "\x1b[33m"; // Yellow case "ERROR": return "\x1b[31m"; // Red default: return ""; } } private resetColor(): string { return this.config.enableColors ? "\x1b[0m" : ""; } debug(message: string, context?: any): void { if (!this.shouldLog(LogLevel.DEBUG)) return; const colorCode = this.getColorCode("DEBUG"); const resetCode = this.resetColor(); const formattedMessage = this.formatMessage("DEBUG", message, context); //console.debug(`${colorCode}${formattedMessage}${resetCode}`); } info(message: string, context?: any): void { if (!this.shouldLog(LogLevel.INFO)) return; const colorCode = this.getColorCode("INFO"); const resetCode = this.resetColor(); const formattedMessage = this.formatMessage("INFO", message, context); //console.error(`${formattedMessage}`); } warn(message: string, context?: any): void { if (!this.shouldLog(LogLevel.WARN)) return; const colorCode = this.getColorCode("WARN"); const resetCode = this.resetColor(); const formattedMessage = this.formatMessage("WARN", message, context); //console.warn(`${colorCode}${formattedMessage}${resetCode}`); } error(message: string, context?: any): void { if (!this.shouldLog(LogLevel.ERROR)) return; const colorCode = this.getColorCode("ERROR"); const resetCode = this.resetColor(); const formattedMessage = this.formatMessage("ERROR", message, context); console.error(`${colorCode}${formattedMessage}${resetCode}`); } /** * Log performance metrics */ performance(operation: string, duration: number, context?: any): void { this.info(`Performance: ${operation} completed in ${duration}ms`, context); } /** * Log API requests */ apiRequest(method: string, url: string, context?: any): void { this.debug(`API Request: ${method} ${url}`, context); } /** * Log API responses */ apiResponse( method: string, url: string, status: number, duration: number, context?: any ): void { const level = status >= 400 ? "warn" : "debug"; this[level]( `API Response: ${method} ${url} - ${status} (${duration}ms)`, context ); } /** * Log memory usage */ memory(operation: string): void { if (!this.shouldLog(LogLevel.DEBUG)) return; const usage = process.memoryUsage(); this.debug(`Memory usage after ${operation}`, { rss: `${Math.round(usage.rss / 1024 / 1024)}MB`, heapTotal: `${Math.round(usage.heapTotal / 1024 / 1024)}MB`, heapUsed: `${Math.round(usage.heapUsed / 1024 / 1024)}MB`, external: `${Math.round(usage.external / 1024 / 1024)}MB`, }); } /** * Set log level dynamically */ setLevel(level: LogLevel): void { this.config.level = level; } /** * Get current log level */ getLevel(): LogLevel { return this.config.level; } } export const logger = new Logger(); export class FigmaServerError extends Error { public readonly type: ErrorType; public readonly context: ErrorContext; public readonly statusCode: number; constructor( message: string, type: ErrorType, context: ErrorContext, statusCode: number = 500 ) { super(message); this.name = "FigmaServerError"; this.type = type; this.context = context; this.statusCode = statusCode; } } /** * Creates a standardized error response for MCP tools */ export const createErrorResponse = ( error: Error | FigmaServerError, operation: string ) => { let errorType = ErrorType.UNKNOWN_ERROR; let statusCode = 500; if (error instanceof FigmaServerError) { errorType = error.type; statusCode = error.statusCode; } logger.error(`Error in ${operation}:`, { type: errorType, message: error.message, stack: error.stack, timestamp: new Date().toISOString(), }); return { isError: true, content: [ { type: "text" as const, text: `Error: ${error.message}`, }, ], }; }; /** * Wraps async operations with error handling */ export const withErrorHandling = <T extends any[], R>( operation: string, fn: (...args: T) => Promise<R> ) => { return async (...args: T): Promise<R> => { try { return await fn(...args); } catch (error) { if (error instanceof FigmaServerError) { throw error; } // Convert unknown errors to FigmaServerError throw new FigmaServerError( error instanceof Error ? error.message : "Unknown error occurred", ErrorType.UNKNOWN_ERROR, { operation, input: args, timestamp: new Date().toISOString(), } ); } }; }; /** * Handles Axios errors specifically */ export const handleAxiosError = (error: any, operation: string): never => { if (error.response) { // Server responded with error status const status = error.response.status; let errorType = ErrorType.API_ERROR; switch (status) { case 401: case 403: errorType = ErrorType.AUTHENTICATION_ERROR; break; case 429: errorType = ErrorType.RATE_LIMIT_ERROR; break; case 400: errorType = ErrorType.VALIDATION_ERROR; break; } throw new FigmaServerError( `Figma API error: ${error.response.data?.message || error.message}`, errorType, { operation, timestamp: new Date().toISOString(), }, status ); } else if (error.request) { // Network error throw new FigmaServerError( "Network error: Unable to reach Figma API", ErrorType.NETWORK_ERROR, { operation, timestamp: new Date().toISOString(), }, 503 ); } else { // Other error throw new FigmaServerError( error.message || "Unknown error occurred", ErrorType.UNKNOWN_ERROR, { operation, timestamp: new Date().toISOString(), } ); } }; /** * Timeout wrapper for promises */ export const withTimeout = <T>( promise: Promise<T>, timeoutMs: number, operation: string ): Promise<T> => { return Promise.race([ promise, new Promise<never>((_, reject) => { setTimeout(() => { reject( new FigmaServerError( `Operation timed out after ${timeoutMs}ms`, ErrorType.NETWORK_ERROR, { operation, timestamp: new Date().toISOString(), }, 408 ) ); }, timeoutMs); }), ]); };

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/toddle-edu/figma-mcp-server'

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