claude-mpm 3.9.8__py3-none-any.whl → 3.9.9__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/base_agent.json +1 -1
- claude_mpm/cli/__init__.py +3 -1
- claude_mpm/cli/commands/__init__.py +3 -1
- claude_mpm/cli/commands/cleanup.py +21 -1
- claude_mpm/cli/commands/mcp.py +821 -0
- claude_mpm/cli/parser.py +148 -1
- claude_mpm/config/memory_guardian_config.py +325 -0
- claude_mpm/constants.py +13 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +76 -19
- claude_mpm/models/state_models.py +433 -0
- claude_mpm/services/communication/__init__.py +2 -2
- claude_mpm/services/communication/socketio.py +18 -16
- claude_mpm/services/infrastructure/__init__.py +4 -1
- claude_mpm/services/infrastructure/logging.py +3 -3
- claude_mpm/services/infrastructure/memory_guardian.py +770 -0
- claude_mpm/services/mcp_gateway/__init__.py +28 -12
- claude_mpm/services/mcp_gateway/main.py +326 -0
- claude_mpm/services/mcp_gateway/registry/__init__.py +6 -3
- claude_mpm/services/mcp_gateway/registry/service_registry.py +397 -0
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +477 -0
- claude_mpm/services/mcp_gateway/server/__init__.py +9 -3
- claude_mpm/services/mcp_gateway/server/mcp_server.py +430 -0
- claude_mpm/services/mcp_gateway/server/mcp_server_simple.py +444 -0
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +373 -0
- claude_mpm/services/mcp_gateway/tools/__init__.py +16 -3
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +497 -0
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +729 -0
- claude_mpm/services/mcp_gateway/tools/hello_world.py +551 -0
- claude_mpm/utils/file_utils.py +293 -0
- claude_mpm/utils/platform_memory.py +524 -0
- claude_mpm/utils/subprocess_utils.py +305 -0
- {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/METADATA +3 -1
- {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/RECORD +39 -28
- claude_mpm/agents/templates/.claude-mpm/memories/README.md +0 -36
- claude_mpm/agents/templates/.claude-mpm/memories/engineer_agent.md +0 -39
- claude_mpm/agents/templates/.claude-mpm/memories/qa_agent.md +0 -38
- claude_mpm/agents/templates/.claude-mpm/memories/research_agent.md +0 -39
- claude_mpm/agents/templates/.claude-mpm/memories/version_control_agent.md +0 -38
- /claude_mpm/agents/templates/{research_memory_efficient.json → backup/research_memory_efficient.json} +0 -0
- {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/top_level.txt +0 -0
| @@ -0,0 +1,444 @@ | |
| 1 | 
            +
            """
         | 
| 2 | 
            +
            Simple MCP Server Implementation
         | 
| 3 | 
            +
            =================================
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            A simplified MCP server implementation that follows the MCP protocol
         | 
| 6 | 
            +
            without requiring the official MCP package.
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            Part of ISS-0035: MCP Server Implementation - Core Server and Tool Registry
         | 
| 9 | 
            +
            """
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            import asyncio
         | 
| 12 | 
            +
            import json
         | 
| 13 | 
            +
            import sys
         | 
| 14 | 
            +
            from typing import Any, Dict, List, Optional, Callable, Set
         | 
| 15 | 
            +
            from datetime import datetime
         | 
| 16 | 
            +
            import traceback
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            from claude_mpm.services.mcp_gateway.core.interfaces import (
         | 
| 19 | 
            +
                IMCPServer,
         | 
| 20 | 
            +
                IMCPToolRegistry,
         | 
| 21 | 
            +
                IMCPCommunication,
         | 
| 22 | 
            +
                MCPToolInvocation,
         | 
| 23 | 
            +
                MCPToolResult,
         | 
| 24 | 
            +
            )
         | 
| 25 | 
            +
            from claude_mpm.services.mcp_gateway.core.base import BaseMCPService, MCPServiceState
         | 
| 26 | 
            +
             | 
| 27 | 
            +
             | 
| 28 | 
            +
            class SimpleMCPServer(BaseMCPService, IMCPServer):
         | 
| 29 | 
            +
                """
         | 
| 30 | 
            +
                Simplified MCP Server implementation using JSON-RPC over stdio.
         | 
| 31 | 
            +
                
         | 
| 32 | 
            +
                WHY: This implementation provides MCP protocol compatibility without
         | 
| 33 | 
            +
                requiring the official MCP package, making it more portable and testable.
         | 
| 34 | 
            +
                
         | 
| 35 | 
            +
                DESIGN DECISIONS:
         | 
| 36 | 
            +
                - Use standard JSON-RPC 2.0 protocol
         | 
| 37 | 
            +
                - Implement core MCP methods (initialize, tools/list, tools/call)
         | 
| 38 | 
            +
                - Handle stdio communication directly
         | 
| 39 | 
            +
                - Maintain compatibility with Claude Desktop's expectations
         | 
| 40 | 
            +
                """
         | 
| 41 | 
            +
                
         | 
| 42 | 
            +
                def __init__(self, server_name: str = "claude-mpm-mcp", version: str = "1.0.0"):
         | 
| 43 | 
            +
                    """
         | 
| 44 | 
            +
                    Initialize Simple MCP Server.
         | 
| 45 | 
            +
                    
         | 
| 46 | 
            +
                    Args:
         | 
| 47 | 
            +
                        server_name: Name of the MCP server
         | 
| 48 | 
            +
                        version: Server version
         | 
| 49 | 
            +
                    """
         | 
| 50 | 
            +
                    super().__init__(f"SimpleMCPServer-{server_name}")
         | 
| 51 | 
            +
                    
         | 
| 52 | 
            +
                    # Server configuration
         | 
| 53 | 
            +
                    self.server_name = server_name
         | 
| 54 | 
            +
                    self.version = version
         | 
| 55 | 
            +
                    
         | 
| 56 | 
            +
                    # Dependencies (injected via setters)
         | 
| 57 | 
            +
                    self._tool_registry: Optional[IMCPToolRegistry] = None
         | 
| 58 | 
            +
                    self._communication: Optional[IMCPCommunication] = None
         | 
| 59 | 
            +
                    
         | 
| 60 | 
            +
                    # Request handlers
         | 
| 61 | 
            +
                    self._handlers: Dict[str, Callable] = {}
         | 
| 62 | 
            +
                    
         | 
| 63 | 
            +
                    # Server capabilities
         | 
| 64 | 
            +
                    self._capabilities = {
         | 
| 65 | 
            +
                        "tools": {},
         | 
| 66 | 
            +
                        "prompts": {},
         | 
| 67 | 
            +
                        "resources": {},
         | 
| 68 | 
            +
                        "experimental": {}
         | 
| 69 | 
            +
                    }
         | 
| 70 | 
            +
                    
         | 
| 71 | 
            +
                    # Metrics
         | 
| 72 | 
            +
                    self._metrics = {
         | 
| 73 | 
            +
                        "requests_handled": 0,
         | 
| 74 | 
            +
                        "errors": 0,
         | 
| 75 | 
            +
                        "tool_invocations": 0,
         | 
| 76 | 
            +
                        "start_time": None,
         | 
| 77 | 
            +
                        "last_request_time": None
         | 
| 78 | 
            +
                    }
         | 
| 79 | 
            +
                    
         | 
| 80 | 
            +
                    # Running state
         | 
| 81 | 
            +
                    self._run_task: Optional[asyncio.Task] = None
         | 
| 82 | 
            +
                    self._shutdown_event = asyncio.Event()
         | 
| 83 | 
            +
                    
         | 
| 84 | 
            +
                    # Setup default handlers
         | 
| 85 | 
            +
                    self._setup_default_handlers()
         | 
| 86 | 
            +
                
         | 
| 87 | 
            +
                def _setup_default_handlers(self) -> None:
         | 
| 88 | 
            +
                    """
         | 
| 89 | 
            +
                    Setup default MCP protocol handlers.
         | 
| 90 | 
            +
                    
         | 
| 91 | 
            +
                    WHY: The MCP protocol requires specific handlers for initialization,
         | 
| 92 | 
            +
                    tool discovery, and tool invocation. We set these up to ensure
         | 
| 93 | 
            +
                    protocol compliance.
         | 
| 94 | 
            +
                    """
         | 
| 95 | 
            +
                    # Initialize handler
         | 
| 96 | 
            +
                    async def handle_initialize(params: Dict[str, Any]) -> Dict[str, Any]:
         | 
| 97 | 
            +
                        """Handle initialize request."""
         | 
| 98 | 
            +
                        self.log_info("Handling initialize request")
         | 
| 99 | 
            +
                        
         | 
| 100 | 
            +
                        return {
         | 
| 101 | 
            +
                            "protocolVersion": "2024-11-05",
         | 
| 102 | 
            +
                            "capabilities": self.get_capabilities(),
         | 
| 103 | 
            +
                            "serverInfo": {
         | 
| 104 | 
            +
                                "name": self.server_name,
         | 
| 105 | 
            +
                                "version": self.version
         | 
| 106 | 
            +
                            }
         | 
| 107 | 
            +
                        }
         | 
| 108 | 
            +
                    
         | 
| 109 | 
            +
                    self._handlers["initialize"] = handle_initialize
         | 
| 110 | 
            +
                    
         | 
| 111 | 
            +
                    # Tools list handler
         | 
| 112 | 
            +
                    async def handle_tools_list(params: Dict[str, Any]) -> Dict[str, Any]:
         | 
| 113 | 
            +
                        """Handle tools/list request."""
         | 
| 114 | 
            +
                        self.log_info("Handling tools/list request")
         | 
| 115 | 
            +
                        
         | 
| 116 | 
            +
                        if not self._tool_registry:
         | 
| 117 | 
            +
                            self.log_warning("No tool registry available")
         | 
| 118 | 
            +
                            return {"tools": []}
         | 
| 119 | 
            +
                        
         | 
| 120 | 
            +
                        tools = []
         | 
| 121 | 
            +
                        for tool_def in self._tool_registry.list_tools():
         | 
| 122 | 
            +
                            tool = {
         | 
| 123 | 
            +
                                "name": tool_def.name,
         | 
| 124 | 
            +
                                "description": tool_def.description,
         | 
| 125 | 
            +
                                "inputSchema": tool_def.input_schema
         | 
| 126 | 
            +
                            }
         | 
| 127 | 
            +
                            tools.append(tool)
         | 
| 128 | 
            +
                        
         | 
| 129 | 
            +
                        self.log_info(f"Returning {len(tools)} tools")
         | 
| 130 | 
            +
                        return {"tools": tools}
         | 
| 131 | 
            +
                    
         | 
| 132 | 
            +
                    self._handlers["tools/list"] = handle_tools_list
         | 
| 133 | 
            +
                    
         | 
| 134 | 
            +
                    # Tools call handler
         | 
| 135 | 
            +
                    async def handle_tools_call(params: Dict[str, Any]) -> Dict[str, Any]:
         | 
| 136 | 
            +
                        """Handle tools/call request."""
         | 
| 137 | 
            +
                        name = params.get("name", "")
         | 
| 138 | 
            +
                        arguments = params.get("arguments", {})
         | 
| 139 | 
            +
                        
         | 
| 140 | 
            +
                        self.log_info(f"Handling tools/call request for tool: {name}")
         | 
| 141 | 
            +
                        
         | 
| 142 | 
            +
                        if not self._tool_registry:
         | 
| 143 | 
            +
                            error_msg = "No tool registry available"
         | 
| 144 | 
            +
                            self.log_error(error_msg)
         | 
| 145 | 
            +
                            return {
         | 
| 146 | 
            +
                                "content": [
         | 
| 147 | 
            +
                                    {"type": "text", "text": f"Error: {error_msg}"}
         | 
| 148 | 
            +
                                ]
         | 
| 149 | 
            +
                            }
         | 
| 150 | 
            +
                        
         | 
| 151 | 
            +
                        # Create invocation request
         | 
| 152 | 
            +
                        invocation = MCPToolInvocation(
         | 
| 153 | 
            +
                            tool_name=name,
         | 
| 154 | 
            +
                            parameters=arguments,
         | 
| 155 | 
            +
                            request_id=f"req_{datetime.now().timestamp()}"
         | 
| 156 | 
            +
                        )
         | 
| 157 | 
            +
                        
         | 
| 158 | 
            +
                        try:
         | 
| 159 | 
            +
                            # Invoke tool through registry
         | 
| 160 | 
            +
                            result = await self._tool_registry.invoke_tool(invocation)
         | 
| 161 | 
            +
                            
         | 
| 162 | 
            +
                            # Update metrics
         | 
| 163 | 
            +
                            self._metrics["tool_invocations"] += 1
         | 
| 164 | 
            +
                            
         | 
| 165 | 
            +
                            # Log invocation
         | 
| 166 | 
            +
                            self.log_tool_invocation(name, result.success, result.execution_time)
         | 
| 167 | 
            +
                            
         | 
| 168 | 
            +
                            if result.success:
         | 
| 169 | 
            +
                                # Return successful result
         | 
| 170 | 
            +
                                if isinstance(result.data, str):
         | 
| 171 | 
            +
                                    content = [{"type": "text", "text": result.data}]
         | 
| 172 | 
            +
                                else:
         | 
| 173 | 
            +
                                    content = [{"type": "text", "text": json.dumps(result.data, indent=2)}]
         | 
| 174 | 
            +
                            else:
         | 
| 175 | 
            +
                                # Return error
         | 
| 176 | 
            +
                                content = [{"type": "text", "text": f"Error: {result.error}"}]
         | 
| 177 | 
            +
                            
         | 
| 178 | 
            +
                            return {"content": content}
         | 
| 179 | 
            +
                                
         | 
| 180 | 
            +
                        except Exception as e:
         | 
| 181 | 
            +
                            error_msg = f"Failed to invoke tool {name}: {str(e)}"
         | 
| 182 | 
            +
                            self.log_error(error_msg)
         | 
| 183 | 
            +
                            self._metrics["errors"] += 1
         | 
| 184 | 
            +
                            return {
         | 
| 185 | 
            +
                                "content": [
         | 
| 186 | 
            +
                                    {"type": "text", "text": f"Error: {error_msg}"}
         | 
| 187 | 
            +
                                ]
         | 
| 188 | 
            +
                            }
         | 
| 189 | 
            +
                    
         | 
| 190 | 
            +
                    self._handlers["tools/call"] = handle_tools_call
         | 
| 191 | 
            +
                    
         | 
| 192 | 
            +
                    # Ping handler
         | 
| 193 | 
            +
                    async def handle_ping(params: Dict[str, Any]) -> Dict[str, Any]:
         | 
| 194 | 
            +
                        """Handle ping request."""
         | 
| 195 | 
            +
                        return {}
         | 
| 196 | 
            +
                    
         | 
| 197 | 
            +
                    self._handlers["ping"] = handle_ping
         | 
| 198 | 
            +
                
         | 
| 199 | 
            +
                def set_tool_registry(self, registry: IMCPToolRegistry) -> None:
         | 
| 200 | 
            +
                    """
         | 
| 201 | 
            +
                    Set the tool registry for the server.
         | 
| 202 | 
            +
                    
         | 
| 203 | 
            +
                    Args:
         | 
| 204 | 
            +
                        registry: Tool registry to use
         | 
| 205 | 
            +
                    """
         | 
| 206 | 
            +
                    self._tool_registry = registry
         | 
| 207 | 
            +
                    self.log_info("Tool registry set")
         | 
| 208 | 
            +
                
         | 
| 209 | 
            +
                def set_communication(self, communication: IMCPCommunication) -> None:
         | 
| 210 | 
            +
                    """
         | 
| 211 | 
            +
                    Set the communication handler.
         | 
| 212 | 
            +
                    
         | 
| 213 | 
            +
                    Args:
         | 
| 214 | 
            +
                        communication: Communication handler to use
         | 
| 215 | 
            +
                    """
         | 
| 216 | 
            +
                    self._communication = communication
         | 
| 217 | 
            +
                    self.log_info("Communication handler set")
         | 
| 218 | 
            +
                
         | 
| 219 | 
            +
                async def _do_initialize(self) -> bool:
         | 
| 220 | 
            +
                    """
         | 
| 221 | 
            +
                    Perform server initialization.
         | 
| 222 | 
            +
                    
         | 
| 223 | 
            +
                    Returns:
         | 
| 224 | 
            +
                        True if initialization successful
         | 
| 225 | 
            +
                    """
         | 
| 226 | 
            +
                    try:
         | 
| 227 | 
            +
                        self.log_info("Initializing Simple MCP server components")
         | 
| 228 | 
            +
                        
         | 
| 229 | 
            +
                        # Validate dependencies
         | 
| 230 | 
            +
                        if not self._tool_registry:
         | 
| 231 | 
            +
                            self.log_warning("No tool registry set - server will have no tools")
         | 
| 232 | 
            +
                        
         | 
| 233 | 
            +
                        # Initialize metrics
         | 
| 234 | 
            +
                        self._metrics["start_time"] = datetime.now().isoformat()
         | 
| 235 | 
            +
                        
         | 
| 236 | 
            +
                        # Update capabilities based on registry
         | 
| 237 | 
            +
                        if self._tool_registry:
         | 
| 238 | 
            +
                            tools = self._tool_registry.list_tools()
         | 
| 239 | 
            +
                            self._capabilities["tools"]["available"] = len(tools)
         | 
| 240 | 
            +
                            self._capabilities["tools"]["names"] = [t.name for t in tools]
         | 
| 241 | 
            +
                        
         | 
| 242 | 
            +
                        self.log_info("Simple MCP server initialization complete")
         | 
| 243 | 
            +
                        return True
         | 
| 244 | 
            +
                        
         | 
| 245 | 
            +
                    except Exception as e:
         | 
| 246 | 
            +
                        self.log_error(f"Failed to initialize Simple MCP server: {e}")
         | 
| 247 | 
            +
                        return False
         | 
| 248 | 
            +
                
         | 
| 249 | 
            +
                async def _do_start(self) -> bool:
         | 
| 250 | 
            +
                    """
         | 
| 251 | 
            +
                    Start the MCP server.
         | 
| 252 | 
            +
                    
         | 
| 253 | 
            +
                    Returns:
         | 
| 254 | 
            +
                        True if startup successful
         | 
| 255 | 
            +
                    """
         | 
| 256 | 
            +
                    try:
         | 
| 257 | 
            +
                        self.log_info("Starting Simple MCP server")
         | 
| 258 | 
            +
                        
         | 
| 259 | 
            +
                        # Clear shutdown event
         | 
| 260 | 
            +
                        self._shutdown_event.clear()
         | 
| 261 | 
            +
                        
         | 
| 262 | 
            +
                        # Start the run task
         | 
| 263 | 
            +
                        self._run_task = asyncio.create_task(self.run())
         | 
| 264 | 
            +
                        
         | 
| 265 | 
            +
                        self.log_info("Simple MCP server started successfully")
         | 
| 266 | 
            +
                        return True
         | 
| 267 | 
            +
                        
         | 
| 268 | 
            +
                    except Exception as e:
         | 
| 269 | 
            +
                        self.log_error(f"Failed to start Simple MCP server: {e}")
         | 
| 270 | 
            +
                        return False
         | 
| 271 | 
            +
                
         | 
| 272 | 
            +
                async def _do_shutdown(self) -> None:
         | 
| 273 | 
            +
                    """
         | 
| 274 | 
            +
                    Shutdown the MCP server gracefully.
         | 
| 275 | 
            +
                    """
         | 
| 276 | 
            +
                    self.log_info("Shutting down Simple MCP server")
         | 
| 277 | 
            +
                    
         | 
| 278 | 
            +
                    # Signal shutdown
         | 
| 279 | 
            +
                    self._shutdown_event.set()
         | 
| 280 | 
            +
                    
         | 
| 281 | 
            +
                    # Cancel run task if active
         | 
| 282 | 
            +
                    if self._run_task and not self._run_task.done():
         | 
| 283 | 
            +
                        self._run_task.cancel()
         | 
| 284 | 
            +
                        try:
         | 
| 285 | 
            +
                            await self._run_task
         | 
| 286 | 
            +
                        except asyncio.CancelledError:
         | 
| 287 | 
            +
                            pass
         | 
| 288 | 
            +
                    
         | 
| 289 | 
            +
                    # Clean up resources
         | 
| 290 | 
            +
                    if self._tool_registry:
         | 
| 291 | 
            +
                        self.log_info("Cleaning up tool registry")
         | 
| 292 | 
            +
                        # Tool registry cleanup if needed
         | 
| 293 | 
            +
                    
         | 
| 294 | 
            +
                    self.log_info("Simple MCP server shutdown complete")
         | 
| 295 | 
            +
                
         | 
| 296 | 
            +
                async def stop(self) -> None:
         | 
| 297 | 
            +
                    """
         | 
| 298 | 
            +
                    Stop the MCP service gracefully.
         | 
| 299 | 
            +
                    
         | 
| 300 | 
            +
                    This implements the IMCPLifecycle interface method.
         | 
| 301 | 
            +
                    """
         | 
| 302 | 
            +
                    await self.shutdown()
         | 
| 303 | 
            +
                
         | 
| 304 | 
            +
                async def handle_request(self, request: Dict[str, Any]) -> Dict[str, Any]:
         | 
| 305 | 
            +
                    """
         | 
| 306 | 
            +
                    Handle an MCP request.
         | 
| 307 | 
            +
                    
         | 
| 308 | 
            +
                    This method routes requests to appropriate handlers based on the method.
         | 
| 309 | 
            +
                    
         | 
| 310 | 
            +
                    Args:
         | 
| 311 | 
            +
                        request: MCP request message
         | 
| 312 | 
            +
                        
         | 
| 313 | 
            +
                    Returns:
         | 
| 314 | 
            +
                        Response message
         | 
| 315 | 
            +
                    """
         | 
| 316 | 
            +
                    try:
         | 
| 317 | 
            +
                        # Update metrics
         | 
| 318 | 
            +
                        self._metrics["requests_handled"] += 1
         | 
| 319 | 
            +
                        self._metrics["last_request_time"] = datetime.now().isoformat()
         | 
| 320 | 
            +
                        
         | 
| 321 | 
            +
                        # Extract request details
         | 
| 322 | 
            +
                        method = request.get("method", "")
         | 
| 323 | 
            +
                        params = request.get("params", {})
         | 
| 324 | 
            +
                        request_id = request.get("id")
         | 
| 325 | 
            +
                        
         | 
| 326 | 
            +
                        self.log_debug(f"Handling request: {method}")
         | 
| 327 | 
            +
                        
         | 
| 328 | 
            +
                        # Check for handler
         | 
| 329 | 
            +
                        if method in self._handlers:
         | 
| 330 | 
            +
                            handler = self._handlers[method]
         | 
| 331 | 
            +
                            result = await handler(params)
         | 
| 332 | 
            +
                            
         | 
| 333 | 
            +
                            # Build response
         | 
| 334 | 
            +
                            response = {
         | 
| 335 | 
            +
                                "jsonrpc": "2.0",
         | 
| 336 | 
            +
                                "id": request_id,
         | 
| 337 | 
            +
                                "result": result
         | 
| 338 | 
            +
                            }
         | 
| 339 | 
            +
                        else:
         | 
| 340 | 
            +
                            # Unknown method
         | 
| 341 | 
            +
                            self.log_warning(f"Unknown method: {method}")
         | 
| 342 | 
            +
                            response = {
         | 
| 343 | 
            +
                                "jsonrpc": "2.0",
         | 
| 344 | 
            +
                                "id": request_id,
         | 
| 345 | 
            +
                                "error": {
         | 
| 346 | 
            +
                                    "code": -32601,
         | 
| 347 | 
            +
                                    "message": f"Method not found: {method}"
         | 
| 348 | 
            +
                                }
         | 
| 349 | 
            +
                            }
         | 
| 350 | 
            +
                        
         | 
| 351 | 
            +
                        return response
         | 
| 352 | 
            +
                        
         | 
| 353 | 
            +
                    except Exception as e:
         | 
| 354 | 
            +
                        self.log_error(f"Error handling request: {e}")
         | 
| 355 | 
            +
                        self._metrics["errors"] += 1
         | 
| 356 | 
            +
                        
         | 
| 357 | 
            +
                        return {
         | 
| 358 | 
            +
                            "jsonrpc": "2.0",
         | 
| 359 | 
            +
                            "id": request.get("id"),
         | 
| 360 | 
            +
                            "error": {
         | 
| 361 | 
            +
                                "code": -32603,
         | 
| 362 | 
            +
                                "message": f"Internal error: {str(e)}"
         | 
| 363 | 
            +
                            }
         | 
| 364 | 
            +
                        }
         | 
| 365 | 
            +
                
         | 
| 366 | 
            +
                async def run(self) -> None:
         | 
| 367 | 
            +
                    """
         | 
| 368 | 
            +
                    Run the MCP server main loop.
         | 
| 369 | 
            +
                    
         | 
| 370 | 
            +
                    This method handles incoming requests using the communication handler.
         | 
| 371 | 
            +
                    
         | 
| 372 | 
            +
                    WHY: We use a simple request/response loop that works with any
         | 
| 373 | 
            +
                    communication handler (stdio, websocket, etc).
         | 
| 374 | 
            +
                    """
         | 
| 375 | 
            +
                    try:
         | 
| 376 | 
            +
                        self.log_info("Starting Simple MCP server main loop")
         | 
| 377 | 
            +
                        
         | 
| 378 | 
            +
                        # If we have a communication handler, use it
         | 
| 379 | 
            +
                        if self._communication:
         | 
| 380 | 
            +
                            while not self._shutdown_event.is_set():
         | 
| 381 | 
            +
                                try:
         | 
| 382 | 
            +
                                    # Receive message
         | 
| 383 | 
            +
                                    message = await self._communication.receive_message()
         | 
| 384 | 
            +
                                    if message:
         | 
| 385 | 
            +
                                        # Handle request
         | 
| 386 | 
            +
                                        response = await self.handle_request(message)
         | 
| 387 | 
            +
                                        # Send response
         | 
| 388 | 
            +
                                        await self._communication.send_message(response)
         | 
| 389 | 
            +
                                    else:
         | 
| 390 | 
            +
                                        # No message, wait a bit
         | 
| 391 | 
            +
                                        await asyncio.sleep(0.1)
         | 
| 392 | 
            +
                                except Exception as e:
         | 
| 393 | 
            +
                                    self.log_error(f"Error in message loop: {e}")
         | 
| 394 | 
            +
                                    self._metrics["errors"] += 1
         | 
| 395 | 
            +
                        else:
         | 
| 396 | 
            +
                            # No communication handler, just wait for shutdown
         | 
| 397 | 
            +
                            self.log_warning("No communication handler set, waiting for shutdown")
         | 
| 398 | 
            +
                            await self._shutdown_event.wait()
         | 
| 399 | 
            +
                        
         | 
| 400 | 
            +
                        self.log_info("Simple MCP server main loop ended")
         | 
| 401 | 
            +
                        
         | 
| 402 | 
            +
                    except Exception as e:
         | 
| 403 | 
            +
                        self.log_error(f"Error in Simple MCP server main loop: {e}")
         | 
| 404 | 
            +
                        self.log_error(f"Traceback: {traceback.format_exc()}")
         | 
| 405 | 
            +
                        self._metrics["errors"] += 1
         | 
| 406 | 
            +
                        raise
         | 
| 407 | 
            +
                
         | 
| 408 | 
            +
                def register_handler(self, method: str, handler: Callable) -> None:
         | 
| 409 | 
            +
                    """
         | 
| 410 | 
            +
                    Register a custom request handler.
         | 
| 411 | 
            +
                    
         | 
| 412 | 
            +
                    Args:
         | 
| 413 | 
            +
                        method: Method name to handle
         | 
| 414 | 
            +
                        handler: Handler function
         | 
| 415 | 
            +
                    """
         | 
| 416 | 
            +
                    self._handlers[method] = handler
         | 
| 417 | 
            +
                    self.log_info(f"Registered handler for method: {method}")
         | 
| 418 | 
            +
                
         | 
| 419 | 
            +
                def get_capabilities(self) -> Dict[str, Any]:
         | 
| 420 | 
            +
                    """
         | 
| 421 | 
            +
                    Get server capabilities.
         | 
| 422 | 
            +
                    
         | 
| 423 | 
            +
                    Returns:
         | 
| 424 | 
            +
                        Dictionary of server capabilities formatted for MCP protocol
         | 
| 425 | 
            +
                    """
         | 
| 426 | 
            +
                    capabilities = {}
         | 
| 427 | 
            +
                    
         | 
| 428 | 
            +
                    # Add tool capabilities if registry is available
         | 
| 429 | 
            +
                    if self._tool_registry:
         | 
| 430 | 
            +
                        capabilities["tools"] = {}
         | 
| 431 | 
            +
                    
         | 
| 432 | 
            +
                    # Add experimental features
         | 
| 433 | 
            +
                    capabilities["experimental"] = {}
         | 
| 434 | 
            +
                    
         | 
| 435 | 
            +
                    return capabilities
         | 
| 436 | 
            +
                
         | 
| 437 | 
            +
                def get_metrics(self) -> Dict[str, Any]:
         | 
| 438 | 
            +
                    """
         | 
| 439 | 
            +
                    Get server metrics.
         | 
| 440 | 
            +
                    
         | 
| 441 | 
            +
                    Returns:
         | 
| 442 | 
            +
                        Server metrics dictionary
         | 
| 443 | 
            +
                    """
         | 
| 444 | 
            +
                    return self._metrics.copy()
         |