claude-mpm 3.9.7__py3-none-any.whl → 3.9.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/templates/ticketing.json +1 -1
- claude_mpm/services/__init__.py +28 -0
- claude_mpm/services/mcp_gateway/__init__.py +122 -0
- claude_mpm/services/mcp_gateway/config/__init__.py +17 -0
- claude_mpm/services/mcp_gateway/config/config_loader.py +232 -0
- claude_mpm/services/mcp_gateway/config/config_schema.py +234 -0
- claude_mpm/services/mcp_gateway/config/configuration.py +371 -0
- claude_mpm/services/mcp_gateway/core/__init__.py +51 -0
- claude_mpm/services/mcp_gateway/core/base.py +315 -0
- claude_mpm/services/mcp_gateway/core/exceptions.py +239 -0
- claude_mpm/services/mcp_gateway/core/interfaces.py +476 -0
- claude_mpm/services/mcp_gateway/registry/__init__.py +9 -0
- claude_mpm/services/mcp_gateway/server/__init__.py +9 -0
- claude_mpm/services/mcp_gateway/tools/__init__.py +9 -0
- {claude_mpm-3.9.7.dist-info → claude_mpm-3.9.8.dist-info}/METADATA +2 -1
- {claude_mpm-3.9.7.dist-info → claude_mpm-3.9.8.dist-info}/RECORD +21 -9
- {claude_mpm-3.9.7.dist-info → claude_mpm-3.9.8.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.7.dist-info → claude_mpm-3.9.8.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.9.7.dist-info → claude_mpm-3.9.8.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.9.7.dist-info → claude_mpm-3.9.8.dist-info}/top_level.txt +0 -0
| @@ -0,0 +1,315 @@ | |
| 1 | 
            +
            """
         | 
| 2 | 
            +
            MCP Gateway Base Classes
         | 
| 3 | 
            +
            ========================
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Base implementations for MCP Gateway services.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Part of ISS-0034: Infrastructure Setup - MCP Gateway Project Foundation
         | 
| 8 | 
            +
            """
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            from enum import Enum
         | 
| 11 | 
            +
            from typing import Any, Dict, Optional
         | 
| 12 | 
            +
            import asyncio
         | 
| 13 | 
            +
            import logging
         | 
| 14 | 
            +
            from pathlib import Path
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            from claude_mpm.services.core.base import BaseService
         | 
| 17 | 
            +
            from claude_mpm.core.logger import get_logger
         | 
| 18 | 
            +
             | 
| 19 | 
            +
             | 
| 20 | 
            +
            class MCPServiceState(Enum):
         | 
| 21 | 
            +
                """MCP service lifecycle states."""
         | 
| 22 | 
            +
                UNINITIALIZED = "uninitialized"
         | 
| 23 | 
            +
                INITIALIZING = "initializing"
         | 
| 24 | 
            +
                INITIALIZED = "initialized"
         | 
| 25 | 
            +
                STARTING = "starting"
         | 
| 26 | 
            +
                RUNNING = "running"
         | 
| 27 | 
            +
                STOPPING = "stopping"
         | 
| 28 | 
            +
                STOPPED = "stopped"
         | 
| 29 | 
            +
                ERROR = "error"
         | 
| 30 | 
            +
             | 
| 31 | 
            +
             | 
| 32 | 
            +
            class BaseMCPService(BaseService):
         | 
| 33 | 
            +
                """
         | 
| 34 | 
            +
                Base class for all MCP Gateway services.
         | 
| 35 | 
            +
                
         | 
| 36 | 
            +
                Extends the claude-mpm BaseService with MCP-specific functionality
         | 
| 37 | 
            +
                including state management, health monitoring, and async lifecycle support.
         | 
| 38 | 
            +
                
         | 
| 39 | 
            +
                WHY: We extend BaseService to maintain consistency with the claude-mpm
         | 
| 40 | 
            +
                architecture while adding MCP-specific capabilities. This ensures all
         | 
| 41 | 
            +
                MCP services integrate seamlessly with the existing service container
         | 
| 42 | 
            +
                and dependency injection system.
         | 
| 43 | 
            +
                """
         | 
| 44 | 
            +
                
         | 
| 45 | 
            +
                def __init__(self, service_name: Optional[str] = None, config: Optional[Dict[str, Any]] = None):
         | 
| 46 | 
            +
                    """
         | 
| 47 | 
            +
                    Initialize MCP service.
         | 
| 48 | 
            +
                    
         | 
| 49 | 
            +
                    Args:
         | 
| 50 | 
            +
                        service_name: Name of the service for logging
         | 
| 51 | 
            +
                        config: Service-specific configuration
         | 
| 52 | 
            +
                    """
         | 
| 53 | 
            +
                    super().__init__(service_name or "MCPService", config)
         | 
| 54 | 
            +
                    self._state = MCPServiceState.UNINITIALIZED
         | 
| 55 | 
            +
                    self._health_status = {
         | 
| 56 | 
            +
                        "healthy": False,
         | 
| 57 | 
            +
                        "state": self._state.value,
         | 
| 58 | 
            +
                        "last_check": None,
         | 
| 59 | 
            +
                        "details": {}
         | 
| 60 | 
            +
                    }
         | 
| 61 | 
            +
                    self._state_lock = asyncio.Lock()
         | 
| 62 | 
            +
                    self._initialization_task = None
         | 
| 63 | 
            +
                    self._shutdown_task = None
         | 
| 64 | 
            +
                
         | 
| 65 | 
            +
                async def initialize(self) -> bool:
         | 
| 66 | 
            +
                    """
         | 
| 67 | 
            +
                    Initialize the MCP service.
         | 
| 68 | 
            +
                    
         | 
| 69 | 
            +
                    This method manages state transitions and ensures thread-safe initialization.
         | 
| 70 | 
            +
                    Subclasses should override _do_initialize() for custom initialization logic.
         | 
| 71 | 
            +
                    
         | 
| 72 | 
            +
                    Returns:
         | 
| 73 | 
            +
                        True if initialization successful, False otherwise
         | 
| 74 | 
            +
                    """
         | 
| 75 | 
            +
                    async with self._state_lock:
         | 
| 76 | 
            +
                        if self._state not in [MCPServiceState.UNINITIALIZED, MCPServiceState.STOPPED]:
         | 
| 77 | 
            +
                            self.log_warning(f"Cannot initialize from state {self._state.value}")
         | 
| 78 | 
            +
                            return False
         | 
| 79 | 
            +
                        
         | 
| 80 | 
            +
                        self._state = MCPServiceState.INITIALIZING
         | 
| 81 | 
            +
                        self.log_info("Initializing MCP service")
         | 
| 82 | 
            +
                    
         | 
| 83 | 
            +
                    try:
         | 
| 84 | 
            +
                        # Call subclass initialization
         | 
| 85 | 
            +
                        success = await self._do_initialize()
         | 
| 86 | 
            +
                        
         | 
| 87 | 
            +
                        async with self._state_lock:
         | 
| 88 | 
            +
                            if success:
         | 
| 89 | 
            +
                                self._state = MCPServiceState.INITIALIZED
         | 
| 90 | 
            +
                                self._initialized = True
         | 
| 91 | 
            +
                                self._health_status["healthy"] = True
         | 
| 92 | 
            +
                                self._health_status["state"] = self._state.value
         | 
| 93 | 
            +
                                self.log_info("MCP service initialized successfully")
         | 
| 94 | 
            +
                            else:
         | 
| 95 | 
            +
                                self._state = MCPServiceState.ERROR
         | 
| 96 | 
            +
                                self._health_status["healthy"] = False
         | 
| 97 | 
            +
                                self._health_status["state"] = self._state.value
         | 
| 98 | 
            +
                                self.log_error("MCP service initialization failed")
         | 
| 99 | 
            +
                            
         | 
| 100 | 
            +
                            return success
         | 
| 101 | 
            +
                            
         | 
| 102 | 
            +
                    except Exception as e:
         | 
| 103 | 
            +
                        async with self._state_lock:
         | 
| 104 | 
            +
                            self._state = MCPServiceState.ERROR
         | 
| 105 | 
            +
                            self._health_status["healthy"] = False
         | 
| 106 | 
            +
                            self._health_status["state"] = self._state.value
         | 
| 107 | 
            +
                            self._health_status["details"]["error"] = str(e)
         | 
| 108 | 
            +
                        
         | 
| 109 | 
            +
                        self.log_error(f"Exception during initialization: {e}")
         | 
| 110 | 
            +
                        return False
         | 
| 111 | 
            +
                
         | 
| 112 | 
            +
                async def _do_initialize(self) -> bool:
         | 
| 113 | 
            +
                    """
         | 
| 114 | 
            +
                    Perform actual initialization logic.
         | 
| 115 | 
            +
                    
         | 
| 116 | 
            +
                    Subclasses should override this method to implement custom initialization.
         | 
| 117 | 
            +
                    
         | 
| 118 | 
            +
                    Returns:
         | 
| 119 | 
            +
                        True if initialization successful
         | 
| 120 | 
            +
                    """
         | 
| 121 | 
            +
                    # Default implementation - subclasses should override
         | 
| 122 | 
            +
                    return True
         | 
| 123 | 
            +
                
         | 
| 124 | 
            +
                async def start(self) -> bool:
         | 
| 125 | 
            +
                    """
         | 
| 126 | 
            +
                    Start the MCP service.
         | 
| 127 | 
            +
                    
         | 
| 128 | 
            +
                    Returns:
         | 
| 129 | 
            +
                        True if startup successful
         | 
| 130 | 
            +
                    """
         | 
| 131 | 
            +
                    async with self._state_lock:
         | 
| 132 | 
            +
                        if self._state != MCPServiceState.INITIALIZED:
         | 
| 133 | 
            +
                            self.log_warning(f"Cannot start from state {self._state.value}")
         | 
| 134 | 
            +
                            return False
         | 
| 135 | 
            +
                        
         | 
| 136 | 
            +
                        self._state = MCPServiceState.STARTING
         | 
| 137 | 
            +
                        self.log_info("Starting MCP service")
         | 
| 138 | 
            +
                    
         | 
| 139 | 
            +
                    try:
         | 
| 140 | 
            +
                        success = await self._do_start()
         | 
| 141 | 
            +
                        
         | 
| 142 | 
            +
                        async with self._state_lock:
         | 
| 143 | 
            +
                            if success:
         | 
| 144 | 
            +
                                self._state = MCPServiceState.RUNNING
         | 
| 145 | 
            +
                                self._health_status["healthy"] = True
         | 
| 146 | 
            +
                                self._health_status["state"] = self._state.value
         | 
| 147 | 
            +
                                self.log_info("MCP service started successfully")
         | 
| 148 | 
            +
                            else:
         | 
| 149 | 
            +
                                self._state = MCPServiceState.ERROR
         | 
| 150 | 
            +
                                self._health_status["healthy"] = False
         | 
| 151 | 
            +
                                self._health_status["state"] = self._state.value
         | 
| 152 | 
            +
                                self.log_error("MCP service startup failed")
         | 
| 153 | 
            +
                            
         | 
| 154 | 
            +
                            return success
         | 
| 155 | 
            +
                            
         | 
| 156 | 
            +
                    except Exception as e:
         | 
| 157 | 
            +
                        async with self._state_lock:
         | 
| 158 | 
            +
                            self._state = MCPServiceState.ERROR
         | 
| 159 | 
            +
                            self._health_status["healthy"] = False
         | 
| 160 | 
            +
                            self._health_status["state"] = self._state.value
         | 
| 161 | 
            +
                            self._health_status["details"]["error"] = str(e)
         | 
| 162 | 
            +
                        
         | 
| 163 | 
            +
                        self.log_error(f"Exception during startup: {e}")
         | 
| 164 | 
            +
                        return False
         | 
| 165 | 
            +
                
         | 
| 166 | 
            +
                async def _do_start(self) -> bool:
         | 
| 167 | 
            +
                    """
         | 
| 168 | 
            +
                    Perform actual startup logic.
         | 
| 169 | 
            +
                    
         | 
| 170 | 
            +
                    Subclasses should override this method to implement custom startup.
         | 
| 171 | 
            +
                    
         | 
| 172 | 
            +
                    Returns:
         | 
| 173 | 
            +
                        True if startup successful
         | 
| 174 | 
            +
                    """
         | 
| 175 | 
            +
                    # Default implementation - subclasses should override
         | 
| 176 | 
            +
                    return True
         | 
| 177 | 
            +
                
         | 
| 178 | 
            +
                async def shutdown(self) -> None:
         | 
| 179 | 
            +
                    """
         | 
| 180 | 
            +
                    Shutdown the MCP service gracefully.
         | 
| 181 | 
            +
                    
         | 
| 182 | 
            +
                    This method manages state transitions and ensures clean shutdown.
         | 
| 183 | 
            +
                    Subclasses should override _do_shutdown() for custom shutdown logic.
         | 
| 184 | 
            +
                    """
         | 
| 185 | 
            +
                    async with self._state_lock:
         | 
| 186 | 
            +
                        if self._state in [MCPServiceState.STOPPED, MCPServiceState.STOPPING]:
         | 
| 187 | 
            +
                            self.log_warning(f"Already in state {self._state.value}")
         | 
| 188 | 
            +
                            return
         | 
| 189 | 
            +
                        
         | 
| 190 | 
            +
                        self._state = MCPServiceState.STOPPING
         | 
| 191 | 
            +
                        self.log_info("Shutting down MCP service")
         | 
| 192 | 
            +
                    
         | 
| 193 | 
            +
                    try:
         | 
| 194 | 
            +
                        await self._do_shutdown()
         | 
| 195 | 
            +
                        
         | 
| 196 | 
            +
                        async with self._state_lock:
         | 
| 197 | 
            +
                            self._state = MCPServiceState.STOPPED
         | 
| 198 | 
            +
                            self._shutdown = True
         | 
| 199 | 
            +
                            self._health_status["healthy"] = False
         | 
| 200 | 
            +
                            self._health_status["state"] = self._state.value
         | 
| 201 | 
            +
                            self.log_info("MCP service shutdown complete")
         | 
| 202 | 
            +
                            
         | 
| 203 | 
            +
                    except Exception as e:
         | 
| 204 | 
            +
                        async with self._state_lock:
         | 
| 205 | 
            +
                            self._state = MCPServiceState.ERROR
         | 
| 206 | 
            +
                            self._health_status["healthy"] = False
         | 
| 207 | 
            +
                            self._health_status["state"] = self._state.value
         | 
| 208 | 
            +
                            self._health_status["details"]["error"] = str(e)
         | 
| 209 | 
            +
                        
         | 
| 210 | 
            +
                        self.log_error(f"Exception during shutdown: {e}")
         | 
| 211 | 
            +
                
         | 
| 212 | 
            +
                async def _do_shutdown(self) -> None:
         | 
| 213 | 
            +
                    """
         | 
| 214 | 
            +
                    Perform actual shutdown logic.
         | 
| 215 | 
            +
                    
         | 
| 216 | 
            +
                    Subclasses should override this method to implement custom shutdown.
         | 
| 217 | 
            +
                    """
         | 
| 218 | 
            +
                    # Default implementation - subclasses should override
         | 
| 219 | 
            +
                    pass
         | 
| 220 | 
            +
                
         | 
| 221 | 
            +
                async def restart(self) -> bool:
         | 
| 222 | 
            +
                    """
         | 
| 223 | 
            +
                    Restart the MCP service.
         | 
| 224 | 
            +
                    
         | 
| 225 | 
            +
                    Returns:
         | 
| 226 | 
            +
                        True if restart successful
         | 
| 227 | 
            +
                    """
         | 
| 228 | 
            +
                    self.log_info("Restarting MCP service")
         | 
| 229 | 
            +
                    
         | 
| 230 | 
            +
                    # Shutdown if running
         | 
| 231 | 
            +
                    if self._state == MCPServiceState.RUNNING:
         | 
| 232 | 
            +
                        await self.shutdown()
         | 
| 233 | 
            +
                    
         | 
| 234 | 
            +
                    # Re-initialize
         | 
| 235 | 
            +
                    if not await self.initialize():
         | 
| 236 | 
            +
                        return False
         | 
| 237 | 
            +
                    
         | 
| 238 | 
            +
                    # Start again
         | 
| 239 | 
            +
                    return await self.start()
         | 
| 240 | 
            +
                
         | 
| 241 | 
            +
                def get_state(self) -> str:
         | 
| 242 | 
            +
                    """
         | 
| 243 | 
            +
                    Get current service state.
         | 
| 244 | 
            +
                    
         | 
| 245 | 
            +
                    Returns:
         | 
| 246 | 
            +
                        Service state string
         | 
| 247 | 
            +
                    """
         | 
| 248 | 
            +
                    return self._state.value
         | 
| 249 | 
            +
                
         | 
| 250 | 
            +
                def is_healthy(self) -> bool:
         | 
| 251 | 
            +
                    """
         | 
| 252 | 
            +
                    Check if service is healthy.
         | 
| 253 | 
            +
                    
         | 
| 254 | 
            +
                    Returns:
         | 
| 255 | 
            +
                        True if service is healthy
         | 
| 256 | 
            +
                    """
         | 
| 257 | 
            +
                    return self._health_status["healthy"]
         | 
| 258 | 
            +
                
         | 
| 259 | 
            +
                def get_health_status(self) -> Dict[str, Any]:
         | 
| 260 | 
            +
                    """
         | 
| 261 | 
            +
                    Get detailed health status.
         | 
| 262 | 
            +
                    
         | 
| 263 | 
            +
                    Returns:
         | 
| 264 | 
            +
                        Health status information
         | 
| 265 | 
            +
                    """
         | 
| 266 | 
            +
                    return self._health_status.copy()
         | 
| 267 | 
            +
                
         | 
| 268 | 
            +
                def update_health_status(self, healthy: bool, details: Optional[Dict[str, Any]] = None) -> None:
         | 
| 269 | 
            +
                    """
         | 
| 270 | 
            +
                    Update health status.
         | 
| 271 | 
            +
                    
         | 
| 272 | 
            +
                    Args:
         | 
| 273 | 
            +
                        healthy: Whether service is healthy
         | 
| 274 | 
            +
                        details: Additional health details
         | 
| 275 | 
            +
                    """
         | 
| 276 | 
            +
                    from datetime import datetime
         | 
| 277 | 
            +
                    
         | 
| 278 | 
            +
                    self._health_status["healthy"] = healthy
         | 
| 279 | 
            +
                    self._health_status["last_check"] = datetime.now().isoformat()
         | 
| 280 | 
            +
                    
         | 
| 281 | 
            +
                    if details:
         | 
| 282 | 
            +
                        self._health_status["details"].update(details)
         | 
| 283 | 
            +
                
         | 
| 284 | 
            +
                # Additional logging methods for MCP-specific events
         | 
| 285 | 
            +
                def log_mcp_event(self, event_type: str, data: Optional[Dict[str, Any]] = None) -> None:
         | 
| 286 | 
            +
                    """
         | 
| 287 | 
            +
                    Log an MCP-specific event.
         | 
| 288 | 
            +
                    
         | 
| 289 | 
            +
                    Args:
         | 
| 290 | 
            +
                        event_type: Type of MCP event
         | 
| 291 | 
            +
                        data: Event data
         | 
| 292 | 
            +
                    """
         | 
| 293 | 
            +
                    message = f"MCP Event: {event_type}"
         | 
| 294 | 
            +
                    if data:
         | 
| 295 | 
            +
                        message += f" - {data}"
         | 
| 296 | 
            +
                    self.log_info(message)
         | 
| 297 | 
            +
                
         | 
| 298 | 
            +
                def log_tool_invocation(self, tool_name: str, success: bool, duration: Optional[float] = None) -> None:
         | 
| 299 | 
            +
                    """
         | 
| 300 | 
            +
                    Log a tool invocation.
         | 
| 301 | 
            +
                    
         | 
| 302 | 
            +
                    Args:
         | 
| 303 | 
            +
                        tool_name: Name of the tool invoked
         | 
| 304 | 
            +
                        success: Whether invocation was successful
         | 
| 305 | 
            +
                        duration: Execution duration in seconds
         | 
| 306 | 
            +
                    """
         | 
| 307 | 
            +
                    status = "successful" if success else "failed"
         | 
| 308 | 
            +
                    message = f"Tool invocation: {tool_name} {status}"
         | 
| 309 | 
            +
                    if duration:
         | 
| 310 | 
            +
                        message += f" ({duration:.3f}s)"
         | 
| 311 | 
            +
                    
         | 
| 312 | 
            +
                    if success:
         | 
| 313 | 
            +
                        self.log_info(message)
         | 
| 314 | 
            +
                    else:
         | 
| 315 | 
            +
                        self.log_warning(message)
         | 
| @@ -0,0 +1,239 @@ | |
| 1 | 
            +
            """
         | 
| 2 | 
            +
            MCP Gateway Exception Classes
         | 
| 3 | 
            +
            =============================
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Custom exceptions for MCP Gateway operations.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Part of ISS-0034: Infrastructure Setup - MCP Gateway Project Foundation
         | 
| 8 | 
            +
            """
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            from typing import Optional, Any, Dict
         | 
| 11 | 
            +
             | 
| 12 | 
            +
             | 
| 13 | 
            +
            class MCPException(Exception):
         | 
| 14 | 
            +
                """
         | 
| 15 | 
            +
                Base exception for all MCP Gateway errors.
         | 
| 16 | 
            +
                
         | 
| 17 | 
            +
                WHY: We create a base exception to allow catching all MCP-related
         | 
| 18 | 
            +
                errors in a single except block while still being able to handle
         | 
| 19 | 
            +
                specific error types when needed.
         | 
| 20 | 
            +
                """
         | 
| 21 | 
            +
                
         | 
| 22 | 
            +
                def __init__(self, message: str, details: Optional[Dict[str, Any]] = None):
         | 
| 23 | 
            +
                    """
         | 
| 24 | 
            +
                    Initialize MCP exception.
         | 
| 25 | 
            +
                    
         | 
| 26 | 
            +
                    Args:
         | 
| 27 | 
            +
                        message: Error message
         | 
| 28 | 
            +
                        details: Additional error details
         | 
| 29 | 
            +
                    """
         | 
| 30 | 
            +
                    super().__init__(message)
         | 
| 31 | 
            +
                    self.message = message
         | 
| 32 | 
            +
                    self.details = details or {}
         | 
| 33 | 
            +
                
         | 
| 34 | 
            +
                def __str__(self) -> str:
         | 
| 35 | 
            +
                    """String representation of the exception."""
         | 
| 36 | 
            +
                    if self.details:
         | 
| 37 | 
            +
                        return f"{self.message} - Details: {self.details}"
         | 
| 38 | 
            +
                    return self.message
         | 
| 39 | 
            +
             | 
| 40 | 
            +
             | 
| 41 | 
            +
            class MCPConfigurationError(MCPException):
         | 
| 42 | 
            +
                """
         | 
| 43 | 
            +
                Raised when MCP configuration is invalid or cannot be loaded.
         | 
| 44 | 
            +
                
         | 
| 45 | 
            +
                Common scenarios:
         | 
| 46 | 
            +
                - Missing required configuration fields
         | 
| 47 | 
            +
                - Invalid configuration values
         | 
| 48 | 
            +
                - Configuration file not found
         | 
| 49 | 
            +
                - YAML parsing errors
         | 
| 50 | 
            +
                """
         | 
| 51 | 
            +
                
         | 
| 52 | 
            +
                def __init__(self, message: str, config_key: Optional[str] = None, 
         | 
| 53 | 
            +
                             expected_type: Optional[str] = None):
         | 
| 54 | 
            +
                    """
         | 
| 55 | 
            +
                    Initialize configuration error.
         | 
| 56 | 
            +
                    
         | 
| 57 | 
            +
                    Args:
         | 
| 58 | 
            +
                        message: Error message
         | 
| 59 | 
            +
                        config_key: Configuration key that caused the error
         | 
| 60 | 
            +
                        expected_type: Expected type for the configuration value
         | 
| 61 | 
            +
                    """
         | 
| 62 | 
            +
                    details = {}
         | 
| 63 | 
            +
                    if config_key:
         | 
| 64 | 
            +
                        details["config_key"] = config_key
         | 
| 65 | 
            +
                    if expected_type:
         | 
| 66 | 
            +
                        details["expected_type"] = expected_type
         | 
| 67 | 
            +
                    
         | 
| 68 | 
            +
                    super().__init__(message, details)
         | 
| 69 | 
            +
             | 
| 70 | 
            +
             | 
| 71 | 
            +
            class MCPToolNotFoundError(MCPException):
         | 
| 72 | 
            +
                """
         | 
| 73 | 
            +
                Raised when a requested tool is not found in the registry.
         | 
| 74 | 
            +
                
         | 
| 75 | 
            +
                This error occurs when:
         | 
| 76 | 
            +
                - Attempting to invoke a non-existent tool
         | 
| 77 | 
            +
                - Trying to unregister a tool that isn't registered
         | 
| 78 | 
            +
                - Searching for a tool that doesn't exist
         | 
| 79 | 
            +
                """
         | 
| 80 | 
            +
                
         | 
| 81 | 
            +
                def __init__(self, tool_name: str, available_tools: Optional[list] = None):
         | 
| 82 | 
            +
                    """
         | 
| 83 | 
            +
                    Initialize tool not found error.
         | 
| 84 | 
            +
                    
         | 
| 85 | 
            +
                    Args:
         | 
| 86 | 
            +
                        tool_name: Name of the tool that wasn't found
         | 
| 87 | 
            +
                        available_tools: List of available tool names for reference
         | 
| 88 | 
            +
                    """
         | 
| 89 | 
            +
                    message = f"Tool '{tool_name}' not found in registry"
         | 
| 90 | 
            +
                    details = {"tool_name": tool_name}
         | 
| 91 | 
            +
                    
         | 
| 92 | 
            +
                    if available_tools:
         | 
| 93 | 
            +
                        details["available_tools"] = available_tools
         | 
| 94 | 
            +
                        message += f". Available tools: {', '.join(available_tools)}"
         | 
| 95 | 
            +
                    
         | 
| 96 | 
            +
                    super().__init__(message, details)
         | 
| 97 | 
            +
             | 
| 98 | 
            +
             | 
| 99 | 
            +
            class MCPServerError(MCPException):
         | 
| 100 | 
            +
                """
         | 
| 101 | 
            +
                Raised when MCP server encounters an error.
         | 
| 102 | 
            +
                
         | 
| 103 | 
            +
                Common scenarios:
         | 
| 104 | 
            +
                - Server initialization failure
         | 
| 105 | 
            +
                - Port binding issues
         | 
| 106 | 
            +
                - Server crash during operation
         | 
| 107 | 
            +
                - Invalid server state transitions
         | 
| 108 | 
            +
                """
         | 
| 109 | 
            +
                
         | 
| 110 | 
            +
                def __init__(self, message: str, server_state: Optional[str] = None, 
         | 
| 111 | 
            +
                             error_code: Optional[int] = None):
         | 
| 112 | 
            +
                    """
         | 
| 113 | 
            +
                    Initialize server error.
         | 
| 114 | 
            +
                    
         | 
| 115 | 
            +
                    Args:
         | 
| 116 | 
            +
                        message: Error message
         | 
| 117 | 
            +
                        server_state: Current server state when error occurred
         | 
| 118 | 
            +
                        error_code: Numeric error code if applicable
         | 
| 119 | 
            +
                    """
         | 
| 120 | 
            +
                    details = {}
         | 
| 121 | 
            +
                    if server_state:
         | 
| 122 | 
            +
                        details["server_state"] = server_state
         | 
| 123 | 
            +
                    if error_code:
         | 
| 124 | 
            +
                        details["error_code"] = error_code
         | 
| 125 | 
            +
                    
         | 
| 126 | 
            +
                    super().__init__(message, details)
         | 
| 127 | 
            +
             | 
| 128 | 
            +
             | 
| 129 | 
            +
            class MCPCommunicationError(MCPException):
         | 
| 130 | 
            +
                """
         | 
| 131 | 
            +
                Raised when communication with MCP client fails.
         | 
| 132 | 
            +
                
         | 
| 133 | 
            +
                This includes:
         | 
| 134 | 
            +
                - Stdio communication failures
         | 
| 135 | 
            +
                - Message parsing errors
         | 
| 136 | 
            +
                - Protocol violations
         | 
| 137 | 
            +
                - Timeout errors
         | 
| 138 | 
            +
                """
         | 
| 139 | 
            +
                
         | 
| 140 | 
            +
                def __init__(self, message: str, direction: Optional[str] = None, 
         | 
| 141 | 
            +
                             raw_data: Optional[str] = None):
         | 
| 142 | 
            +
                    """
         | 
| 143 | 
            +
                    Initialize communication error.
         | 
| 144 | 
            +
                    
         | 
| 145 | 
            +
                    Args:
         | 
| 146 | 
            +
                        message: Error message
         | 
| 147 | 
            +
                        direction: Direction of communication ("send" or "receive")
         | 
| 148 | 
            +
                        raw_data: Raw data that caused the error (for debugging)
         | 
| 149 | 
            +
                    """
         | 
| 150 | 
            +
                    details = {}
         | 
| 151 | 
            +
                    if direction:
         | 
| 152 | 
            +
                        details["direction"] = direction
         | 
| 153 | 
            +
                    if raw_data and len(raw_data) < 1000:  # Limit raw data size in exceptions
         | 
| 154 | 
            +
                        details["raw_data"] = raw_data
         | 
| 155 | 
            +
                    elif raw_data:
         | 
| 156 | 
            +
                        details["raw_data"] = raw_data[:1000] + "... (truncated)"
         | 
| 157 | 
            +
                    
         | 
| 158 | 
            +
                    super().__init__(message, details)
         | 
| 159 | 
            +
             | 
| 160 | 
            +
             | 
| 161 | 
            +
            class MCPValidationError(MCPException):
         | 
| 162 | 
            +
                """
         | 
| 163 | 
            +
                Raised when validation fails.
         | 
| 164 | 
            +
                
         | 
| 165 | 
            +
                Used for:
         | 
| 166 | 
            +
                - Tool parameter validation
         | 
| 167 | 
            +
                - Schema validation
         | 
| 168 | 
            +
                - Input validation
         | 
| 169 | 
            +
                - Response validation
         | 
| 170 | 
            +
                """
         | 
| 171 | 
            +
                
         | 
| 172 | 
            +
                def __init__(self, message: str, field: Optional[str] = None, 
         | 
| 173 | 
            +
                             expected: Optional[Any] = None, actual: Optional[Any] = None):
         | 
| 174 | 
            +
                    """
         | 
| 175 | 
            +
                    Initialize validation error.
         | 
| 176 | 
            +
                    
         | 
| 177 | 
            +
                    Args:
         | 
| 178 | 
            +
                        message: Error message
         | 
| 179 | 
            +
                        field: Field that failed validation
         | 
| 180 | 
            +
                        expected: Expected value or type
         | 
| 181 | 
            +
                        actual: Actual value received
         | 
| 182 | 
            +
                    """
         | 
| 183 | 
            +
                    details = {}
         | 
| 184 | 
            +
                    if field:
         | 
| 185 | 
            +
                        details["field"] = field
         | 
| 186 | 
            +
                    if expected is not None:
         | 
| 187 | 
            +
                        details["expected"] = str(expected)
         | 
| 188 | 
            +
                    if actual is not None:
         | 
| 189 | 
            +
                        details["actual"] = str(actual)
         | 
| 190 | 
            +
                    
         | 
| 191 | 
            +
                    super().__init__(message, details)
         | 
| 192 | 
            +
             | 
| 193 | 
            +
             | 
| 194 | 
            +
            class MCPTimeoutError(MCPException):
         | 
| 195 | 
            +
                """
         | 
| 196 | 
            +
                Raised when an operation times out.
         | 
| 197 | 
            +
                
         | 
| 198 | 
            +
                Common scenarios:
         | 
| 199 | 
            +
                - Tool invocation timeout
         | 
| 200 | 
            +
                - Server startup timeout
         | 
| 201 | 
            +
                - Communication timeout
         | 
| 202 | 
            +
                """
         | 
| 203 | 
            +
                
         | 
| 204 | 
            +
                def __init__(self, operation: str, timeout_seconds: float):
         | 
| 205 | 
            +
                    """
         | 
| 206 | 
            +
                    Initialize timeout error.
         | 
| 207 | 
            +
                    
         | 
| 208 | 
            +
                    Args:
         | 
| 209 | 
            +
                        operation: Operation that timed out
         | 
| 210 | 
            +
                        timeout_seconds: Timeout duration in seconds
         | 
| 211 | 
            +
                    """
         | 
| 212 | 
            +
                    message = f"Operation '{operation}' timed out after {timeout_seconds} seconds"
         | 
| 213 | 
            +
                    details = {
         | 
| 214 | 
            +
                        "operation": operation,
         | 
| 215 | 
            +
                        "timeout_seconds": timeout_seconds
         | 
| 216 | 
            +
                    }
         | 
| 217 | 
            +
                    super().__init__(message, details)
         | 
| 218 | 
            +
             | 
| 219 | 
            +
             | 
| 220 | 
            +
            class MCPAuthenticationError(MCPException):
         | 
| 221 | 
            +
                """
         | 
| 222 | 
            +
                Raised when authentication fails.
         | 
| 223 | 
            +
                
         | 
| 224 | 
            +
                For future use when MCP supports authentication.
         | 
| 225 | 
            +
                """
         | 
| 226 | 
            +
                
         | 
| 227 | 
            +
                def __init__(self, message: str, auth_method: Optional[str] = None):
         | 
| 228 | 
            +
                    """
         | 
| 229 | 
            +
                    Initialize authentication error.
         | 
| 230 | 
            +
                    
         | 
| 231 | 
            +
                    Args:
         | 
| 232 | 
            +
                        message: Error message
         | 
| 233 | 
            +
                        auth_method: Authentication method that failed
         | 
| 234 | 
            +
                    """
         | 
| 235 | 
            +
                    details = {}
         | 
| 236 | 
            +
                    if auth_method:
         | 
| 237 | 
            +
                        details["auth_method"] = auth_method
         | 
| 238 | 
            +
                    
         | 
| 239 | 
            +
                    super().__init__(message, details)
         |