Skip to main content
Glama

MaverickMCP

by wshobson
MIT License
165
  • Apple
exceptions.py14.6 kB
""" Custom exception classes for MaverickMCP with comprehensive error handling. This module provides a unified exception hierarchy with proper error codes, HTTP status codes, and standardized error responses. """ from typing import Any class MaverickException(Exception): """Base exception for all Maverick errors.""" # Default values can be overridden by subclasses error_code: str = "INTERNAL_ERROR" status_code: int = 500 def __init__( self, message: str, error_code: str | None = None, status_code: int | None = None, field: str | None = None, context: dict[str, Any] | None = None, recoverable: bool = True, ): super().__init__(message) self.message = message self.error_code = error_code or self.__class__.error_code self.status_code = status_code or self.__class__.status_code self.field = field self.context = context or {} self.recoverable = recoverable def to_dict(self) -> dict[str, Any]: """Convert exception to dictionary for API responses.""" result: dict[str, Any] = { "code": self.error_code, "message": self.message, } if self.field: result["field"] = self.field if self.context: result["context"] = self.context return result def __repr__(self) -> str: """String representation of the exception.""" return f"{self.__class__.__name__}('{self.message}', code='{self.error_code}')" # Validation exceptions class ValidationError(MaverickException): """Raised when input validation fails.""" # Research and agent exceptions class ResearchError(MaverickException): """Raised when research operations fail.""" error_code = "RESEARCH_ERROR" status_code = 500 def __init__( self, message: str, research_type: str | None = None, provider: str | None = None, error_code: str | None = None, status_code: int | None = None, field: str | None = None, context: dict[str, Any] | None = None, recoverable: bool = True, ): super().__init__( message=message, error_code=error_code, status_code=status_code, field=field, context=context, recoverable=recoverable, ) self.research_type = research_type self.provider = provider def to_dict(self) -> dict[str, Any]: """Convert exception to dictionary for API responses.""" result = super().to_dict() if self.research_type: result["research_type"] = self.research_type if self.provider: result["provider"] = self.provider return result class WebSearchError(ResearchError): """Raised when web search operations fail.""" error_code = "WEB_SEARCH_ERROR" class ContentAnalysisError(ResearchError): """Raised when content analysis fails.""" error_code = "CONTENT_ANALYSIS_ERROR" class AgentExecutionError(MaverickException): """Raised when agent execution fails.""" error_code = "AGENT_EXECUTION_ERROR" status_code = 500 # Authentication/Authorization exceptions class AuthenticationError(MaverickException): """Raised when authentication fails.""" error_code = "AUTHENTICATION_ERROR" status_code = 401 def __init__(self, message: str = "Authentication failed", **kwargs): super().__init__(message, **kwargs) class AuthorizationError(MaverickException): """Raised when authorization fails.""" error_code = "AUTHORIZATION_ERROR" status_code = 403 def __init__( self, message: str = "Insufficient permissions", resource: str | None = None, action: str | None = None, **kwargs, ): if resource and action: message = f"Unauthorized access to {resource} for action '{action}'" super().__init__(message, **kwargs) if resource: self.context["resource"] = resource if action: self.context["action"] = action # Resource exceptions class NotFoundError(MaverickException): """Raised when a requested resource is not found.""" error_code = "NOT_FOUND" status_code = 404 def __init__(self, resource: str, identifier: str | None = None, **kwargs): message = f"{resource} not found" if identifier: message += f": {identifier}" super().__init__(message, **kwargs) self.context["resource"] = resource if identifier: self.context["identifier"] = identifier class ConflictError(MaverickException): """Raised when there's a conflict with existing data.""" error_code = "CONFLICT" status_code = 409 def __init__(self, message: str, field: str | None = None, **kwargs): super().__init__(message, field=field, **kwargs) # Rate limiting exceptions class RateLimitError(MaverickException): """Raised when rate limit is exceeded.""" error_code = "RATE_LIMIT_EXCEEDED" status_code = 429 def __init__( self, message: str = "Rate limit exceeded", retry_after: int | None = None, **kwargs, ): super().__init__(message, **kwargs) if retry_after: self.context["retry_after"] = retry_after # External service exceptions class ExternalServiceError(MaverickException): """Raised when an external service fails.""" error_code = "EXTERNAL_SERVICE_ERROR" status_code = 503 def __init__( self, service: str, message: str, original_error: str | None = None, **kwargs ): super().__init__(message, **kwargs) self.context["service"] = service if original_error: self.context["original_error"] = original_error # Data provider exceptions class DataProviderError(MaverickException): """Base exception for data provider errors.""" error_code = "DATA_PROVIDER_ERROR" status_code = 503 def __init__(self, provider: str, message: str, **kwargs): super().__init__(message, **kwargs) self.context["provider"] = provider class DataNotFoundError(DataProviderError): """Raised when requested data is not found.""" error_code = "DATA_NOT_FOUND" status_code = 404 def __init__(self, symbol: str, date_range: tuple | None = None, **kwargs): message = f"Data not found for symbol '{symbol}'" if date_range: message += f" in range {date_range[0]} to {date_range[1]}" super().__init__("cache", message, **kwargs) self.context["symbol"] = symbol if date_range: self.context["date_range"] = date_range class APIRateLimitError(DataProviderError): """Raised when API rate limit is exceeded.""" error_code = "RATE_LIMIT_EXCEEDED" status_code = 429 def __init__(self, provider: str, retry_after: int | None = None, **kwargs): message = f"Rate limit exceeded for {provider}" if retry_after: message += f". Retry after {retry_after} seconds" super().__init__(provider, message, recoverable=True, **kwargs) if retry_after: self.context["retry_after"] = retry_after class APIConnectionError(DataProviderError): """Raised when API connection fails.""" error_code = "API_CONNECTION_ERROR" status_code = 503 def __init__(self, provider: str, endpoint: str, reason: str, **kwargs): message = f"Failed to connect to {provider} at {endpoint}: {reason}" super().__init__(provider, message, recoverable=True, **kwargs) self.context["endpoint"] = endpoint self.context["connection_reason"] = reason # Database exceptions class DatabaseError(MaverickException): """Base exception for database errors.""" error_code = "DATABASE_ERROR" status_code = 500 def __init__(self, operation: str, message: str, **kwargs): super().__init__(message, **kwargs) self.context["operation"] = operation class DatabaseConnectionError(DatabaseError): """Raised when database connection fails.""" error_code = "DATABASE_CONNECTION_ERROR" status_code = 503 def __init__(self, reason: str, **kwargs): message = f"Database connection failed: {reason}" super().__init__("connect", message, recoverable=True, **kwargs) class DataIntegrityError(DatabaseError): """Raised when data integrity check fails.""" error_code = "DATA_INTEGRITY_ERROR" status_code = 422 def __init__( self, message: str, table: str | None = None, constraint: str | None = None, **kwargs, ): super().__init__("integrity_check", message, recoverable=False, **kwargs) if table: self.context["table"] = table if constraint: self.context["constraint"] = constraint # Cache exceptions class CacheError(MaverickException): """Base exception for cache errors.""" error_code = "CACHE_ERROR" status_code = 503 def __init__(self, operation: str, message: str, **kwargs): super().__init__(message, **kwargs) self.context["operation"] = operation class CacheConnectionError(CacheError): """Raised when cache connection fails.""" error_code = "CACHE_CONNECTION_ERROR" status_code = 503 def __init__(self, cache_type: str, reason: str, **kwargs): message = f"{cache_type} cache connection failed: {reason}" super().__init__("connect", message, recoverable=True, **kwargs) self.context["cache_type"] = cache_type # Configuration exceptions class ConfigurationError(MaverickException): """Raised when there's a configuration problem.""" error_code = "CONFIGURATION_ERROR" status_code = 500 def __init__(self, message: str, config_key: str | None = None, **kwargs): super().__init__(message, **kwargs) if config_key: self.context["config_key"] = config_key # Webhook exceptions class WebhookError(MaverickException): """Raised when webhook processing fails.""" error_code = "WEBHOOK_ERROR" status_code = 400 def __init__( self, message: str, event_type: str | None = None, event_id: str | None = None, **kwargs, ): super().__init__(message, **kwargs) if event_type: self.context["event_type"] = event_type if event_id: self.context["event_id"] = event_id # Agent-specific exceptions class AgentInitializationError(MaverickException): """Raised when agent initialization fails.""" error_code = "AGENT_INIT_ERROR" status_code = 500 def __init__(self, agent_type: str, reason: str, **kwargs): message = f"Failed to initialize {agent_type}: {reason}" super().__init__(message, **kwargs) self.context["agent_type"] = agent_type self.context["reason"] = reason class PersonaConfigurationError(MaverickException): """Raised when persona configuration is invalid.""" error_code = "PERSONA_CONFIG_ERROR" status_code = 400 def __init__(self, persona: str, valid_personas: list, **kwargs): message = ( f"Invalid persona '{persona}'. Valid options: {', '.join(valid_personas)}" ) super().__init__(message, **kwargs) self.context["invalid_persona"] = persona self.context["valid_personas"] = valid_personas class ToolRegistrationError(MaverickException): """Raised when tool registration fails.""" error_code = "TOOL_REGISTRATION_ERROR" status_code = 500 def __init__(self, tool_name: str, reason: str, **kwargs): message = f"Failed to register tool '{tool_name}': {reason}" super().__init__(message, **kwargs) self.context["tool_name"] = tool_name self.context["reason"] = reason # Circuit breaker exceptions class CircuitBreakerError(MaverickException): """Raised when circuit breaker is open.""" error_code = "CIRCUIT_BREAKER_OPEN" status_code = 503 def __init__(self, service: str, failure_count: int, threshold: int, **kwargs): message = ( f"Circuit breaker open for {service}: {failure_count}/{threshold} failures" ) super().__init__(message, recoverable=True, **kwargs) self.context["service"] = service self.context["failure_count"] = failure_count self.context["threshold"] = threshold # Parameter validation exceptions class ParameterValidationError(ValidationError): """Raised when function parameters are invalid.""" error_code = "PARAMETER_VALIDATION_ERROR" status_code = 400 def __init__(self, param_name: str, expected_type: str, actual_type: str, **kwargs): reason = f"Expected {expected_type}, got {actual_type}" message = f"Validation failed for '{param_name}': {reason}" super().__init__(message, field=param_name, **kwargs) self.context["expected_type"] = expected_type self.context["actual_type"] = actual_type # Error code constants ERROR_CODES = { "VALIDATION_ERROR": "Request validation failed", "AUTHENTICATION_ERROR": "Authentication failed", "AUTHORIZATION_ERROR": "Insufficient permissions", "NOT_FOUND": "Resource not found", "CONFLICT": "Resource conflict", "RATE_LIMIT_EXCEEDED": "Too many requests", "EXTERNAL_SERVICE_ERROR": "External service unavailable", "DATA_PROVIDER_ERROR": "Data provider error", "DATA_NOT_FOUND": "Data not found", "API_CONNECTION_ERROR": "API connection failed", "DATABASE_ERROR": "Database error", "DATABASE_CONNECTION_ERROR": "Database connection failed", "DATA_INTEGRITY_ERROR": "Data integrity violation", "CACHE_ERROR": "Cache error", "CACHE_CONNECTION_ERROR": "Cache connection failed", "CONFIGURATION_ERROR": "Configuration error", "WEBHOOK_ERROR": "Webhook processing failed", "AGENT_INIT_ERROR": "Agent initialization failed", "PERSONA_CONFIG_ERROR": "Invalid persona configuration", "TOOL_REGISTRATION_ERROR": "Tool registration failed", "CIRCUIT_BREAKER_OPEN": "Service unavailable - circuit breaker open", "PARAMETER_VALIDATION_ERROR": "Invalid parameter", "INTERNAL_ERROR": "Internal server error", } def get_error_message(code: str) -> str: """Get human-readable message for error code.""" return ERROR_CODES.get(code, "Unknown error") # Backward compatibility alias MaverickMCPError = MaverickException

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/wshobson/maverick-mcp'

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