claude-mpm 4.0.22__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.
- claude_mpm/BUILD_NUMBER +1 -1
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +4 -1
- claude_mpm/agents/BASE_PM.md +3 -0
- claude_mpm/agents/templates/code_analyzer.json +3 -3
- claude_mpm/agents/templates/data_engineer.json +2 -2
- claude_mpm/agents/templates/documentation.json +36 -9
- claude_mpm/agents/templates/engineer.json +2 -2
- claude_mpm/agents/templates/ops.json +2 -2
- claude_mpm/agents/templates/qa.json +2 -2
- claude_mpm/agents/templates/refactoring_engineer.json +65 -43
- claude_mpm/agents/templates/security.json +2 -2
- claude_mpm/agents/templates/version_control.json +2 -2
- claude_mpm/agents/templates/web_ui.json +2 -2
- claude_mpm/cli/commands/agents.py +453 -113
- claude_mpm/cli/commands/aggregate.py +107 -15
- claude_mpm/cli/commands/cleanup.py +142 -10
- claude_mpm/cli/commands/config.py +358 -224
- claude_mpm/cli/commands/info.py +184 -75
- claude_mpm/cli/commands/mcp_command_router.py +5 -76
- claude_mpm/cli/commands/mcp_install_commands.py +68 -36
- claude_mpm/cli/commands/mcp_server_commands.py +30 -37
- claude_mpm/cli/commands/memory.py +331 -61
- claude_mpm/cli/commands/monitor.py +101 -7
- claude_mpm/cli/commands/run.py +368 -8
- claude_mpm/cli/commands/tickets.py +206 -24
- claude_mpm/cli/parsers/mcp_parser.py +3 -0
- claude_mpm/cli/shared/__init__.py +40 -0
- claude_mpm/cli/shared/argument_patterns.py +212 -0
- claude_mpm/cli/shared/command_base.py +234 -0
- claude_mpm/cli/shared/error_handling.py +238 -0
- claude_mpm/cli/shared/output_formatters.py +231 -0
- claude_mpm/config/agent_config.py +29 -8
- claude_mpm/core/container.py +6 -4
- claude_mpm/core/framework_loader.py +32 -9
- claude_mpm/core/service_registry.py +4 -2
- claude_mpm/core/shared/__init__.py +17 -0
- claude_mpm/core/shared/config_loader.py +320 -0
- claude_mpm/core/shared/path_resolver.py +277 -0
- claude_mpm/core/shared/singleton_manager.py +208 -0
- claude_mpm/hooks/claude_hooks/memory_integration.py +4 -2
- claude_mpm/hooks/claude_hooks/response_tracking.py +14 -3
- claude_mpm/hooks/memory_integration_hook.py +11 -2
- claude_mpm/services/agents/deployment/agent_deployment.py +43 -23
- claude_mpm/services/agents/deployment/deployment_wrapper.py +71 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +1 -0
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +43 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +4 -0
- claude_mpm/services/agents/deployment/processors/agent_processor.py +1 -1
- claude_mpm/services/agents/loading/base_agent_manager.py +11 -3
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +14 -5
- claude_mpm/services/event_aggregator.py +4 -2
- claude_mpm/services/mcp_gateway/config/config_loader.py +89 -28
- claude_mpm/services/mcp_gateway/config/configuration.py +29 -0
- claude_mpm/services/mcp_gateway/registry/service_registry.py +22 -5
- claude_mpm/services/memory/builder.py +6 -1
- claude_mpm/services/response_tracker.py +3 -1
- claude_mpm/services/runner_configuration_service.py +15 -6
- claude_mpm/services/shared/__init__.py +20 -0
- claude_mpm/services/shared/async_service_base.py +219 -0
- claude_mpm/services/shared/config_service_base.py +292 -0
- claude_mpm/services/shared/lifecycle_service_base.py +317 -0
- claude_mpm/services/shared/manager_base.py +303 -0
- claude_mpm/services/shared/service_factory.py +308 -0
- {claude_mpm-4.0.22.dist-info → claude_mpm-4.0.25.dist-info}/METADATA +19 -13
- {claude_mpm-4.0.22.dist-info → claude_mpm-4.0.25.dist-info}/RECORD +70 -54
- {claude_mpm-4.0.22.dist-info → claude_mpm-4.0.25.dist-info}/WHEEL +0 -0
- {claude_mpm-4.0.22.dist-info → claude_mpm-4.0.25.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.0.22.dist-info → claude_mpm-4.0.25.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.0.22.dist-info → claude_mpm-4.0.25.dist-info}/top_level.txt +0 -0
claude_mpm/cli/commands/info.py
CHANGED
|
@@ -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
|
|
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
|
-
|
|
188
|
+
Main entry point for info command.
|
|
18
189
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
190
|
+
This function maintains backward compatibility while using the new BaseCommand pattern.
|
|
191
|
+
"""
|
|
192
|
+
command = InfoCommand()
|
|
193
|
+
result = command.execute(args)
|
|
22
194
|
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
|
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
|
|
48
|
-
print("\n2️⃣ Configuring Claude
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
"
|
|
105
|
-
"
|
|
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
|
|
141
|
+
"""Get the Claude Code configuration file path.
|
|
125
142
|
|
|
126
143
|
Returns:
|
|
127
|
-
Path or None: Path to Claude
|
|
144
|
+
Path or None: Path to Claude Code config file
|
|
128
145
|
"""
|
|
129
|
-
import platform
|
|
130
146
|
from pathlib import Path
|
|
131
147
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
#
|
|
159
|
-
|
|
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
|
-
|
|
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:
|
|
196
|
+
# 4. Last resort: check project's local venv (development mode)
|
|
167
197
|
project_root = Path(__file__).parent.parent.parent.parent.parent
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
|
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:
|
|
29
|
-
|
|
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
|
-
|
|
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": "{
|
|
65
|
-
print(f' "args": ["
|
|
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("
|
|
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:
|
|
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
|
-
#
|
|
88
|
-
|
|
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
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
#
|
|
102
|
-
|
|
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
|
-
#
|
|
105
|
-
|
|
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
|
|
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
|
|