claude-mpm 3.9.8__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/base_agent.json +1 -1
- claude_mpm/agents/templates/memory_manager.json +155 -0
- claude_mpm/cli/__init__.py +18 -3
- claude_mpm/cli/commands/__init__.py +6 -1
- claude_mpm/cli/commands/cleanup.py +21 -1
- claude_mpm/cli/commands/mcp.py +967 -0
- claude_mpm/cli/commands/run_guarded.py +511 -0
- claude_mpm/cli/parser.py +156 -3
- claude_mpm/config/experimental_features.py +219 -0
- claude_mpm/config/memory_guardian_config.py +325 -0
- claude_mpm/config/memory_guardian_yaml.py +335 -0
- claude_mpm/constants.py +14 -0
- claude_mpm/core/memory_aware_runner.py +353 -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/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/logging.py +3 -3
- claude_mpm/services/infrastructure/memory_dashboard.py +479 -0
- claude_mpm/services/infrastructure/memory_guardian.py +944 -0
- 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 +39 -23
- 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 +356 -0
- claude_mpm/services/mcp_gateway/manager.py +334 -0
- claude_mpm/services/mcp_gateway/registry/__init__.py +6 -3
- claude_mpm/services/mcp_gateway/registry/service_registry.py +393 -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_gateway.py +431 -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 +496 -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/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/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.11.dist-info}/METADATA +27 -2
- {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.11.dist-info}/RECORD +56 -32
- 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.11.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.11.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.11.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.11.dist-info}/top_level.txt +0 -0
| @@ -0,0 +1,967 @@ | |
| 1 | 
            +
            """
         | 
| 2 | 
            +
            MCP Gateway command implementation for claude-mpm.
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            WHY: This module provides CLI commands for managing the MCP (Model Context Protocol) Gateway,
         | 
| 5 | 
            +
            allowing users to start, stop, configure, and test MCP server functionality.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            DESIGN DECISION: We follow the existing CLI pattern using a main function
         | 
| 8 | 
            +
            that dispatches to specific subcommand handlers, maintaining consistency
         | 
| 9 | 
            +
            with other command modules like agents.py and memory.py.
         | 
| 10 | 
            +
            """
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            import sys
         | 
| 13 | 
            +
            import json
         | 
| 14 | 
            +
            import asyncio
         | 
| 15 | 
            +
            from pathlib import Path
         | 
| 16 | 
            +
            from typing import Optional, Dict, Any, List
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            from ...core.logger import get_logger
         | 
| 19 | 
            +
            from ...constants import MCPCommands
         | 
| 20 | 
            +
             | 
| 21 | 
            +
             | 
| 22 | 
            +
            def manage_mcp(args):
         | 
| 23 | 
            +
                """
         | 
| 24 | 
            +
                Manage MCP Gateway server and tools.
         | 
| 25 | 
            +
                
         | 
| 26 | 
            +
                WHY: The MCP Gateway provides Model Context Protocol integration for Claude MPM,
         | 
| 27 | 
            +
                enabling tool invocation and external service integration. This command provides
         | 
| 28 | 
            +
                a unified interface for all MCP-related operations.
         | 
| 29 | 
            +
                
         | 
| 30 | 
            +
                DESIGN DECISION: When no subcommand is provided, we show the server status
         | 
| 31 | 
            +
                as the default action, giving users a quick overview of the MCP system state.
         | 
| 32 | 
            +
                
         | 
| 33 | 
            +
                Args:
         | 
| 34 | 
            +
                    args: Parsed command line arguments with mcp_command attribute
         | 
| 35 | 
            +
                """
         | 
| 36 | 
            +
                logger = get_logger("cli.mcp")
         | 
| 37 | 
            +
                
         | 
| 38 | 
            +
                try:
         | 
| 39 | 
            +
                    # Import MCP Gateway services with lazy loading
         | 
| 40 | 
            +
                    from ...services.mcp_gateway import (
         | 
| 41 | 
            +
                        MCPGateway,
         | 
| 42 | 
            +
                        ToolRegistry,
         | 
| 43 | 
            +
                        MCPConfiguration,
         | 
| 44 | 
            +
                        MCPServiceRegistry
         | 
| 45 | 
            +
                    )
         | 
| 46 | 
            +
                    
         | 
| 47 | 
            +
                    if not args.mcp_command:
         | 
| 48 | 
            +
                        # No subcommand - show status by default
         | 
| 49 | 
            +
                        return _show_status(args, logger)
         | 
| 50 | 
            +
                    
         | 
| 51 | 
            +
                    # Route to specific command handlers
         | 
| 52 | 
            +
                    if args.mcp_command == MCPCommands.START.value:
         | 
| 53 | 
            +
                        import asyncio
         | 
| 54 | 
            +
                        return asyncio.run(_start_server(args, logger))
         | 
| 55 | 
            +
                    
         | 
| 56 | 
            +
                    elif args.mcp_command == MCPCommands.STOP.value:
         | 
| 57 | 
            +
                        return _stop_server(args, logger)
         | 
| 58 | 
            +
                    
         | 
| 59 | 
            +
                    elif args.mcp_command == MCPCommands.STATUS.value:
         | 
| 60 | 
            +
                        return _show_status(args, logger)
         | 
| 61 | 
            +
                    
         | 
| 62 | 
            +
                    elif args.mcp_command == MCPCommands.TOOLS.value:
         | 
| 63 | 
            +
                        return _manage_tools(args, logger)
         | 
| 64 | 
            +
                    
         | 
| 65 | 
            +
                    elif args.mcp_command == MCPCommands.REGISTER.value:
         | 
| 66 | 
            +
                        return _register_tool(args, logger)
         | 
| 67 | 
            +
                    
         | 
| 68 | 
            +
                    elif args.mcp_command == MCPCommands.TEST.value:
         | 
| 69 | 
            +
                        return _test_tool(args, logger)
         | 
| 70 | 
            +
                    
         | 
| 71 | 
            +
                    elif args.mcp_command == MCPCommands.INSTALL.value:
         | 
| 72 | 
            +
                        return _install_gateway(args, logger)
         | 
| 73 | 
            +
                    
         | 
| 74 | 
            +
                    elif args.mcp_command == MCPCommands.CONFIG.value:
         | 
| 75 | 
            +
                        return _manage_config(args, logger)
         | 
| 76 | 
            +
                    
         | 
| 77 | 
            +
                    else:
         | 
| 78 | 
            +
                        logger.error(f"Unknown MCP command: {args.mcp_command}")
         | 
| 79 | 
            +
                        print(f"Unknown MCP command: {args.mcp_command}")
         | 
| 80 | 
            +
                        _show_help()
         | 
| 81 | 
            +
                        return 1
         | 
| 82 | 
            +
                    
         | 
| 83 | 
            +
                except ImportError as e:
         | 
| 84 | 
            +
                    logger.error(f"MCP Gateway services not available: {e}")
         | 
| 85 | 
            +
                    print("Error: MCP Gateway services not available")
         | 
| 86 | 
            +
                    print("This may indicate a missing dependency. Try running:")
         | 
| 87 | 
            +
                    print("  pip install mcp")
         | 
| 88 | 
            +
                    return 1
         | 
| 89 | 
            +
                except Exception as e:
         | 
| 90 | 
            +
                    logger.error(f"Error managing MCP Gateway: {e}", exc_info=True)
         | 
| 91 | 
            +
                    print(f"Error: {e}")
         | 
| 92 | 
            +
                    return 1
         | 
| 93 | 
            +
             | 
| 94 | 
            +
             | 
| 95 | 
            +
            def _show_help():
         | 
| 96 | 
            +
                """Show available MCP commands."""
         | 
| 97 | 
            +
                print("\nAvailable MCP commands:")
         | 
| 98 | 
            +
                print("  install  - Install and configure MCP Gateway")
         | 
| 99 | 
            +
                print("  start    - Start the MCP Gateway server")
         | 
| 100 | 
            +
                print("  stop     - Stop the MCP Gateway server")
         | 
| 101 | 
            +
                print("  status   - Check server and tool status")
         | 
| 102 | 
            +
                print("  tools    - List and manage registered tools")
         | 
| 103 | 
            +
                print("  register - Register a new tool")
         | 
| 104 | 
            +
                print("  test     - Test tool invocation")
         | 
| 105 | 
            +
                print("  config   - View and manage configuration")
         | 
| 106 | 
            +
                print()
         | 
| 107 | 
            +
                print("Use 'claude-mpm mcp <command> --help' for more information")
         | 
| 108 | 
            +
             | 
| 109 | 
            +
             | 
| 110 | 
            +
            # Daemon mode removed - MCP servers should be simple stdio responders
         | 
| 111 | 
            +
             | 
| 112 | 
            +
             | 
| 113 | 
            +
            async def _start_server(args, logger):
         | 
| 114 | 
            +
                """
         | 
| 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 | 
            +
             | 
| 121 | 
            +
                Args:
         | 
| 122 | 
            +
                    args: Command arguments with optional port and configuration
         | 
| 123 | 
            +
                    logger: Logger instance
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                Returns:
         | 
| 126 | 
            +
                    int: Exit code (0 for success, non-zero for failure)
         | 
| 127 | 
            +
                """
         | 
| 128 | 
            +
                from ...services.mcp_gateway.manager import (
         | 
| 129 | 
            +
                    start_global_gateway,
         | 
| 130 | 
            +
                    run_global_gateway,
         | 
| 131 | 
            +
                    is_gateway_running,
         | 
| 132 | 
            +
                    get_gateway_status
         | 
| 133 | 
            +
                )
         | 
| 134 | 
            +
                
         | 
| 135 | 
            +
                try:
         | 
| 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")
         | 
| 150 | 
            +
                        return 1
         | 
| 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 | 
            +
             | 
| 159 | 
            +
                    return 0
         | 
| 160 | 
            +
                    
         | 
| 161 | 
            +
                except KeyboardInterrupt:
         | 
| 162 | 
            +
                    print("\nServer interrupted by user")
         | 
| 163 | 
            +
                    return 0
         | 
| 164 | 
            +
                except Exception as e:
         | 
| 165 | 
            +
                    logger.error(f"Failed to start MCP server: {e}", exc_info=True)
         | 
| 166 | 
            +
                    print(f"Error starting server: {e}")
         | 
| 167 | 
            +
                    return 1
         | 
| 168 | 
            +
             | 
| 169 | 
            +
             | 
| 170 | 
            +
            def _stop_server(args, logger):
         | 
| 171 | 
            +
                """
         | 
| 172 | 
            +
                Stop the MCP Gateway server.
         | 
| 173 | 
            +
                
         | 
| 174 | 
            +
                WHY: Users need a clean way to stop the MCP server when it's no longer needed
         | 
| 175 | 
            +
                or when they need to restart it with different configuration.
         | 
| 176 | 
            +
                
         | 
| 177 | 
            +
                Args:
         | 
| 178 | 
            +
                    args: Command arguments
         | 
| 179 | 
            +
                    logger: Logger instance
         | 
| 180 | 
            +
                    
         | 
| 181 | 
            +
                Returns:
         | 
| 182 | 
            +
                    int: Exit code (0 for success, non-zero for failure)
         | 
| 183 | 
            +
                """
         | 
| 184 | 
            +
                try:
         | 
| 185 | 
            +
                    # Check for running server process
         | 
| 186 | 
            +
                    pid_file = Path.home() / ".claude-mpm" / "mcp_server.pid"
         | 
| 187 | 
            +
                    
         | 
| 188 | 
            +
                    if not pid_file.exists():
         | 
| 189 | 
            +
                        print("No MCP server process found")
         | 
| 190 | 
            +
                        return 0
         | 
| 191 | 
            +
                    
         | 
| 192 | 
            +
                    # Read PID and attempt to stop
         | 
| 193 | 
            +
                    import signal
         | 
| 194 | 
            +
                    import os
         | 
| 195 | 
            +
                    
         | 
| 196 | 
            +
                    with open(pid_file, 'r') as f:
         | 
| 197 | 
            +
                        pid = int(f.read().strip())
         | 
| 198 | 
            +
                    
         | 
| 199 | 
            +
                    try:
         | 
| 200 | 
            +
                        # Send termination signal
         | 
| 201 | 
            +
                        os.kill(pid, signal.SIGTERM)
         | 
| 202 | 
            +
                        print(f"Sent stop signal to MCP server (PID: {pid})")
         | 
| 203 | 
            +
                        
         | 
| 204 | 
            +
                        # Wait for graceful shutdown
         | 
| 205 | 
            +
                        import time
         | 
| 206 | 
            +
                        for _ in range(10):
         | 
| 207 | 
            +
                            try:
         | 
| 208 | 
            +
                                os.kill(pid, 0)  # Check if process still exists
         | 
| 209 | 
            +
                                time.sleep(0.5)
         | 
| 210 | 
            +
                            except ProcessLookupError:
         | 
| 211 | 
            +
                                break
         | 
| 212 | 
            +
                        else:
         | 
| 213 | 
            +
                            # Force kill if still running
         | 
| 214 | 
            +
                            os.kill(pid, signal.SIGKILL)
         | 
| 215 | 
            +
                            print("Force stopped MCP server")
         | 
| 216 | 
            +
                        
         | 
| 217 | 
            +
                        # Clean up PID file
         | 
| 218 | 
            +
                        pid_file.unlink()
         | 
| 219 | 
            +
                        print("MCP server stopped successfully")
         | 
| 220 | 
            +
                        return 0
         | 
| 221 | 
            +
                        
         | 
| 222 | 
            +
                    except ProcessLookupError:
         | 
| 223 | 
            +
                        # Process already stopped
         | 
| 224 | 
            +
                        pid_file.unlink()
         | 
| 225 | 
            +
                        print("MCP server was not running")
         | 
| 226 | 
            +
                        return 0
         | 
| 227 | 
            +
                        
         | 
| 228 | 
            +
                except Exception as e:
         | 
| 229 | 
            +
                    logger.error(f"Failed to stop MCP server: {e}", exc_info=True)
         | 
| 230 | 
            +
                    print(f"Error stopping server: {e}")
         | 
| 231 | 
            +
                    return 1
         | 
| 232 | 
            +
             | 
| 233 | 
            +
             | 
| 234 | 
            +
            def _show_status(args, logger):
         | 
| 235 | 
            +
                """
         | 
| 236 | 
            +
                Show MCP Gateway server and tool status.
         | 
| 237 | 
            +
                
         | 
| 238 | 
            +
                WHY: Users need visibility into the current state of the MCP system,
         | 
| 239 | 
            +
                including server status, registered tools, and configuration.
         | 
| 240 | 
            +
                
         | 
| 241 | 
            +
                Args:
         | 
| 242 | 
            +
                    args: Command arguments
         | 
| 243 | 
            +
                    logger: Logger instance
         | 
| 244 | 
            +
                    
         | 
| 245 | 
            +
                Returns:
         | 
| 246 | 
            +
                    int: Exit code (0 for success, non-zero for failure)
         | 
| 247 | 
            +
                """
         | 
| 248 | 
            +
                from ...services.mcp_gateway import MCPServiceRegistry, ToolRegistry
         | 
| 249 | 
            +
                
         | 
| 250 | 
            +
                try:
         | 
| 251 | 
            +
                    print("MCP Gateway Status")
         | 
| 252 | 
            +
                    print("=" * 50)
         | 
| 253 | 
            +
                    
         | 
| 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>")
         | 
| 263 | 
            +
                    
         | 
| 264 | 
            +
                    print()
         | 
| 265 | 
            +
                    
         | 
| 266 | 
            +
                    # Show registered tools
         | 
| 267 | 
            +
                    print("Registered Tools:")
         | 
| 268 | 
            +
                    print("-" * 30)
         | 
| 269 | 
            +
                    
         | 
| 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
         | 
| 282 | 
            +
                    
         | 
| 283 | 
            +
                    tools = tool_registry.list_tools()
         | 
| 284 | 
            +
                    if tools:
         | 
| 285 | 
            +
                        for tool in tools:
         | 
| 286 | 
            +
                            print(f"  ✅ {tool.name}: {tool.description}")
         | 
| 287 | 
            +
                    else:
         | 
| 288 | 
            +
                        print("  No tools registered")
         | 
| 289 | 
            +
                    
         | 
| 290 | 
            +
                    print()
         | 
| 291 | 
            +
                    
         | 
| 292 | 
            +
                    # Show configuration
         | 
| 293 | 
            +
                    print("Configuration:")
         | 
| 294 | 
            +
                    print("-" * 30)
         | 
| 295 | 
            +
             | 
| 296 | 
            +
                    if config_file.exists():
         | 
| 297 | 
            +
                        print(f"  Config file: {config_file}")
         | 
| 298 | 
            +
                    else:
         | 
| 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'}")
         | 
| 306 | 
            +
                    
         | 
| 307 | 
            +
                    # Show verbose details if requested
         | 
| 308 | 
            +
                    if getattr(args, 'verbose', False):
         | 
| 309 | 
            +
                        print()
         | 
| 310 | 
            +
                        print("Service Registry:")
         | 
| 311 | 
            +
                        print("-" * 30)
         | 
| 312 | 
            +
                        
         | 
| 313 | 
            +
                        registry = MCPServiceRegistry()
         | 
| 314 | 
            +
                        services = registry.list_services()
         | 
| 315 | 
            +
                        
         | 
| 316 | 
            +
                        for service_id, service_info in services.items():
         | 
| 317 | 
            +
                            print(f"  {service_id}:")
         | 
| 318 | 
            +
                            print(f"    State: {service_info['state']}")
         | 
| 319 | 
            +
                            print(f"    Type: {service_info['type']}")
         | 
| 320 | 
            +
                    
         | 
| 321 | 
            +
                    return 0
         | 
| 322 | 
            +
                    
         | 
| 323 | 
            +
                except Exception as e:
         | 
| 324 | 
            +
                    logger.error(f"Failed to show MCP status: {e}", exc_info=True)
         | 
| 325 | 
            +
                    print(f"Error checking status: {e}")
         | 
| 326 | 
            +
                    return 1
         | 
| 327 | 
            +
             | 
| 328 | 
            +
             | 
| 329 | 
            +
            def _manage_tools(args, logger):
         | 
| 330 | 
            +
                """
         | 
| 331 | 
            +
                List and manage registered MCP tools.
         | 
| 332 | 
            +
                
         | 
| 333 | 
            +
                WHY: Users need to see what tools are available and manage their
         | 
| 334 | 
            +
                registration and configuration.
         | 
| 335 | 
            +
                
         | 
| 336 | 
            +
                Args:
         | 
| 337 | 
            +
                    args: Command arguments with optional filters
         | 
| 338 | 
            +
                    logger: Logger instance
         | 
| 339 | 
            +
                    
         | 
| 340 | 
            +
                Returns:
         | 
| 341 | 
            +
                    int: Exit code (0 for success, non-zero for failure)
         | 
| 342 | 
            +
                """
         | 
| 343 | 
            +
                from ...services.mcp_gateway import ToolRegistry
         | 
| 344 | 
            +
                
         | 
| 345 | 
            +
                try:
         | 
| 346 | 
            +
                    # Check for subcommand
         | 
| 347 | 
            +
                    action = getattr(args, 'tool_action', 'list')
         | 
| 348 | 
            +
                    
         | 
| 349 | 
            +
                    # Get tool registry with fallback to running server
         | 
| 350 | 
            +
                    tool_registry = _create_tool_registry_with_fallback(logger)
         | 
| 351 | 
            +
                    
         | 
| 352 | 
            +
                    if action == 'list':
         | 
| 353 | 
            +
                        # List all tools
         | 
| 354 | 
            +
                        tools = tool_registry.list_tools()
         | 
| 355 | 
            +
                        
         | 
| 356 | 
            +
                        if not tools:
         | 
| 357 | 
            +
                            print("No tools registered")
         | 
| 358 | 
            +
                            return 0
         | 
| 359 | 
            +
                        
         | 
| 360 | 
            +
                        print("Registered MCP Tools:")
         | 
| 361 | 
            +
                        print("=" * 50)
         | 
| 362 | 
            +
                        
         | 
| 363 | 
            +
                        # Group tools by category if available
         | 
| 364 | 
            +
                        by_category = {}
         | 
| 365 | 
            +
                        for tool in tools:
         | 
| 366 | 
            +
                            category = getattr(tool, 'category', 'General')
         | 
| 367 | 
            +
                            if category not in by_category:
         | 
| 368 | 
            +
                                by_category[category] = []
         | 
| 369 | 
            +
                            by_category[category].append(tool)
         | 
| 370 | 
            +
                        
         | 
| 371 | 
            +
                        for category, category_tools in sorted(by_category.items()):
         | 
| 372 | 
            +
                            print(f"\n{category}:")
         | 
| 373 | 
            +
                            print("-" * 30)
         | 
| 374 | 
            +
                            
         | 
| 375 | 
            +
                            for tool in category_tools:
         | 
| 376 | 
            +
                                print(f"  ✅ {tool.name}")
         | 
| 377 | 
            +
                                print(f"      {tool.description}")
         | 
| 378 | 
            +
                                
         | 
| 379 | 
            +
                                if getattr(args, 'verbose', False):
         | 
| 380 | 
            +
                                    # Show input schema
         | 
| 381 | 
            +
                                    print(f"      Input Schema: {json.dumps(tool.input_schema, indent=8)}")
         | 
| 382 | 
            +
                        
         | 
| 383 | 
            +
                    elif action == 'enable':
         | 
| 384 | 
            +
                        # Enable a tool
         | 
| 385 | 
            +
                        tool_name = args.tool_name
         | 
| 386 | 
            +
                        if tool_registry.enable_tool(tool_name):
         | 
| 387 | 
            +
                            print(f"✅ Enabled tool: {tool_name}")
         | 
| 388 | 
            +
                            return 0
         | 
| 389 | 
            +
                        else:
         | 
| 390 | 
            +
                            print(f"❌ Failed to enable tool: {tool_name}")
         | 
| 391 | 
            +
                            return 1
         | 
| 392 | 
            +
                    
         | 
| 393 | 
            +
                    elif action == 'disable':
         | 
| 394 | 
            +
                        # Disable a tool
         | 
| 395 | 
            +
                        tool_name = args.tool_name
         | 
| 396 | 
            +
                        if tool_registry.disable_tool(tool_name):
         | 
| 397 | 
            +
                            print(f"✅ Disabled tool: {tool_name}")
         | 
| 398 | 
            +
                            return 0
         | 
| 399 | 
            +
                        else:
         | 
| 400 | 
            +
                            print(f"❌ Failed to disable tool: {tool_name}")
         | 
| 401 | 
            +
                            return 1
         | 
| 402 | 
            +
                    
         | 
| 403 | 
            +
                    else:
         | 
| 404 | 
            +
                        print(f"Unknown tool action: {action}")
         | 
| 405 | 
            +
                        return 1
         | 
| 406 | 
            +
                    
         | 
| 407 | 
            +
                    return 0
         | 
| 408 | 
            +
                    
         | 
| 409 | 
            +
                except Exception as e:
         | 
| 410 | 
            +
                    logger.error(f"Failed to manage tools: {e}", exc_info=True)
         | 
| 411 | 
            +
                    print(f"Error managing tools: {e}")
         | 
| 412 | 
            +
                    return 1
         | 
| 413 | 
            +
             | 
| 414 | 
            +
             | 
| 415 | 
            +
            def _register_tool(args, logger):
         | 
| 416 | 
            +
                """
         | 
| 417 | 
            +
                Register a new MCP tool.
         | 
| 418 | 
            +
                
         | 
| 419 | 
            +
                WHY: Users need to add custom tools to the MCP Gateway for use
         | 
| 420 | 
            +
                in Claude sessions.
         | 
| 421 | 
            +
                
         | 
| 422 | 
            +
                Args:
         | 
| 423 | 
            +
                    args: Command arguments with tool definition
         | 
| 424 | 
            +
                    logger: Logger instance
         | 
| 425 | 
            +
                    
         | 
| 426 | 
            +
                Returns:
         | 
| 427 | 
            +
                    int: Exit code (0 for success, non-zero for failure)
         | 
| 428 | 
            +
                """
         | 
| 429 | 
            +
                from ...services.mcp_gateway import ToolRegistry
         | 
| 430 | 
            +
                from ...services.mcp_gateway.core.interfaces import MCPToolDefinition
         | 
| 431 | 
            +
                
         | 
| 432 | 
            +
                try:
         | 
| 433 | 
            +
                    # Get tool details from arguments
         | 
| 434 | 
            +
                    tool_name = args.name
         | 
| 435 | 
            +
                    tool_description = args.description
         | 
| 436 | 
            +
                    
         | 
| 437 | 
            +
                    # Parse input schema
         | 
| 438 | 
            +
                    if args.schema_file:
         | 
| 439 | 
            +
                        with open(args.schema_file, 'r') as f:
         | 
| 440 | 
            +
                            input_schema = json.load(f)
         | 
| 441 | 
            +
                    else:
         | 
| 442 | 
            +
                        # Basic schema
         | 
| 443 | 
            +
                        input_schema = {
         | 
| 444 | 
            +
                            "type": "object",
         | 
| 445 | 
            +
                            "properties": {},
         | 
| 446 | 
            +
                            "required": []
         | 
| 447 | 
            +
                        }
         | 
| 448 | 
            +
                    
         | 
| 449 | 
            +
                    # Create tool definition
         | 
| 450 | 
            +
                    tool_def = MCPToolDefinition(
         | 
| 451 | 
            +
                        name=tool_name,
         | 
| 452 | 
            +
                        description=tool_description,
         | 
| 453 | 
            +
                        input_schema=input_schema,
         | 
| 454 | 
            +
                        enabled=True
         | 
| 455 | 
            +
                    )
         | 
| 456 | 
            +
                    
         | 
| 457 | 
            +
                    # Register with tool registry
         | 
| 458 | 
            +
                    tool_registry = ToolRegistry()
         | 
| 459 | 
            +
                    
         | 
| 460 | 
            +
                    if args.adapter:
         | 
| 461 | 
            +
                        # Register with custom adapter
         | 
| 462 | 
            +
                        logger.info(f"Registering tool with adapter: {args.adapter}")
         | 
| 463 | 
            +
                        # Import and instantiate adapter
         | 
| 464 | 
            +
                        # This would be extended based on adapter framework
         | 
| 465 | 
            +
                        print(f"Custom adapter registration not yet implemented: {args.adapter}")
         | 
| 466 | 
            +
                        return 1
         | 
| 467 | 
            +
                    else:
         | 
| 468 | 
            +
                        # Register as a simple tool
         | 
| 469 | 
            +
                        success = tool_registry.register_tool(tool_def)
         | 
| 470 | 
            +
                        
         | 
| 471 | 
            +
                        if success:
         | 
| 472 | 
            +
                            print(f"✅ Successfully registered tool: {tool_name}")
         | 
| 473 | 
            +
                            
         | 
| 474 | 
            +
                            # Save to configuration if requested
         | 
| 475 | 
            +
                            if args.save:
         | 
| 476 | 
            +
                                config_file = Path.home() / ".claude-mpm" / "mcp_config.yaml"
         | 
| 477 | 
            +
                                # Update configuration with new tool
         | 
| 478 | 
            +
                                print(f"Tool configuration saved to: {config_file}")
         | 
| 479 | 
            +
                            
         | 
| 480 | 
            +
                            return 0
         | 
| 481 | 
            +
                        else:
         | 
| 482 | 
            +
                            print(f"❌ Failed to register tool: {tool_name}")
         | 
| 483 | 
            +
                            return 1
         | 
| 484 | 
            +
                    
         | 
| 485 | 
            +
                except Exception as e:
         | 
| 486 | 
            +
                    logger.error(f"Failed to register tool: {e}", exc_info=True)
         | 
| 487 | 
            +
                    print(f"Error registering tool: {e}")
         | 
| 488 | 
            +
                    return 1
         | 
| 489 | 
            +
             | 
| 490 | 
            +
             | 
| 491 | 
            +
            def _test_tool(args, logger):
         | 
| 492 | 
            +
                """
         | 
| 493 | 
            +
                Test MCP tool invocation.
         | 
| 494 | 
            +
                
         | 
| 495 | 
            +
                WHY: Users need to verify that tools are working correctly before
         | 
| 496 | 
            +
                using them in Claude sessions.
         | 
| 497 | 
            +
                
         | 
| 498 | 
            +
                Args:
         | 
| 499 | 
            +
                    args: Command arguments with tool name and test parameters
         | 
| 500 | 
            +
                    logger: Logger instance
         | 
| 501 | 
            +
                    
         | 
| 502 | 
            +
                Returns:
         | 
| 503 | 
            +
                    int: Exit code (0 for success, non-zero for failure)
         | 
| 504 | 
            +
                """
         | 
| 505 | 
            +
                from ...services.mcp_gateway import ToolRegistry
         | 
| 506 | 
            +
                from ...services.mcp_gateway.core.interfaces import MCPToolInvocation
         | 
| 507 | 
            +
                
         | 
| 508 | 
            +
                try:
         | 
| 509 | 
            +
                    # Get tool name and arguments
         | 
| 510 | 
            +
                    tool_name = args.tool_name
         | 
| 511 | 
            +
                    
         | 
| 512 | 
            +
                    # Parse tool arguments
         | 
| 513 | 
            +
                    if args.args_file:
         | 
| 514 | 
            +
                        with open(args.args_file, 'r') as f:
         | 
| 515 | 
            +
                            tool_args = json.load(f)
         | 
| 516 | 
            +
                    elif args.args:
         | 
| 517 | 
            +
                        tool_args = json.loads(args.args)
         | 
| 518 | 
            +
                    else:
         | 
| 519 | 
            +
                        tool_args = {}
         | 
| 520 | 
            +
                    
         | 
| 521 | 
            +
                    print(f"Testing tool: {tool_name}")
         | 
| 522 | 
            +
                    print(f"Arguments: {json.dumps(tool_args, indent=2)}")
         | 
| 523 | 
            +
                    print("-" * 50)
         | 
| 524 | 
            +
                    
         | 
| 525 | 
            +
                    # Get tool registry with fallback to running server
         | 
| 526 | 
            +
                    tool_registry = _create_tool_registry_with_fallback(logger)
         | 
| 527 | 
            +
                    
         | 
| 528 | 
            +
                    # Create invocation request
         | 
| 529 | 
            +
                    invocation = MCPToolInvocation(
         | 
| 530 | 
            +
                        tool_name=tool_name,
         | 
| 531 | 
            +
                        parameters=tool_args,
         | 
| 532 | 
            +
                        context={"source": "cli_test"}
         | 
| 533 | 
            +
                    )
         | 
| 534 | 
            +
                    
         | 
| 535 | 
            +
                    # Invoke tool
         | 
| 536 | 
            +
                    result = asyncio.run(tool_registry.invoke_tool(invocation))
         | 
| 537 | 
            +
                    
         | 
| 538 | 
            +
                    if result.success:
         | 
| 539 | 
            +
                        print("✅ Tool invocation successful!")
         | 
| 540 | 
            +
                        print(f"Result: {json.dumps(result.data, indent=2)}")
         | 
| 541 | 
            +
                        print(f"Execution time: {result.execution_time:.3f}s")
         | 
| 542 | 
            +
                    else:
         | 
| 543 | 
            +
                        print("❌ Tool invocation failed!")
         | 
| 544 | 
            +
                        print(f"Error: {result.error}")
         | 
| 545 | 
            +
                    
         | 
| 546 | 
            +
                    return 0 if result.success else 1
         | 
| 547 | 
            +
                    
         | 
| 548 | 
            +
                except Exception as e:
         | 
| 549 | 
            +
                    logger.error(f"Failed to test tool: {e}", exc_info=True)
         | 
| 550 | 
            +
                    print(f"Error testing tool: {e}")
         | 
| 551 | 
            +
                    return 1
         | 
| 552 | 
            +
             | 
| 553 | 
            +
             | 
| 554 | 
            +
            def _install_gateway(args, logger):
         | 
| 555 | 
            +
                """
         | 
| 556 | 
            +
                Install and configure the MCP Gateway.
         | 
| 557 | 
            +
                
         | 
| 558 | 
            +
                WHY: Users need a simple way to set up the MCP Gateway with
         | 
| 559 | 
            +
                default configuration and tools.
         | 
| 560 | 
            +
                
         | 
| 561 | 
            +
                Args:
         | 
| 562 | 
            +
                    args: Command arguments
         | 
| 563 | 
            +
                    logger: Logger instance
         | 
| 564 | 
            +
                    
         | 
| 565 | 
            +
                Returns:
         | 
| 566 | 
            +
                    int: Exit code (0 for success, non-zero for failure)
         | 
| 567 | 
            +
                """
         | 
| 568 | 
            +
                try:
         | 
| 569 | 
            +
                    print("Installing MCP Gateway...")
         | 
| 570 | 
            +
                    print("=" * 50)
         | 
| 571 | 
            +
                    
         | 
| 572 | 
            +
                    # Create configuration directory
         | 
| 573 | 
            +
                    config_dir = Path.home() / ".claude-mpm"
         | 
| 574 | 
            +
                    config_dir.mkdir(exist_ok=True)
         | 
| 575 | 
            +
                    
         | 
| 576 | 
            +
                    # Create default configuration
         | 
| 577 | 
            +
                    config_file = config_dir / "mcp_config.yaml"
         | 
| 578 | 
            +
                    
         | 
| 579 | 
            +
                    if config_file.exists() and not getattr(args, 'force', False):
         | 
| 580 | 
            +
                        print(f"Configuration already exists: {config_file}")
         | 
| 581 | 
            +
                        print("Use --force to overwrite")
         | 
| 582 | 
            +
                        return 1
         | 
| 583 | 
            +
                    
         | 
| 584 | 
            +
                    # Default configuration
         | 
| 585 | 
            +
                    default_config = {
         | 
| 586 | 
            +
                        "server": {
         | 
| 587 | 
            +
                            "name": "claude-mpm-mcp",
         | 
| 588 | 
            +
                            "version": "1.0.0",
         | 
| 589 | 
            +
                            "port": 8766,
         | 
| 590 | 
            +
                            "mode": "stdio"
         | 
| 591 | 
            +
                        },
         | 
| 592 | 
            +
                        "tools": {
         | 
| 593 | 
            +
                            "load_defaults": True,
         | 
| 594 | 
            +
                            "custom_tools_dir": str(config_dir / "mcp_tools"),
         | 
| 595 | 
            +
                            "enabled": [
         | 
| 596 | 
            +
                                "echo",
         | 
| 597 | 
            +
                                "calculator",
         | 
| 598 | 
            +
                                "system_info"
         | 
| 599 | 
            +
                            ]
         | 
| 600 | 
            +
                        },
         | 
| 601 | 
            +
                        "logging": {
         | 
| 602 | 
            +
                            "level": "INFO",
         | 
| 603 | 
            +
                            "file": str(config_dir / "logs" / "mcp_server.log")
         | 
| 604 | 
            +
                        }
         | 
| 605 | 
            +
                    }
         | 
| 606 | 
            +
                    
         | 
| 607 | 
            +
                    # Write configuration
         | 
| 608 | 
            +
                    import yaml
         | 
| 609 | 
            +
                    with open(config_file, 'w') as f:
         | 
| 610 | 
            +
                        yaml.dump(default_config, f, default_flow_style=False)
         | 
| 611 | 
            +
                    
         | 
| 612 | 
            +
                    print(f"✅ Created configuration: {config_file}")
         | 
| 613 | 
            +
                    
         | 
| 614 | 
            +
                    # Create tools directory
         | 
| 615 | 
            +
                    tools_dir = config_dir / "mcp_tools"
         | 
| 616 | 
            +
                    tools_dir.mkdir(exist_ok=True)
         | 
| 617 | 
            +
                    print(f"✅ Created tools directory: {tools_dir}")
         | 
| 618 | 
            +
                    
         | 
| 619 | 
            +
                    # Create logs directory
         | 
| 620 | 
            +
                    logs_dir = config_dir / "logs"
         | 
| 621 | 
            +
                    logs_dir.mkdir(exist_ok=True)
         | 
| 622 | 
            +
                    print(f"✅ Created logs directory: {logs_dir}")
         | 
| 623 | 
            +
                    
         | 
| 624 | 
            +
                    # Test MCP package installation
         | 
| 625 | 
            +
                    try:
         | 
| 626 | 
            +
                        import mcp
         | 
| 627 | 
            +
                        print("✅ MCP package is installed")
         | 
| 628 | 
            +
                    except ImportError:
         | 
| 629 | 
            +
                        print("⚠️  MCP package not found. Installing...")
         | 
| 630 | 
            +
                        import subprocess
         | 
| 631 | 
            +
                        result = subprocess.run(
         | 
| 632 | 
            +
                            [sys.executable, "-m", "pip", "install", "mcp"],
         | 
| 633 | 
            +
                            capture_output=True,
         | 
| 634 | 
            +
                            text=True
         | 
| 635 | 
            +
                        )
         | 
| 636 | 
            +
                        if result.returncode == 0:
         | 
| 637 | 
            +
                            print("✅ MCP package installed successfully")
         | 
| 638 | 
            +
                        else:
         | 
| 639 | 
            +
                            print("❌ Failed to install MCP package")
         | 
| 640 | 
            +
                            print(result.stderr)
         | 
| 641 | 
            +
                            return 1
         | 
| 642 | 
            +
                    
         | 
| 643 | 
            +
                    print()
         | 
| 644 | 
            +
                    print("MCP Gateway installation complete!")
         | 
| 645 | 
            +
                    print()
         | 
| 646 | 
            +
                    print("Next steps:")
         | 
| 647 | 
            +
                    print("1. Start the server: claude-mpm mcp start")
         | 
| 648 | 
            +
                    print("2. Check status: claude-mpm mcp status")
         | 
| 649 | 
            +
                    print("3. List tools: claude-mpm mcp tools")
         | 
| 650 | 
            +
                    
         | 
| 651 | 
            +
                    return 0
         | 
| 652 | 
            +
                    
         | 
| 653 | 
            +
                except Exception as e:
         | 
| 654 | 
            +
                    logger.error(f"Failed to install MCP Gateway: {e}", exc_info=True)
         | 
| 655 | 
            +
                    print(f"Error during installation: {e}")
         | 
| 656 | 
            +
                    return 1
         | 
| 657 | 
            +
             | 
| 658 | 
            +
             | 
| 659 | 
            +
            def _manage_config(args, logger):
         | 
| 660 | 
            +
                """
         | 
| 661 | 
            +
                View and manage MCP Gateway configuration.
         | 
| 662 | 
            +
                
         | 
| 663 | 
            +
                WHY: Users need to view and modify MCP configuration without
         | 
| 664 | 
            +
                manually editing YAML files.
         | 
| 665 | 
            +
                
         | 
| 666 | 
            +
                Args:
         | 
| 667 | 
            +
                    args: Command arguments
         | 
| 668 | 
            +
                    logger: Logger instance
         | 
| 669 | 
            +
                    
         | 
| 670 | 
            +
                Returns:
         | 
| 671 | 
            +
                    int: Exit code (0 for success, non-zero for failure)
         | 
| 672 | 
            +
                """
         | 
| 673 | 
            +
                try:
         | 
| 674 | 
            +
                    config_file = Path.home() / ".claude-mpm" / "mcp_config.yaml"
         | 
| 675 | 
            +
                    
         | 
| 676 | 
            +
                    action = getattr(args, 'config_action', 'view')
         | 
| 677 | 
            +
                    
         | 
| 678 | 
            +
                    if action == 'view':
         | 
| 679 | 
            +
                        # View current configuration
         | 
| 680 | 
            +
                        if not config_file.exists():
         | 
| 681 | 
            +
                            print("No configuration file found")
         | 
| 682 | 
            +
                            print("Run 'claude-mpm mcp install' to create default configuration")
         | 
| 683 | 
            +
                            return 1
         | 
| 684 | 
            +
                        
         | 
| 685 | 
            +
                        import yaml
         | 
| 686 | 
            +
                        with open(config_file, 'r') as f:
         | 
| 687 | 
            +
                            config = yaml.safe_load(f)
         | 
| 688 | 
            +
                        
         | 
| 689 | 
            +
                        print("MCP Gateway Configuration")
         | 
| 690 | 
            +
                        print("=" * 50)
         | 
| 691 | 
            +
                        print(yaml.dump(config, default_flow_style=False))
         | 
| 692 | 
            +
                        
         | 
| 693 | 
            +
                    elif action == 'edit':
         | 
| 694 | 
            +
                        # Edit configuration
         | 
| 695 | 
            +
                        if not config_file.exists():
         | 
| 696 | 
            +
                            print("No configuration file found")
         | 
| 697 | 
            +
                            return 1
         | 
| 698 | 
            +
                        
         | 
| 699 | 
            +
                        # Open in default editor
         | 
| 700 | 
            +
                        import os
         | 
| 701 | 
            +
                        import subprocess
         | 
| 702 | 
            +
                        
         | 
| 703 | 
            +
                        editor = os.environ.get('EDITOR', 'nano')
         | 
| 704 | 
            +
                        subprocess.call([editor, str(config_file)])
         | 
| 705 | 
            +
                        
         | 
| 706 | 
            +
                        print("Configuration updated")
         | 
| 707 | 
            +
                        
         | 
| 708 | 
            +
                    elif action == 'reset':
         | 
| 709 | 
            +
                        # Reset to default configuration
         | 
| 710 | 
            +
                        if config_file.exists():
         | 
| 711 | 
            +
                            # Backup existing configuration
         | 
| 712 | 
            +
                            backup_file = config_file.with_suffix('.yaml.bak')
         | 
| 713 | 
            +
                            config_file.rename(backup_file)
         | 
| 714 | 
            +
                            print(f"Backed up existing configuration to: {backup_file}")
         | 
| 715 | 
            +
                        
         | 
| 716 | 
            +
                        # Run install to create default configuration
         | 
| 717 | 
            +
                        args.force = True
         | 
| 718 | 
            +
                        return _install_gateway(args, logger)
         | 
| 719 | 
            +
                    
         | 
| 720 | 
            +
                    else:
         | 
| 721 | 
            +
                        print(f"Unknown config action: {action}")
         | 
| 722 | 
            +
                        return 1
         | 
| 723 | 
            +
                    
         | 
| 724 | 
            +
                    return 0
         | 
| 725 | 
            +
                    
         | 
| 726 | 
            +
                except Exception as e:
         | 
| 727 | 
            +
                    logger.error(f"Failed to manage configuration: {e}", exc_info=True)
         | 
| 728 | 
            +
                    print(f"Error managing configuration: {e}")
         | 
| 729 | 
            +
                    return 1
         | 
| 730 | 
            +
             | 
| 731 | 
            +
             | 
| 732 | 
            +
            async def _load_default_tools(tool_registry, logger):
         | 
| 733 | 
            +
                """
         | 
| 734 | 
            +
                Load default MCP tools into the registry.
         | 
| 735 | 
            +
                
         | 
| 736 | 
            +
                WHY: We provide a set of default tools for common operations
         | 
| 737 | 
            +
                to get users started quickly.
         | 
| 738 | 
            +
                
         | 
| 739 | 
            +
                Args:
         | 
| 740 | 
            +
                    tool_registry: ToolRegistry instance
         | 
| 741 | 
            +
                    logger: Logger instance
         | 
| 742 | 
            +
                """
         | 
| 743 | 
            +
                try:
         | 
| 744 | 
            +
                    # Import default tool adapters
         | 
| 745 | 
            +
                    from ...services.mcp_gateway.tools.base_adapter import (
         | 
| 746 | 
            +
                        EchoToolAdapter,
         | 
| 747 | 
            +
                        CalculatorToolAdapter,
         | 
| 748 | 
            +
                        SystemInfoToolAdapter
         | 
| 749 | 
            +
                    )
         | 
| 750 | 
            +
                    
         | 
| 751 | 
            +
                    # Register default tools
         | 
| 752 | 
            +
                    default_tools = [
         | 
| 753 | 
            +
                        EchoToolAdapter(),
         | 
| 754 | 
            +
                        CalculatorToolAdapter(),
         | 
| 755 | 
            +
                        SystemInfoToolAdapter()
         | 
| 756 | 
            +
                    ]
         | 
| 757 | 
            +
                    
         | 
| 758 | 
            +
                    for adapter in default_tools:
         | 
| 759 | 
            +
                        await adapter.initialize()
         | 
| 760 | 
            +
                        tool_registry.register_tool(adapter)
         | 
| 761 | 
            +
                        tool_def = adapter.get_definition()
         | 
| 762 | 
            +
                        logger.debug(f"Loaded default tool: {tool_def.name}")
         | 
| 763 | 
            +
                    
         | 
| 764 | 
            +
                except Exception as e:
         | 
| 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
         |