Skip to main content
Glama
error-handler.ts7.8 kB
import { Logger } from '../logger.js'; export interface MCPError { type: 'user' | 'system' | 'permission' | 'validation' | 'connection'; message: string; details?: any; suggestions?: string[]; recoverable: boolean; } export class ErrorHandler { private logger: Logger; constructor(logger: Logger) { this.logger = logger.child({ component: 'ErrorHandler' }); } /** * Map Foundry errors to user-friendly MCP errors */ mapFoundryError(error: any, context: string): MCPError { const errorMessage = error instanceof Error ? error.message : String(error); const errorLower = errorMessage.toLowerCase(); // Permission errors if (errorLower.includes('access denied') || errorLower.includes('permission')) { return { type: 'permission', message: 'Permission denied for this operation', details: errorMessage, suggestions: [ 'Check module settings in Foundry VTT', 'Ensure you have the required permissions', 'Ask a GM to enable this feature' ], recoverable: true, }; } // Connection errors if (errorLower.includes('connection') || errorLower.includes('websocket') || errorLower.includes('timeout')) { return { type: 'connection', message: 'Connection to Foundry VTT failed', details: errorMessage, suggestions: [ 'Ensure Foundry VTT is running', 'Check that the MCP Bridge module is enabled', 'Verify connection settings in module configuration' ], recoverable: true, }; } // Validation errors if (errorLower.includes('not found') || errorLower.includes('invalid') || errorLower.includes('missing')) { if (context.includes('compendium') || context.includes('creature')) { return { type: 'validation', message: 'Creature not found in compendiums', details: errorMessage, suggestions: [ 'Try searching with a different creature name', 'Check if the compendium pack is available', 'Use more specific terms (e.g., "goblin warrior" instead of "goblin")' ], recoverable: true, }; } return { type: 'validation', message: 'Invalid request or missing data', details: errorMessage, suggestions: [ 'Check that all required parameters are provided', 'Verify the data exists in Foundry VTT' ], recoverable: true, }; } // Actor creation specific errors if (errorLower.includes('actor creation') || errorLower.includes('create actor')) { return { type: 'system', message: 'Failed to create actor in Foundry VTT', details: errorMessage, suggestions: [ 'Check that the source compendium entry is valid', 'Ensure Foundry VTT has sufficient permissions', 'Try creating actors one at a time instead of in bulk' ], recoverable: true, }; } // Scene/token errors if (errorLower.includes('scene') || errorLower.includes('token')) { return { type: 'system', message: 'Failed to modify scene or place tokens', details: errorMessage, suggestions: [ 'Ensure a scene is currently active', 'Check scene modification permissions', 'Try creating actors without adding to scene' ], recoverable: true, }; } // Transaction/rollback errors if (errorLower.includes('rollback') || errorLower.includes('transaction')) { return { type: 'system', message: 'Operation was rolled back due to errors', details: errorMessage, suggestions: [ 'The system prevented partial failures by undoing changes', 'Try the operation again with different parameters', 'Check Foundry VTT console for more details' ], recoverable: true, }; } // Generic system errors return { type: 'system', message: 'An unexpected error occurred', details: errorMessage, suggestions: [ 'Check Foundry VTT console for more details', 'Try the operation again', 'Contact support if the issue persists' ], recoverable: false, }; } /** * Format error for Claude response */ formatErrorForClaude(mcpError: MCPError, toolName: string): string { const typeEmoji = this.getErrorEmoji(mcpError.type); const recoveryText = mcpError.recoverable ? '🔄 **This can be fixed**' : '⚠️ **System error**'; let message = `${typeEmoji} **${mcpError.message}**\n\n${recoveryText}`; if (mcpError.suggestions && mcpError.suggestions.length > 0) { message += '\n\n**Suggestions:**\n'; message += mcpError.suggestions.map(suggestion => `• ${suggestion}`).join('\n'); } if (mcpError.type === 'validation' && toolName === 'create-actor-from-compendium') { message += '\n\n💡 **Tip:** Try using the `search-compendium` tool first to see what creatures are available.'; } return message; } /** * Log error with appropriate level */ logError(mcpError: MCPError, toolName: string, originalError?: any): void { const logData = { toolName, errorType: mcpError.type, message: mcpError.message, recoverable: mcpError.recoverable, details: mcpError.details, }; switch (mcpError.type) { case 'user': case 'validation': this.logger.warn('User/validation error', logData); break; case 'permission': this.logger.warn('Permission error', logData); break; case 'connection': this.logger.error('Connection error', logData); break; case 'system': default: this.logger.error('System error', logData); if (originalError) { this.logger.error('Original error details', originalError); } break; } } /** * Get emoji for error type */ private getErrorEmoji(type: MCPError['type']): string { switch (type) { case 'user': return '👤'; case 'validation': return '❌'; case 'permission': return '🔒'; case 'connection': return '🔌'; case 'system': return '⚙️'; default: return '❓'; } } /** * Handle tool execution error with proper formatting */ handleToolError(error: any, toolName: string, context: string = ''): never { const mcpError = this.mapFoundryError(error, `${toolName} ${context}`.trim()); this.logError(mcpError, toolName, error); const formattedMessage = this.formatErrorForClaude(mcpError, toolName); throw new Error(formattedMessage); } /** * Create validation error for missing parameters */ createValidationError(message: string, suggestions: string[] = []): MCPError { return { type: 'validation', message, suggestions: [ ...suggestions, 'Check the tool documentation for required parameters' ], recoverable: true, }; } /** * Create permission error with helpful context */ createPermissionError(operation: string, setting?: string): MCPError { const suggestions = [ 'Ask a GM to enable this feature in Foundry VTT', 'Check the MCP Bridge module settings' ]; if (setting) { suggestions.unshift(`Enable the "${setting}" setting in the MCP Bridge module`); } return { type: 'permission', message: `${operation} is not allowed`, suggestions, recoverable: true, }; } } // Note: ErrorHandler should be instantiated with a proper logger, not exported as singleton

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/adambdooley/foundry-vtt-mcp'

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