Skip to main content
Glama

MCP Image Generator

by shinpr
errors.ts•8.98 kB
/** * Custom error classes for MCP server * Provides specific error types with structured error codes and suggestions */ /** * Structured error format for consistent error reporting */ export interface StructuredError { code: string message: string suggestion: string timestamp: string context?: Record<string, unknown> } /** * Result type pattern for explicit error handling */ export type Result<T, E> = { ok: true; value: T } | { ok: false; error: E } /** * Base class for all application errors with structured error support */ export abstract class BaseError extends Error { abstract readonly code: string abstract readonly suggestion: string readonly timestamp: string readonly context: Record<string, unknown> | undefined constructor(message: string, context?: Record<string, unknown>) { super(message) this.name = this.constructor.name this.timestamp = new Date().toISOString() this.context = context } toStructuredError(): StructuredError { return { code: this.code, message: this.message, suggestion: this.suggestion, timestamp: this.timestamp, ...(this.context && { context: this.context }), } } } /** * Error for input validation failures */ export class InputValidationError extends BaseError { readonly code = 'INPUT_VALIDATION_ERROR' constructor( message: string, public readonly suggestion: string ) { super(message) } } /** * Error for file operation failures with intelligent suggestion system */ export class FileOperationError extends BaseError { readonly code = 'FILE_OPERATION_ERROR' get suggestion(): string { const message = this.message.toLowerCase() if ( message.includes('permission') || message.includes('eacces') || message.includes('access denied') ) { return 'Check file and directory permissions for the output path' } if (message.includes('space') || message.includes('enospc') || message.includes('disk full')) { return 'Free up disk space or choose a different output directory' } if ( message.includes('enoent') || message.includes('no such file') || message.includes('not found') ) { return 'Ensure the output directory exists and is accessible' } if ( message.includes('emfile') || message.includes('too many') || message.includes('file descriptor') ) { return 'Close unused files or restart the application to free file handles' } if (message.includes('readonly') || message.includes('read-only')) { return 'Choose a writable directory for file output' } return 'Check file system permissions and available disk space' } } /** * Error for Gemini API failures with intelligent suggestion system */ export class GeminiAPIError extends BaseError { readonly code = 'GEMINI_API_ERROR' private customSuggestion?: string constructor( message: string, suggestionOrContext?: string | Record<string, unknown>, statusCodeOrContext?: number | Record<string, unknown> ) { let context: Record<string, unknown> | undefined let statusCode: number | undefined // Handle backward compatibility with old constructor signature if (typeof suggestionOrContext === 'string') { // Old signature: (message, suggestion, statusCode?) statusCode = typeof statusCodeOrContext === 'number' ? statusCodeOrContext : undefined } else { // New signature: (message, context?, statusCode?) context = suggestionOrContext statusCode = typeof statusCodeOrContext === 'number' ? statusCodeOrContext : undefined } super(message, context) if (typeof suggestionOrContext === 'string') { this.customSuggestion = suggestionOrContext } Object.defineProperty(this, 'statusCode', { value: statusCode, writable: false }) } get suggestion(): string { // Use custom suggestion if provided (backward compatibility) if (this.customSuggestion) { return this.customSuggestion } // Check if suggestion is in context if ( this.context && 'suggestion' in this.context && typeof this.context['suggestion'] === 'string' ) { return this.context['suggestion'] } // Otherwise use intelligent suggestion system const message = this.message.toLowerCase() if (message.includes('authentication') || message.includes('unauthorized')) { return 'Check GEMINI_API_KEY environment variable and ensure it has proper permissions' } if (message.includes('rate limit') || message.includes('quota') || message.includes('429')) { return 'Wait before retrying or upgrade API quota limits' } if (message.includes('model') || message.includes('access') || message.includes('permission')) { return 'Ensure you have access to gemini-2.5-flash-image-preview model' } if (message.includes('timeout') || message.includes('503') || message.includes('502')) { return 'The service is temporarily unavailable. Please retry after a few moments' } if (message.includes('payload') || message.includes('request') || message.includes('400')) { return 'Check request format and parameters according to API specification' } return 'Check API configuration and retry the request' } } /** * Error for network-related failures with intelligent suggestion system */ export class NetworkError extends BaseError { readonly code = 'NETWORK_ERROR' private customSuggestion?: string constructor( message: string, suggestionOrContext?: string | Record<string, unknown>, causeOrContext?: Error | Record<string, unknown> ) { let context: Record<string, unknown> | undefined let cause: Error | undefined // Handle backward compatibility with old constructor signature if (typeof suggestionOrContext === 'string') { // Old signature: (message, suggestion, cause?) cause = causeOrContext instanceof Error ? causeOrContext : undefined } else { // New signature: (message, context?, cause?) context = suggestionOrContext cause = causeOrContext instanceof Error ? causeOrContext : undefined } super(message, context) if (typeof suggestionOrContext === 'string') { this.customSuggestion = suggestionOrContext } Object.defineProperty(this, 'cause', { value: cause, writable: false }) } get suggestion(): string { // Use custom suggestion if provided (backward compatibility) if (this.customSuggestion) { return this.customSuggestion } // Otherwise use intelligent suggestion system const message = this.message.toLowerCase() const stack = this.stack?.toLowerCase() || '' if (message.includes('timeout') || message.includes('etimedout')) { return 'Check network connection stability and retry with higher timeout' } if (message.includes('dns') || message.includes('enotfound') || stack.includes('getaddrinfo')) { return 'Check internet connection and DNS settings' } if (message.includes('econnrefused') || message.includes('connection refused')) { return 'Service may be temporarily unavailable, please retry later' } if (message.includes('econnreset') || message.includes('connection reset')) { return 'Network connection was interrupted, please retry' } if (message.includes('proxy') || message.includes('tunnel')) { return 'Check proxy settings and firewall configuration' } return 'Check network connectivity and firewall settings' } } /** * Error for configuration failures */ export class ConfigError extends BaseError { readonly code = 'CONFIG_ERROR' constructor( message: string, public readonly suggestion: string ) { super(message) } } /** * Error for security violations and attacks with intelligent suggestion system */ export class SecurityError extends BaseError { readonly code = 'SECURITY_ERROR' get suggestion(): string { const message = this.message.toLowerCase() if (message.includes('null byte')) { return 'Ensure your request meets security requirements' } if (message.includes('path') || message.includes('traversal') || message.includes('..')) { return 'Use valid file paths within allowed directories only' } if ( message.includes('extension') || message.includes('filetype') || message.includes('format') ) { return 'Use supported file extensions: .png, .jpg, .jpeg, .webp' } if (message.includes('size') || message.includes('large') || message.includes('limit')) { return 'Ensure file size is within allowed limits (max 10MB)' } if (message.includes('malicious') || message.includes('suspicious')) { return 'The request contains potentially harmful content. Please review and try again' } return 'Ensure your request meets security requirements' } }

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/shinpr/mcp-image'

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