Skip to main content
Glama

LLM Gateway MCP Server

exceptions.py16.4 kB
""" Comprehensive exception system for the Ultimate MCP Server. This module implements a hierarchical, structured exception system designed to provide consistent error handling, reporting, and formatting across the MCP ecosystem. The exceptions are designed to support both internal error handling and MCP protocol-compliant error responses for client applications and LLMs. Key design principles: 1. HIERARCHICAL: Exception types form a logical inheritance tree with ToolError at the root 2. CONTEXTUAL: Exceptions carry rich metadata including error codes, details, and context 3. FORMATTABLE: All exceptions can be converted to standard response dictionaries 4. TRACEABLE: Original causes and stack traces are preserved for debugging 5. ACTIONABLE: Error responses include specific information to aid recovery Exception hierarchy: - ToolError (base class) ├── ToolInputError (parameter validation issues) ├── ToolExecutionError (runtime execution failures) ├── ProviderError (LLM provider issues) │ ├── RateLimitError (provider throttling) │ └── AuthenticationError (auth failures) ├── ResourceError (resource access/manipulation) ├── ValidationError (general validation issues) ├── ConfigurationError (config problems) └── StorageError (storage operation failures) The module also provides a format_error_response() function that standardizes any exception (including non-ToolError exceptions) into a consistent error response format compatible with the MCP protocol. Example usage: ```python # Raising a specific exception with context if not os.path.exists(file_path): raise ResourceError( message="Cannot access required resource", resource_type="file", resource_id=file_path ) # Catching and formatting an error try: result = process_data(data) except Exception as e: # Convert to standard response format for API error_response = format_error_response(e) return error_response ``` """ import traceback from typing import Any, Dict class ToolError(Exception): """ Base exception class for all tool-related errors in the Ultimate MCP Server. ToolError serves as the foundation of the MCP error handling system, providing a consistent interface for reporting, formatting, and categorizing errors that occur during tool execution. All specialized error types in the system inherit from this class, ensuring consistent error handling across the codebase. This exception class enhances Python's standard Exception with: - Error codes: Standardized identifiers for error categorization and programmatic handling - HTTP status mapping: Optional association with HTTP status codes for API responses - Detailed context: Support for rich error details and contextual information - Structured formatting: Conversion to standardized error response dictionaries The error hierarchy is designed to provide increasingly specific error types while maintaining a consistent structure that can be easily interpreted by error handlers, logging systems, and API responses. Error responses created from ToolError instances follow the MCP protocol format and include consistent fields for error type, message, details, and context. Usage example: ```python try: # Some operation that might fail result = process_data(data) except ToolInputError as e: # Handle input validation errors specifically log_validation_error(e) except ToolError as e: # Handle all other tool errors generically report_tool_error(e) ``` """ def __init__(self, message, error_code=None, details=None, context=None, http_status_code: int | None = None): """Initialize the tool error. Args: message: Error message error_code: Error code (for categorization) details: Additional error details dictionary context: Context dictionary (will be merged into details and stored) http_status_code: Optional HTTP status code associated with the error. """ self.error_code = error_code or "TOOL_ERROR" self.http_status_code = http_status_code # Combine details and context, giving precedence to context if keys overlap combined_details = details.copy() if details else {} # Start with a copy of details or empty dict if context and isinstance(context, dict): combined_details.update(context) # Merge context into the combined dict self.details = combined_details # Store the combined dictionary self.context = context or {} # Also store original context separately for compatibility super().__init__(message) class ToolInputError(ToolError): """Exception raised for errors in the tool input parameters.""" def __init__(self, message, param_name=None, expected_type=None, provided_value=None, details=None): """Initialize the tool input error. Args: message: Error message param_name: Name of the problematic parameter expected_type: Expected parameter type provided_value: Value that was provided details: Additional error details """ error_details = details or {} if param_name: error_details["param_name"] = param_name if expected_type: error_details["expected_type"] = str(expected_type) if provided_value is not None: error_details["provided_value"] = str(provided_value) super().__init__( message, error_code="INVALID_PARAMETER", details=error_details ) class ToolExecutionError(ToolError): """Exception raised when a tool execution fails.""" def __init__(self, message, cause=None, details=None): """Initialize the tool execution error. Args: message: Error message cause: Original exception that caused the error details: Additional error details """ error_details = details or {} if cause: error_details["cause"] = str(cause) error_details["traceback"] = traceback.format_exc() super().__init__( message, error_code="EXECUTION_ERROR", details=error_details ) class ProviderError(ToolError): """Exception raised for provider-specific errors.""" def __init__(self, message, provider=None, model=None, cause=None, details=None): """Initialize the provider error. Args: message: Error message provider: Name of the provider model: Model name cause: Original exception that caused the error details: Additional error details """ error_details = details or {} if provider: error_details["provider"] = provider if model: error_details["model"] = model if cause: error_details["cause"] = str(cause) error_details["traceback"] = traceback.format_exc() super().__init__( message, error_code="PROVIDER_ERROR", details=error_details ) class ResourceError(ToolError): """Exception raised for resource-related errors.""" def __init__(self, message, resource_type=None, resource_id=None, cause=None, details=None): """Initialize the resource error. Args: message: Error message resource_type: Type of resource (e.g., "document", "embedding") resource_id: Resource identifier cause: Original exception that caused the error details: Additional error details """ error_details = details or {} if resource_type: error_details["resource_type"] = resource_type if resource_id: error_details["resource_id"] = resource_id if cause: error_details["cause"] = str(cause) super().__init__( message, error_code="RESOURCE_ERROR", details=error_details ) class RateLimitError(ProviderError): """Exception raised when a provider's rate limit is reached.""" def __init__(self, message, provider=None, retry_after=None, details=None): """Initialize the rate limit error. Args: message: Error message provider: Name of the provider retry_after: Seconds to wait before retrying details: Additional error details """ error_details = details or {} if retry_after is not None: error_details["retry_after"] = retry_after super().__init__( message, provider=provider, error_code="RATE_LIMIT_ERROR", details=error_details ) class AuthenticationError(ProviderError): """Exception raised when authentication with a provider fails.""" def __init__(self, message, provider=None, details=None): """Initialize the authentication error. Args: message: Error message provider: Name of the provider details: Additional error details """ super().__init__( message, provider=provider, error_code="AUTHENTICATION_ERROR", details=details ) class ValidationError(ToolError): """Exception raised when validation of input/output fails.""" def __init__(self, message, field_errors=None, details=None): """Initialize the validation error. Args: message: Error message field_errors: Dictionary of field-specific errors details: Additional error details """ error_details = details or {} if field_errors: error_details["field_errors"] = field_errors super().__init__( message, error_code="VALIDATION_ERROR", details=error_details ) class ConfigurationError(ToolError): """Exception raised when there is an issue with configuration.""" def __init__(self, message, config_key=None, details=None): """Initialize the configuration error. Args: message: Error message config_key: Key of the problematic configuration details: Additional error details """ error_details = details or {} if config_key: error_details["config_key"] = config_key super().__init__( message, error_code="CONFIGURATION_ERROR", details=error_details ) class StorageError(ToolError): """Exception raised when there is an issue with storage operations.""" def __init__(self, message, operation=None, location=None, details=None): """Initialize the storage error. Args: message: Error message operation: Storage operation that failed location: Location of the storage operation details: Additional error details """ error_details = details or {} if operation: error_details["operation"] = operation if location: error_details["location"] = location super().__init__( message, error_code="STORAGE_ERROR", details=error_details ) def format_error_response(error: Exception) -> Dict[str, Any]: """ Format any exception into a standardized MCP-compliant error response dictionary. This utility function creates a structured error response that follows the MCP protocol format, ensuring consistency in error reporting across different components. It handles both ToolError instances (with their rich error metadata) and standard Python exceptions, automatically extracting relevant information to create detailed, actionable error responses. The function performs special processing for different error types: - For ToolError and subclasses: Extracts error code, details, and context from the exception - For ToolInputError with path validation: Enhances messages with more user-friendly text - For standard Python exceptions: Captures traceback and generates appropriate error codes The resulting dictionary always contains these standardized fields: - error: Human-readable error message (string) - error_code: Categorized error code (string) - error_type: Name of the exception class (string) - details: Dictionary with additional error information (object) - success: Always false for errors (boolean) - isError: Always true, used by MCP protocol handlers (boolean) Args: error: Any exception instance to format into a response Returns: Dictionary containing standardized error information following the MCP protocol Example: ```python try: result = perform_operation() except Exception as e: error_response = format_error_response(e) return error_response # Ready for API response ``` """ if isinstance(error, ToolError): # For ToolError instances, extract structured information error_type = error.__class__.__name__ error_message = str(error) error_details = error.details or {} # Include context in the message for better clarity in user-facing errors context = getattr(error, 'context', None) if context and isinstance(context, dict): # Create a more specific error message based on error type if isinstance(error, ToolInputError): # For path validation errors, add more helpful information if 'path' in context and error_message.endswith('does not exist.'): error_message = f"File not found: {context.get('path')}" elif 'path' in context and 'is not a regular file' in error_message: if 'directory' in error_message.lower(): error_message = f"Cannot read directory as file: {context.get('path')}. Use list_directory instead." else: error_message = f"Path exists but is not a file: {context.get('path')}" # Add context to details for more information error_details["context"] = context # Look for error_type in details if available if "error_type" in error_details: error_type_from_details = error_details["error_type"] # Use this in the response directly response_error_type = error_type_from_details else: response_error_type = error_type # Create a standard error response that the demo can easily process return { "error": error_message, "error_code": error.error_code, "error_type": response_error_type, "details": error_details, "success": False, "isError": True } else: # For unknown errors, use the actual error message instead of a generic message error_message = str(error) if not error_message or error_message.strip() == "": error_message = f"Unknown error of type {type(error).__name__}" # Match the same response structure for consistency return { "error": error_message, "error_code": "UNKNOWN_ERROR", "error_type": type(error).__name__, "details": { "type": type(error).__name__, "message": error_message, "traceback": traceback.format_exc() }, "success": False, "isError": True }

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/Dicklesworthstone/llm_gateway_mcp_server'

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