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 CHANGED
@@ -1 +1 @@
1
- 4.4.9
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 🔴
@@ -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": "pipx",
50
- "args": ["run", "kuzu-memory", "mcp", "serve"]
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": "pipx",
55
- "args": ["run", "mcp-ticketer", "mcp"]
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": "pipx",
60
- "args": ["run", "mcp-browser", "mcp"],
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
- "command": "pipx",
66
- "args": ["run", "mcp-vector-search", "-m", "mcp_vector_search.mcp.server", "{project_root}"],
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 get_static_service_config(self, service_name: str) -> Optional[Dict]:
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
- # Special handling for mcp-vector-search: replace {project_root} placeholder
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}", str(self.project_root)) if "{project_root}" in arg else arg
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
- return static_config
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
- # Check and fix mcp-ticketer dependencies ONCE before processing projects
464
- # This avoids running the same pipx inject command 100+ times
465
- mcp_ticketer_fixed = False
466
- if "mcp-ticketer" in self.PIPX_SERVICES:
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.STATIC_MCP_CONFIGS[service_name].copy()
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
- # Special handling for mcp-vector-search: replace {project_root} placeholder
506
- if service_name == "mcp-vector-search":
507
- # Use the project key as the project root for each project
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-mpm
3
- Version: 4.4.9
3
+ Version: 4.4.11
4
4
  Summary: Claude Multi-Agent Project Manager - Orchestrate Claude with agent delegation and ticket tracking
5
5
  Author-email: Bob Matsuoka <bob@matsuoka.com>
6
6
  Maintainer: Claude MPM Team
@@ -1,5 +1,5 @@
1
1
  claude_mpm/BUILD_NUMBER,sha256=toytnNjkIKPgQaGwDqQdC1rpNTAdSEc6Vja50d7Ovug,4
2
- claude_mpm/VERSION,sha256=k8-5xMoy2UlbzZa4MVGVy1j0Ym4rIg_47OvMJE5eNKg,6
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=wTjmrnJjGo3bFvQQcj1VJ1wEFgYePaOe3NFGa_1vf50,25963
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=mqvz1ePtrzzaP6S2oG2teJQx5mq3QM4HXgNKmEXVau8,8814
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=iEBnl9rPFR-PccOol8ZdufyCvhZvVN47wYtTteQVpBY,35112
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=SrHdpRbEc62e7mkfC_m8aGFtQIkhNOmiiCQZ78MME98,40800
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.9.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
777
- claude_mpm-4.4.9.dist-info/METADATA,sha256=RSN3ddmTCsiu9uTUoaSFYFeB7O56BR7awyIOpLkSVsA,17517
778
- claude_mpm-4.4.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
779
- claude_mpm-4.4.9.dist-info/entry_points.txt,sha256=FDPZgz8JOvD-6iuXY2l9Zbo9zYVRuE4uz4Qr0vLeGOk,471
780
- claude_mpm-4.4.9.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
781
- claude_mpm-4.4.9.dist-info/RECORD,,
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