claude-mpm 4.0.23__py3-none-any.whl → 4.0.25__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 (60) hide show
  1. claude_mpm/BUILD_NUMBER +1 -1
  2. claude_mpm/VERSION +1 -1
  3. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +4 -1
  4. claude_mpm/agents/BASE_PM.md +3 -0
  5. claude_mpm/agents/templates/code_analyzer.json +2 -2
  6. claude_mpm/cli/commands/agents.py +453 -113
  7. claude_mpm/cli/commands/aggregate.py +107 -15
  8. claude_mpm/cli/commands/cleanup.py +142 -10
  9. claude_mpm/cli/commands/config.py +358 -224
  10. claude_mpm/cli/commands/info.py +184 -75
  11. claude_mpm/cli/commands/mcp_command_router.py +5 -76
  12. claude_mpm/cli/commands/mcp_install_commands.py +68 -36
  13. claude_mpm/cli/commands/mcp_server_commands.py +30 -37
  14. claude_mpm/cli/commands/memory.py +331 -61
  15. claude_mpm/cli/commands/monitor.py +101 -7
  16. claude_mpm/cli/commands/run.py +368 -8
  17. claude_mpm/cli/commands/tickets.py +206 -24
  18. claude_mpm/cli/parsers/mcp_parser.py +3 -0
  19. claude_mpm/cli/shared/__init__.py +40 -0
  20. claude_mpm/cli/shared/argument_patterns.py +212 -0
  21. claude_mpm/cli/shared/command_base.py +234 -0
  22. claude_mpm/cli/shared/error_handling.py +238 -0
  23. claude_mpm/cli/shared/output_formatters.py +231 -0
  24. claude_mpm/config/agent_config.py +29 -8
  25. claude_mpm/core/container.py +6 -4
  26. claude_mpm/core/service_registry.py +4 -2
  27. claude_mpm/core/shared/__init__.py +17 -0
  28. claude_mpm/core/shared/config_loader.py +320 -0
  29. claude_mpm/core/shared/path_resolver.py +277 -0
  30. claude_mpm/core/shared/singleton_manager.py +208 -0
  31. claude_mpm/hooks/claude_hooks/memory_integration.py +4 -2
  32. claude_mpm/hooks/claude_hooks/response_tracking.py +14 -3
  33. claude_mpm/hooks/memory_integration_hook.py +11 -2
  34. claude_mpm/services/agents/deployment/agent_deployment.py +43 -23
  35. claude_mpm/services/agents/deployment/deployment_wrapper.py +71 -0
  36. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +1 -0
  37. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +43 -0
  38. claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +4 -0
  39. claude_mpm/services/agents/deployment/processors/agent_processor.py +1 -1
  40. claude_mpm/services/agents/loading/base_agent_manager.py +11 -3
  41. claude_mpm/services/agents/registry/deployed_agent_discovery.py +14 -5
  42. claude_mpm/services/event_aggregator.py +4 -2
  43. claude_mpm/services/mcp_gateway/config/config_loader.py +89 -28
  44. claude_mpm/services/mcp_gateway/config/configuration.py +29 -0
  45. claude_mpm/services/mcp_gateway/registry/service_registry.py +22 -5
  46. claude_mpm/services/memory/builder.py +6 -1
  47. claude_mpm/services/response_tracker.py +3 -1
  48. claude_mpm/services/runner_configuration_service.py +15 -6
  49. claude_mpm/services/shared/__init__.py +20 -0
  50. claude_mpm/services/shared/async_service_base.py +219 -0
  51. claude_mpm/services/shared/config_service_base.py +292 -0
  52. claude_mpm/services/shared/lifecycle_service_base.py +317 -0
  53. claude_mpm/services/shared/manager_base.py +303 -0
  54. claude_mpm/services/shared/service_factory.py +308 -0
  55. {claude_mpm-4.0.23.dist-info → claude_mpm-4.0.25.dist-info}/METADATA +19 -13
  56. {claude_mpm-4.0.23.dist-info → claude_mpm-4.0.25.dist-info}/RECORD +60 -44
  57. {claude_mpm-4.0.23.dist-info → claude_mpm-4.0.25.dist-info}/WHEEL +0 -0
  58. {claude_mpm-4.0.23.dist-info → claude_mpm-4.0.25.dist-info}/entry_points.txt +0 -0
  59. {claude_mpm-4.0.23.dist-info → claude_mpm-4.0.25.dist-info}/licenses/LICENSE +0 -0
  60. {claude_mpm-4.0.23.dist-info → claude_mpm-4.0.25.dist-info}/top_level.txt +0 -0
@@ -1,90 +1,199 @@
1
- from pathlib import Path
2
-
3
1
  """
4
2
  Info command implementation for claude-mpm.
5
3
 
6
4
  WHY: This module provides system information and configuration details to help
7
5
  users understand their claude-mpm setup and troubleshoot issues.
6
+
7
+ DESIGN DECISIONS:
8
+ - Use BaseCommand for consistent CLI patterns
9
+ - Leverage shared utilities for argument parsing and output formatting
10
+ - Support multiple output formats (json, yaml, table, text)
11
+ - Provide comprehensive system information for troubleshooting
8
12
  """
9
13
 
10
14
  import shutil
11
-
12
- from ...core.logger import get_logger
15
+ from pathlib import Path
16
+ from typing import Dict, Any, List
17
+
18
+ from ..shared import BaseCommand, CommandResult
19
+
20
+
21
+ class InfoCommand(BaseCommand):
22
+ """Information display command using shared utilities."""
23
+
24
+ def __init__(self):
25
+ super().__init__("info")
26
+
27
+ def validate_args(self, args) -> str:
28
+ """Validate command arguments."""
29
+ # Info command doesn't require specific validation
30
+ return None
31
+
32
+ def run(self, args) -> CommandResult:
33
+ """Execute the info command."""
34
+ try:
35
+ # Gather system information
36
+ info_data = self._gather_system_info(args)
37
+
38
+ output_format = getattr(args, 'format', 'text')
39
+
40
+ if output_format in ['json', 'yaml']:
41
+ # Structured output
42
+ return CommandResult.success_result("System information retrieved", data=info_data)
43
+ else:
44
+ # Text output
45
+ self._display_text_info(info_data)
46
+ return CommandResult.success_result("System information displayed")
47
+
48
+ except Exception as e:
49
+ self.logger.error(f"Error gathering system info: {e}", exc_info=True)
50
+ return CommandResult.error_result(f"Error gathering system info: {e}")
51
+
52
+ def _gather_system_info(self, args) -> Dict[str, Any]:
53
+ """Gather comprehensive system information."""
54
+ try:
55
+ from ...core.framework_loader import FrameworkLoader
56
+ except ImportError:
57
+ from claude_mpm.core.framework_loader import FrameworkLoader
58
+
59
+ # Framework information
60
+ framework_path = getattr(args, 'framework_path', None)
61
+ loader = FrameworkLoader(framework_path)
62
+
63
+ framework_info = {
64
+ "loaded": loader.framework_content["loaded"],
65
+ "name": "claude-multiagent-pm" if loader.framework_content["loaded"] else "Not found",
66
+ "version": loader.framework_content.get('version', 'unknown'),
67
+ "path": str(loader.framework_path) if loader.framework_path else None,
68
+ "agents": loader.get_agent_list() if loader.framework_content["loaded"] else []
69
+ }
70
+
71
+ # Configuration information
72
+ config_info = {
73
+ "log_directory": getattr(args, 'log_dir', None) or '~/.claude-mpm/logs'
74
+ }
75
+
76
+ # Agent hierarchy
77
+ agent_hierarchy = {}
78
+ core_agents = []
79
+ if loader.agent_registry:
80
+ hierarchy = loader.agent_registry.get_agent_hierarchy()
81
+ agent_hierarchy = {
82
+ "project_agents": len(hierarchy['project']),
83
+ "user_agents": len(hierarchy['user']),
84
+ "system_agents": len(hierarchy['system']),
85
+ "project_agent_list": hierarchy['project'],
86
+ "user_agent_list": hierarchy['user'],
87
+ "system_agent_list": hierarchy['system']
88
+ }
89
+ core_agents = loader.agent_registry.get_core_agents()
90
+
91
+ # Dependencies check
92
+ dependencies = self._check_dependencies()
93
+
94
+ return {
95
+ "framework": framework_info,
96
+ "configuration": config_info,
97
+ "agent_hierarchy": agent_hierarchy,
98
+ "core_agents": core_agents,
99
+ "dependencies": dependencies
100
+ }
101
+
102
+ def _check_dependencies(self) -> Dict[str, Any]:
103
+ """Check system dependencies."""
104
+ dependencies = {}
105
+
106
+ # Check Claude CLI
107
+ claude_path = shutil.which("claude")
108
+ dependencies["claude_cli"] = {
109
+ "installed": bool(claude_path),
110
+ "path": claude_path,
111
+ "status": "✓ Installed" if claude_path else "✗ Not found in PATH"
112
+ }
113
+
114
+ # Check ai-trackdown-pytools
115
+ try:
116
+ import ai_trackdown_pytools
117
+ dependencies["ai_trackdown_pytools"] = {
118
+ "installed": True,
119
+ "status": "✓ Installed"
120
+ }
121
+ except ImportError:
122
+ dependencies["ai_trackdown_pytools"] = {
123
+ "installed": False,
124
+ "status": "✗ Not installed"
125
+ }
126
+
127
+ # Check Claude Code hooks
128
+ claude_settings = Path.home() / ".claude" / "settings.json"
129
+ dependencies["claude_code_hooks"] = {
130
+ "installed": claude_settings.exists(),
131
+ "settings_path": str(claude_settings),
132
+ "status": "✓ Installed" if claude_settings.exists() else "✗ Not installed",
133
+ "install_command": "python scripts/install_hooks.py" if not claude_settings.exists() else None
134
+ }
135
+
136
+ return dependencies
137
+
138
+ def _display_text_info(self, info_data: Dict[str, Any]) -> None:
139
+ """Display information in text format."""
140
+ print("Claude MPM - Multi-Agent Project Manager")
141
+ print("=" * 50)
142
+
143
+ # Framework info
144
+ framework = info_data["framework"]
145
+ print(f"Framework: {framework['name']}")
146
+ if framework["loaded"]:
147
+ print(f"Version: {framework['version']}")
148
+ print(f"Path: {framework['path']}")
149
+ print(f"Agents: {', '.join(framework['agents'])}")
150
+
151
+ print()
152
+
153
+ # Configuration
154
+ config = info_data["configuration"]
155
+ print("Configuration:")
156
+ print(f" Log directory: {config['log_directory']}")
157
+
158
+ # Agent hierarchy
159
+ hierarchy = info_data["agent_hierarchy"]
160
+ if hierarchy:
161
+ print("\nAgent Hierarchy:")
162
+ print(f" Project agents: {hierarchy['project_agents']}")
163
+ print(f" User agents: {hierarchy['user_agents']}")
164
+ print(f" System agents: {hierarchy['system_agents']}")
165
+
166
+ # Core agents
167
+ core_agents = info_data["core_agents"]
168
+ if core_agents:
169
+ print(f"\nCore Agents: {', '.join(core_agents)}")
170
+
171
+ # Dependencies
172
+ print("\nDependencies:")
173
+ deps = info_data["dependencies"]
174
+
175
+ for dep_name, dep_info in deps.items():
176
+ print(f" {dep_info['status']}")
177
+ if dep_name == "claude_cli" and dep_info["path"]:
178
+ print(f" Path: {dep_info['path']}")
179
+ elif dep_name == "claude_code_hooks":
180
+ if dep_info["installed"]:
181
+ print(" Use /mpm commands in Claude Code")
182
+ else:
183
+ print(f" Run: {dep_info['install_command']}")
13
184
 
14
185
 
15
186
  def show_info(args):
16
187
  """
17
- Show framework and configuration information.
188
+ Main entry point for info command.
18
189
 
19
- WHY: Users need to verify their installation, check dependencies, and understand
20
- what agents are available. This command provides a comprehensive overview of
21
- the claude-mpm environment.
190
+ This function maintains backward compatibility while using the new BaseCommand pattern.
191
+ """
192
+ command = InfoCommand()
193
+ result = command.execute(args)
22
194
 
23
- DESIGN DECISION: We check for all major components and dependencies, showing
24
- both what's working (✓) and what's missing (✗) to help with troubleshooting.
195
+ # Print result if structured output format is requested
196
+ if hasattr(args, 'format') and args.format in ['json', 'yaml']:
197
+ command.print_result(result, args)
25
198
 
26
- Args:
27
- args: Parsed command line arguments
28
- """
29
- try:
30
- from ...core.framework_loader import FrameworkLoader
31
- except ImportError:
32
- from claude_mpm.core.framework_loader import FrameworkLoader
33
-
34
- print("Claude MPM - Multi-Agent Project Manager")
35
- print("=" * 50)
36
-
37
- # Framework info
38
- loader = FrameworkLoader(args.framework_path)
39
- if loader.framework_content["loaded"]:
40
- print(f"Framework: claude-multiagent-pm")
41
- print(f"Version: {loader.framework_content['version']}")
42
- print(f"Path: {loader.framework_path}")
43
- print(f"Agents: {', '.join(loader.get_agent_list())}")
44
- else:
45
- print("Framework: Not found (using minimal instructions)")
46
-
47
- print()
48
-
49
- # Configuration
50
- print("Configuration:")
51
- print(f" Log directory: {args.log_dir or '~/.claude-mpm/logs'}")
52
-
53
- # Show agent hierarchy
54
- if loader.agent_registry:
55
- hierarchy = loader.agent_registry.get_agent_hierarchy()
56
- print("\nAgent Hierarchy:")
57
- print(f" Project agents: {len(hierarchy['project'])}")
58
- print(f" User agents: {len(hierarchy['user'])}")
59
- print(f" System agents: {len(hierarchy['system'])}")
60
-
61
- # Show core agents
62
- core_agents = loader.agent_registry.get_core_agents()
63
- print(f"\nCore Agents: {', '.join(core_agents)}")
64
-
65
- # Check dependencies
66
- print("\nDependencies:")
67
-
68
- # Check Claude CLI
69
- claude_path = shutil.which("claude")
70
- if claude_path:
71
- print(f" ✓ Claude CLI: {claude_path}")
72
- else:
73
- print(" ✗ Claude CLI: Not found in PATH")
74
-
75
- # Check ai-trackdown-pytools
76
- try:
77
- import ai_trackdown_pytools
78
-
79
- print(" ✓ ai-trackdown-pytools: Installed")
80
- except ImportError:
81
- print(" ✗ ai-trackdown-pytools: Not installed")
82
-
83
- # Check Claude Code hooks
84
- claude_settings = Path.home() / ".claude" / "settings.json"
85
- if claude_settings.exists():
86
- print(" ✓ Claude Code Hooks: Installed")
87
- print(" Use /mpm commands in Claude Code")
88
- else:
89
- print(" ✗ Claude Code Hooks: Not installed")
90
- print(" Run: python scripts/install_hooks.py")
199
+ return result.exit_code
@@ -122,82 +122,11 @@ class MCPCommandRouter:
122
122
 
123
123
  def _run_server(self, args) -> int:
124
124
  """Run server command handler - direct server execution."""
125
- try:
126
- self.logger.info("Starting MCP server directly via CLI command")
127
-
128
- # Import the server components
129
- from claude_mpm.services.mcp_gateway.server.stdio_server import SimpleMCPServer
130
-
131
- # Create server instance
132
- server = SimpleMCPServer(name="claude-mpm-gateway", version="1.0.0")
133
-
134
- if args.test:
135
- self.logger.info("Running in test mode")
136
- print("🧪 Starting MCP server in test mode...", file=sys.stderr)
137
- print(" This will run the server with stdio communication.", file=sys.stderr)
138
- print(" Press Ctrl+C to stop.\n", file=sys.stderr)
139
-
140
- # Run the server, handling event loop properly
141
- try:
142
- # Check if there's already an event loop running
143
- loop = asyncio.get_running_loop()
144
- # If we get here, there's already a loop running
145
- # We need to run in a subprocess to avoid conflicts
146
- import subprocess
147
- import json
148
-
149
- # Create a simple script to run the server
150
- script_content = f'''
151
- import asyncio
152
- import sys
153
- import os
154
- sys.path.insert(0, "{os.path.join(os.path.dirname(__file__), '..', '..', '..')}")
155
-
156
- async def main():
157
- from claude_mpm.services.mcp_gateway.server.stdio_server import SimpleMCPServer
158
- server = SimpleMCPServer(name="claude-mpm-gateway", version="1.0.0")
159
- await server.run()
160
-
161
- if __name__ == "__main__":
162
- asyncio.run(main())
163
- '''
164
-
165
- # Write the script to a temporary file
166
- import tempfile
167
- with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
168
- f.write(script_content)
169
- temp_script = f.name
170
-
171
- try:
172
- # Run the server in a subprocess
173
- result = subprocess.run([sys.executable, temp_script])
174
- return result.returncode
175
- finally:
176
- # Clean up the temporary file
177
- os.unlink(temp_script)
178
-
179
- except RuntimeError:
180
- # No event loop running, we can use asyncio.run
181
- async def run_async():
182
- await server.run()
183
-
184
- asyncio.run(run_async())
185
- return 0
186
-
187
- except ImportError as e:
188
- self.logger.error(f"Failed to import MCP server: {e}")
189
- print(f"❌ Error: Could not import MCP server components: {e}", file=sys.stderr)
190
- print("\nMake sure the MCP package is installed:", file=sys.stderr)
191
- print(" pip install mcp", file=sys.stderr)
192
- return 1
193
- except KeyboardInterrupt:
194
- self.logger.info("MCP server interrupted")
195
- print("\n🛑 MCP server stopped", file=sys.stderr)
196
- return 0
197
- except Exception as e:
198
- self.logger.error(f"Server error: {e}")
199
- print(f"❌ Error running server: {e}", file=sys.stderr)
200
- return 1
125
+ # Simply delegate to the async start_server method using asyncio.run
126
+ from .mcp_server_commands import MCPServerCommands
127
+
128
+ handler = MCPServerCommands(self.logger)
129
+ return asyncio.run(handler.start_server(args))
201
130
 
202
131
  def _show_help(self):
203
132
  """Show available MCP commands."""
@@ -20,7 +20,7 @@ class MCPInstallCommands:
20
20
  """Install and configure MCP gateway.
21
21
 
22
22
  WHY: This command installs the MCP package dependencies and configures
23
- Claude Desktop to use the MCP gateway server directly via the CLI command.
23
+ Claude Code to use the MCP gateway server directly via the CLI command.
24
24
 
25
25
  DESIGN DECISION: We handle both package installation and configuration
26
26
  in one command for user convenience, using the new direct CLI approach.
@@ -44,15 +44,15 @@ class MCPInstallCommands:
44
44
  print("\nPlease install manually with: pip install mcp")
45
45
  return 1
46
46
 
47
- # Step 2: Configure Claude Desktop with the new CLI command
48
- print("\n2️⃣ Configuring Claude Desktop...")
47
+ # Step 2: Configure Claude Code with the new CLI command
48
+ print("\n2️⃣ Configuring Claude Code (~/.claude.json)...")
49
49
  try:
50
50
  success = self._configure_claude_desktop(args.force)
51
51
  if success:
52
52
  print("✅ Configuration completed successfully")
53
53
  print("\n🎉 MCP Gateway is ready to use!")
54
54
  print("\nNext steps:")
55
- print("1. Restart Claude Desktop")
55
+ print("1. Restart Claude Code (if running)")
56
56
  print("2. Test the server: claude-mpm mcp server --test")
57
57
  print("3. Check status: claude-mpm mcp status")
58
58
  return 0
@@ -65,7 +65,11 @@ class MCPInstallCommands:
65
65
  return 1
66
66
 
67
67
  def _configure_claude_desktop(self, force=False):
68
- """Configure Claude Desktop to use the MCP gateway via CLI command.
68
+ """Configure Claude Code to use the MCP gateway via CLI command.
69
+
70
+ WHY: Claude Code reads MCP server configurations from ~/.claude.json
71
+ (not ~/.claude/settings.local.json). This method updates that file
72
+ to include the claude-mpm-gateway server configuration.
69
73
 
70
74
  Args:
71
75
  force: Whether to overwrite existing configuration
@@ -78,10 +82,10 @@ class MCPInstallCommands:
78
82
  from pathlib import Path
79
83
  from datetime import datetime
80
84
 
81
- # Determine Claude Desktop config path based on platform
85
+ # Determine Claude Code config path
82
86
  config_path = self._get_claude_config_path()
83
87
  if not config_path:
84
- print("❌ Could not determine Claude Desktop configuration path")
88
+ print("❌ Could not determine Claude Code configuration path")
85
89
  return False
86
90
 
87
91
  print(f" Configuration path: {config_path}")
@@ -97,14 +101,27 @@ class MCPInstallCommands:
97
101
  print("❌ Could not find claude-mpm executable")
98
102
  return False
99
103
 
100
- mcp_config = {
101
- "command": claude_mpm_path,
102
- "args": ["mcp", "server"],
103
- "env": {
104
- "PYTHONPATH": str(Path(__file__).parent.parent.parent.parent),
105
- "MCP_MODE": "production"
104
+ # Determine if we need to use -m claude_mpm or direct command
105
+ if claude_mpm_path.endswith(('python', 'python3', 'python.exe', 'python3.exe')):
106
+ # Using Python interpreter directly
107
+ mcp_config = {
108
+ "command": claude_mpm_path,
109
+ "args": ["-m", "claude_mpm", "mcp", "server"],
110
+ "env": {
111
+ "PYTHONPATH": str(Path(__file__).parent.parent.parent.parent),
112
+ "MCP_MODE": "production"
113
+ }
114
+ }
115
+ else:
116
+ # Using installed claude-mpm command
117
+ mcp_config = {
118
+ "command": claude_mpm_path,
119
+ "args": ["mcp", "server"],
120
+ "env": {
121
+ "PYTHONPATH": str(Path(__file__).parent.parent.parent.parent),
122
+ "MCP_MODE": "production"
123
+ }
106
124
  }
107
- }
108
125
 
109
126
  # Update configuration
110
127
  if "mcpServers" not in config:
@@ -121,53 +138,68 @@ class MCPInstallCommands:
121
138
  return self._save_config(config, config_path)
122
139
 
123
140
  def _get_claude_config_path(self):
124
- """Get the Claude Desktop configuration file path based on platform.
141
+ """Get the Claude Code configuration file path.
125
142
 
126
143
  Returns:
127
- Path or None: Path to Claude Desktop config file
144
+ Path or None: Path to Claude Code config file
128
145
  """
129
- import platform
130
146
  from pathlib import Path
131
147
 
132
- system = platform.system()
133
-
134
- if system == "Darwin": # macOS
135
- return Path.home() / "Library" / "Application Support" / "Claude" / "claude_desktop_config.json"
136
- elif system == "Windows":
137
- return Path.home() / "AppData" / "Roaming" / "Claude" / "claude_desktop_config.json"
138
- elif system == "Linux":
139
- return Path.home() / ".config" / "Claude" / "claude_desktop_config.json"
140
- else:
141
- print(f"❌ Unsupported platform: {system}")
142
- return None
148
+ # Claude Code reads MCP server configurations from ~/.claude.json
149
+ # This is the actual file that Claude Code uses for MCP servers
150
+ # NOT ~/.claude/settings.local.json
151
+ return Path.home() / ".claude.json"
143
152
 
144
153
  def _find_claude_mpm_executable(self):
145
154
  """Find the claude-mpm executable path.
146
155
 
156
+ WHY: We need to find the installed claude-mpm command to use as the
157
+ MCP server command. This ensures we're using the properly installed
158
+ version with all dependencies, not a raw Python script.
159
+
160
+ DESIGN DECISION: We prioritize in this order:
161
+ 1. System-installed claude-mpm (most reliable)
162
+ 2. Virtual environment claude-mpm (development)
163
+ 3. Python module invocation (fallback)
164
+
147
165
  Returns:
148
166
  str or None: Path to claude-mpm executable
149
167
  """
150
168
  import shutil
151
169
  import sys
170
+ import os
152
171
 
153
- # Try to find claude-mpm in PATH
172
+ # 1. Try to find claude-mpm in PATH (system-wide or venv)
154
173
  claude_mpm_path = shutil.which("claude-mpm")
155
174
  if claude_mpm_path:
175
+ print(f" Found claude-mpm: {claude_mpm_path}")
156
176
  return claude_mpm_path
157
177
 
158
- # If not in PATH, try using python -m claude_mpm
159
- # This works if claude-mpm is installed in the current Python environment
178
+ # 2. Check if we're in a virtual environment
179
+ if hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix):
180
+ # We're in a virtual environment
181
+ venv_bin = Path(sys.prefix) / ("Scripts" if sys.platform == "win32" else "bin")
182
+ venv_claude_mpm = venv_bin / "claude-mpm"
183
+ if venv_claude_mpm.exists():
184
+ print(f" Found claude-mpm in venv: {venv_claude_mpm}")
185
+ return str(venv_claude_mpm)
186
+
187
+ # 3. Check if claude_mpm module is installed and use Python to run it
160
188
  try:
161
189
  import claude_mpm
162
- return f"{sys.executable} -m claude_mpm"
190
+ # Return the Python executable - we'll handle the -m args separately
191
+ print(f" Using Python module: {sys.executable} -m claude_mpm")
192
+ return sys.executable
163
193
  except ImportError:
164
194
  pass
165
195
 
166
- # Last resort: try relative to current script
196
+ # 4. Last resort: check project's local venv (development mode)
167
197
  project_root = Path(__file__).parent.parent.parent.parent.parent
168
- local_script = project_root / "scripts" / "claude-mpm"
169
- if local_script.exists():
170
- return str(local_script)
198
+ local_venv_bin = project_root / "venv" / ("Scripts" if sys.platform == "win32" else "bin")
199
+ local_claude_mpm = local_venv_bin / "claude-mpm"
200
+ if local_claude_mpm.exists():
201
+ print(f" Found claude-mpm in project venv: {local_claude_mpm}")
202
+ return str(local_claude_mpm)
171
203
 
172
204
  return None
173
205
 
@@ -6,7 +6,7 @@ Extracted from mcp.py to reduce complexity and improve maintainability.
6
6
 
7
7
  import asyncio
8
8
  import os
9
- import subprocess
9
+ import shutil
10
10
  import sys
11
11
  from pathlib import Path
12
12
 
@@ -25,8 +25,8 @@ class MCPServerCommands:
25
25
  implementation that Claude Desktop can communicate with.
26
26
  NOTE: MCP is for Claude Desktop's Code features.
27
27
 
28
- DESIGN DECISION: We now use the wrapper script to ensure proper
29
- environment setup regardless of how the server is invoked.
28
+ DESIGN DECISION: Run the server directly in the same process to ensure
29
+ Claude Desktop sees the correct command path, not a wrapper script.
30
30
  """
31
31
  self.logger.info("MCP server start command called")
32
32
 
@@ -54,22 +54,28 @@ class MCPServerCommands:
54
54
 
55
55
  # Find project root for paths
56
56
  project_root = Path(__file__).parent.parent.parent.parent.parent
57
- wrapper_path = project_root / "scripts" / "mcp_wrapper.py"
57
+
58
+ # Use the direct command, not the wrapper
59
+ import shutil
60
+ claude_mpm_path = shutil.which("claude-mpm")
61
+ if not claude_mpm_path:
62
+ # Fallback to current executable
63
+ claude_mpm_path = sys.executable.replace("python", "claude-mpm")
58
64
 
59
65
  print("\n Add this to your Claude Desktop configuration:")
60
66
  print(" (~/Library/Application Support/Claude/claude_desktop_config.json on macOS)")
61
67
  print("\n {")
62
68
  print(' "mcpServers": {')
63
69
  print(' "claude-mpm-gateway": {')
64
- print(f' "command": "{sys.executable}",')
65
- print(f' "args": ["{wrapper_path}"],')
70
+ print(f' "command": "{claude_mpm_path}",')
71
+ print(f' "args": ["mcp", "server"],')
66
72
  print(f' "cwd": "{project_root}"')
67
73
  print(' }')
68
74
  print(' }')
69
75
  print(' }')
70
76
  print("\n3. Restart Claude Desktop to load the MCP server")
71
77
  print("\nTo test the server directly:")
72
- print(" python scripts/mcp_wrapper.py")
78
+ print(" claude-mpm mcp server")
73
79
  print("\nTo check running MCP processes:")
74
80
  print(" python scripts/check_mcp_processes.py")
75
81
  print("\nFor more information, see:")
@@ -77,46 +83,35 @@ class MCPServerCommands:
77
83
 
78
84
  return 0
79
85
 
80
- # Default behavior: Use the wrapper script for proper environment setup
86
+ # Default behavior: Run the server directly in this process
81
87
  if test_mode:
82
- print("🧪 Starting MCP server in test mode...")
83
- print(" This will run the server with stdio communication.")
84
- print(" Press Ctrl+C to stop.\n")
88
+ print("🧪 Starting MCP server in test mode...", file=sys.stderr)
89
+ print(" This will run the server with stdio communication.", file=sys.stderr)
90
+ print(" Press Ctrl+C to stop.\n", file=sys.stderr)
85
91
 
86
92
  try:
87
- # Instead of running directly, we should use the wrapper script
88
- # for consistent environment setup
89
- import subprocess
90
- from pathlib import Path
91
-
92
- # Find the wrapper script
93
- project_root = Path(__file__).parent.parent.parent.parent.parent
94
- wrapper_script = project_root / "scripts" / "mcp_wrapper.py"
93
+ # Import and run the server directly
94
+ from claude_mpm.services.mcp_gateway.server.stdio_server import SimpleMCPServer
95
95
 
96
- if not wrapper_script.exists():
97
- print(f"❌ Error: Wrapper script not found at {wrapper_script}", file=sys.stderr)
98
- print("\nPlease ensure the wrapper script is installed.", file=sys.stderr)
99
- return 1
96
+ # Set environment variable if in test mode
97
+ if test_mode:
98
+ os.environ["MCP_MODE"] = "test"
99
+ else:
100
+ os.environ["MCP_MODE"] = "production"
100
101
 
101
- # Run the wrapper script
102
- print(f"Starting MCP server via wrapper: {wrapper_script}", file=sys.stderr)
102
+ # Create and run the server
103
+ self.logger.info("Starting MCP Gateway Server directly...")
104
+ server = SimpleMCPServer(name="claude-mpm-gateway", version="1.0.0")
103
105
 
104
- # Use subprocess to run the wrapper
105
- # This ensures proper environment setup
106
- result = subprocess.run(
107
- [sys.executable, str(wrapper_script)],
108
- cwd=str(project_root),
109
- env={**os.environ, "MCP_MODE": "test" if test_mode else "production"}
110
- )
106
+ # Run the server asynchronously
107
+ await server.run()
111
108
 
112
- return result.returncode
109
+ return 0
113
110
 
114
111
  except ImportError as e:
115
112
  self.logger.error(f"Failed to import MCP server: {e}")
116
113
  # Don't print to stdout as it would interfere with JSON-RPC protocol
117
114
  # Log to stderr instead
118
- import sys
119
-
120
115
  print(
121
116
  f"❌ Error: Could not import MCP server components: {e}", file=sys.stderr
122
117
  )
@@ -129,8 +124,6 @@ class MCPServerCommands:
129
124
  return 0
130
125
  except Exception as e:
131
126
  self.logger.error(f"Server error: {e}")
132
- import sys
133
-
134
127
  print(f"❌ Error running server: {e}", file=sys.stderr)
135
128
  return 1
136
129