claude-mpm 4.14.9__py3-none-any.whl → 4.15.0__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.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (43) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/templates/project_organizer.json +3 -3
  3. claude_mpm/cli/commands/auto_configure.py +7 -9
  4. claude_mpm/cli/commands/local_deploy.py +3 -2
  5. claude_mpm/cli/commands/mpm_init.py +4 -4
  6. claude_mpm/cli/commands/mpm_init_handler.py +8 -3
  7. claude_mpm/core/base_service.py +13 -12
  8. claude_mpm/core/enums.py +36 -1
  9. claude_mpm/services/agents/auto_config_manager.py +9 -10
  10. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
  11. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
  12. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
  13. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
  14. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
  15. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +6 -4
  16. claude_mpm/services/agents/registry/modification_tracker.py +5 -2
  17. claude_mpm/services/command_handler_service.py +11 -5
  18. claude_mpm/services/core/interfaces/process.py +6 -6
  19. claude_mpm/services/core/models/__init__.py +0 -2
  20. claude_mpm/services/core/models/agent_config.py +12 -28
  21. claude_mpm/services/core/models/process.py +19 -42
  22. claude_mpm/services/local_ops/__init__.py +3 -3
  23. claude_mpm/services/local_ops/process_manager.py +12 -12
  24. claude_mpm/services/local_ops/state_manager.py +6 -5
  25. claude_mpm/services/local_ops/unified_manager.py +2 -2
  26. claude_mpm/services/mcp_config_manager.py +7 -2
  27. claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
  28. claude_mpm/services/mcp_gateway/core/base.py +18 -31
  29. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +71 -24
  30. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +23 -22
  31. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +2 -2
  32. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +2 -2
  33. claude_mpm/services/unified/deployment_strategies/local.py +3 -3
  34. claude_mpm/services/unified/deployment_strategies/utils.py +5 -5
  35. claude_mpm/services/unified/deployment_strategies/vercel.py +6 -6
  36. claude_mpm/services/unified/unified_analyzer.py +8 -5
  37. claude_mpm/services/unified/unified_deployment.py +2 -2
  38. {claude_mpm-4.14.9.dist-info → claude_mpm-4.15.0.dist-info}/METADATA +1 -1
  39. {claude_mpm-4.14.9.dist-info → claude_mpm-4.15.0.dist-info}/RECORD +43 -43
  40. {claude_mpm-4.14.9.dist-info → claude_mpm-4.15.0.dist-info}/WHEEL +0 -0
  41. {claude_mpm-4.14.9.dist-info → claude_mpm-4.15.0.dist-info}/entry_points.txt +0 -0
  42. {claude_mpm-4.14.9.dist-info → claude_mpm-4.15.0.dist-info}/licenses/LICENSE +0 -0
  43. {claude_mpm-4.14.9.dist-info → claude_mpm-4.15.0.dist-info}/top_level.txt +0 -0
@@ -8,25 +8,12 @@ Part of ISS-0034: Infrastructure Setup - MCP Gateway Project Foundation
8
8
  """
9
9
 
10
10
  import asyncio
11
- from enum import Enum
12
11
  from typing import Any, Dict, Optional
13
12
 
13
+ from claude_mpm.core.enums import ServiceState
14
14
  from claude_mpm.services.core.base import BaseService
15
15
 
16
16
 
17
- class MCPServiceState(Enum):
18
- """MCP service lifecycle states."""
19
-
20
- UNINITIALIZED = "uninitialized"
21
- INITIALIZING = "initializing"
22
- INITIALIZED = "initialized"
23
- STARTING = "starting"
24
- RUNNING = "running"
25
- STOPPING = "stopping"
26
- STOPPED = "stopped"
27
- ERROR = "error"
28
-
29
-
30
17
  class BaseMCPService(BaseService):
31
18
  """
32
19
  Base class for all MCP Gateway services.
@@ -53,7 +40,7 @@ class BaseMCPService(BaseService):
53
40
  config: Service-specific configuration
54
41
  """
55
42
  super().__init__(service_name or "MCPService", config)
56
- self._state = MCPServiceState.UNINITIALIZED
43
+ self._state = ServiceState.UNINITIALIZED
57
44
  self._health_status = {
58
45
  "healthy": False,
59
46
  "state": self._state.value,
@@ -76,13 +63,13 @@ class BaseMCPService(BaseService):
76
63
  """
77
64
  async with self._state_lock:
78
65
  if self._state not in [
79
- MCPServiceState.UNINITIALIZED,
80
- MCPServiceState.STOPPED,
66
+ ServiceState.UNINITIALIZED,
67
+ ServiceState.STOPPED,
81
68
  ]:
82
69
  self.log_warning(f"Cannot initialize from state {self._state.value}")
83
70
  return False
84
71
 
85
- self._state = MCPServiceState.INITIALIZING
72
+ self._state = ServiceState.INITIALIZING
86
73
  self.log_info("Initializing MCP service")
87
74
 
88
75
  try:
@@ -91,13 +78,13 @@ class BaseMCPService(BaseService):
91
78
 
92
79
  async with self._state_lock:
93
80
  if success:
94
- self._state = MCPServiceState.INITIALIZED
81
+ self._state = ServiceState.INITIALIZED
95
82
  self._initialized = True
96
83
  self._health_status["healthy"] = True
97
84
  self._health_status["state"] = self._state.value
98
85
  self.log_info("MCP service initialized successfully")
99
86
  else:
100
- self._state = MCPServiceState.ERROR
87
+ self._state = ServiceState.ERROR
101
88
  self._health_status["healthy"] = False
102
89
  self._health_status["state"] = self._state.value
103
90
  self.log_error("MCP service initialization failed")
@@ -106,7 +93,7 @@ class BaseMCPService(BaseService):
106
93
 
107
94
  except Exception as e:
108
95
  async with self._state_lock:
109
- self._state = MCPServiceState.ERROR
96
+ self._state = ServiceState.ERROR
110
97
  self._health_status["healthy"] = False
111
98
  self._health_status["state"] = self._state.value
112
99
  self._health_status["details"]["error"] = str(e)
@@ -134,11 +121,11 @@ class BaseMCPService(BaseService):
134
121
  True if startup successful
135
122
  """
136
123
  async with self._state_lock:
137
- if self._state != MCPServiceState.INITIALIZED:
124
+ if self._state != ServiceState.INITIALIZED:
138
125
  self.log_warning(f"Cannot start from state {self._state.value}")
139
126
  return False
140
127
 
141
- self._state = MCPServiceState.STARTING
128
+ self._state = ServiceState.STARTING
142
129
  self.log_info("Starting MCP service")
143
130
 
144
131
  try:
@@ -146,12 +133,12 @@ class BaseMCPService(BaseService):
146
133
 
147
134
  async with self._state_lock:
148
135
  if success:
149
- self._state = MCPServiceState.RUNNING
136
+ self._state = ServiceState.RUNNING
150
137
  self._health_status["healthy"] = True
151
138
  self._health_status["state"] = self._state.value
152
139
  self.log_info("MCP service started successfully")
153
140
  else:
154
- self._state = MCPServiceState.ERROR
141
+ self._state = ServiceState.ERROR
155
142
  self._health_status["healthy"] = False
156
143
  self._health_status["state"] = self._state.value
157
144
  self.log_error("MCP service startup failed")
@@ -160,7 +147,7 @@ class BaseMCPService(BaseService):
160
147
 
161
148
  except Exception as e:
162
149
  async with self._state_lock:
163
- self._state = MCPServiceState.ERROR
150
+ self._state = ServiceState.ERROR
164
151
  self._health_status["healthy"] = False
165
152
  self._health_status["state"] = self._state.value
166
153
  self._health_status["details"]["error"] = str(e)
@@ -188,18 +175,18 @@ class BaseMCPService(BaseService):
188
175
  Subclasses should override _do_shutdown() for custom shutdown logic.
189
176
  """
190
177
  async with self._state_lock:
191
- if self._state in [MCPServiceState.STOPPED, MCPServiceState.STOPPING]:
178
+ if self._state in [ServiceState.STOPPED, ServiceState.STOPPING]:
192
179
  self.log_warning(f"Already in state {self._state.value}")
193
180
  return
194
181
 
195
- self._state = MCPServiceState.STOPPING
182
+ self._state = ServiceState.STOPPING
196
183
  self.log_info("Shutting down MCP service")
197
184
 
198
185
  try:
199
186
  await self._do_shutdown()
200
187
 
201
188
  async with self._state_lock:
202
- self._state = MCPServiceState.STOPPED
189
+ self._state = ServiceState.STOPPED
203
190
  self._shutdown = True
204
191
  self._health_status["healthy"] = False
205
192
  self._health_status["state"] = self._state.value
@@ -207,7 +194,7 @@ class BaseMCPService(BaseService):
207
194
 
208
195
  except Exception as e:
209
196
  async with self._state_lock:
210
- self._state = MCPServiceState.ERROR
197
+ self._state = ServiceState.ERROR
211
198
  self._health_status["healthy"] = False
212
199
  self._health_status["state"] = self._state.value
213
200
  self._health_status["details"]["error"] = str(e)
@@ -232,7 +219,7 @@ class BaseMCPService(BaseService):
232
219
  self.log_info("Restarting MCP service")
233
220
 
234
221
  # Shutdown if running
235
- if self._state == MCPServiceState.RUNNING:
222
+ if self._state == ServiceState.RUNNING:
236
223
  await self.shutdown()
237
224
 
238
225
  # Re-initialize
@@ -2,12 +2,22 @@
2
2
  External MCP Services Integration
3
3
  ==================================
4
4
 
5
- Manages installation and basic setup of external MCP services like mcp-vector-search
5
+ Manages detection and setup of external MCP services like mcp-vector-search
6
6
  and mcp-browser. These services run as separate MCP servers in Claude Code,
7
7
  not as part of the Claude MPM MCP Gateway.
8
8
 
9
- Note: As of the latest architecture, external services are registered as separate
10
- MCP servers in Claude Code configuration, not as tools within the gateway.
9
+ IMPORTANT: External services are NOT auto-installed. Users must manually install
10
+ them using pipx or pip. This gives users explicit control over which optional
11
+ services they want to enable.
12
+
13
+ Installation:
14
+ pipx install mcp-vector-search
15
+ pipx install mcp-browser
16
+ pipx install kuzu-memory
17
+ pipx install mcp-ticketer
18
+
19
+ Note: External services are registered as separate MCP servers in Claude Code
20
+ configuration, not as tools within the gateway.
11
21
  """
12
22
 
13
23
  import json
@@ -20,7 +30,12 @@ from claude_mpm.services.mcp_gateway.tools.base_adapter import BaseToolAdapter
20
30
 
21
31
 
22
32
  class ExternalMCPService(BaseToolAdapter):
23
- """Base class for external MCP service integration."""
33
+ """Base class for external MCP service integration.
34
+
35
+ External services are detected if already installed but are NOT
36
+ automatically installed. Users must install them manually using
37
+ pipx or pip to enable these optional features.
38
+ """
24
39
 
25
40
  def __init__(self, service_name: str, package_name: str):
26
41
  """
@@ -65,27 +80,37 @@ class ExternalMCPService(BaseToolAdapter):
65
80
  )
66
81
 
67
82
  async def initialize(
68
- self, auto_install: bool = True, interactive: bool = True
83
+ self, auto_install: bool = False, interactive: bool = False
69
84
  ) -> bool:
70
85
  """Initialize the external service.
71
86
 
87
+ NOTE: Auto-installation is disabled by default (v4.9.0+). Users must
88
+ manually install external services using pipx or pip.
89
+
72
90
  Args:
73
- auto_install: Whether to automatically install if not found
74
- interactive: Whether to prompt user for installation preferences
91
+ auto_install: Whether to automatically install if not found (default: False)
92
+ Deprecated - will be removed in future versions
93
+ interactive: Whether to prompt user for installation preferences (default: False)
94
+ Only used if auto_install=True
75
95
  """
76
96
  try:
77
97
  # Check if package is installed
78
98
  self._is_installed = await self._check_installation()
79
99
 
80
100
  if not self._is_installed and auto_install:
81
- self.logger.info(
82
- f"{self.package_name} not installed - attempting installation"
101
+ # This path is deprecated but kept for backward compatibility
102
+ self.logger.warning(
103
+ f"Auto-installation is deprecated. Please install {self.package_name} manually: "
104
+ f"pipx install {self.package_name}"
83
105
  )
84
106
  await self._install_package(interactive=interactive)
85
107
  self._is_installed = await self._check_installation()
86
108
 
87
109
  if not self._is_installed:
88
- self.logger.warning(f"{self.package_name} is not available")
110
+ self.logger.debug(
111
+ f"{self.package_name} is not available. "
112
+ f"Install manually with: pipx install {self.package_name}"
113
+ )
89
114
  return False
90
115
 
91
116
  self.logger.info(f"{self.package_name} is available")
@@ -522,13 +547,20 @@ class MCPBrowserService(ExternalMCPService):
522
547
  class ExternalMCPServiceManager:
523
548
  """Manager for external MCP services.
524
549
 
525
- This manager is responsible for checking and installing Python packages
526
- for external MCP services. The actual registration of these services
527
- happens in Claude Code configuration as separate MCP servers.
550
+ This manager is responsible for detecting (but NOT installing) Python packages
551
+ for external MCP services. The actual registration of these services happens
552
+ in Claude Code configuration as separate MCP servers.
553
+
554
+ IMPORTANT: As of v4.9.0, this manager NO LONGER auto-installs missing services.
555
+ Users must manually install external services using pipx or pip:
556
+ - pipx install mcp-vector-search
557
+ - pipx install mcp-browser
558
+ - pipx install kuzu-memory
559
+ - pipx install mcp-ticketer
528
560
 
529
- Note: This class is maintained for backward compatibility and package
530
- management. The actual tool registration is handled by separate MCP
531
- server instances in Claude Code.
561
+ Note: This class is maintained for backward compatibility and service detection.
562
+ The actual tool registration is handled by separate MCP server instances in
563
+ Claude Code.
532
564
  """
533
565
 
534
566
  def __init__(self):
@@ -539,20 +571,34 @@ class ExternalMCPServiceManager:
539
571
  async def initialize_services(self) -> List[ExternalMCPService]:
540
572
  """Initialize all external MCP services.
541
573
 
542
- This method checks if external service packages are installed
543
- and attempts to install them if missing. It does NOT register
544
- them as tools in the gateway - they run as separate MCP servers.
574
+ This method checks if external service packages are already installed
575
+ and registers them if available. It does NOT auto-install missing services.
576
+
577
+ External MCP services (mcp-vector-search, mcp-browser, kuzu-memory, mcp-ticketer)
578
+ must be manually installed by users. This gives users explicit control over
579
+ which services they want to use.
580
+
581
+ Installation instructions:
582
+ - mcp-vector-search: pipx install mcp-vector-search
583
+ - mcp-browser: pipx install mcp-browser
584
+ - kuzu-memory: pipx install kuzu-memory
585
+ - mcp-ticketer: pipx install mcp-ticketer
586
+
587
+ Services run as separate MCP servers in Claude Code, not as tools within
588
+ the gateway.
545
589
  """
546
590
  # Create service instances
547
- # Note: kuzu-memory is configured via MCPConfigManager and runs as a separate MCP server
548
- # It doesn't need to be included here since it's already set up through the MCP config
591
+ # Note: kuzu-memory and mcp-ticketer are configured via MCPConfigManager
592
+ # and run as separate MCP servers. They don't need to be included here
593
+ # since they're already set up through the MCP config.
549
594
  services = [MCPVectorSearchService(), MCPBrowserService()]
550
595
 
551
- # Initialize each service
596
+ # Initialize each service (check if installed, but DO NOT auto-install)
552
597
  initialized_services = []
553
598
  for service in services:
554
599
  try:
555
- if await service.initialize():
600
+ # Pass auto_install=False to prevent automatic installation
601
+ if await service.initialize(auto_install=False, interactive=False):
556
602
  initialized_services.append(service)
557
603
  if self.logger:
558
604
  self.logger.info(
@@ -560,7 +606,8 @@ class ExternalMCPServiceManager:
560
606
  )
561
607
  elif self.logger:
562
608
  self.logger.debug(
563
- f"Service not available (optional): {service.service_name}"
609
+ f"Service not available (optional): {service.service_name}. "
610
+ f"Install manually with: pipx install {service.package_name}"
564
611
  )
565
612
  except Exception as e:
566
613
  if self.logger:
@@ -26,7 +26,7 @@ from typing import Any, Dict
26
26
  import psutil
27
27
 
28
28
  from claude_mpm.config.paths import paths
29
- from claude_mpm.core.enums import OperationResult, ServiceState
29
+ from claude_mpm.core.enums import HealthStatus, OperationResult, ServiceState
30
30
  from claude_mpm.core.logger import get_logger
31
31
  from claude_mpm.services.mcp_gateway.core.interfaces import (
32
32
  MCPToolDefinition,
@@ -220,7 +220,7 @@ class HealthCheckTool(BaseToolAdapter):
220
220
 
221
221
  if memory_usage > 90:
222
222
  check_result["errors"].append("High memory usage detected")
223
- check_result["status"] = "unhealthy"
223
+ check_result["status"] = HealthStatus.UNHEALTHY
224
224
  elif memory_usage > 80:
225
225
  check_result["warnings"].append("Elevated memory usage")
226
226
 
@@ -233,7 +233,7 @@ class HealthCheckTool(BaseToolAdapter):
233
233
 
234
234
  if cpu_usage > 95:
235
235
  check_result["errors"].append("High CPU usage detected")
236
- check_result["status"] = "unhealthy"
236
+ check_result["status"] = HealthStatus.UNHEALTHY
237
237
  elif cpu_usage > 80:
238
238
  check_result["warnings"].append("Elevated CPU usage")
239
239
 
@@ -249,7 +249,7 @@ class HealthCheckTool(BaseToolAdapter):
249
249
 
250
250
  if disk_usage_percent > 95:
251
251
  check_result["errors"].append("Disk space critically low")
252
- check_result["status"] = "unhealthy"
252
+ check_result["status"] = HealthStatus.UNHEALTHY
253
253
  elif disk_usage_percent > 85:
254
254
  check_result["warnings"].append("Disk space running low")
255
255
 
@@ -263,7 +263,7 @@ class HealthCheckTool(BaseToolAdapter):
263
263
  }
264
264
 
265
265
  except Exception as e:
266
- check_result["status"] = "error"
266
+ check_result["status"] = HealthStatus.UNHEALTHY
267
267
  check_result["errors"].append(f"System health check failed: {e}")
268
268
 
269
269
  return check_result
@@ -303,10 +303,10 @@ class HealthCheckTool(BaseToolAdapter):
303
303
 
304
304
  if not mcp_dir.exists():
305
305
  check_result["errors"].append("MCP directory does not exist")
306
- check_result["status"] = "unhealthy"
306
+ check_result["status"] = HealthStatus.UNHEALTHY
307
307
 
308
308
  except Exception as e:
309
- check_result["status"] = "error"
309
+ check_result["status"] = HealthStatus.UNHEALTHY
310
310
  check_result["errors"].append(f"Gateway health check failed: {e}")
311
311
 
312
312
  return check_result
@@ -354,11 +354,11 @@ class HealthCheckTool(BaseToolAdapter):
354
354
  )
355
355
 
356
356
  if len(available_essential) == 0:
357
- check_result["status"] = "unhealthy"
357
+ check_result["status"] = HealthStatus.UNHEALTHY
358
358
  check_result["errors"].append("No essential tools available")
359
359
 
360
360
  except Exception as e:
361
- check_result["status"] = "error"
361
+ check_result["status"] = HealthStatus.UNHEALTHY
362
362
  check_result["errors"].append(f"Tools health check failed: {e}")
363
363
 
364
364
  return check_result
@@ -402,7 +402,7 @@ class HealthCheckTool(BaseToolAdapter):
402
402
  }
403
403
 
404
404
  except Exception as e:
405
- check_result["status"] = "error"
405
+ check_result["status"] = HealthStatus.UNHEALTHY
406
406
  check_result["errors"].append(f"Config health check failed: {e}")
407
407
 
408
408
  return check_result
@@ -414,13 +414,13 @@ class HealthCheckTool(BaseToolAdapter):
414
414
 
415
415
  statuses = [check.get("status", "unknown") for check in checks.values()]
416
416
 
417
- if "error" in statuses:
418
- return "error"
419
- if "unhealthy" in statuses:
417
+ # Check for unhealthy (handle both string and enum)
418
+ if HealthStatus.UNHEALTHY in statuses or "unhealthy" in statuses:
420
419
  return "unhealthy"
421
420
  if "warning" in statuses:
422
421
  return "warning"
423
- if all(status == "healthy" for status in statuses):
422
+ # Check for healthy (handle both string and enum)
423
+ if all(status in (HealthStatus.HEALTHY, "healthy") for status in statuses):
424
424
  return "healthy"
425
425
  return "unknown"
426
426
 
@@ -437,19 +437,20 @@ class HealthCheckTool(BaseToolAdapter):
437
437
  for check_name, check_result in checks.items():
438
438
  status = check_result.get("status", "unknown")
439
439
 
440
- if status == "healthy":
440
+ # Check for healthy (handle both string and enum)
441
+ if status in (HealthStatus.HEALTHY, "healthy"):
441
442
  summary["healthy"] += 1
442
- elif status in ["warning", "unhealthy"]:
443
+ elif status in ["warning", HealthStatus.UNHEALTHY, "unhealthy"]:
443
444
  summary["warnings"] += 1
444
445
  # Collect warning messages
445
446
  warnings = check_result.get("warnings", [])
446
447
  for warning in warnings:
447
448
  summary["issues"].append(f"{check_name}: {warning}")
448
- elif status == "error":
449
- summary["errors"] += 1
450
- # Collect error messages
451
- errors = check_result.get("errors", [])
452
- for error in errors:
453
- summary["issues"].append(f"{check_name}: {error}")
449
+ # Collect error messages if status is unhealthy
450
+ if status in (HealthStatus.UNHEALTHY, "unhealthy"):
451
+ summary["errors"] += 1
452
+ errors = check_result.get("errors", [])
453
+ for error in errors:
454
+ summary["issues"].append(f"{check_name}: {error}")
454
455
 
455
456
  return summary
@@ -196,7 +196,7 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
196
196
  total_metrics = {}
197
197
  for file_path in code_files:
198
198
  file_result = self._analyze_file(file_path, options)
199
- if file_result["status"] == "success":
199
+ if file_result["status"] == OperationResult.SUCCESS:
200
200
  results["files"].append(file_result)
201
201
 
202
202
  # Aggregate metrics
@@ -427,7 +427,7 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
427
427
  """Extract key metrics from analysis results."""
428
428
  metrics = {}
429
429
 
430
- if analysis_result.get("status") != "success":
430
+ if analysis_result.get("status") != OperationResult.SUCCESS:
431
431
  return metrics
432
432
 
433
433
  # Extract relevant metrics
@@ -302,7 +302,7 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
302
302
 
303
303
  file_result = self._analyze_file(file_path, options)
304
304
  if (
305
- file_result["status"] == "success"
305
+ file_result["status"] == OperationResult.SUCCESS
306
306
  and file_result["vulnerabilities"]
307
307
  ):
308
308
  results["files"].append(file_result)
@@ -646,7 +646,7 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
646
646
  """Extract key metrics from analysis results."""
647
647
  metrics = {}
648
648
 
649
- if analysis_result.get("status") != "success":
649
+ if analysis_result.get("status") != OperationResult.SUCCESS:
650
650
  return metrics
651
651
 
652
652
  if analysis_result.get("type") == "file":
@@ -19,7 +19,7 @@ from typing import Any, Dict, List, Optional
19
19
 
20
20
  import yaml
21
21
 
22
- from claude_mpm.core.enums import OperationResult
22
+ from claude_mpm.core.enums import HealthStatus, OperationResult
23
23
  from claude_mpm.core.logging_utils import get_logger
24
24
  from claude_mpm.services.unified.strategies import StrategyMetadata, StrategyPriority
25
25
 
@@ -289,10 +289,10 @@ class LocalDeploymentStrategy(DeploymentStrategy):
289
289
 
290
290
  # Check if any file is missing
291
291
  if any(not check for check in health["checks"].values()):
292
- health["status"] = "degraded"
292
+ health["status"] = HealthStatus.DEGRADED
293
293
 
294
294
  else:
295
- health["status"] = "unhealthy"
295
+ health["status"] = HealthStatus.UNHEALTHY
296
296
 
297
297
  return health
298
298
 
@@ -23,7 +23,7 @@ from datetime import datetime, timezone
23
23
  from pathlib import Path
24
24
  from typing import Any, Dict, List, Optional, Tuple, Union
25
25
 
26
- from claude_mpm.core.enums import OperationResult
26
+ from claude_mpm.core.enums import HealthStatus, OperationResult
27
27
  from claude_mpm.core.logging_utils import get_logger
28
28
 
29
29
  logger = get_logger(__name__)
@@ -293,14 +293,14 @@ def verify_deployment_health(
293
293
 
294
294
  # Determine overall status
295
295
  if all(health["checks"].values()):
296
- health["status"] = "healthy"
296
+ health["status"] = HealthStatus.HEALTHY
297
297
  elif any(health["checks"].values()):
298
- health["status"] = "degraded"
298
+ health["status"] = HealthStatus.DEGRADED
299
299
  else:
300
- health["status"] = "unhealthy"
300
+ health["status"] = HealthStatus.UNHEALTHY
301
301
 
302
302
  except Exception as e:
303
- health["status"] = "error"
303
+ health["status"] = HealthStatus.UNKNOWN
304
304
  health["errors"].append(str(e))
305
305
 
306
306
  return health
@@ -13,7 +13,7 @@ from datetime import datetime, timezone
13
13
  from pathlib import Path
14
14
  from typing import Any, Dict, List, Optional
15
15
 
16
- from claude_mpm.core.enums import OperationResult
16
+ from claude_mpm.core.enums import HealthStatus, OperationResult
17
17
  from claude_mpm.core.logging_utils import get_logger
18
18
  from claude_mpm.services.unified.strategies import StrategyMetadata, StrategyPriority
19
19
 
@@ -314,7 +314,7 @@ class VercelDeploymentStrategy(DeploymentStrategy):
314
314
  }
315
315
 
316
316
  if not deployment_url:
317
- health["status"] = "unhealthy"
317
+ health["status"] = HealthStatus.UNHEALTHY
318
318
  health["error"] = "No deployment URL"
319
319
  return health
320
320
 
@@ -342,14 +342,14 @@ class VercelDeploymentStrategy(DeploymentStrategy):
342
342
 
343
343
  # Determine overall status
344
344
  if all(health["checks"].values()):
345
- health["status"] = "healthy"
345
+ health["status"] = HealthStatus.HEALTHY
346
346
  elif any(health["checks"].values()):
347
- health["status"] = "degraded"
347
+ health["status"] = HealthStatus.DEGRADED
348
348
  else:
349
- health["status"] = "unhealthy"
349
+ health["status"] = HealthStatus.UNHEALTHY
350
350
 
351
351
  except Exception as e:
352
- health["status"] = "unhealthy"
352
+ health["status"] = HealthStatus.UNHEALTHY
353
353
  health["error"] = str(e)
354
354
 
355
355
  return health
@@ -25,7 +25,7 @@ Features:
25
25
  from pathlib import Path
26
26
  from typing import Any, Dict, List, Optional, Union
27
27
 
28
- from claude_mpm.core.enums import ServiceState, ValidationSeverity
28
+ from claude_mpm.core.enums import OperationResult, ServiceState, ValidationSeverity
29
29
  from claude_mpm.core.logging_utils import get_logger
30
30
 
31
31
  from .interfaces import (
@@ -344,8 +344,8 @@ class UnifiedAnalyzer(IAnalyzerService, IUnifiedService):
344
344
 
345
345
  if not result1.success or not result2.success:
346
346
  return {
347
- "success": False,
348
- "error": "Failed to analyze one or both targets",
347
+ OperationResult.SUCCESS.value: False,
348
+ OperationResult.ERROR.value: "Failed to analyze one or both targets",
349
349
  }
350
350
 
351
351
  # Compare metrics
@@ -355,7 +355,7 @@ class UnifiedAnalyzer(IAnalyzerService, IUnifiedService):
355
355
  finding_diff = self._compare_findings(result1.findings, result2.findings)
356
356
 
357
357
  return {
358
- "success": True,
358
+ OperationResult.SUCCESS.value: True,
359
359
  "target1": str(target1),
360
360
  "target2": str(target2),
361
361
  "metric_differences": metric_diff,
@@ -379,7 +379,10 @@ class UnifiedAnalyzer(IAnalyzerService, IUnifiedService):
379
379
 
380
380
  except Exception as e:
381
381
  self._logger.error(f"Comparison error: {e!s}")
382
- return {"success": False, "error": str(e)}
382
+ return {
383
+ OperationResult.SUCCESS.value: False,
384
+ OperationResult.ERROR.value: str(e),
385
+ }
383
386
 
384
387
  def get_recommendations(
385
388
  self, analysis_result: AnalysisResult
@@ -305,7 +305,7 @@ class UnifiedDeploymentService(IDeploymentService, IUnifiedService):
305
305
  }
306
306
 
307
307
  # Update metrics
308
- if result.get("success", False):
308
+ if result.get(OperationResult.SUCCESS.value, False):
309
309
  self._metrics["successful_deployments"] += 1
310
310
  deployed_path = Path(target) / Path(source).name
311
311
 
@@ -319,7 +319,7 @@ class UnifiedDeploymentService(IDeploymentService, IUnifiedService):
319
319
  self._metrics["failed_deployments"] += 1
320
320
  return DeploymentResult(
321
321
  success=False,
322
- message=result.get("error", "Deployment failed"),
322
+ message=result.get(OperationResult.ERROR.value, "Deployment failed"),
323
323
  metadata=result,
324
324
  )
325
325
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-mpm
3
- Version: 4.14.9
3
+ Version: 4.15.0
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