claude-mpm 4.14.8__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/agent_loader.py +13 -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 +124 -12
- 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/dependency_analyzer.py +1 -1
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +3 -3
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +3 -3
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +1 -1
- 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.8.dist-info → claude_mpm-4.15.0.dist-info}/METADATA +1 -1
- {claude_mpm-4.14.8.dist-info → claude_mpm-4.15.0.dist-info}/RECORD +47 -47
- {claude_mpm-4.14.8.dist-info → claude_mpm-4.15.0.dist-info}/WHEEL +0 -0
- {claude_mpm-4.14.8.dist-info → claude_mpm-4.15.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.14.8.dist-info → claude_mpm-4.15.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.14.8.dist-info → claude_mpm-4.15.0.dist-info}/top_level.txt +0 -0
|
@@ -30,9 +30,10 @@ from typing import Dict, List, Optional
|
|
|
30
30
|
import psutil
|
|
31
31
|
from filelock import FileLock
|
|
32
32
|
|
|
33
|
+
from claude_mpm.core.enums import ServiceState
|
|
33
34
|
from claude_mpm.services.core.base import SyncBaseService
|
|
34
35
|
from claude_mpm.services.core.interfaces.process import IDeploymentStateManager
|
|
35
|
-
from claude_mpm.services.core.models.process import DeploymentState
|
|
36
|
+
from claude_mpm.services.core.models.process import DeploymentState
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
class StateCorruptionError(Exception):
|
|
@@ -206,12 +207,12 @@ class DeploymentStateManager(SyncBaseService, IDeploymentStateManager):
|
|
|
206
207
|
states = self.load_state()
|
|
207
208
|
return list(states.values())
|
|
208
209
|
|
|
209
|
-
def get_deployments_by_status(self, status:
|
|
210
|
+
def get_deployments_by_status(self, status: ServiceState) -> List[DeploymentState]:
|
|
210
211
|
"""
|
|
211
212
|
Get all deployments with a specific status.
|
|
212
213
|
|
|
213
214
|
Args:
|
|
214
|
-
status:
|
|
215
|
+
status: ServiceState to filter by
|
|
215
216
|
|
|
216
217
|
Returns:
|
|
217
218
|
List of matching DeploymentState objects
|
|
@@ -295,14 +296,14 @@ class DeploymentStateManager(SyncBaseService, IDeploymentStateManager):
|
|
|
295
296
|
return False
|
|
296
297
|
|
|
297
298
|
def update_deployment_status(
|
|
298
|
-
self, deployment_id: str, status:
|
|
299
|
+
self, deployment_id: str, status: ServiceState
|
|
299
300
|
) -> bool:
|
|
300
301
|
"""
|
|
301
302
|
Update the status of a deployment.
|
|
302
303
|
|
|
303
304
|
Args:
|
|
304
305
|
deployment_id: Unique deployment identifier
|
|
305
|
-
status: New
|
|
306
|
+
status: New ServiceState
|
|
306
307
|
|
|
307
308
|
Returns:
|
|
308
309
|
True if updated, False if deployment not found
|
|
@@ -45,12 +45,12 @@ from typing import Any, Dict, List, Optional
|
|
|
45
45
|
|
|
46
46
|
import yaml
|
|
47
47
|
|
|
48
|
+
from claude_mpm.core.enums import ServiceState
|
|
48
49
|
from claude_mpm.services.core.base import SyncBaseService
|
|
49
50
|
from claude_mpm.services.core.models.health import DeploymentHealth
|
|
50
51
|
from claude_mpm.services.core.models.process import (
|
|
51
52
|
DeploymentState,
|
|
52
53
|
ProcessInfo,
|
|
53
|
-
ProcessStatus,
|
|
54
54
|
StartConfig,
|
|
55
55
|
)
|
|
56
56
|
from claude_mpm.services.core.models.restart import RestartConfig, RestartHistory
|
|
@@ -384,7 +384,7 @@ class UnifiedLocalOpsManager(SyncBaseService):
|
|
|
384
384
|
|
|
385
385
|
def list_deployments(
|
|
386
386
|
self,
|
|
387
|
-
status_filter: Optional[
|
|
387
|
+
status_filter: Optional[ServiceState] = None,
|
|
388
388
|
) -> List[DeploymentState]:
|
|
389
389
|
"""
|
|
390
390
|
List all deployments.
|
|
@@ -1145,11 +1145,16 @@ class MCPConfigManager:
|
|
|
1145
1145
|
|
|
1146
1146
|
# Check each service for issues
|
|
1147
1147
|
for service_name in self.PIPX_SERVICES:
|
|
1148
|
-
|
|
1148
|
+
# Check if service is enabled in config
|
|
1149
|
+
if not self.should_enable_service(service_name):
|
|
1150
|
+
self.logger.debug(f"Skipping {service_name} (disabled in config)")
|
|
1151
|
+
continue
|
|
1152
|
+
|
|
1153
|
+
self.logger.debug(f"🔍 Checking {service_name} for issues...")
|
|
1149
1154
|
issue_type = self._detect_service_issue(service_name)
|
|
1150
1155
|
if issue_type:
|
|
1151
1156
|
services_to_fix.append((service_name, issue_type))
|
|
1152
|
-
self.logger.
|
|
1157
|
+
self.logger.debug(f" ⚠️ Found issue with {service_name}: {issue_type}")
|
|
1153
1158
|
else:
|
|
1154
1159
|
self.logger.debug(f" ✅ {service_name} is functioning correctly")
|
|
1155
1160
|
|
|
@@ -5,7 +5,7 @@ MCP Gateway Core Module
|
|
|
5
5
|
Core interfaces and base classes for the MCP Gateway service.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from .base import BaseMCPService
|
|
8
|
+
from .base import BaseMCPService
|
|
9
9
|
from .exceptions import (
|
|
10
10
|
MCPCommunicationError,
|
|
11
11
|
MCPConfigurationError,
|
|
@@ -38,7 +38,6 @@ __all__ = [
|
|
|
38
38
|
# Exceptions
|
|
39
39
|
"MCPException",
|
|
40
40
|
"MCPServerError",
|
|
41
|
-
"MCPServiceState",
|
|
42
41
|
"MCPToolNotFoundError",
|
|
43
42
|
"MCPValidationError",
|
|
44
43
|
]
|
|
@@ -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
|
|
@@ -599,7 +599,7 @@ class DependencyAnalyzerStrategy(AnalyzerStrategy):
|
|
|
599
599
|
"""Extract key metrics from analysis results."""
|
|
600
600
|
metrics = {}
|
|
601
601
|
|
|
602
|
-
if analysis_result.get("status") !=
|
|
602
|
+
if analysis_result.get("status") != OperationResult.SUCCESS:
|
|
603
603
|
return metrics
|
|
604
604
|
|
|
605
605
|
# Extract dependency counts
|
|
@@ -241,7 +241,7 @@ class PerformanceAnalyzerStrategy(AnalyzerStrategy):
|
|
|
241
241
|
|
|
242
242
|
except Exception as e:
|
|
243
243
|
logger.error(f"Error analyzing file {file_path}: {e}")
|
|
244
|
-
results["status"] =
|
|
244
|
+
results["status"] = OperationResult.ERROR
|
|
245
245
|
results["error"] = str(e)
|
|
246
246
|
|
|
247
247
|
return results
|
|
@@ -292,7 +292,7 @@ class PerformanceAnalyzerStrategy(AnalyzerStrategy):
|
|
|
292
292
|
continue
|
|
293
293
|
|
|
294
294
|
file_result = self._analyze_file(file_path, options)
|
|
295
|
-
if file_result["status"] ==
|
|
295
|
+
if file_result["status"] == OperationResult.SUCCESS:
|
|
296
296
|
results["files_analyzed"] += 1
|
|
297
297
|
total_score += file_result["performance_score"]
|
|
298
298
|
|
|
@@ -803,7 +803,7 @@ class PerformanceAnalyzerStrategy(AnalyzerStrategy):
|
|
|
803
803
|
"""Extract key metrics from analysis results."""
|
|
804
804
|
metrics = {}
|
|
805
805
|
|
|
806
|
-
if analysis_result.get("status") !=
|
|
806
|
+
if analysis_result.get("status") != OperationResult.SUCCESS:
|
|
807
807
|
return metrics
|
|
808
808
|
|
|
809
809
|
if analysis_result.get("type") == "file":
|
|
@@ -242,7 +242,7 @@ class SecurityAnalyzerStrategy(AnalyzerStrategy):
|
|
|
242
242
|
|
|
243
243
|
except Exception as e:
|
|
244
244
|
logger.error(f"Error analyzing file {file_path}: {e}")
|
|
245
|
-
results["status"] =
|
|
245
|
+
results["status"] = OperationResult.ERROR
|
|
246
246
|
results["error"] = str(e)
|
|
247
247
|
|
|
248
248
|
return results
|
|
@@ -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":
|
|
@@ -641,7 +641,7 @@ class StructureAnalyzerStrategy(AnalyzerStrategy):
|
|
|
641
641
|
"""Extract key metrics from analysis results."""
|
|
642
642
|
metrics = {}
|
|
643
643
|
|
|
644
|
-
if analysis_result.get("status") !=
|
|
644
|
+
if analysis_result.get("status") != OperationResult.SUCCESS:
|
|
645
645
|
return metrics
|
|
646
646
|
|
|
647
647
|
# Extract structure statistics
|
|
@@ -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
|
|