Skip to main content
Glama

Todoist MCP Server

error-handling.ts8.31 kB
/** * Standardized error handling utilities for the Todoist MCP server */ import { TodoistAPIError, ValidationError, TaskNotFoundError, LabelNotFoundError, AuthenticationError, } from "../errors.js"; /** * Standard error context interface for providing additional error information */ export interface ErrorContext { operation: string; entityType?: string; entityId?: string; userId?: string; timestamp?: string; additionalInfo?: Record<string, unknown>; } /** * Error severity levels for logging and monitoring */ /* eslint-disable no-unused-vars */ export enum ErrorSeverity { LOW = "low", MEDIUM = "medium", HIGH = "high", CRITICAL = "critical", } /* eslint-enable no-unused-vars */ // Enum values available as ErrorSeverity.LOW, etc. /** * Enhanced error information for structured error handling */ export interface EnhancedError { originalError: Error; context: ErrorContext; severity: ErrorSeverity; isRetryable: boolean; errorCode: string; } /** * Centralized error handler class for consistent error processing */ export class ErrorHandler { /** * Handles API-related errors with context and throws appropriate error types * * @param operation - Description of the operation that failed * @param error - The original error that occurred * @param context - Additional context about the operation * @throws TodoistAPIError with enhanced error information */ static handleAPIError(operation: string, error: unknown): never { // Enhanced context for potential future use // const enhancedContext: ErrorContext = { // operation, // timestamp: new Date().toISOString(), // ...context, // }; if (error instanceof Error) { // Check for authentication errors if (this.isAuthenticationError(error)) { throw new AuthenticationError(); } // Check for validation errors if (this.isValidationError(error)) { throw new ValidationError( `Validation failed during ${operation}: ${error.message}` ); } throw new TodoistAPIError(`Failed to ${operation}`, error); } throw new TodoistAPIError( `Failed to ${operation}`, new Error(String(error)) ); } /** * Handles task search errors with specific error types * * @param taskName - Name of the task that wasn't found * @param operation - The operation that was attempted * @param context - Additional context * @throws TaskNotFoundError */ static handleTaskNotFound(taskName: string): never { // Enhanced context for potential future use // const enhancedContext: ErrorContext = { // operation, // entityType: "task", // entityId: taskName, // timestamp: new Date().toISOString(), // ...context, // }; throw new TaskNotFoundError(taskName); } /** * Handles label search errors with specific error types * * @param labelIdentifier - Name or ID of the label that wasn't found * @param operation - The operation that was attempted * @param context - Additional context * @throws LabelNotFoundError */ static handleLabelNotFound(labelIdentifier: string): never { // Enhanced context for potential future use // const enhancedContext: ErrorContext = { // operation, // entityType: "label", // entityId: labelIdentifier, // timestamp: new Date().toISOString(), // ...context, // }; throw new LabelNotFoundError(labelIdentifier); } /** * Handles validation errors with detailed context * * @param field - The field that failed validation * @param value - The invalid value * @param operation - The operation that was attempted * @param validationRule - Description of the validation rule that was violated * @throws ValidationError */ static handleValidationError( field: string, value: unknown, operation: string, validationRule: string ): never { const message = `Validation failed for field '${field}' during ${operation}: ${validationRule}. Received: ${String(value)}`; throw new ValidationError(message); } /** * Wraps async operations with standardized error handling * * @param operation - Description of the operation * @param asyncFn - The async function to execute * @returns Promise that resolves to the operation result */ static async wrapAsync<T>( operation: string, asyncFn: () => Promise<T> ): Promise<T> { try { return await asyncFn(); } catch (error) { this.handleAPIError(operation, error); } } /** * Creates an enhanced error object with additional metadata * * @param error - The original error * @param context - Error context * @param severity - Error severity level * @param isRetryable - Whether the operation can be retried * @returns Enhanced error object */ static createEnhancedError( error: Error, context: ErrorContext, severity: ErrorSeverity = ErrorSeverity.MEDIUM, isRetryable = false ): EnhancedError { return { originalError: error, context, severity, isRetryable, errorCode: this.generateErrorCode(error, context), }; } /** * Determines if an error is an authentication error based on its properties * * @param error - The error to check * @returns boolean indicating if it's an authentication error */ private static isAuthenticationError(error: Error): boolean { const message = error.message.toLowerCase(); return ( message.includes("unauthorized") || message.includes("authentication") || message.includes("invalid token") || message.includes("forbidden") || message.includes("401") || message.includes("403") ); } /** * Determines if an error is a validation error based on its properties * * @param error - The error to check * @returns boolean indicating if it's a validation error */ private static isValidationError(error: Error): boolean { const message = error.message.toLowerCase(); return ( message.includes("validation") || message.includes("invalid input") || message.includes("bad request") || message.includes("400") ); } /** * Generates a unique error code for tracking and debugging * * @param error - The original error * @param context - Error context * @returns Generated error code */ private static generateErrorCode( error: Error, context: ErrorContext ): string { const timestamp = context.timestamp || new Date().toISOString(); const operation = context.operation.replace(/\s+/g, "_").toUpperCase(); const errorType = error.constructor.name.toUpperCase(); const hash = Math.abs( (error.message + timestamp).split("").reduce((a, b) => { a = (a << 5) - a + b.charCodeAt(0); return a & a; }, 0) ) .toString(16) .substring(0, 6); return `${errorType}_${operation}_${hash}`; } } /** * Decorator function for automatic error handling in async methods * * @param operation - Description of the operation * @param context - Additional context for error handling * @returns Method decorator */ export function withErrorHandling(operation: string): MethodDecorator { return function ( _target: unknown, _propertyName: string | symbol, descriptor: PropertyDescriptor ): void { const method = descriptor.value; descriptor.value = async function (...args: unknown[]): Promise<unknown> { try { return await method.apply(this, args); } catch (error) { ErrorHandler.handleAPIError(operation, error); } }; }; } /** * Utility function to safely execute operations with error handling * * @param operation - Description of the operation * @param fn - Function to execute * @param defaultValue - Default value to return on error * @returns Result of the function or default value */ export async function safeExecute<T>( operation: string, fn: () => Promise<T>, defaultValue: T ): Promise<T> { try { return await fn(); } catch (error) { console.error(`Safe execution failed for ${operation}:`, error); return defaultValue; } }

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/greirson/mcp-todoist'

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