claude-mpm 3.9.9__py3-none-any.whl → 3.9.11__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/memory_manager.json +155 -0
- claude_mpm/cli/__init__.py +15 -2
- claude_mpm/cli/commands/__init__.py +3 -0
- claude_mpm/cli/commands/mcp.py +280 -134
- claude_mpm/cli/commands/run_guarded.py +511 -0
- claude_mpm/cli/parser.py +8 -2
- claude_mpm/config/experimental_features.py +219 -0
- claude_mpm/config/memory_guardian_yaml.py +335 -0
- claude_mpm/constants.py +1 -0
- claude_mpm/core/memory_aware_runner.py +353 -0
- claude_mpm/services/infrastructure/context_preservation.py +537 -0
- claude_mpm/services/infrastructure/graceful_degradation.py +616 -0
- claude_mpm/services/infrastructure/health_monitor.py +775 -0
- claude_mpm/services/infrastructure/memory_dashboard.py +479 -0
- claude_mpm/services/infrastructure/memory_guardian.py +189 -15
- claude_mpm/services/infrastructure/restart_protection.py +642 -0
- claude_mpm/services/infrastructure/state_manager.py +774 -0
- claude_mpm/services/mcp_gateway/__init__.py +11 -11
- claude_mpm/services/mcp_gateway/core/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/core/interfaces.py +10 -9
- claude_mpm/services/mcp_gateway/main.py +35 -5
- claude_mpm/services/mcp_gateway/manager.py +334 -0
- claude_mpm/services/mcp_gateway/registry/service_registry.py +4 -8
- claude_mpm/services/mcp_gateway/server/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/server/{mcp_server.py → mcp_gateway.py} +60 -59
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +1 -2
- claude_mpm/services/ticket_manager.py +8 -8
- claude_mpm/services/ticket_manager_di.py +5 -5
- claude_mpm/storage/__init__.py +9 -0
- claude_mpm/storage/state_storage.py +556 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-3.9.11.dist-info}/METADATA +25 -2
- {claude_mpm-3.9.9.dist-info → claude_mpm-3.9.11.dist-info}/RECORD +37 -24
- claude_mpm/services/mcp_gateway/server/mcp_server_simple.py +0 -444
- {claude_mpm-3.9.9.dist-info → claude_mpm-3.9.11.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-3.9.11.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-3.9.11.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-3.9.11.dist-info}/top_level.txt +0 -0
    
        claude_mpm/cli/commands/mcp.py
    CHANGED
    
    | @@ -38,7 +38,7 @@ def manage_mcp(args): | |
| 38 38 | 
             
                try:
         | 
| 39 39 | 
             
                    # Import MCP Gateway services with lazy loading
         | 
| 40 40 | 
             
                    from ...services.mcp_gateway import (
         | 
| 41 | 
            -
                         | 
| 41 | 
            +
                        MCPGateway,
         | 
| 42 42 | 
             
                        ToolRegistry,
         | 
| 43 43 | 
             
                        MCPConfiguration,
         | 
| 44 44 | 
             
                        MCPServiceRegistry
         | 
| @@ -107,94 +107,55 @@ def _show_help(): | |
| 107 107 | 
             
                print("Use 'claude-mpm mcp <command> --help' for more information")
         | 
| 108 108 |  | 
| 109 109 |  | 
| 110 | 
            +
            # Daemon mode removed - MCP servers should be simple stdio responders
         | 
| 111 | 
            +
             | 
| 112 | 
            +
             | 
| 110 113 | 
             
            async def _start_server(args, logger):
         | 
| 111 114 | 
             
                """
         | 
| 112 | 
            -
                Start the MCP Gateway  | 
| 113 | 
            -
             | 
| 114 | 
            -
                WHY: Users need to start the MCP  | 
| 115 | 
            -
                and external service integration in Claude sessions.
         | 
| 116 | 
            -
                
         | 
| 115 | 
            +
                Start the MCP Gateway using the global manager.
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                WHY: Users need to start the MCP gateway to enable tool invocation
         | 
| 118 | 
            +
                and external service integration in Claude sessions. We use the global
         | 
| 119 | 
            +
                manager to ensure only one instance runs per installation.
         | 
| 120 | 
            +
             | 
| 117 121 | 
             
                Args:
         | 
| 118 122 | 
             
                    args: Command arguments with optional port and configuration
         | 
| 119 123 | 
             
                    logger: Logger instance
         | 
| 120 | 
            -
             | 
| 124 | 
            +
             | 
| 121 125 | 
             
                Returns:
         | 
| 122 126 | 
             
                    int: Exit code (0 for success, non-zero for failure)
         | 
| 123 127 | 
             
                """
         | 
| 124 | 
            -
                from ...services.mcp_gateway import  | 
| 125 | 
            -
             | 
| 128 | 
            +
                from ...services.mcp_gateway.manager import (
         | 
| 129 | 
            +
                    start_global_gateway,
         | 
| 130 | 
            +
                    run_global_gateway,
         | 
| 131 | 
            +
                    is_gateway_running,
         | 
| 132 | 
            +
                    get_gateway_status
         | 
| 133 | 
            +
                )
         | 
| 126 134 |  | 
| 127 135 | 
             
                try:
         | 
| 128 | 
            -
                     | 
| 129 | 
            -
             | 
| 130 | 
            -
                    #  | 
| 131 | 
            -
                     | 
| 132 | 
            -
             | 
| 133 | 
            -
             | 
| 134 | 
            -
             | 
| 135 | 
            -
             | 
| 136 | 
            -
             | 
| 137 | 
            -
             | 
| 138 | 
            -
             | 
| 139 | 
            -
             | 
| 140 | 
            -
             | 
| 141 | 
            -
             | 
| 142 | 
            -
                    server = MCPServer(
         | 
| 143 | 
            -
                        server_name=config.get("mcp.server.name", "claude-mpm-gateway"),
         | 
| 144 | 
            -
                        version=config.get("mcp.server.version", "1.0.0")
         | 
| 145 | 
            -
                    )
         | 
| 146 | 
            -
                    
         | 
| 147 | 
            -
                    # Initialize tool registry
         | 
| 148 | 
            -
                    tool_registry = ToolRegistry()
         | 
| 149 | 
            -
                    
         | 
| 150 | 
            -
                    # Load default tools if enabled
         | 
| 151 | 
            -
                    if config.get("mcp.tools.enabled", True):
         | 
| 152 | 
            -
                        logger.info("Loading default tools")
         | 
| 153 | 
            -
                        _load_default_tools(tool_registry, logger)
         | 
| 154 | 
            -
                    
         | 
| 155 | 
            -
                    # Set dependencies
         | 
| 156 | 
            -
                    server.set_tool_registry(tool_registry)
         | 
| 157 | 
            -
                    
         | 
| 158 | 
            -
                    # Start server based on mode
         | 
| 159 | 
            -
                    mode = getattr(args, 'mode', 'stdio')
         | 
| 160 | 
            -
                    
         | 
| 161 | 
            -
                    if mode == 'stdio':
         | 
| 162 | 
            -
                        # Standard I/O mode for Claude integration
         | 
| 163 | 
            -
                        logger.info("Starting MCP server in stdio mode")
         | 
| 164 | 
            -
                        stdio_handler = StdioHandler(server)
         | 
| 165 | 
            -
                        
         | 
| 166 | 
            -
                        # Run the server
         | 
| 167 | 
            -
                        asyncio.run(stdio_handler.run())
         | 
| 168 | 
            -
                        
         | 
| 169 | 
            -
                    elif mode == 'standalone':
         | 
| 170 | 
            -
                        # Standalone mode for testing
         | 
| 171 | 
            -
                        port = getattr(args, 'port', 8766)
         | 
| 172 | 
            -
                        logger.info(f"Starting MCP server in standalone mode on port {port}")
         | 
| 173 | 
            -
                        
         | 
| 174 | 
            -
                        # Create async event loop and run server
         | 
| 175 | 
            -
                        loop = asyncio.new_event_loop()
         | 
| 176 | 
            -
                        asyncio.set_event_loop(loop)
         | 
| 177 | 
            -
                        
         | 
| 178 | 
            -
                        async def run_standalone():
         | 
| 179 | 
            -
                            await server.start()
         | 
| 180 | 
            -
                            print(f"MCP Gateway server started on port {port}")
         | 
| 181 | 
            -
                            print("Press Ctrl+C to stop")
         | 
| 182 | 
            -
                            
         | 
| 183 | 
            -
                            # Keep server running
         | 
| 184 | 
            -
                            try:
         | 
| 185 | 
            -
                                await asyncio.Event().wait()
         | 
| 186 | 
            -
                            except KeyboardInterrupt:
         | 
| 187 | 
            -
                                print("\nStopping server...")
         | 
| 188 | 
            -
                                await server.stop()
         | 
| 189 | 
            -
                        
         | 
| 190 | 
            -
                        loop.run_until_complete(run_standalone())
         | 
| 191 | 
            -
                        
         | 
| 192 | 
            -
                    else:
         | 
| 193 | 
            -
                        logger.error(f"Unknown server mode: {mode}")
         | 
| 194 | 
            -
                        print(f"Error: Unknown server mode: {mode}")
         | 
| 136 | 
            +
                    logger.info("Starting MCP Gateway")
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                    # Note: MCP gateways don't "run" as background services
         | 
| 139 | 
            +
                    # They are stdio-based protocol handlers activated by MCP clients
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                    # Get configuration values
         | 
| 142 | 
            +
                    gateway_name = getattr(args, 'name', 'claude-mpm-gateway')
         | 
| 143 | 
            +
                    version = getattr(args, 'version', '1.0.0')
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                    # Initialize the global gateway components
         | 
| 146 | 
            +
                    logger.info(f"Initializing MCP gateway: {gateway_name}")
         | 
| 147 | 
            +
                    if not await start_global_gateway(gateway_name, version):
         | 
| 148 | 
            +
                        logger.error("Failed to initialize MCP gateway")
         | 
| 149 | 
            +
                        print("Error: Failed to initialize MCP gateway")
         | 
| 195 150 | 
             
                        return 1
         | 
| 196 | 
            -
             | 
| 197 | 
            -
                     | 
| 151 | 
            +
             | 
| 152 | 
            +
                    # Run the gateway stdio handler
         | 
| 153 | 
            +
                    logger.info("Starting MCP gateway stdio handler")
         | 
| 154 | 
            +
                    print("MCP Gateway ready for Claude Desktop", file=sys.stderr)
         | 
| 155 | 
            +
                    print("Listening for MCP protocol messages on stdin/stdout...", file=sys.stderr)
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                    await run_global_gateway()
         | 
| 158 | 
            +
             | 
| 198 159 | 
             
                    return 0
         | 
| 199 160 |  | 
| 200 161 | 
             
                except KeyboardInterrupt:
         | 
| @@ -290,23 +251,15 @@ def _show_status(args, logger): | |
| 290 251 | 
             
                    print("MCP Gateway Status")
         | 
| 291 252 | 
             
                    print("=" * 50)
         | 
| 292 253 |  | 
| 293 | 
            -
                    # Check  | 
| 294 | 
            -
                     | 
| 295 | 
            -
             | 
| 296 | 
            -
                     | 
| 297 | 
            -
             | 
| 298 | 
            -
             | 
| 299 | 
            -
             | 
| 300 | 
            -
             | 
| 301 | 
            -
             | 
| 302 | 
            -
                        try:
         | 
| 303 | 
            -
                            os.kill(pid, 0)
         | 
| 304 | 
            -
                            print(f"Server Status: ✅ Running (PID: {pid})")
         | 
| 305 | 
            -
                        except ProcessLookupError:
         | 
| 306 | 
            -
                            print("Server Status: ❌ Not running (stale PID file)")
         | 
| 307 | 
            -
                            pid_file.unlink()
         | 
| 308 | 
            -
                    else:
         | 
| 309 | 
            -
                        print("Server Status: ❌ Not running")
         | 
| 254 | 
            +
                    # Check gateway manager status
         | 
| 255 | 
            +
                    from ...services.mcp_gateway.manager import get_gateway_status, is_gateway_running
         | 
| 256 | 
            +
             | 
| 257 | 
            +
                    # MCP gateways are stdio-based protocol handlers, not background services
         | 
| 258 | 
            +
                    print("Gateway Status: ℹ️  MCP protocol handler (stdio-based)")
         | 
| 259 | 
            +
                    print("  • Activated on-demand by MCP clients (Claude Desktop)")
         | 
| 260 | 
            +
                    print("  • No background processes - communicates via stdin/stdout")
         | 
| 261 | 
            +
                    print("  • Ready for Claude Desktop integration")
         | 
| 262 | 
            +
                    print("  • Test with: claude-mpm mcp test <tool_name>")
         | 
| 310 263 |  | 
| 311 264 | 
             
                    print()
         | 
| 312 265 |  | 
| @@ -314,21 +267,23 @@ def _show_status(args, logger): | |
| 314 267 | 
             
                    print("Registered Tools:")
         | 
| 315 268 | 
             
                    print("-" * 30)
         | 
| 316 269 |  | 
| 317 | 
            -
                    #  | 
| 318 | 
            -
                     | 
| 319 | 
            -
             | 
| 320 | 
            -
             | 
| 321 | 
            -
             | 
| 322 | 
            -
                     | 
| 323 | 
            -
                         | 
| 324 | 
            -
                         | 
| 325 | 
            -
             | 
| 270 | 
            +
                    # Get tool registry with fallback to running server
         | 
| 271 | 
            +
                    try:
         | 
| 272 | 
            +
                        tool_registry = _create_tool_registry_with_fallback(logger)
         | 
| 273 | 
            +
                        config_file = Path.home() / ".claude-mcp" / "mcp_config.yaml"
         | 
| 274 | 
            +
                        config = _load_config_sync(config_file if config_file.exists() else None, logger)
         | 
| 275 | 
            +
                    except ValueError as e:
         | 
| 276 | 
            +
                        print(f"Configuration Error: {e}")
         | 
| 277 | 
            +
                        return 1
         | 
| 278 | 
            +
                    except Exception as e:
         | 
| 279 | 
            +
                        logger.error(f"Failed to initialize: {e}")
         | 
| 280 | 
            +
                        print(f"Error: Failed to initialize MCP components: {e}")
         | 
| 281 | 
            +
                        return 1
         | 
| 326 282 |  | 
| 327 283 | 
             
                    tools = tool_registry.list_tools()
         | 
| 328 284 | 
             
                    if tools:
         | 
| 329 285 | 
             
                        for tool in tools:
         | 
| 330 | 
            -
                             | 
| 331 | 
            -
                            print(f"  {status} {tool.name}: {tool.description}")
         | 
| 286 | 
            +
                            print(f"  ✅ {tool.name}: {tool.description}")
         | 
| 332 287 | 
             
                    else:
         | 
| 333 288 | 
             
                        print("  No tools registered")
         | 
| 334 289 |  | 
| @@ -337,14 +292,17 @@ def _show_status(args, logger): | |
| 337 292 | 
             
                    # Show configuration
         | 
| 338 293 | 
             
                    print("Configuration:")
         | 
| 339 294 | 
             
                    print("-" * 30)
         | 
| 340 | 
            -
             | 
| 295 | 
            +
             | 
| 341 296 | 
             
                    if config_file.exists():
         | 
| 342 297 | 
             
                        print(f"  Config file: {config_file}")
         | 
| 343 | 
            -
                        print(f"  Server name: {config.get('mcp.server.name', 'claude-mpm-gateway')}")
         | 
| 344 | 
            -
                        print(f"  Version: {config.get('mcp.server.version', '1.0.0')}")
         | 
| 345 | 
            -
                        print(f"  Tools enabled: {'Yes' if config.get('mcp.tools.enabled', True) else 'No'}")
         | 
| 346 298 | 
             
                    else:
         | 
| 347 299 | 
             
                        print("  Using default configuration")
         | 
| 300 | 
            +
             | 
| 301 | 
            +
                    server_config = config.get("mcp", {}).get("server", {})
         | 
| 302 | 
            +
                    tools_config = config.get("mcp", {}).get("tools", {})
         | 
| 303 | 
            +
                    print(f"  Server name: {server_config.get('name', 'claude-mpm-gateway')}")
         | 
| 304 | 
            +
                    print(f"  Version: {server_config.get('version', '1.0.0')}")
         | 
| 305 | 
            +
                    print(f"  Tools enabled: {'Yes' if tools_config.get('enabled', True) else 'No'}")
         | 
| 348 306 |  | 
| 349 307 | 
             
                    # Show verbose details if requested
         | 
| 350 308 | 
             
                    if getattr(args, 'verbose', False):
         | 
| @@ -388,15 +346,8 @@ def _manage_tools(args, logger): | |
| 388 346 | 
             
                    # Check for subcommand
         | 
| 389 347 | 
             
                    action = getattr(args, 'tool_action', 'list')
         | 
| 390 348 |  | 
| 391 | 
            -
                    #  | 
| 392 | 
            -
                    tool_registry =  | 
| 393 | 
            -
                    
         | 
| 394 | 
            -
                    # Load tools from configuration
         | 
| 395 | 
            -
                    config_file = Path.home() / ".claude-mcp" / "mcp_config.yaml"
         | 
| 396 | 
            -
                    if config_file.exists():
         | 
| 397 | 
            -
                        # For now, just load default tools if config file exists
         | 
| 398 | 
            -
                        # TODO: Implement proper async config loading for CLI commands
         | 
| 399 | 
            -
                        _load_default_tools(tool_registry, logger)
         | 
| 349 | 
            +
                    # Get tool registry with fallback to running server
         | 
| 350 | 
            +
                    tool_registry = _create_tool_registry_with_fallback(logger)
         | 
| 400 351 |  | 
| 401 352 | 
             
                    if action == 'list':
         | 
| 402 353 | 
             
                        # List all tools
         | 
| @@ -422,8 +373,7 @@ def _manage_tools(args, logger): | |
| 422 373 | 
             
                            print("-" * 30)
         | 
| 423 374 |  | 
| 424 375 | 
             
                            for tool in category_tools:
         | 
| 425 | 
            -
                                 | 
| 426 | 
            -
                                print(f"  {status} {tool.name}")
         | 
| 376 | 
            +
                                print(f"  ✅ {tool.name}")
         | 
| 427 377 | 
             
                                print(f"      {tool.description}")
         | 
| 428 378 |  | 
| 429 379 | 
             
                                if getattr(args, 'verbose', False):
         | 
| @@ -572,21 +522,14 @@ def _test_tool(args, logger): | |
| 572 522 | 
             
                    print(f"Arguments: {json.dumps(tool_args, indent=2)}")
         | 
| 573 523 | 
             
                    print("-" * 50)
         | 
| 574 524 |  | 
| 575 | 
            -
                    #  | 
| 576 | 
            -
                    tool_registry =  | 
| 577 | 
            -
                    
         | 
| 578 | 
            -
                    # Load tools
         | 
| 579 | 
            -
                    config_file = Path.home() / ".claude-mcp" / "mcp_config.yaml"
         | 
| 580 | 
            -
                    if config_file.exists():
         | 
| 581 | 
            -
                        # For now, just load default tools if config file exists
         | 
| 582 | 
            -
                        # TODO: Implement proper async config loading for CLI commands
         | 
| 583 | 
            -
                        _load_default_tools(tool_registry, logger)
         | 
| 525 | 
            +
                    # Get tool registry with fallback to running server
         | 
| 526 | 
            +
                    tool_registry = _create_tool_registry_with_fallback(logger)
         | 
| 584 527 |  | 
| 585 528 | 
             
                    # Create invocation request
         | 
| 586 529 | 
             
                    invocation = MCPToolInvocation(
         | 
| 587 530 | 
             
                        tool_name=tool_name,
         | 
| 588 | 
            -
                         | 
| 589 | 
            -
                         | 
| 531 | 
            +
                        parameters=tool_args,
         | 
| 532 | 
            +
                        context={"source": "cli_test"}
         | 
| 590 533 | 
             
                    )
         | 
| 591 534 |  | 
| 592 535 | 
             
                    # Invoke tool
         | 
| @@ -594,7 +537,8 @@ def _test_tool(args, logger): | |
| 594 537 |  | 
| 595 538 | 
             
                    if result.success:
         | 
| 596 539 | 
             
                        print("✅ Tool invocation successful!")
         | 
| 597 | 
            -
                        print(f"Result: {json.dumps(result. | 
| 540 | 
            +
                        print(f"Result: {json.dumps(result.data, indent=2)}")
         | 
| 541 | 
            +
                        print(f"Execution time: {result.execution_time:.3f}s")
         | 
| 598 542 | 
             
                    else:
         | 
| 599 543 | 
             
                        print("❌ Tool invocation failed!")
         | 
| 600 544 | 
             
                        print(f"Error: {result.error}")
         | 
| @@ -818,4 +762,206 @@ async def _load_default_tools(tool_registry, logger): | |
| 818 762 | 
             
                        logger.debug(f"Loaded default tool: {tool_def.name}")
         | 
| 819 763 |  | 
| 820 764 | 
             
                except Exception as e:
         | 
| 821 | 
            -
                    logger.error(f"Failed to load default tools: {e}", exc_info=True)
         | 
| 765 | 
            +
                    logger.error(f"Failed to load default tools: {e}", exc_info=True)
         | 
| 766 | 
            +
                    raise
         | 
| 767 | 
            +
             | 
| 768 | 
            +
             | 
| 769 | 
            +
            def _load_config_sync(config_path=None, logger=None):
         | 
| 770 | 
            +
                """
         | 
| 771 | 
            +
                Load MCP configuration synchronously with validation.
         | 
| 772 | 
            +
             | 
| 773 | 
            +
                Args:
         | 
| 774 | 
            +
                    config_path: Optional path to configuration file
         | 
| 775 | 
            +
                    logger: Optional logger for error reporting
         | 
| 776 | 
            +
             | 
| 777 | 
            +
                Returns:
         | 
| 778 | 
            +
                    Dictionary with configuration data
         | 
| 779 | 
            +
             | 
| 780 | 
            +
                Raises:
         | 
| 781 | 
            +
                    ValueError: If configuration is invalid
         | 
| 782 | 
            +
                """
         | 
| 783 | 
            +
                # Use default configuration structure
         | 
| 784 | 
            +
                default_config = {
         | 
| 785 | 
            +
                    "mcp": {
         | 
| 786 | 
            +
                        "server": {
         | 
| 787 | 
            +
                            "name": "claude-mpm-gateway",
         | 
| 788 | 
            +
                            "version": "1.0.0",
         | 
| 789 | 
            +
                            "description": "Claude MPM MCP Gateway Server",
         | 
| 790 | 
            +
                        },
         | 
| 791 | 
            +
                        "tools": {
         | 
| 792 | 
            +
                            "enabled": True,
         | 
| 793 | 
            +
                            "auto_discover": True,
         | 
| 794 | 
            +
                        },
         | 
| 795 | 
            +
                        "logging": {
         | 
| 796 | 
            +
                            "level": "INFO",
         | 
| 797 | 
            +
                        }
         | 
| 798 | 
            +
                    }
         | 
| 799 | 
            +
                }
         | 
| 800 | 
            +
             | 
| 801 | 
            +
                if config_path and Path(config_path).exists():
         | 
| 802 | 
            +
                    try:
         | 
| 803 | 
            +
                        import yaml
         | 
| 804 | 
            +
                        with open(config_path, 'r') as f:
         | 
| 805 | 
            +
                            loaded_config = yaml.safe_load(f) or {}
         | 
| 806 | 
            +
             | 
| 807 | 
            +
                        # Validate configuration structure
         | 
| 808 | 
            +
                        if not isinstance(loaded_config, dict):
         | 
| 809 | 
            +
                            raise ValueError("Configuration must be a dictionary")
         | 
| 810 | 
            +
             | 
| 811 | 
            +
                        # Validate MCP section if present
         | 
| 812 | 
            +
                        if "mcp" in loaded_config:
         | 
| 813 | 
            +
                            mcp_config = loaded_config["mcp"]
         | 
| 814 | 
            +
                            if not isinstance(mcp_config, dict):
         | 
| 815 | 
            +
                                raise ValueError("mcp section must be a dictionary")
         | 
| 816 | 
            +
             | 
| 817 | 
            +
                            # Validate server section
         | 
| 818 | 
            +
                            if "server" in mcp_config:
         | 
| 819 | 
            +
                                server_config = mcp_config["server"]
         | 
| 820 | 
            +
                                if not isinstance(server_config, dict):
         | 
| 821 | 
            +
                                    raise ValueError("mcp.server section must be a dictionary")
         | 
| 822 | 
            +
             | 
| 823 | 
            +
                                # Validate server name
         | 
| 824 | 
            +
                                if "name" in server_config:
         | 
| 825 | 
            +
                                    if not isinstance(server_config["name"], str) or not server_config["name"].strip():
         | 
| 826 | 
            +
                                        raise ValueError("mcp.server.name must be a non-empty string")
         | 
| 827 | 
            +
             | 
| 828 | 
            +
                            # Validate tools section
         | 
| 829 | 
            +
                            if "tools" in mcp_config:
         | 
| 830 | 
            +
                                tools_config = mcp_config["tools"]
         | 
| 831 | 
            +
                                if not isinstance(tools_config, dict):
         | 
| 832 | 
            +
                                    raise ValueError("mcp.tools section must be a dictionary")
         | 
| 833 | 
            +
             | 
| 834 | 
            +
                                if "enabled" in tools_config and not isinstance(tools_config["enabled"], bool):
         | 
| 835 | 
            +
                                    raise ValueError("mcp.tools.enabled must be a boolean")
         | 
| 836 | 
            +
             | 
| 837 | 
            +
                        # Merge with defaults
         | 
| 838 | 
            +
                        def merge_dict(base, overlay):
         | 
| 839 | 
            +
                            for key, value in overlay.items():
         | 
| 840 | 
            +
                                if key in base and isinstance(base[key], dict) and isinstance(value, dict):
         | 
| 841 | 
            +
                                    merge_dict(base[key], value)
         | 
| 842 | 
            +
                                else:
         | 
| 843 | 
            +
                                    base[key] = value
         | 
| 844 | 
            +
             | 
| 845 | 
            +
                        merge_dict(default_config, loaded_config)
         | 
| 846 | 
            +
             | 
| 847 | 
            +
                        if logger:
         | 
| 848 | 
            +
                            logger.info(f"Successfully loaded configuration from {config_path}")
         | 
| 849 | 
            +
             | 
| 850 | 
            +
                    except yaml.YAMLError as e:
         | 
| 851 | 
            +
                        error_msg = f"Invalid YAML in configuration file {config_path}: {e}"
         | 
| 852 | 
            +
                        if logger:
         | 
| 853 | 
            +
                            logger.error(error_msg)
         | 
| 854 | 
            +
                        raise ValueError(error_msg)
         | 
| 855 | 
            +
                    except ValueError as e:
         | 
| 856 | 
            +
                        error_msg = f"Configuration validation error in {config_path}: {e}"
         | 
| 857 | 
            +
                        if logger:
         | 
| 858 | 
            +
                            logger.error(error_msg)
         | 
| 859 | 
            +
                        raise
         | 
| 860 | 
            +
                    except Exception as e:
         | 
| 861 | 
            +
                        error_msg = f"Failed to load configuration from {config_path}: {e}"
         | 
| 862 | 
            +
                        if logger:
         | 
| 863 | 
            +
                            logger.warning(error_msg)
         | 
| 864 | 
            +
                        # Fall back to defaults for other errors
         | 
| 865 | 
            +
             | 
| 866 | 
            +
                return default_config
         | 
| 867 | 
            +
             | 
| 868 | 
            +
             | 
| 869 | 
            +
            def _load_default_tools_sync(tool_registry, logger):
         | 
| 870 | 
            +
                """
         | 
| 871 | 
            +
                Load default MCP tools into the registry synchronously.
         | 
| 872 | 
            +
             | 
| 873 | 
            +
                Args:
         | 
| 874 | 
            +
                    tool_registry: ToolRegistry instance
         | 
| 875 | 
            +
                    logger: Logger instance
         | 
| 876 | 
            +
                """
         | 
| 877 | 
            +
                try:
         | 
| 878 | 
            +
                    # Import default tool adapters
         | 
| 879 | 
            +
                    from ...services.mcp_gateway.tools.base_adapter import (
         | 
| 880 | 
            +
                        EchoToolAdapter,
         | 
| 881 | 
            +
                        CalculatorToolAdapter,
         | 
| 882 | 
            +
                        SystemInfoToolAdapter
         | 
| 883 | 
            +
                    )
         | 
| 884 | 
            +
             | 
| 885 | 
            +
                    # Register default tools
         | 
| 886 | 
            +
                    default_tools = [
         | 
| 887 | 
            +
                        EchoToolAdapter(),
         | 
| 888 | 
            +
                        CalculatorToolAdapter(),
         | 
| 889 | 
            +
                        SystemInfoToolAdapter()
         | 
| 890 | 
            +
                    ]
         | 
| 891 | 
            +
             | 
| 892 | 
            +
                    for adapter in default_tools:
         | 
| 893 | 
            +
                        # Initialize synchronously (skip async parts for CLI)
         | 
| 894 | 
            +
                        adapter._initialized = True
         | 
| 895 | 
            +
                        tool_registry.register_tool(adapter)
         | 
| 896 | 
            +
                        tool_def = adapter.get_definition()
         | 
| 897 | 
            +
                        logger.debug(f"Loaded default tool: {tool_def.name}")
         | 
| 898 | 
            +
             | 
| 899 | 
            +
                except Exception as e:
         | 
| 900 | 
            +
                    logger.error(f"Failed to load default tools: {e}", exc_info=True)
         | 
| 901 | 
            +
             | 
| 902 | 
            +
             | 
| 903 | 
            +
            def _get_server_tools_via_mcp(logger):
         | 
| 904 | 
            +
                """
         | 
| 905 | 
            +
                Get tools from running MCP server via MCP protocol.
         | 
| 906 | 
            +
             | 
| 907 | 
            +
                Returns:
         | 
| 908 | 
            +
                    List of tool definitions or None if server not accessible
         | 
| 909 | 
            +
                """
         | 
| 910 | 
            +
                try:
         | 
| 911 | 
            +
                    import json
         | 
| 912 | 
            +
                    import subprocess
         | 
| 913 | 
            +
                    import tempfile
         | 
| 914 | 
            +
             | 
| 915 | 
            +
                    # Create a simple MCP client request
         | 
| 916 | 
            +
                    request = {
         | 
| 917 | 
            +
                        "jsonrpc": "2.0",
         | 
| 918 | 
            +
                        "id": 1,
         | 
| 919 | 
            +
                        "method": "tools/list",
         | 
| 920 | 
            +
                        "params": {}
         | 
| 921 | 
            +
                    }
         | 
| 922 | 
            +
             | 
| 923 | 
            +
                    # Try to communicate with running server
         | 
| 924 | 
            +
                    pid_file = Path.home() / ".claude-mcp" / "mcp_server.pid"
         | 
| 925 | 
            +
                    if not pid_file.exists():
         | 
| 926 | 
            +
                        return None
         | 
| 927 | 
            +
             | 
| 928 | 
            +
                    # For now, return None since we need a proper MCP client implementation
         | 
| 929 | 
            +
                    # This is a placeholder for future implementation
         | 
| 930 | 
            +
                    return None
         | 
| 931 | 
            +
             | 
| 932 | 
            +
                except Exception as e:
         | 
| 933 | 
            +
                    logger.debug(f"Could not connect to running server: {e}")
         | 
| 934 | 
            +
                    return None
         | 
| 935 | 
            +
             | 
| 936 | 
            +
             | 
| 937 | 
            +
            def _create_tool_registry_with_fallback(logger):
         | 
| 938 | 
            +
                """
         | 
| 939 | 
            +
                Create tool registry, trying to connect to running server first.
         | 
| 940 | 
            +
             | 
| 941 | 
            +
                Args:
         | 
| 942 | 
            +
                    logger: Logger instance
         | 
| 943 | 
            +
             | 
| 944 | 
            +
                Returns:
         | 
| 945 | 
            +
                    ToolRegistry instance with tools loaded
         | 
| 946 | 
            +
                """
         | 
| 947 | 
            +
                from ...services.mcp_gateway import ToolRegistry
         | 
| 948 | 
            +
             | 
| 949 | 
            +
                # Create registry
         | 
| 950 | 
            +
                tool_registry = ToolRegistry()
         | 
| 951 | 
            +
             | 
| 952 | 
            +
                # Try to get tools from running server
         | 
| 953 | 
            +
                server_tools = _get_server_tools_via_mcp(logger)
         | 
| 954 | 
            +
             | 
| 955 | 
            +
                if server_tools:
         | 
| 956 | 
            +
                    logger.debug("Connected to running MCP server")
         | 
| 957 | 
            +
                    # TODO: Populate registry with server tools
         | 
| 958 | 
            +
                    # For now, fall back to loading default tools
         | 
| 959 | 
            +
             | 
| 960 | 
            +
                # Fallback: Load default tools locally
         | 
| 961 | 
            +
                config_file = Path.home() / ".claude-mcp" / "mcp_config.yaml"
         | 
| 962 | 
            +
                config = _load_config_sync(config_file if config_file.exists() else None, logger)
         | 
| 963 | 
            +
             | 
| 964 | 
            +
                if config.get("mcp", {}).get("tools", {}).get("enabled", True):
         | 
| 965 | 
            +
                    _load_default_tools_sync(tool_registry, logger)
         | 
| 966 | 
            +
             | 
| 967 | 
            +
                return tool_registry
         |