claude-mpm 4.4.9__py3-none-any.whl → 4.4.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/PM_INSTRUCTIONS.md +34 -0
- claude_mpm/scripts/mpm_doctor.py +2 -2
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +27 -0
- claude_mpm/services/mcp_config_manager.py +402 -26
- {claude_mpm-4.4.9.dist-info → claude_mpm-4.4.11.dist-info}/METADATA +1 -1
- {claude_mpm-4.4.9.dist-info → claude_mpm-4.4.11.dist-info}/RECORD +11 -11
- {claude_mpm-4.4.9.dist-info → claude_mpm-4.4.11.dist-info}/entry_points.txt +1 -0
- {claude_mpm-4.4.9.dist-info → claude_mpm-4.4.11.dist-info}/WHEEL +0 -0
- {claude_mpm-4.4.9.dist-info → claude_mpm-4.4.11.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.4.9.dist-info → claude_mpm-4.4.11.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
4.4.
|
1
|
+
4.4.11
|
@@ -67,12 +67,45 @@
|
|
67
67
|
✓ TodoWrite - For tracking delegated work
|
68
68
|
✓ Read - ONLY for reading ONE file maximum (more = violation)
|
69
69
|
✓ Bash - ONLY for `ls`, `pwd` (NOT for investigation)
|
70
|
+
✓ SlashCommand - For executing Claude MPM commands (see MPM Commands section below)
|
70
71
|
✓ mcp__mcp-vector-search__* - For quick code search BEFORE delegation (helps better task definition)
|
71
72
|
❌ Grep/Glob - FORBIDDEN for PM (delegate to Research for deep investigation)
|
72
73
|
❌ WebSearch/WebFetch - FORBIDDEN for PM (delegate to Research)
|
73
74
|
|
74
75
|
**VIOLATION TRACKING ACTIVE**: Each violation logged, escalated, and reported.
|
75
76
|
|
77
|
+
## CLAUDE MPM SLASH COMMANDS
|
78
|
+
|
79
|
+
**IMPORTANT**: Claude MPM has special slash commands that are NOT file paths. These are framework commands that must be executed using the SlashCommand tool.
|
80
|
+
|
81
|
+
### Common MPM Commands
|
82
|
+
These commands start with `/mpm-` and are Claude MPM system commands:
|
83
|
+
- `/mpm-doctor` - Run system diagnostics (use SlashCommand tool)
|
84
|
+
- `/mpm-init` - Initialize MPM project (use SlashCommand tool)
|
85
|
+
- `/mpm-status` - Check MPM service status (use SlashCommand tool)
|
86
|
+
- `/mpm-monitor` - Control monitoring services (use SlashCommand tool)
|
87
|
+
- `/mpm-browser-monitor` - Control browser monitoring (use SlashCommand tool)
|
88
|
+
|
89
|
+
### How to Execute MPM Commands
|
90
|
+
✅ **CORRECT**: Use SlashCommand tool
|
91
|
+
```
|
92
|
+
SlashCommand: command="/mpm-doctor"
|
93
|
+
SlashCommand: command="/mpm-monitor start"
|
94
|
+
```
|
95
|
+
|
96
|
+
❌ **WRONG**: Treating as file paths or bash commands
|
97
|
+
```
|
98
|
+
Bash: ./mpm-doctor # WRONG - not a file
|
99
|
+
Bash: /mpm-doctor # WRONG - not a file path
|
100
|
+
Read: /mpm-doctor # WRONG - not a file to read
|
101
|
+
```
|
102
|
+
|
103
|
+
### Recognition Rules
|
104
|
+
- If user mentions `/mpm-*` → It's a Claude MPM command → Use SlashCommand
|
105
|
+
- If command starts with slash and is NOT a file path → Check if it's an MPM command
|
106
|
+
- MPM commands are system operations, NOT files or scripts
|
107
|
+
- Always use SlashCommand tool for these operations
|
108
|
+
|
76
109
|
## NO ASSERTION WITHOUT VERIFICATION RULE
|
77
110
|
|
78
111
|
**CRITICAL**: PM MUST NEVER make claims without evidence from agents.
|
@@ -139,6 +172,7 @@
|
|
139
172
|
| "what is", "how does", "where is" | "I'll have Research investigate" | Research |
|
140
173
|
| "error", "bug", "issue" | "I'll have QA reproduce this" | QA |
|
141
174
|
| "slow", "performance" | "I'll have QA benchmark this" | QA |
|
175
|
+
| "/mpm-doctor", "/mpm-status", etc | "I'll run the MPM command" | Use SlashCommand tool (NOT bash) |
|
142
176
|
| ANY question about code | "I'll have Research examine this" | Research |
|
143
177
|
|
144
178
|
### 🔴 CIRCUIT BREAKER - IMPLEMENTATION DETECTION 🔴
|
claude_mpm/scripts/mpm_doctor.py
CHANGED
@@ -51,9 +51,9 @@ def run_diagnostics(
|
|
51
51
|
- warning_count: int - number of warnings found
|
52
52
|
- message: str - optional error message if failed
|
53
53
|
"""
|
54
|
-
from claude_mpm.core.logging_utils import get_logger
|
54
|
+
from claude_mpm.core.logging_utils import get_logger
|
55
55
|
|
56
|
-
logger = get_logger(__name__)
|
56
|
+
logger = get_logger(__name__)
|
57
57
|
|
58
58
|
# Create diagnostic runner
|
59
59
|
runner = DiagnosticRunner(verbose=verbose, fix=fix)
|
@@ -84,6 +84,33 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
84
84
|
sub_results = []
|
85
85
|
services_status = {}
|
86
86
|
|
87
|
+
# Use MCPConfigManager to detect and fix corrupted installations
|
88
|
+
from claude_mpm.services.mcp_config_manager import MCPConfigManager
|
89
|
+
mcp_manager = MCPConfigManager()
|
90
|
+
|
91
|
+
# Run comprehensive fix for all MCP service issues
|
92
|
+
fix_success, fix_message = mcp_manager.fix_mcp_service_issues()
|
93
|
+
if fix_message and fix_message != "All MCP services are functioning correctly":
|
94
|
+
# Create diagnostic result for the fixes
|
95
|
+
fix_result = DiagnosticResult(
|
96
|
+
category="MCP Service Fixes",
|
97
|
+
status=DiagnosticStatus.OK if fix_success else DiagnosticStatus.WARNING,
|
98
|
+
message=fix_message,
|
99
|
+
details={"auto_fix_applied": True}
|
100
|
+
)
|
101
|
+
sub_results.append(fix_result)
|
102
|
+
|
103
|
+
# Also ensure configurations are updated for all projects
|
104
|
+
config_success, config_message = mcp_manager.ensure_mcp_services_configured()
|
105
|
+
if config_message and config_message != "All MCP services already configured correctly":
|
106
|
+
config_result = DiagnosticResult(
|
107
|
+
category="MCP Configuration Update",
|
108
|
+
status=DiagnosticStatus.OK if config_success else DiagnosticStatus.WARNING,
|
109
|
+
message=config_message,
|
110
|
+
details={"auto_config_applied": True}
|
111
|
+
)
|
112
|
+
sub_results.append(config_result)
|
113
|
+
|
87
114
|
# Check for kuzu-memory configuration issues and offer auto-fix
|
88
115
|
kuzu_config_result = self._check_and_fix_kuzu_memory_config()
|
89
116
|
if kuzu_config_result:
|
@@ -46,24 +46,25 @@ class MCPConfigManager:
|
|
46
46
|
STATIC_MCP_CONFIGS = {
|
47
47
|
"kuzu-memory": {
|
48
48
|
"type": "stdio",
|
49
|
-
"command": "
|
50
|
-
"args": ["
|
49
|
+
"command": "kuzu-memory", # Use direct binary, will be resolved to full path
|
50
|
+
"args": ["mcp", "serve"] # v1.1.0+ uses 'mcp serve' command
|
51
51
|
},
|
52
52
|
"mcp-ticketer": {
|
53
53
|
"type": "stdio",
|
54
|
-
"command": "
|
55
|
-
"args": ["
|
54
|
+
"command": "mcp-ticketer", # Use direct binary to preserve injected dependencies
|
55
|
+
"args": ["mcp"]
|
56
56
|
},
|
57
57
|
"mcp-browser": {
|
58
58
|
"type": "stdio",
|
59
|
-
"command": "
|
60
|
-
"args": ["
|
59
|
+
"command": "mcp-browser", # Use direct binary
|
60
|
+
"args": ["mcp"],
|
61
61
|
"env": {"MCP_BROWSER_HOME": str(Path.home() / ".mcp-browser")}
|
62
62
|
},
|
63
63
|
"mcp-vector-search": {
|
64
64
|
"type": "stdio",
|
65
|
-
|
66
|
-
"
|
65
|
+
# Use pipx venv's Python directly for module execution
|
66
|
+
"command": str(Path.home() / ".local" / "pipx" / "venvs" / "mcp-vector-search" / "bin" / "python"),
|
67
|
+
"args": ["-m", "mcp_vector_search.mcp.server", "{project_root}"],
|
67
68
|
"env": {}
|
68
69
|
}
|
69
70
|
}
|
@@ -222,12 +223,95 @@ class MCPConfigManager:
|
|
222
223
|
|
223
224
|
return None
|
224
225
|
|
225
|
-
def
|
226
|
+
def test_service_command(self, service_name: str, config: Dict) -> bool:
|
227
|
+
"""
|
228
|
+
Test if a service configuration actually works.
|
229
|
+
|
230
|
+
Args:
|
231
|
+
service_name: Name of the MCP service
|
232
|
+
config: Service configuration to test
|
233
|
+
|
234
|
+
Returns:
|
235
|
+
True if service responds correctly, False otherwise
|
236
|
+
"""
|
237
|
+
try:
|
238
|
+
import shutil
|
239
|
+
|
240
|
+
# Build command - handle pipx PATH issues
|
241
|
+
command = config["command"]
|
242
|
+
|
243
|
+
# If command is pipx and not found, try common paths
|
244
|
+
if command == "pipx":
|
245
|
+
pipx_path = shutil.which("pipx")
|
246
|
+
if not pipx_path:
|
247
|
+
# Try common pipx locations
|
248
|
+
for possible_path in [
|
249
|
+
"/opt/homebrew/bin/pipx",
|
250
|
+
"/usr/local/bin/pipx",
|
251
|
+
str(Path.home() / ".local" / "bin" / "pipx"),
|
252
|
+
]:
|
253
|
+
if Path(possible_path).exists():
|
254
|
+
command = possible_path
|
255
|
+
break
|
256
|
+
else:
|
257
|
+
command = pipx_path
|
258
|
+
|
259
|
+
cmd = [command]
|
260
|
+
|
261
|
+
# Add test args (--help or --version)
|
262
|
+
if "args" in config:
|
263
|
+
# For MCP services, test with --help after the subcommand
|
264
|
+
test_args = config["args"].copy()
|
265
|
+
# Replace project root placeholder for testing
|
266
|
+
test_args = [
|
267
|
+
arg.replace("{project_root}", str(self.project_root)) if "{project_root}" in arg else arg
|
268
|
+
for arg in test_args
|
269
|
+
]
|
270
|
+
|
271
|
+
# Add --help at the end
|
272
|
+
if service_name == "mcp-vector-search":
|
273
|
+
# For Python module invocation, just test if Python can import the module
|
274
|
+
cmd.extend(test_args[:2]) # Just python -m module_name
|
275
|
+
cmd.extend(["--help"])
|
276
|
+
else:
|
277
|
+
cmd.extend(test_args)
|
278
|
+
cmd.append("--help")
|
279
|
+
else:
|
280
|
+
cmd.append("--help")
|
281
|
+
|
282
|
+
# Run test command with timeout
|
283
|
+
result = subprocess.run(
|
284
|
+
cmd,
|
285
|
+
capture_output=True,
|
286
|
+
text=True,
|
287
|
+
timeout=5,
|
288
|
+
check=False,
|
289
|
+
env=config.get("env", {})
|
290
|
+
)
|
291
|
+
|
292
|
+
# Check if command executed (exit code 0 or 1 for help)
|
293
|
+
if result.returncode in [0, 1]:
|
294
|
+
# Additional check for import errors in stderr
|
295
|
+
if "ModuleNotFoundError" in result.stderr or "ImportError" in result.stderr:
|
296
|
+
self.logger.debug(f"Service {service_name} has import errors")
|
297
|
+
return False
|
298
|
+
return True
|
299
|
+
|
300
|
+
except subprocess.TimeoutExpired:
|
301
|
+
# Timeout might mean the service started successfully and is waiting for input
|
302
|
+
return True
|
303
|
+
except Exception as e:
|
304
|
+
self.logger.debug(f"Error testing {service_name}: {e}")
|
305
|
+
|
306
|
+
return False
|
307
|
+
|
308
|
+
def get_static_service_config(self, service_name: str, project_path: Optional[str] = None) -> Optional[Dict]:
|
226
309
|
"""
|
227
310
|
Get the static, known-good configuration for an MCP service.
|
228
311
|
|
229
312
|
Args:
|
230
313
|
service_name: Name of the MCP service
|
314
|
+
project_path: Optional project path to use (defaults to current project)
|
231
315
|
|
232
316
|
Returns:
|
233
317
|
Static service configuration dict or None if service not known
|
@@ -236,11 +320,79 @@ class MCPConfigManager:
|
|
236
320
|
return None
|
237
321
|
|
238
322
|
config = self.STATIC_MCP_CONFIGS[service_name].copy()
|
323
|
+
import shutil
|
324
|
+
|
325
|
+
# Resolve service binary commands to full paths
|
326
|
+
if service_name in ["kuzu-memory", "mcp-ticketer", "mcp-browser"]:
|
327
|
+
# Try to find the full path of the binary
|
328
|
+
binary_name = config["command"]
|
329
|
+
binary_path = shutil.which(binary_name)
|
330
|
+
|
331
|
+
if not binary_path:
|
332
|
+
# Try common installation locations
|
333
|
+
possible_paths = [
|
334
|
+
f"/opt/homebrew/bin/{binary_name}",
|
335
|
+
f"/usr/local/bin/{binary_name}",
|
336
|
+
str(Path.home() / ".local" / "bin" / binary_name),
|
337
|
+
]
|
338
|
+
for path in possible_paths:
|
339
|
+
if Path(path).exists():
|
340
|
+
binary_path = path
|
341
|
+
break
|
342
|
+
|
343
|
+
if binary_path:
|
344
|
+
config["command"] = binary_path
|
345
|
+
# If still not found, keep the binary name and hope it's in PATH
|
346
|
+
|
347
|
+
# Resolve pipx command to full path if needed
|
348
|
+
elif config.get("command") == "pipx":
|
349
|
+
pipx_path = shutil.which("pipx")
|
350
|
+
if not pipx_path:
|
351
|
+
# Try common pipx locations
|
352
|
+
for possible_path in [
|
353
|
+
"/opt/homebrew/bin/pipx",
|
354
|
+
"/usr/local/bin/pipx",
|
355
|
+
str(Path.home() / ".local" / "bin" / "pipx"),
|
356
|
+
]:
|
357
|
+
if Path(possible_path).exists():
|
358
|
+
pipx_path = possible_path
|
359
|
+
break
|
360
|
+
if pipx_path:
|
361
|
+
config["command"] = pipx_path
|
362
|
+
else:
|
363
|
+
# Keep as "pipx" and hope it's in PATH when executed
|
364
|
+
config["command"] = "pipx"
|
239
365
|
|
240
|
-
#
|
366
|
+
# Handle user-specific paths for mcp-vector-search
|
241
367
|
if service_name == "mcp-vector-search":
|
368
|
+
# Get the correct pipx venv path for the current user
|
369
|
+
home = Path.home()
|
370
|
+
python_path = home / ".local" / "pipx" / "venvs" / "mcp-vector-search" / "bin" / "python"
|
371
|
+
|
372
|
+
# Check if the Python interpreter exists, if not fallback to pipx run
|
373
|
+
if python_path.exists():
|
374
|
+
config["command"] = str(python_path)
|
375
|
+
else:
|
376
|
+
# Fallback to pipx run method
|
377
|
+
import shutil
|
378
|
+
pipx_path = shutil.which("pipx")
|
379
|
+
if not pipx_path:
|
380
|
+
# Try common pipx locations
|
381
|
+
for possible_path in [
|
382
|
+
"/opt/homebrew/bin/pipx",
|
383
|
+
"/usr/local/bin/pipx",
|
384
|
+
str(Path.home() / ".local" / "bin" / "pipx"),
|
385
|
+
]:
|
386
|
+
if Path(possible_path).exists():
|
387
|
+
pipx_path = possible_path
|
388
|
+
break
|
389
|
+
config["command"] = pipx_path if pipx_path else "pipx"
|
390
|
+
config["args"] = ["run", "--spec", "mcp-vector-search", "python"] + config["args"]
|
391
|
+
|
392
|
+
# Use provided project path or current project
|
393
|
+
project_root = project_path if project_path else str(self.project_root)
|
242
394
|
config["args"] = [
|
243
|
-
arg.replace("{project_root}",
|
395
|
+
arg.replace("{project_root}", project_root) if "{project_root}" in arg else arg
|
244
396
|
for arg in config["args"]
|
245
397
|
]
|
246
398
|
|
@@ -262,7 +414,12 @@ class MCPConfigManager:
|
|
262
414
|
# First try to get static configuration
|
263
415
|
static_config = self.get_static_service_config(service_name)
|
264
416
|
if static_config:
|
265
|
-
|
417
|
+
# Validate that the static config actually works
|
418
|
+
if self.test_service_command(service_name, static_config):
|
419
|
+
self.logger.debug(f"Static config for {service_name} validated successfully")
|
420
|
+
return static_config
|
421
|
+
else:
|
422
|
+
self.logger.warning(f"Static config for {service_name} failed validation, trying fallback")
|
266
423
|
|
267
424
|
# Fall back to detection-based configuration for unknown services
|
268
425
|
import shutil
|
@@ -460,11 +617,10 @@ class MCPConfigManager:
|
|
460
617
|
claude_config["projects"] = {}
|
461
618
|
updated = True
|
462
619
|
|
463
|
-
#
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
mcp_ticketer_fixed = self._check_and_fix_mcp_ticketer_dependencies()
|
620
|
+
# Fix any corrupted MCP service installations first
|
621
|
+
fix_success, fix_message = self.fix_mcp_service_issues()
|
622
|
+
if not fix_success:
|
623
|
+
self.logger.warning(f"Some MCP services could not be fixed: {fix_message}")
|
468
624
|
|
469
625
|
# Process ALL projects in the config, not just current one
|
470
626
|
projects_to_update = list(claude_config.get("projects", {}).keys())
|
@@ -499,16 +655,12 @@ class MCPConfigManager:
|
|
499
655
|
|
500
656
|
# Check and fix each service configuration
|
501
657
|
for service_name in self.PIPX_SERVICES:
|
502
|
-
# Get the correct static configuration
|
503
|
-
correct_config = self.
|
658
|
+
# Get the correct static configuration with project-specific paths
|
659
|
+
correct_config = self.get_static_service_config(service_name, project_key)
|
504
660
|
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
correct_config["args"] = [
|
509
|
-
arg.replace("{project_root}", project_key) if "{project_root}" in arg else arg
|
510
|
-
for arg in correct_config["args"]
|
511
|
-
]
|
661
|
+
if not correct_config:
|
662
|
+
self.logger.warning(f"No static config available for {service_name}")
|
663
|
+
continue
|
512
664
|
|
513
665
|
# Check if service exists and has correct configuration
|
514
666
|
existing_config = project_config["mcpServers"].get(service_name)
|
@@ -837,6 +989,207 @@ class MCPConfigManager:
|
|
837
989
|
self.logger.debug(f"Could not check/fix mcp-ticketer dependencies: {e}")
|
838
990
|
return False
|
839
991
|
|
992
|
+
def fix_mcp_service_issues(self) -> Tuple[bool, str]:
|
993
|
+
"""
|
994
|
+
Detect and fix corrupted MCP service installations.
|
995
|
+
|
996
|
+
This method:
|
997
|
+
1. Tests each MCP service for import/execution errors
|
998
|
+
2. Automatically reinstalls corrupted services
|
999
|
+
3. Fixes missing dependencies (like mcp-ticketer's gql)
|
1000
|
+
4. Validates fixes worked
|
1001
|
+
|
1002
|
+
Returns:
|
1003
|
+
Tuple of (success, message)
|
1004
|
+
"""
|
1005
|
+
self.logger.info("🔍 Checking MCP services for issues...")
|
1006
|
+
|
1007
|
+
services_to_fix = []
|
1008
|
+
fixed_services = []
|
1009
|
+
failed_services = []
|
1010
|
+
|
1011
|
+
# Check each service for issues
|
1012
|
+
for service_name in self.PIPX_SERVICES:
|
1013
|
+
issue_type = self._detect_service_issue(service_name)
|
1014
|
+
if issue_type:
|
1015
|
+
services_to_fix.append((service_name, issue_type))
|
1016
|
+
self.logger.debug(f"Found issue with {service_name}: {issue_type}")
|
1017
|
+
|
1018
|
+
if not services_to_fix:
|
1019
|
+
return True, "All MCP services are functioning correctly"
|
1020
|
+
|
1021
|
+
# Fix each problematic service
|
1022
|
+
for service_name, issue_type in services_to_fix:
|
1023
|
+
self.logger.info(f"🔧 Fixing {service_name}: {issue_type}")
|
1024
|
+
|
1025
|
+
if issue_type == "not_installed":
|
1026
|
+
# Install the service
|
1027
|
+
success, method = self._install_service_with_fallback(service_name)
|
1028
|
+
if success:
|
1029
|
+
fixed_services.append(f"{service_name} (installed via {method})")
|
1030
|
+
else:
|
1031
|
+
failed_services.append(f"{service_name} (installation failed)")
|
1032
|
+
|
1033
|
+
elif issue_type == "import_error":
|
1034
|
+
# Reinstall to fix corrupted installation
|
1035
|
+
self.logger.info(f" Reinstalling {service_name} to fix import errors...")
|
1036
|
+
success = self._reinstall_service(service_name)
|
1037
|
+
if success:
|
1038
|
+
# Special handling for mcp-ticketer - inject missing gql dependency
|
1039
|
+
if service_name == "mcp-ticketer":
|
1040
|
+
self._check_and_fix_mcp_ticketer_dependencies()
|
1041
|
+
fixed_services.append(f"{service_name} (reinstalled)")
|
1042
|
+
else:
|
1043
|
+
failed_services.append(f"{service_name} (reinstall failed)")
|
1044
|
+
|
1045
|
+
elif issue_type == "missing_dependency":
|
1046
|
+
# Fix missing dependencies
|
1047
|
+
if service_name == "mcp-ticketer":
|
1048
|
+
if self._check_and_fix_mcp_ticketer_dependencies():
|
1049
|
+
fixed_services.append(f"{service_name} (dependency fixed)")
|
1050
|
+
else:
|
1051
|
+
failed_services.append(f"{service_name} (dependency fix failed)")
|
1052
|
+
else:
|
1053
|
+
failed_services.append(f"{service_name} (unknown dependency issue)")
|
1054
|
+
|
1055
|
+
elif issue_type == "path_issue":
|
1056
|
+
# Path issues are handled by config updates
|
1057
|
+
self.logger.info(f" Path issue for {service_name} will be fixed by config update")
|
1058
|
+
fixed_services.append(f"{service_name} (config updated)")
|
1059
|
+
|
1060
|
+
# Build result message
|
1061
|
+
messages = []
|
1062
|
+
if fixed_services:
|
1063
|
+
messages.append(f"✅ Fixed: {', '.join(fixed_services)}")
|
1064
|
+
if failed_services:
|
1065
|
+
messages.append(f"❌ Failed: {', '.join(failed_services)}")
|
1066
|
+
|
1067
|
+
# Return success if at least some services were fixed
|
1068
|
+
success = len(fixed_services) > 0 or len(failed_services) == 0
|
1069
|
+
message = " | ".join(messages) if messages else "No services needed fixing"
|
1070
|
+
|
1071
|
+
# Provide manual fix instructions if auto-fix failed
|
1072
|
+
if failed_services:
|
1073
|
+
message += "\n\n💡 Manual fix instructions:"
|
1074
|
+
for failed in failed_services:
|
1075
|
+
service = failed.split(" ")[0]
|
1076
|
+
message += f"\n • {service}: pipx uninstall {service} && pipx install {service}"
|
1077
|
+
|
1078
|
+
return success, message
|
1079
|
+
|
1080
|
+
def _detect_service_issue(self, service_name: str) -> Optional[str]:
|
1081
|
+
"""
|
1082
|
+
Detect what type of issue a service has.
|
1083
|
+
|
1084
|
+
Returns:
|
1085
|
+
Issue type: 'not_installed', 'import_error', 'missing_dependency', 'path_issue', or None
|
1086
|
+
"""
|
1087
|
+
import shutil
|
1088
|
+
|
1089
|
+
# First check if pipx is available
|
1090
|
+
if not shutil.which("pipx"):
|
1091
|
+
return "not_installed" # Can't use pipx services without pipx
|
1092
|
+
|
1093
|
+
# Try to run the service with --help to detect issues
|
1094
|
+
try:
|
1095
|
+
# Test with pipx run
|
1096
|
+
result = subprocess.run(
|
1097
|
+
["pipx", "run", service_name, "--help"],
|
1098
|
+
capture_output=True,
|
1099
|
+
text=True,
|
1100
|
+
timeout=10,
|
1101
|
+
check=False
|
1102
|
+
)
|
1103
|
+
|
1104
|
+
# Check for specific error patterns
|
1105
|
+
stderr_lower = result.stderr.lower()
|
1106
|
+
stdout_lower = result.stdout.lower()
|
1107
|
+
combined_output = stderr_lower + stdout_lower
|
1108
|
+
|
1109
|
+
# Not installed
|
1110
|
+
if "no apps associated" in combined_output or "not found" in combined_output:
|
1111
|
+
return "not_installed"
|
1112
|
+
|
1113
|
+
# Import errors (like mcp-ticketer's corrupted state)
|
1114
|
+
if "modulenotfounderror" in combined_output or "importerror" in combined_output:
|
1115
|
+
# Check if it's specifically the gql dependency for mcp-ticketer
|
1116
|
+
if service_name == "mcp-ticketer" and "gql" in combined_output:
|
1117
|
+
return "missing_dependency"
|
1118
|
+
return "import_error"
|
1119
|
+
|
1120
|
+
# Path issues
|
1121
|
+
if "no such file or directory" in combined_output:
|
1122
|
+
return "path_issue"
|
1123
|
+
|
1124
|
+
# If help text appears, service is working
|
1125
|
+
if "usage:" in combined_output or "help" in combined_output or result.returncode in [0, 1]:
|
1126
|
+
return None # Service is working
|
1127
|
+
|
1128
|
+
# Unknown issue
|
1129
|
+
if result.returncode not in [0, 1]:
|
1130
|
+
return "unknown_error"
|
1131
|
+
|
1132
|
+
except subprocess.TimeoutExpired:
|
1133
|
+
# Timeout might mean service is actually working but waiting for input
|
1134
|
+
return None
|
1135
|
+
except Exception as e:
|
1136
|
+
self.logger.debug(f"Error detecting issue for {service_name}: {e}")
|
1137
|
+
return "unknown_error"
|
1138
|
+
|
1139
|
+
return None
|
1140
|
+
|
1141
|
+
def _reinstall_service(self, service_name: str) -> bool:
|
1142
|
+
"""
|
1143
|
+
Reinstall a corrupted MCP service.
|
1144
|
+
|
1145
|
+
Args:
|
1146
|
+
service_name: Name of the service to reinstall
|
1147
|
+
|
1148
|
+
Returns:
|
1149
|
+
True if successful, False otherwise
|
1150
|
+
"""
|
1151
|
+
try:
|
1152
|
+
self.logger.debug(f"Uninstalling {service_name}...")
|
1153
|
+
|
1154
|
+
# First uninstall the corrupted version
|
1155
|
+
uninstall_result = subprocess.run(
|
1156
|
+
["pipx", "uninstall", service_name],
|
1157
|
+
capture_output=True,
|
1158
|
+
text=True,
|
1159
|
+
timeout=30,
|
1160
|
+
check=False
|
1161
|
+
)
|
1162
|
+
|
1163
|
+
# Don't check return code - uninstall might fail if partially corrupted
|
1164
|
+
self.logger.debug(f"Uninstall result: {uninstall_result.returncode}")
|
1165
|
+
|
1166
|
+
# Now reinstall
|
1167
|
+
self.logger.debug(f"Installing fresh {service_name}...")
|
1168
|
+
install_result = subprocess.run(
|
1169
|
+
["pipx", "install", service_name],
|
1170
|
+
capture_output=True,
|
1171
|
+
text=True,
|
1172
|
+
timeout=120,
|
1173
|
+
check=False
|
1174
|
+
)
|
1175
|
+
|
1176
|
+
if install_result.returncode == 0:
|
1177
|
+
# Verify the reinstall worked
|
1178
|
+
issue = self._detect_service_issue(service_name)
|
1179
|
+
if issue is None:
|
1180
|
+
self.logger.info(f"✅ Successfully reinstalled {service_name}")
|
1181
|
+
return True
|
1182
|
+
else:
|
1183
|
+
self.logger.warning(f"Reinstalled {service_name} but still has issue: {issue}")
|
1184
|
+
return False
|
1185
|
+
else:
|
1186
|
+
self.logger.error(f"Failed to reinstall {service_name}: {install_result.stderr}")
|
1187
|
+
return False
|
1188
|
+
|
1189
|
+
except Exception as e:
|
1190
|
+
self.logger.error(f"Error reinstalling {service_name}: {e}")
|
1191
|
+
return False
|
1192
|
+
|
840
1193
|
def _verify_service_installed(self, service_name: str, method: str) -> bool:
|
841
1194
|
"""
|
842
1195
|
Verify that a service was successfully installed and is functional.
|
@@ -912,3 +1265,26 @@ class MCPConfigManager:
|
|
912
1265
|
self.logger.debug(f"Verification error for {service_name}: {e}")
|
913
1266
|
|
914
1267
|
return False
|
1268
|
+
|
1269
|
+
def _get_fallback_config(self, service_name: str, project_path: str) -> Optional[Dict]:
|
1270
|
+
"""
|
1271
|
+
Get a fallback configuration for a service if the primary config fails.
|
1272
|
+
|
1273
|
+
Args:
|
1274
|
+
service_name: Name of the MCP service
|
1275
|
+
project_path: Project path to use
|
1276
|
+
|
1277
|
+
Returns:
|
1278
|
+
Fallback configuration or None
|
1279
|
+
"""
|
1280
|
+
# Special fallback for mcp-vector-search using pipx run
|
1281
|
+
if service_name == "mcp-vector-search":
|
1282
|
+
return {
|
1283
|
+
"type": "stdio",
|
1284
|
+
"command": "pipx",
|
1285
|
+
"args": ["run", "--spec", "mcp-vector-search", "python", "-m", "mcp_vector_search.mcp.server", project_path],
|
1286
|
+
"env": {}
|
1287
|
+
}
|
1288
|
+
|
1289
|
+
# For other services, try pipx run
|
1290
|
+
return None
|
@@ -1,5 +1,5 @@
|
|
1
1
|
claude_mpm/BUILD_NUMBER,sha256=toytnNjkIKPgQaGwDqQdC1rpNTAdSEc6Vja50d7Ovug,4
|
2
|
-
claude_mpm/VERSION,sha256=
|
2
|
+
claude_mpm/VERSION,sha256=wsjLXi1090mCdeXOCOer2wlQEWPDV51wB6VBJvhb3hw,7
|
3
3
|
claude_mpm/__init__.py,sha256=lyTZAYGH4DTaFGLRNWJKk5Q5oTjzN5I6AXmfVX-Jff0,1512
|
4
4
|
claude_mpm/__main__.py,sha256=Ro5UBWBoQaSAIoSqWAr7zkbLyvi4sSy28WShqAhKJG0,723
|
5
5
|
claude_mpm/constants.py,sha256=cChN3myrAcF3jC-6DvHnBFTEnwlDk-TAsIXPvUZr_yw,5953
|
@@ -15,7 +15,7 @@ claude_mpm/agents/BASE_RESEARCH.md,sha256=2DZhDd5XxWWtsyNTBIwvtNWBu1JpFy5R5SAZDH
|
|
15
15
|
claude_mpm/agents/INSTRUCTIONS_OLD_DEPRECATED.md,sha256=zQZhrhVq9NLCtSjVX-aC0xcgueemSuPhKyv0SjEOyIQ,25852
|
16
16
|
claude_mpm/agents/MEMORY.md,sha256=KbRwY_RYdOvTvFC2DD-ATfwjHkQWJ5PIjlGws_7RmjI,3307
|
17
17
|
claude_mpm/agents/OUTPUT_STYLE.md,sha256=IYbo4jmICihrfnChbdrRpwVk4VobCcNyYqZqd53VXMk,533
|
18
|
-
claude_mpm/agents/PM_INSTRUCTIONS.md,sha256=
|
18
|
+
claude_mpm/agents/PM_INSTRUCTIONS.md,sha256=bdf38c2PS0qrXg1wq2lFDKAzkCmUQs1L8uTVwJk_TlY,27438
|
19
19
|
claude_mpm/agents/WORKFLOW.md,sha256=m4hjQNX8tZKNlyVPjvGJJXc-UG3yzkscmu9Nwfcddsw,2373
|
20
20
|
claude_mpm/agents/__init__.py,sha256=jRFxvV_DIZ-NdENa-703Xu3YpwvlQj6yv-mQ6FgmldM,3220
|
21
21
|
claude_mpm/agents/agent-template.yaml,sha256=mRlz5Yd0SmknTeoJWgFkZXzEF5T7OmGBJGs2-KPT93k,1969
|
@@ -398,7 +398,7 @@ claude_mpm/scripts/claude-hook-handler.sh,sha256=xe6dKubrjK1JDO0SJdc1-tA6C7YSb5Y
|
|
398
398
|
claude_mpm/scripts/launch_monitor.py,sha256=Q7hN4Wurw45veLWPSXk0WfvkKxQ1Snz7TjZsV_pNWQc,2418
|
399
399
|
claude_mpm/scripts/mcp_server.py,sha256=_i9ydtI7AcO-Eb7gzbIDbcJY4PKRQRYNobB8eMailI4,2259
|
400
400
|
claude_mpm/scripts/mcp_wrapper.py,sha256=PvfHJShcsQHGJZD-RN3RnwLOzemAKYZ2kW_QfTkGzkk,1105
|
401
|
-
claude_mpm/scripts/mpm_doctor.py,sha256=
|
401
|
+
claude_mpm/scripts/mpm_doctor.py,sha256=98vBiTIB4xpaSgOvTP9N3EU22V2Odxd9DivhQizG0VM,8822
|
402
402
|
claude_mpm/scripts/socketio_daemon.py,sha256=I1n0ny6UZTQWKN-_ZiNKK37jJTRgRESyHDAVvXWK33M,6038
|
403
403
|
claude_mpm/scripts/start_activity_logging.py,sha256=1G9bFYiBSkpxSRXyKht73nL5gH-4zwukLutWWKAipGg,2869
|
404
404
|
claude_mpm/services/__init__.py,sha256=X10comSYoSvoi4jaz9a7ztqcYhCZMX7QmX7nwHGJjCM,7495
|
@@ -411,7 +411,7 @@ claude_mpm/services/event_aggregator.py,sha256=DDcehIZVpiEDzs9o18gDZyvjMBHCq2H8H
|
|
411
411
|
claude_mpm/services/exceptions.py,sha256=5lVZETr_6-xk0ItH7BTfYUiX5RlckS1e8ah_UalYG9c,26475
|
412
412
|
claude_mpm/services/hook_installer_service.py,sha256=z3kKeriEY1Y9bFesuGlHBxhCtc0Wzd3Zv02k2_rEyGo,19727
|
413
413
|
claude_mpm/services/hook_service.py,sha256=rZnMn_4qxX5g9KAn0IQdoG50WmySNfsTmfG0XHuRHXk,15737
|
414
|
-
claude_mpm/services/mcp_config_manager.py,sha256=
|
414
|
+
claude_mpm/services/mcp_config_manager.py,sha256=Rsbk9Ygq7tJWqZI67VnYWSW30_9ptmLjyX7xf-rJcho,50465
|
415
415
|
claude_mpm/services/mcp_service_verifier.py,sha256=ngiegCngX18AFehfyJdvqQAvscoIBvFN_DeOoGTjxj0,25164
|
416
416
|
claude_mpm/services/memory_hook_service.py,sha256=pRlTClkRcw30Jhwbha4BC8IMdzKZxF8aWqf52JlntgY,11600
|
417
417
|
claude_mpm/services/monitor_build_service.py,sha256=8gWR9CaqgXdG6-OjOFXGpk28GCcJTlHhojkUYnMCebI,12160
|
@@ -560,7 +560,7 @@ claude_mpm/services/diagnostics/checks/filesystem_check.py,sha256=V5HoHDYlSuoK2l
|
|
560
560
|
claude_mpm/services/diagnostics/checks/installation_check.py,sha256=WoTt15R8Wg-6k2JZFAtmffFuih1AIyCX71QOHEFH-Ro,19562
|
561
561
|
claude_mpm/services/diagnostics/checks/instructions_check.py,sha256=VbgBorl0RpFvxKQ_SC1gibTmGSiXaKSp-vVZt6hbH1g,16290
|
562
562
|
claude_mpm/services/diagnostics/checks/mcp_check.py,sha256=SftuhP70abopyMD8GlLA_K3XHEYnBAeITggUQI0cYP4,12173
|
563
|
-
claude_mpm/services/diagnostics/checks/mcp_services_check.py,sha256=
|
563
|
+
claude_mpm/services/diagnostics/checks/mcp_services_check.py,sha256=ESE5ITzNjkCWWKMQdQoTBDLK8pZQ9MJY4x-E_nVuRww,42278
|
564
564
|
claude_mpm/services/diagnostics/checks/monitor_check.py,sha256=NUx5G1yjHWlukZmwhUz4o8STRWgsQEx01YjIMReNC0A,10096
|
565
565
|
claude_mpm/services/diagnostics/checks/startup_log_check.py,sha256=DrXdml2rHvmhFBdb_sntE3xmwaP_DZIKjdVbCn8Dy7E,12258
|
566
566
|
claude_mpm/services/event_bus/__init__.py,sha256=ETCo4a6puIeyVWAv55uCDjjhzNyUwbVAHEcAVkVapx8,688
|
@@ -773,9 +773,9 @@ claude_mpm/utils/subprocess_utils.py,sha256=D0izRT8anjiUb_JG72zlJR_JAw1cDkb7kalN
|
|
773
773
|
claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
|
774
774
|
claude_mpm/validation/agent_validator.py,sha256=Nm2WmcbCb0EwOG4nFcikc3wVdiiAfjGBBI3YoR6ainQ,20915
|
775
775
|
claude_mpm/validation/frontmatter_validator.py,sha256=IDBOCBweO6umydSnUJjBh81sKk3cy9hRFYm61DCiXbI,7020
|
776
|
-
claude_mpm-4.4.
|
777
|
-
claude_mpm-4.4.
|
778
|
-
claude_mpm-4.4.
|
779
|
-
claude_mpm-4.4.
|
780
|
-
claude_mpm-4.4.
|
781
|
-
claude_mpm-4.4.
|
776
|
+
claude_mpm-4.4.11.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
|
777
|
+
claude_mpm-4.4.11.dist-info/METADATA,sha256=GfGvZ-4PutVAH_2DAeQ7S5E-KubU1gqXSwbqnB2yh2Y,17518
|
778
|
+
claude_mpm-4.4.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
779
|
+
claude_mpm-4.4.11.dist-info/entry_points.txt,sha256=Vlw3GNi-OtTpKSrez04iNrPmxNxYDpIWxmJCxiZ5Tx8,526
|
780
|
+
claude_mpm-4.4.11.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
|
781
|
+
claude_mpm-4.4.11.dist-info/RECORD,,
|
@@ -1,5 +1,6 @@
|
|
1
1
|
[console_scripts]
|
2
2
|
claude-mpm = claude_mpm.cli:main
|
3
|
+
claude-mpm-doctor = claude_mpm.scripts.mpm_doctor:main
|
3
4
|
claude-mpm-mcp = claude_mpm.services.mcp_gateway.server.stdio_server:main_sync
|
4
5
|
claude-mpm-mcp-wrapper = claude_mpm.scripts.mcp_wrapper:entry_point
|
5
6
|
claude-mpm-monitor = claude_mpm.scripts.launch_monitor:main
|
File without changes
|
File without changes
|
File without changes
|