claude-mpm 3.9.8__py3-none-any.whl → 3.9.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/base_agent.json +1 -1
  3. claude_mpm/cli/__init__.py +3 -1
  4. claude_mpm/cli/commands/__init__.py +3 -1
  5. claude_mpm/cli/commands/cleanup.py +21 -1
  6. claude_mpm/cli/commands/mcp.py +821 -0
  7. claude_mpm/cli/parser.py +148 -1
  8. claude_mpm/config/memory_guardian_config.py +325 -0
  9. claude_mpm/constants.py +13 -0
  10. claude_mpm/hooks/claude_hooks/hook_handler.py +76 -19
  11. claude_mpm/models/state_models.py +433 -0
  12. claude_mpm/services/communication/__init__.py +2 -2
  13. claude_mpm/services/communication/socketio.py +18 -16
  14. claude_mpm/services/infrastructure/__init__.py +4 -1
  15. claude_mpm/services/infrastructure/logging.py +3 -3
  16. claude_mpm/services/infrastructure/memory_guardian.py +770 -0
  17. claude_mpm/services/mcp_gateway/__init__.py +28 -12
  18. claude_mpm/services/mcp_gateway/main.py +326 -0
  19. claude_mpm/services/mcp_gateway/registry/__init__.py +6 -3
  20. claude_mpm/services/mcp_gateway/registry/service_registry.py +397 -0
  21. claude_mpm/services/mcp_gateway/registry/tool_registry.py +477 -0
  22. claude_mpm/services/mcp_gateway/server/__init__.py +9 -3
  23. claude_mpm/services/mcp_gateway/server/mcp_server.py +430 -0
  24. claude_mpm/services/mcp_gateway/server/mcp_server_simple.py +444 -0
  25. claude_mpm/services/mcp_gateway/server/stdio_handler.py +373 -0
  26. claude_mpm/services/mcp_gateway/tools/__init__.py +16 -3
  27. claude_mpm/services/mcp_gateway/tools/base_adapter.py +497 -0
  28. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +729 -0
  29. claude_mpm/services/mcp_gateway/tools/hello_world.py +551 -0
  30. claude_mpm/utils/file_utils.py +293 -0
  31. claude_mpm/utils/platform_memory.py +524 -0
  32. claude_mpm/utils/subprocess_utils.py +305 -0
  33. {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/METADATA +3 -1
  34. {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/RECORD +39 -28
  35. claude_mpm/agents/templates/.claude-mpm/memories/README.md +0 -36
  36. claude_mpm/agents/templates/.claude-mpm/memories/engineer_agent.md +0 -39
  37. claude_mpm/agents/templates/.claude-mpm/memories/qa_agent.md +0 -38
  38. claude_mpm/agents/templates/.claude-mpm/memories/research_agent.md +0 -39
  39. claude_mpm/agents/templates/.claude-mpm/memories/version_control_agent.md +0 -38
  40. /claude_mpm/agents/templates/{research_memory_efficient.json → backup/research_memory_efficient.json} +0 -0
  41. {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/WHEEL +0 -0
  42. {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/entry_points.txt +0 -0
  43. {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/licenses/LICENSE +0 -0
  44. {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,821 @@
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
+ MCPServer,
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
+ async def _start_server(args, logger):
111
+ """
112
+ Start the MCP Gateway server.
113
+
114
+ WHY: Users need to start the MCP server to enable tool invocation
115
+ and external service integration in Claude sessions.
116
+
117
+ Args:
118
+ args: Command arguments with optional port and configuration
119
+ logger: Logger instance
120
+
121
+ Returns:
122
+ int: Exit code (0 for success, non-zero for failure)
123
+ """
124
+ from ...services.mcp_gateway import MCPServer, ToolRegistry, MCPConfiguration
125
+ from ...services.mcp_gateway.server.stdio_handler import StdioHandler
126
+
127
+ try:
128
+ print("Starting MCP Gateway server...")
129
+
130
+ # Load configuration
131
+ config_path = getattr(args, 'config_file', None)
132
+ if config_path and Path(config_path).exists():
133
+ logger.info(f"Loading configuration from: {config_path}")
134
+ config = MCPConfiguration(config_path=Path(config_path))
135
+ await config.initialize()
136
+ else:
137
+ logger.info("Using default MCP configuration")
138
+ config = MCPConfiguration()
139
+ await config.initialize()
140
+
141
+ # Initialize server components
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}")
195
+ return 1
196
+
197
+ print("MCP Gateway server stopped")
198
+ return 0
199
+
200
+ except KeyboardInterrupt:
201
+ print("\nServer interrupted by user")
202
+ return 0
203
+ except Exception as e:
204
+ logger.error(f"Failed to start MCP server: {e}", exc_info=True)
205
+ print(f"Error starting server: {e}")
206
+ return 1
207
+
208
+
209
+ def _stop_server(args, logger):
210
+ """
211
+ Stop the MCP Gateway server.
212
+
213
+ WHY: Users need a clean way to stop the MCP server when it's no longer needed
214
+ or when they need to restart it with different configuration.
215
+
216
+ Args:
217
+ args: Command arguments
218
+ logger: Logger instance
219
+
220
+ Returns:
221
+ int: Exit code (0 for success, non-zero for failure)
222
+ """
223
+ try:
224
+ # Check for running server process
225
+ pid_file = Path.home() / ".claude-mpm" / "mcp_server.pid"
226
+
227
+ if not pid_file.exists():
228
+ print("No MCP server process found")
229
+ return 0
230
+
231
+ # Read PID and attempt to stop
232
+ import signal
233
+ import os
234
+
235
+ with open(pid_file, 'r') as f:
236
+ pid = int(f.read().strip())
237
+
238
+ try:
239
+ # Send termination signal
240
+ os.kill(pid, signal.SIGTERM)
241
+ print(f"Sent stop signal to MCP server (PID: {pid})")
242
+
243
+ # Wait for graceful shutdown
244
+ import time
245
+ for _ in range(10):
246
+ try:
247
+ os.kill(pid, 0) # Check if process still exists
248
+ time.sleep(0.5)
249
+ except ProcessLookupError:
250
+ break
251
+ else:
252
+ # Force kill if still running
253
+ os.kill(pid, signal.SIGKILL)
254
+ print("Force stopped MCP server")
255
+
256
+ # Clean up PID file
257
+ pid_file.unlink()
258
+ print("MCP server stopped successfully")
259
+ return 0
260
+
261
+ except ProcessLookupError:
262
+ # Process already stopped
263
+ pid_file.unlink()
264
+ print("MCP server was not running")
265
+ return 0
266
+
267
+ except Exception as e:
268
+ logger.error(f"Failed to stop MCP server: {e}", exc_info=True)
269
+ print(f"Error stopping server: {e}")
270
+ return 1
271
+
272
+
273
+ def _show_status(args, logger):
274
+ """
275
+ Show MCP Gateway server and tool status.
276
+
277
+ WHY: Users need visibility into the current state of the MCP system,
278
+ including server status, registered tools, and configuration.
279
+
280
+ Args:
281
+ args: Command arguments
282
+ logger: Logger instance
283
+
284
+ Returns:
285
+ int: Exit code (0 for success, non-zero for failure)
286
+ """
287
+ from ...services.mcp_gateway import MCPServiceRegistry, ToolRegistry
288
+
289
+ try:
290
+ print("MCP Gateway Status")
291
+ print("=" * 50)
292
+
293
+ # Check server status
294
+ pid_file = Path.home() / ".claude-mpm" / "mcp_server.pid"
295
+
296
+ if pid_file.exists():
297
+ with open(pid_file, 'r') as f:
298
+ pid = int(f.read().strip())
299
+
300
+ # Check if process is running
301
+ import os
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")
310
+
311
+ print()
312
+
313
+ # Show registered tools
314
+ print("Registered Tools:")
315
+ print("-" * 30)
316
+
317
+ # Initialize tool registry to check tools
318
+ tool_registry = ToolRegistry()
319
+
320
+ # Load configuration to check enabled tools
321
+ config_file = Path.home() / ".claude-mpm" / "mcp_config.yaml"
322
+ if config_file.exists():
323
+ # For now, just load default tools if config file exists
324
+ # TODO: Implement proper async config loading for CLI commands
325
+ _load_default_tools(tool_registry, logger)
326
+
327
+ tools = tool_registry.list_tools()
328
+ if tools:
329
+ for tool in tools:
330
+ status = "✅" if tool.enabled else "❌"
331
+ print(f" {status} {tool.name}: {tool.description}")
332
+ else:
333
+ print(" No tools registered")
334
+
335
+ print()
336
+
337
+ # Show configuration
338
+ print("Configuration:")
339
+ print("-" * 30)
340
+
341
+ if config_file.exists():
342
+ 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
+ else:
347
+ print(" Using default configuration")
348
+
349
+ # Show verbose details if requested
350
+ if getattr(args, 'verbose', False):
351
+ print()
352
+ print("Service Registry:")
353
+ print("-" * 30)
354
+
355
+ registry = MCPServiceRegistry()
356
+ services = registry.list_services()
357
+
358
+ for service_id, service_info in services.items():
359
+ print(f" {service_id}:")
360
+ print(f" State: {service_info['state']}")
361
+ print(f" Type: {service_info['type']}")
362
+
363
+ return 0
364
+
365
+ except Exception as e:
366
+ logger.error(f"Failed to show MCP status: {e}", exc_info=True)
367
+ print(f"Error checking status: {e}")
368
+ return 1
369
+
370
+
371
+ def _manage_tools(args, logger):
372
+ """
373
+ List and manage registered MCP tools.
374
+
375
+ WHY: Users need to see what tools are available and manage their
376
+ registration and configuration.
377
+
378
+ Args:
379
+ args: Command arguments with optional filters
380
+ logger: Logger instance
381
+
382
+ Returns:
383
+ int: Exit code (0 for success, non-zero for failure)
384
+ """
385
+ from ...services.mcp_gateway import ToolRegistry
386
+
387
+ try:
388
+ # Check for subcommand
389
+ action = getattr(args, 'tool_action', 'list')
390
+
391
+ # Initialize tool registry
392
+ tool_registry = ToolRegistry()
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)
400
+
401
+ if action == 'list':
402
+ # List all tools
403
+ tools = tool_registry.list_tools()
404
+
405
+ if not tools:
406
+ print("No tools registered")
407
+ return 0
408
+
409
+ print("Registered MCP Tools:")
410
+ print("=" * 50)
411
+
412
+ # Group tools by category if available
413
+ by_category = {}
414
+ for tool in tools:
415
+ category = getattr(tool, 'category', 'General')
416
+ if category not in by_category:
417
+ by_category[category] = []
418
+ by_category[category].append(tool)
419
+
420
+ for category, category_tools in sorted(by_category.items()):
421
+ print(f"\n{category}:")
422
+ print("-" * 30)
423
+
424
+ for tool in category_tools:
425
+ status = "✅" if tool.enabled else "❌"
426
+ print(f" {status} {tool.name}")
427
+ print(f" {tool.description}")
428
+
429
+ if getattr(args, 'verbose', False):
430
+ # Show input schema
431
+ print(f" Input Schema: {json.dumps(tool.input_schema, indent=8)}")
432
+
433
+ elif action == 'enable':
434
+ # Enable a tool
435
+ tool_name = args.tool_name
436
+ if tool_registry.enable_tool(tool_name):
437
+ print(f"✅ Enabled tool: {tool_name}")
438
+ return 0
439
+ else:
440
+ print(f"❌ Failed to enable tool: {tool_name}")
441
+ return 1
442
+
443
+ elif action == 'disable':
444
+ # Disable a tool
445
+ tool_name = args.tool_name
446
+ if tool_registry.disable_tool(tool_name):
447
+ print(f"✅ Disabled tool: {tool_name}")
448
+ return 0
449
+ else:
450
+ print(f"❌ Failed to disable tool: {tool_name}")
451
+ return 1
452
+
453
+ else:
454
+ print(f"Unknown tool action: {action}")
455
+ return 1
456
+
457
+ return 0
458
+
459
+ except Exception as e:
460
+ logger.error(f"Failed to manage tools: {e}", exc_info=True)
461
+ print(f"Error managing tools: {e}")
462
+ return 1
463
+
464
+
465
+ def _register_tool(args, logger):
466
+ """
467
+ Register a new MCP tool.
468
+
469
+ WHY: Users need to add custom tools to the MCP Gateway for use
470
+ in Claude sessions.
471
+
472
+ Args:
473
+ args: Command arguments with tool definition
474
+ logger: Logger instance
475
+
476
+ Returns:
477
+ int: Exit code (0 for success, non-zero for failure)
478
+ """
479
+ from ...services.mcp_gateway import ToolRegistry
480
+ from ...services.mcp_gateway.core.interfaces import MCPToolDefinition
481
+
482
+ try:
483
+ # Get tool details from arguments
484
+ tool_name = args.name
485
+ tool_description = args.description
486
+
487
+ # Parse input schema
488
+ if args.schema_file:
489
+ with open(args.schema_file, 'r') as f:
490
+ input_schema = json.load(f)
491
+ else:
492
+ # Basic schema
493
+ input_schema = {
494
+ "type": "object",
495
+ "properties": {},
496
+ "required": []
497
+ }
498
+
499
+ # Create tool definition
500
+ tool_def = MCPToolDefinition(
501
+ name=tool_name,
502
+ description=tool_description,
503
+ input_schema=input_schema,
504
+ enabled=True
505
+ )
506
+
507
+ # Register with tool registry
508
+ tool_registry = ToolRegistry()
509
+
510
+ if args.adapter:
511
+ # Register with custom adapter
512
+ logger.info(f"Registering tool with adapter: {args.adapter}")
513
+ # Import and instantiate adapter
514
+ # This would be extended based on adapter framework
515
+ print(f"Custom adapter registration not yet implemented: {args.adapter}")
516
+ return 1
517
+ else:
518
+ # Register as a simple tool
519
+ success = tool_registry.register_tool(tool_def)
520
+
521
+ if success:
522
+ print(f"✅ Successfully registered tool: {tool_name}")
523
+
524
+ # Save to configuration if requested
525
+ if args.save:
526
+ config_file = Path.home() / ".claude-mpm" / "mcp_config.yaml"
527
+ # Update configuration with new tool
528
+ print(f"Tool configuration saved to: {config_file}")
529
+
530
+ return 0
531
+ else:
532
+ print(f"❌ Failed to register tool: {tool_name}")
533
+ return 1
534
+
535
+ except Exception as e:
536
+ logger.error(f"Failed to register tool: {e}", exc_info=True)
537
+ print(f"Error registering tool: {e}")
538
+ return 1
539
+
540
+
541
+ def _test_tool(args, logger):
542
+ """
543
+ Test MCP tool invocation.
544
+
545
+ WHY: Users need to verify that tools are working correctly before
546
+ using them in Claude sessions.
547
+
548
+ Args:
549
+ args: Command arguments with tool name and test parameters
550
+ logger: Logger instance
551
+
552
+ Returns:
553
+ int: Exit code (0 for success, non-zero for failure)
554
+ """
555
+ from ...services.mcp_gateway import ToolRegistry
556
+ from ...services.mcp_gateway.core.interfaces import MCPToolInvocation
557
+
558
+ try:
559
+ # Get tool name and arguments
560
+ tool_name = args.tool_name
561
+
562
+ # Parse tool arguments
563
+ if args.args_file:
564
+ with open(args.args_file, 'r') as f:
565
+ tool_args = json.load(f)
566
+ elif args.args:
567
+ tool_args = json.loads(args.args)
568
+ else:
569
+ tool_args = {}
570
+
571
+ print(f"Testing tool: {tool_name}")
572
+ print(f"Arguments: {json.dumps(tool_args, indent=2)}")
573
+ print("-" * 50)
574
+
575
+ # Initialize tool registry
576
+ tool_registry = ToolRegistry()
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)
584
+
585
+ # Create invocation request
586
+ invocation = MCPToolInvocation(
587
+ tool_name=tool_name,
588
+ arguments=tool_args,
589
+ request_id=f"test-{tool_name}"
590
+ )
591
+
592
+ # Invoke tool
593
+ result = asyncio.run(tool_registry.invoke_tool(invocation))
594
+
595
+ if result.success:
596
+ print("✅ Tool invocation successful!")
597
+ print(f"Result: {json.dumps(result.result, indent=2)}")
598
+ else:
599
+ print("❌ Tool invocation failed!")
600
+ print(f"Error: {result.error}")
601
+
602
+ return 0 if result.success else 1
603
+
604
+ except Exception as e:
605
+ logger.error(f"Failed to test tool: {e}", exc_info=True)
606
+ print(f"Error testing tool: {e}")
607
+ return 1
608
+
609
+
610
+ def _install_gateway(args, logger):
611
+ """
612
+ Install and configure the MCP Gateway.
613
+
614
+ WHY: Users need a simple way to set up the MCP Gateway with
615
+ default configuration and tools.
616
+
617
+ Args:
618
+ args: Command arguments
619
+ logger: Logger instance
620
+
621
+ Returns:
622
+ int: Exit code (0 for success, non-zero for failure)
623
+ """
624
+ try:
625
+ print("Installing MCP Gateway...")
626
+ print("=" * 50)
627
+
628
+ # Create configuration directory
629
+ config_dir = Path.home() / ".claude-mpm"
630
+ config_dir.mkdir(exist_ok=True)
631
+
632
+ # Create default configuration
633
+ config_file = config_dir / "mcp_config.yaml"
634
+
635
+ if config_file.exists() and not getattr(args, 'force', False):
636
+ print(f"Configuration already exists: {config_file}")
637
+ print("Use --force to overwrite")
638
+ return 1
639
+
640
+ # Default configuration
641
+ default_config = {
642
+ "server": {
643
+ "name": "claude-mpm-mcp",
644
+ "version": "1.0.0",
645
+ "port": 8766,
646
+ "mode": "stdio"
647
+ },
648
+ "tools": {
649
+ "load_defaults": True,
650
+ "custom_tools_dir": str(config_dir / "mcp_tools"),
651
+ "enabled": [
652
+ "echo",
653
+ "calculator",
654
+ "system_info"
655
+ ]
656
+ },
657
+ "logging": {
658
+ "level": "INFO",
659
+ "file": str(config_dir / "logs" / "mcp_server.log")
660
+ }
661
+ }
662
+
663
+ # Write configuration
664
+ import yaml
665
+ with open(config_file, 'w') as f:
666
+ yaml.dump(default_config, f, default_flow_style=False)
667
+
668
+ print(f"✅ Created configuration: {config_file}")
669
+
670
+ # Create tools directory
671
+ tools_dir = config_dir / "mcp_tools"
672
+ tools_dir.mkdir(exist_ok=True)
673
+ print(f"✅ Created tools directory: {tools_dir}")
674
+
675
+ # Create logs directory
676
+ logs_dir = config_dir / "logs"
677
+ logs_dir.mkdir(exist_ok=True)
678
+ print(f"✅ Created logs directory: {logs_dir}")
679
+
680
+ # Test MCP package installation
681
+ try:
682
+ import mcp
683
+ print("✅ MCP package is installed")
684
+ except ImportError:
685
+ print("⚠️ MCP package not found. Installing...")
686
+ import subprocess
687
+ result = subprocess.run(
688
+ [sys.executable, "-m", "pip", "install", "mcp"],
689
+ capture_output=True,
690
+ text=True
691
+ )
692
+ if result.returncode == 0:
693
+ print("✅ MCP package installed successfully")
694
+ else:
695
+ print("❌ Failed to install MCP package")
696
+ print(result.stderr)
697
+ return 1
698
+
699
+ print()
700
+ print("MCP Gateway installation complete!")
701
+ print()
702
+ print("Next steps:")
703
+ print("1. Start the server: claude-mpm mcp start")
704
+ print("2. Check status: claude-mpm mcp status")
705
+ print("3. List tools: claude-mpm mcp tools")
706
+
707
+ return 0
708
+
709
+ except Exception as e:
710
+ logger.error(f"Failed to install MCP Gateway: {e}", exc_info=True)
711
+ print(f"Error during installation: {e}")
712
+ return 1
713
+
714
+
715
+ def _manage_config(args, logger):
716
+ """
717
+ View and manage MCP Gateway configuration.
718
+
719
+ WHY: Users need to view and modify MCP configuration without
720
+ manually editing YAML files.
721
+
722
+ Args:
723
+ args: Command arguments
724
+ logger: Logger instance
725
+
726
+ Returns:
727
+ int: Exit code (0 for success, non-zero for failure)
728
+ """
729
+ try:
730
+ config_file = Path.home() / ".claude-mpm" / "mcp_config.yaml"
731
+
732
+ action = getattr(args, 'config_action', 'view')
733
+
734
+ if action == 'view':
735
+ # View current configuration
736
+ if not config_file.exists():
737
+ print("No configuration file found")
738
+ print("Run 'claude-mpm mcp install' to create default configuration")
739
+ return 1
740
+
741
+ import yaml
742
+ with open(config_file, 'r') as f:
743
+ config = yaml.safe_load(f)
744
+
745
+ print("MCP Gateway Configuration")
746
+ print("=" * 50)
747
+ print(yaml.dump(config, default_flow_style=False))
748
+
749
+ elif action == 'edit':
750
+ # Edit configuration
751
+ if not config_file.exists():
752
+ print("No configuration file found")
753
+ return 1
754
+
755
+ # Open in default editor
756
+ import os
757
+ import subprocess
758
+
759
+ editor = os.environ.get('EDITOR', 'nano')
760
+ subprocess.call([editor, str(config_file)])
761
+
762
+ print("Configuration updated")
763
+
764
+ elif action == 'reset':
765
+ # Reset to default configuration
766
+ if config_file.exists():
767
+ # Backup existing configuration
768
+ backup_file = config_file.with_suffix('.yaml.bak')
769
+ config_file.rename(backup_file)
770
+ print(f"Backed up existing configuration to: {backup_file}")
771
+
772
+ # Run install to create default configuration
773
+ args.force = True
774
+ return _install_gateway(args, logger)
775
+
776
+ else:
777
+ print(f"Unknown config action: {action}")
778
+ return 1
779
+
780
+ return 0
781
+
782
+ except Exception as e:
783
+ logger.error(f"Failed to manage configuration: {e}", exc_info=True)
784
+ print(f"Error managing configuration: {e}")
785
+ return 1
786
+
787
+
788
+ async def _load_default_tools(tool_registry, logger):
789
+ """
790
+ Load default MCP tools into the registry.
791
+
792
+ WHY: We provide a set of default tools for common operations
793
+ to get users started quickly.
794
+
795
+ Args:
796
+ tool_registry: ToolRegistry instance
797
+ logger: Logger instance
798
+ """
799
+ try:
800
+ # Import default tool adapters
801
+ from ...services.mcp_gateway.tools.base_adapter import (
802
+ EchoToolAdapter,
803
+ CalculatorToolAdapter,
804
+ SystemInfoToolAdapter
805
+ )
806
+
807
+ # Register default tools
808
+ default_tools = [
809
+ EchoToolAdapter(),
810
+ CalculatorToolAdapter(),
811
+ SystemInfoToolAdapter()
812
+ ]
813
+
814
+ for adapter in default_tools:
815
+ await adapter.initialize()
816
+ tool_registry.register_tool(adapter)
817
+ tool_def = adapter.get_definition()
818
+ logger.debug(f"Loaded default tool: {tool_def.name}")
819
+
820
+ except Exception as e:
821
+ logger.error(f"Failed to load default tools: {e}", exc_info=True)