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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/templates/project_organizer.json +3 -3
- claude_mpm/cli/commands/auto_configure.py +7 -9
- claude_mpm/cli/commands/local_deploy.py +3 -2
- claude_mpm/cli/commands/mpm_init.py +4 -4
- claude_mpm/cli/commands/mpm_init_handler.py +8 -3
- claude_mpm/core/base_service.py +13 -12
- claude_mpm/core/enums.py +36 -1
- claude_mpm/services/agents/auto_config_manager.py +9 -10
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +6 -4
- claude_mpm/services/agents/registry/modification_tracker.py +5 -2
- claude_mpm/services/command_handler_service.py +11 -5
- claude_mpm/services/core/interfaces/process.py +6 -6
- claude_mpm/services/core/models/__init__.py +0 -2
- claude_mpm/services/core/models/agent_config.py +12 -28
- claude_mpm/services/core/models/process.py +19 -42
- claude_mpm/services/local_ops/__init__.py +3 -3
- claude_mpm/services/local_ops/process_manager.py +12 -12
- claude_mpm/services/local_ops/state_manager.py +6 -5
- claude_mpm/services/local_ops/unified_manager.py +2 -2
- claude_mpm/services/mcp_config_manager.py +7 -2
- claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
- claude_mpm/services/mcp_gateway/core/base.py +18 -31
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +71 -24
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +23 -22
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +2 -2
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +2 -2
- claude_mpm/services/unified/deployment_strategies/local.py +3 -3
- claude_mpm/services/unified/deployment_strategies/utils.py +5 -5
- claude_mpm/services/unified/deployment_strategies/vercel.py +6 -6
- claude_mpm/services/unified/unified_analyzer.py +8 -5
- claude_mpm/services/unified/unified_deployment.py +2 -2
- {claude_mpm-4.14.9.dist-info → claude_mpm-4.15.0.dist-info}/METADATA +1 -1
- {claude_mpm-4.14.9.dist-info → claude_mpm-4.15.0.dist-info}/RECORD +43 -43
- {claude_mpm-4.14.9.dist-info → claude_mpm-4.15.0.dist-info}/WHEEL +0 -0
- {claude_mpm-4.14.9.dist-info → claude_mpm-4.15.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.14.9.dist-info → claude_mpm-4.15.0.dist-info}/licenses/LICENSE +0 -0
- {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 =
|
|
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
|
-
|
|
80
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 !=
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 [
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 ==
|
|
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
|
|
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
|
-
|
|
10
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
82
|
-
|
|
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.
|
|
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
|
|
526
|
-
for external MCP services. The actual registration of these services
|
|
527
|
-
|
|
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
|
|
530
|
-
|
|
531
|
-
|
|
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
|
|
544
|
-
|
|
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
|
|
548
|
-
#
|
|
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
|
-
|
|
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"] =
|
|
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"] =
|
|
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"] =
|
|
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"] =
|
|
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"] =
|
|
306
|
+
check_result["status"] = HealthStatus.UNHEALTHY
|
|
307
307
|
|
|
308
308
|
except Exception as e:
|
|
309
|
-
check_result["status"] =
|
|
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"] =
|
|
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"] =
|
|
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"] =
|
|
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
|
-
|
|
418
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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"] ==
|
|
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") !=
|
|
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"] ==
|
|
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") !=
|
|
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"] =
|
|
292
|
+
health["status"] = HealthStatus.DEGRADED
|
|
293
293
|
|
|
294
294
|
else:
|
|
295
|
-
health["status"] =
|
|
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"] =
|
|
296
|
+
health["status"] = HealthStatus.HEALTHY
|
|
297
297
|
elif any(health["checks"].values()):
|
|
298
|
-
health["status"] =
|
|
298
|
+
health["status"] = HealthStatus.DEGRADED
|
|
299
299
|
else:
|
|
300
|
-
health["status"] =
|
|
300
|
+
health["status"] = HealthStatus.UNHEALTHY
|
|
301
301
|
|
|
302
302
|
except Exception as e:
|
|
303
|
-
health["status"] =
|
|
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"] =
|
|
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"] =
|
|
345
|
+
health["status"] = HealthStatus.HEALTHY
|
|
346
346
|
elif any(health["checks"].values()):
|
|
347
|
-
health["status"] =
|
|
347
|
+
health["status"] = HealthStatus.DEGRADED
|
|
348
348
|
else:
|
|
349
|
-
health["status"] =
|
|
349
|
+
health["status"] = HealthStatus.UNHEALTHY
|
|
350
350
|
|
|
351
351
|
except Exception as e:
|
|
352
|
-
health["status"] =
|
|
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
|
-
|
|
348
|
-
|
|
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
|
-
|
|
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 {
|
|
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(
|
|
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(
|
|
322
|
+
message=result.get(OperationResult.ERROR.value, "Deployment failed"),
|
|
323
323
|
metadata=result,
|
|
324
324
|
)
|
|
325
325
|
|