claude-mpm 4.5.6__py3-none-any.whl → 4.5.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/__init__.py +20 -5
- claude_mpm/agents/BASE_OPS.md +10 -0
- claude_mpm/agents/PM_INSTRUCTIONS.md +28 -4
- claude_mpm/agents/agent_loader.py +19 -2
- claude_mpm/agents/base_agent_loader.py +5 -5
- claude_mpm/agents/templates/agent-manager.json +3 -3
- claude_mpm/agents/templates/agentic-coder-optimizer.json +3 -3
- claude_mpm/agents/templates/api_qa.json +1 -1
- claude_mpm/agents/templates/clerk-ops.json +3 -3
- claude_mpm/agents/templates/code_analyzer.json +3 -3
- claude_mpm/agents/templates/dart_engineer.json +294 -0
- claude_mpm/agents/templates/data_engineer.json +3 -3
- claude_mpm/agents/templates/documentation.json +2 -2
- claude_mpm/agents/templates/engineer.json +2 -2
- claude_mpm/agents/templates/gcp_ops_agent.json +2 -2
- claude_mpm/agents/templates/imagemagick.json +1 -1
- claude_mpm/agents/templates/local_ops_agent.json +363 -49
- claude_mpm/agents/templates/memory_manager.json +2 -2
- claude_mpm/agents/templates/nextjs_engineer.json +2 -2
- claude_mpm/agents/templates/ops.json +2 -2
- claude_mpm/agents/templates/php-engineer.json +1 -1
- claude_mpm/agents/templates/project_organizer.json +1 -1
- claude_mpm/agents/templates/prompt-engineer.json +6 -4
- claude_mpm/agents/templates/python_engineer.json +2 -2
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/react_engineer.json +3 -3
- claude_mpm/agents/templates/refactoring_engineer.json +3 -3
- claude_mpm/agents/templates/research.json +2 -2
- claude_mpm/agents/templates/security.json +2 -2
- claude_mpm/agents/templates/ticketing.json +2 -2
- claude_mpm/agents/templates/typescript_engineer.json +2 -2
- claude_mpm/agents/templates/vercel_ops_agent.json +2 -2
- claude_mpm/agents/templates/version_control.json +2 -2
- claude_mpm/agents/templates/web_qa.json +6 -6
- claude_mpm/agents/templates/web_ui.json +3 -3
- claude_mpm/cli/__init__.py +49 -19
- claude_mpm/cli/commands/configure.py +591 -7
- claude_mpm/cli/parsers/configure_parser.py +5 -0
- claude_mpm/core/__init__.py +53 -17
- claude_mpm/core/config.py +1 -1
- claude_mpm/core/log_manager.py +7 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +16 -11
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +9 -11
- claude_mpm/services/__init__.py +140 -156
- claude_mpm/services/agents/deployment/deployment_config_loader.py +21 -0
- claude_mpm/services/agents/loading/base_agent_manager.py +12 -2
- claude_mpm/services/async_session_logger.py +112 -96
- claude_mpm/services/claude_session_logger.py +63 -61
- claude_mpm/services/mcp_config_manager.py +328 -38
- claude_mpm/services/mcp_gateway/__init__.py +98 -94
- claude_mpm/services/monitor/event_emitter.py +1 -1
- claude_mpm/services/orphan_detection.py +791 -0
- claude_mpm/services/project_port_allocator.py +601 -0
- claude_mpm/services/response_tracker.py +17 -6
- claude_mpm/services/session_manager.py +176 -0
- {claude_mpm-4.5.6.dist-info → claude_mpm-4.5.11.dist-info}/METADATA +1 -1
- {claude_mpm-4.5.6.dist-info → claude_mpm-4.5.11.dist-info}/RECORD +62 -58
- {claude_mpm-4.5.6.dist-info → claude_mpm-4.5.11.dist-info}/WHEEL +0 -0
- {claude_mpm-4.5.6.dist-info → claude_mpm-4.5.11.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.5.6.dist-info → claude_mpm-4.5.11.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.5.6.dist-info → claude_mpm-4.5.11.dist-info}/top_level.txt +0 -0
@@ -41,6 +41,16 @@ class MCPConfigManager:
|
|
41
41
|
"kuzu-memory",
|
42
42
|
}
|
43
43
|
|
44
|
+
# Known missing dependencies for MCP services that pipx doesn't handle automatically
|
45
|
+
# Maps service names to list of missing dependencies that need injection
|
46
|
+
SERVICE_MISSING_DEPENDENCIES = {
|
47
|
+
"mcp-ticketer": [
|
48
|
+
"gql"
|
49
|
+
], # mcp-ticketer v0.1.8+ needs gql but doesn't declare it
|
50
|
+
# Add more services here as needed, e.g.:
|
51
|
+
# "another-service": ["dep1", "dep2"],
|
52
|
+
}
|
53
|
+
|
44
54
|
# Static known-good MCP service configurations
|
45
55
|
# These are the correct, tested configurations that work reliably
|
46
56
|
# Note: Commands will be resolved to full paths dynamically in get_static_service_config()
|
@@ -72,15 +82,75 @@ class MCPConfigManager:
|
|
72
82
|
},
|
73
83
|
}
|
74
84
|
|
75
|
-
def __init__(self):
|
76
|
-
"""Initialize the MCP configuration manager.
|
85
|
+
def __init__(self, config=None):
|
86
|
+
"""Initialize the MCP configuration manager.
|
87
|
+
|
88
|
+
Args:
|
89
|
+
config: Optional Config object for filtering services
|
90
|
+
"""
|
77
91
|
self.logger = get_logger(__name__)
|
78
92
|
self.pipx_base = Path.home() / ".local" / "pipx" / "venvs"
|
79
93
|
self.project_root = Path.cwd()
|
80
94
|
|
95
|
+
# Validate config type if provided
|
96
|
+
if config is not None:
|
97
|
+
from ..core.config import Config
|
98
|
+
|
99
|
+
if not isinstance(config, Config):
|
100
|
+
self.logger.warning(
|
101
|
+
f"Invalid config type provided to MCPConfigManager: "
|
102
|
+
f"{type(config).__name__}. Expected Config. "
|
103
|
+
f"Proceeding with config=None (all services enabled)."
|
104
|
+
)
|
105
|
+
config = None
|
106
|
+
|
107
|
+
self.config = config
|
108
|
+
|
81
109
|
# Use the proper Claude config file location
|
82
110
|
self.claude_config_path = ConfigLocation.CLAUDE_JSON.value
|
83
111
|
|
112
|
+
def should_enable_service(self, service_name: str) -> bool:
|
113
|
+
"""
|
114
|
+
Check if an MCP service should be enabled based on startup configuration.
|
115
|
+
|
116
|
+
Args:
|
117
|
+
service_name: Name of the MCP service
|
118
|
+
|
119
|
+
Returns:
|
120
|
+
True if the service should be enabled, False otherwise
|
121
|
+
"""
|
122
|
+
# If no config provided, enable all services by default
|
123
|
+
if self.config is None:
|
124
|
+
return True
|
125
|
+
|
126
|
+
# Import Config here to avoid circular import at module level
|
127
|
+
from ..core.config import Config
|
128
|
+
|
129
|
+
# Validate config type
|
130
|
+
if not isinstance(self.config, Config):
|
131
|
+
self.logger.warning(
|
132
|
+
f"Invalid config type: {type(self.config).__name__}, "
|
133
|
+
f"expected Config. Enabling all services by default."
|
134
|
+
)
|
135
|
+
return True
|
136
|
+
|
137
|
+
# Get startup configuration
|
138
|
+
enabled_services = self.config.get("startup.enabled_mcp_services", None)
|
139
|
+
|
140
|
+
# If no startup preferences configured, enable all services
|
141
|
+
if enabled_services is None:
|
142
|
+
return True
|
143
|
+
|
144
|
+
# Check if this service is in the enabled list
|
145
|
+
is_enabled = service_name in enabled_services
|
146
|
+
|
147
|
+
if not is_enabled:
|
148
|
+
self.logger.debug(
|
149
|
+
f"MCP service '{service_name}' disabled by startup configuration"
|
150
|
+
)
|
151
|
+
|
152
|
+
return is_enabled
|
153
|
+
|
84
154
|
def detect_service_path(self, service_name: str) -> Optional[str]:
|
85
155
|
"""
|
86
156
|
Detect the best path for an MCP service.
|
@@ -660,10 +730,8 @@ class MCPConfigManager:
|
|
660
730
|
claude_config["projects"] = {}
|
661
731
|
updated = True
|
662
732
|
|
663
|
-
#
|
664
|
-
|
665
|
-
if not fix_success:
|
666
|
-
self.logger.warning(f"Some MCP services could not be fixed: {fix_message}")
|
733
|
+
# Note: fix_mcp_service_issues() is already called during CLI initialization
|
734
|
+
# Calling it here would duplicate the service health checks
|
667
735
|
|
668
736
|
# Process ALL projects in the config, not just current one
|
669
737
|
projects_to_update = list(claude_config.get("projects", {}).keys())
|
@@ -696,19 +764,10 @@ class MCPConfigManager:
|
|
696
764
|
project_config["mcpServers"] = {}
|
697
765
|
updated = True
|
698
766
|
|
699
|
-
# Check and fix each service configuration
|
700
|
-
|
701
|
-
# Get the correct static configuration with project-specific paths
|
702
|
-
correct_config = self.get_static_service_config(
|
703
|
-
service_name, project_key
|
704
|
-
)
|
705
|
-
|
706
|
-
if not correct_config:
|
707
|
-
self.logger.warning(
|
708
|
-
f"No static config available for {service_name}"
|
709
|
-
)
|
710
|
-
continue
|
767
|
+
# Check and fix each service configuration - now filtered by startup config
|
768
|
+
services_to_configure = self.get_filtered_services()
|
711
769
|
|
770
|
+
for service_name, correct_config in services_to_configure.items():
|
712
771
|
# Check if service exists and has correct configuration
|
713
772
|
existing_config = project_config["mcpServers"].get(service_name)
|
714
773
|
|
@@ -734,6 +793,29 @@ class MCPConfigManager:
|
|
734
793
|
f"Updated MCP service config for {service_name} in project {Path(project_key).name}"
|
735
794
|
)
|
736
795
|
|
796
|
+
# Remove disabled services from configuration
|
797
|
+
if self.config is not None:
|
798
|
+
# Import Config here to avoid circular import
|
799
|
+
from ..core.config import Config
|
800
|
+
|
801
|
+
if isinstance(self.config, Config):
|
802
|
+
enabled_services = self.config.get(
|
803
|
+
"startup.enabled_mcp_services", None
|
804
|
+
)
|
805
|
+
if enabled_services is not None:
|
806
|
+
# Remove services that are not in the enabled list
|
807
|
+
services_to_remove = []
|
808
|
+
for service_name in project_config["mcpServers"]:
|
809
|
+
if service_name not in enabled_services:
|
810
|
+
services_to_remove.append(service_name)
|
811
|
+
|
812
|
+
for service_name in services_to_remove:
|
813
|
+
del project_config["mcpServers"][service_name]
|
814
|
+
updated = True
|
815
|
+
self.logger.debug(
|
816
|
+
f"Removed disabled service {service_name} from project {Path(project_key).name}"
|
817
|
+
)
|
818
|
+
|
737
819
|
# Write updated config if changes were made
|
738
820
|
if updated:
|
739
821
|
try:
|
@@ -943,6 +1025,13 @@ class MCPConfigManager:
|
|
943
1025
|
)
|
944
1026
|
|
945
1027
|
if result.returncode == 0:
|
1028
|
+
# Inject any missing dependencies if needed
|
1029
|
+
if service_name in self.SERVICE_MISSING_DEPENDENCIES:
|
1030
|
+
self.logger.debug(
|
1031
|
+
f"Injecting missing dependencies for newly installed {service_name}..."
|
1032
|
+
)
|
1033
|
+
self._inject_missing_dependencies(service_name)
|
1034
|
+
|
946
1035
|
# Verify installation worked
|
947
1036
|
if self._verify_service_installed(service_name, "pipx"):
|
948
1037
|
return True, "pipx"
|
@@ -1050,18 +1139,19 @@ class MCPConfigManager:
|
|
1050
1139
|
Returns:
|
1051
1140
|
Tuple of (success, message)
|
1052
1141
|
"""
|
1053
|
-
self.logger.info("🔍 Checking MCP services for issues...")
|
1054
|
-
|
1055
1142
|
services_to_fix = []
|
1056
1143
|
fixed_services = []
|
1057
1144
|
failed_services = []
|
1058
1145
|
|
1059
1146
|
# Check each service for issues
|
1060
1147
|
for service_name in self.PIPX_SERVICES:
|
1148
|
+
self.logger.info(f"🔍 Checking {service_name} for issues...")
|
1061
1149
|
issue_type = self._detect_service_issue(service_name)
|
1062
1150
|
if issue_type:
|
1063
1151
|
services_to_fix.append((service_name, issue_type))
|
1064
|
-
self.logger.
|
1152
|
+
self.logger.info(f" ⚠️ Found issue with {service_name}: {issue_type}")
|
1153
|
+
else:
|
1154
|
+
self.logger.debug(f" ✅ {service_name} is functioning correctly")
|
1065
1155
|
|
1066
1156
|
if not services_to_fix:
|
1067
1157
|
return True, "All MCP services are functioning correctly"
|
@@ -1092,18 +1182,45 @@ class MCPConfigManager:
|
|
1092
1182
|
failed_services.append(f"{service_name} (reinstall failed)")
|
1093
1183
|
|
1094
1184
|
elif issue_type == "missing_dependency":
|
1095
|
-
# Fix missing dependencies
|
1185
|
+
# Fix missing dependencies - try injection first, then reinstall if needed
|
1186
|
+
self.logger.info(
|
1187
|
+
f" {service_name} has missing dependencies - attempting fix..."
|
1188
|
+
)
|
1189
|
+
|
1190
|
+
# First try to inject dependencies without reinstalling
|
1191
|
+
injection_success = self._inject_missing_dependencies(service_name)
|
1192
|
+
|
1193
|
+
if injection_success:
|
1194
|
+
# Verify the fix worked
|
1195
|
+
issue_after_injection = self._detect_service_issue(service_name)
|
1196
|
+
if issue_after_injection is None:
|
1197
|
+
fixed_services.append(f"{service_name} (dependencies injected)")
|
1198
|
+
self.logger.info(
|
1199
|
+
f" ✅ Fixed {service_name} with dependency injection"
|
1200
|
+
)
|
1201
|
+
continue # Move to next service
|
1202
|
+
|
1203
|
+
# If injection alone didn't work, try full reinstall
|
1096
1204
|
self.logger.info(
|
1097
|
-
|
1205
|
+
" Dependency injection insufficient, trying full reinstall..."
|
1098
1206
|
)
|
1099
1207
|
success = self._auto_reinstall_mcp_service(service_name)
|
1100
1208
|
if success:
|
1101
|
-
fixed_services.append(
|
1102
|
-
|
1103
|
-
self.logger.warning(
|
1104
|
-
f" Auto-reinstall failed for {service_name}. Manual fix: "
|
1105
|
-
f"pipx uninstall {service_name} && pipx install {service_name}"
|
1209
|
+
fixed_services.append(
|
1210
|
+
f"{service_name} (auto-reinstalled with dependencies)"
|
1106
1211
|
)
|
1212
|
+
else:
|
1213
|
+
# Provide specific manual fix for known services
|
1214
|
+
if service_name == "mcp-ticketer":
|
1215
|
+
self.logger.warning(
|
1216
|
+
f" Auto-fix failed for {service_name}. Manual fix: "
|
1217
|
+
f"pipx uninstall {service_name} && pipx install {service_name} && pipx inject {service_name} gql"
|
1218
|
+
)
|
1219
|
+
else:
|
1220
|
+
self.logger.warning(
|
1221
|
+
f" Auto-reinstall failed for {service_name}. Manual fix: "
|
1222
|
+
f"pipx uninstall {service_name} && pipx install {service_name}"
|
1223
|
+
)
|
1107
1224
|
failed_services.append(f"{service_name} (auto-reinstall failed)")
|
1108
1225
|
|
1109
1226
|
elif issue_type == "path_issue":
|
@@ -1129,7 +1246,16 @@ class MCPConfigManager:
|
|
1129
1246
|
message += "\n\n💡 Manual fix instructions:"
|
1130
1247
|
for failed in failed_services:
|
1131
1248
|
service = failed.split(" ")[0]
|
1132
|
-
|
1249
|
+
if service in self.SERVICE_MISSING_DEPENDENCIES:
|
1250
|
+
deps = " ".join(
|
1251
|
+
[
|
1252
|
+
f"&& pipx inject {service} {dep}"
|
1253
|
+
for dep in self.SERVICE_MISSING_DEPENDENCIES[service]
|
1254
|
+
]
|
1255
|
+
)
|
1256
|
+
message += f"\n • {service}: pipx uninstall {service} && pipx install {service} {deps}"
|
1257
|
+
else:
|
1258
|
+
message += f"\n • {service}: pipx uninstall {service} && pipx install {service}"
|
1133
1259
|
|
1134
1260
|
return success, message
|
1135
1261
|
|
@@ -1148,7 +1274,66 @@ class MCPConfigManager:
|
|
1148
1274
|
|
1149
1275
|
# Try to run the service with --help to detect issues
|
1150
1276
|
try:
|
1151
|
-
#
|
1277
|
+
# First check if service is installed in pipx venv
|
1278
|
+
pipx_venv_bin = self.pipx_base / service_name / "bin" / service_name
|
1279
|
+
if pipx_venv_bin.exists():
|
1280
|
+
# Test the installed version directly (has injected dependencies)
|
1281
|
+
# This avoids using pipx run which downloads a fresh cache copy without dependencies
|
1282
|
+
self.logger.debug(
|
1283
|
+
f" Testing {service_name} from installed pipx venv: {pipx_venv_bin}"
|
1284
|
+
)
|
1285
|
+
result = subprocess.run(
|
1286
|
+
[str(pipx_venv_bin), "--help"],
|
1287
|
+
capture_output=True,
|
1288
|
+
text=True,
|
1289
|
+
timeout=10,
|
1290
|
+
check=False,
|
1291
|
+
)
|
1292
|
+
|
1293
|
+
# Check for specific error patterns in installed version
|
1294
|
+
stderr_lower = result.stderr.lower()
|
1295
|
+
stdout_lower = result.stdout.lower()
|
1296
|
+
combined_output = stderr_lower + stdout_lower
|
1297
|
+
|
1298
|
+
# Import errors in installed version (should be rare if dependencies injected)
|
1299
|
+
if (
|
1300
|
+
"modulenotfounderror" in combined_output
|
1301
|
+
or "importerror" in combined_output
|
1302
|
+
):
|
1303
|
+
# Check if it's specifically the gql dependency for mcp-ticketer
|
1304
|
+
if service_name == "mcp-ticketer" and "gql" in combined_output:
|
1305
|
+
return "missing_dependency"
|
1306
|
+
return "import_error"
|
1307
|
+
|
1308
|
+
# Path issues
|
1309
|
+
if "no such file or directory" in combined_output:
|
1310
|
+
return "path_issue"
|
1311
|
+
|
1312
|
+
# If help text appears, service is working
|
1313
|
+
if (
|
1314
|
+
"usage:" in combined_output
|
1315
|
+
or "help" in combined_output
|
1316
|
+
or result.returncode in [0, 1]
|
1317
|
+
):
|
1318
|
+
self.logger.debug(
|
1319
|
+
f" {service_name} is working correctly (installed in venv)"
|
1320
|
+
)
|
1321
|
+
return None # Service is working
|
1322
|
+
|
1323
|
+
# Unknown issue
|
1324
|
+
if result.returncode not in [0, 1]:
|
1325
|
+
self.logger.debug(
|
1326
|
+
f"{service_name} returned unexpected exit code: {result.returncode}"
|
1327
|
+
)
|
1328
|
+
return "unknown_error"
|
1329
|
+
|
1330
|
+
return None # Default to working if no issues detected
|
1331
|
+
|
1332
|
+
# Service not installed in pipx venv - use pipx run for detection
|
1333
|
+
# Note: pipx run uses cache which may not have injected dependencies
|
1334
|
+
self.logger.debug(
|
1335
|
+
f" Testing {service_name} via pipx run (not installed in venv)"
|
1336
|
+
)
|
1152
1337
|
result = subprocess.run(
|
1153
1338
|
["pipx", "run", service_name, "--help"],
|
1154
1339
|
capture_output=True,
|
@@ -1169,15 +1354,17 @@ class MCPConfigManager:
|
|
1169
1354
|
):
|
1170
1355
|
return "not_installed"
|
1171
1356
|
|
1172
|
-
# Import errors
|
1357
|
+
# Import errors when using pipx run (cache version)
|
1173
1358
|
if (
|
1174
1359
|
"modulenotfounderror" in combined_output
|
1175
1360
|
or "importerror" in combined_output
|
1176
1361
|
):
|
1177
|
-
#
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1362
|
+
# Don't report missing_dependency for cache version - it may be missing injected deps
|
1363
|
+
# Just report that service needs to be installed properly
|
1364
|
+
self.logger.debug(
|
1365
|
+
f"{service_name} has import errors in pipx run cache - needs proper installation"
|
1366
|
+
)
|
1367
|
+
return "not_installed"
|
1181
1368
|
|
1182
1369
|
# Path issues
|
1183
1370
|
if "no such file or directory" in combined_output:
|
@@ -1240,6 +1427,13 @@ class MCPConfigManager:
|
|
1240
1427
|
)
|
1241
1428
|
|
1242
1429
|
if install_result.returncode == 0:
|
1430
|
+
# Inject any missing dependencies if needed
|
1431
|
+
if service_name in self.SERVICE_MISSING_DEPENDENCIES:
|
1432
|
+
self.logger.debug(
|
1433
|
+
f"Injecting missing dependencies for {service_name}..."
|
1434
|
+
)
|
1435
|
+
self._inject_missing_dependencies(service_name)
|
1436
|
+
|
1243
1437
|
# Verify the reinstall worked
|
1244
1438
|
issue = self._detect_service_issue(service_name)
|
1245
1439
|
if issue is None:
|
@@ -1258,6 +1452,66 @@ class MCPConfigManager:
|
|
1258
1452
|
self.logger.error(f"Error reinstalling {service_name}: {e}")
|
1259
1453
|
return False
|
1260
1454
|
|
1455
|
+
def _inject_missing_dependencies(self, service_name: str) -> bool:
|
1456
|
+
"""
|
1457
|
+
Inject missing dependencies into a pipx-installed MCP service.
|
1458
|
+
|
1459
|
+
Some MCP services don't properly declare all their dependencies in their
|
1460
|
+
package metadata, which causes import errors when pipx creates isolated
|
1461
|
+
virtual environments. This method injects the missing dependencies using
|
1462
|
+
pipx inject.
|
1463
|
+
|
1464
|
+
Args:
|
1465
|
+
service_name: Name of the MCP service to fix
|
1466
|
+
|
1467
|
+
Returns:
|
1468
|
+
True if dependencies were injected successfully or no injection needed, False otherwise
|
1469
|
+
"""
|
1470
|
+
# Check if this service has known missing dependencies
|
1471
|
+
if service_name not in self.SERVICE_MISSING_DEPENDENCIES:
|
1472
|
+
return True # No dependencies to inject
|
1473
|
+
|
1474
|
+
missing_deps = self.SERVICE_MISSING_DEPENDENCIES[service_name]
|
1475
|
+
if not missing_deps:
|
1476
|
+
return True # No dependencies to inject
|
1477
|
+
|
1478
|
+
self.logger.info(
|
1479
|
+
f" → Injecting missing dependencies for {service_name}: {', '.join(missing_deps)}"
|
1480
|
+
)
|
1481
|
+
|
1482
|
+
all_successful = True
|
1483
|
+
for dep in missing_deps:
|
1484
|
+
try:
|
1485
|
+
self.logger.debug(f" Injecting {dep} into {service_name}...")
|
1486
|
+
result = subprocess.run(
|
1487
|
+
["pipx", "inject", service_name, dep],
|
1488
|
+
capture_output=True,
|
1489
|
+
text=True,
|
1490
|
+
timeout=60,
|
1491
|
+
check=False,
|
1492
|
+
)
|
1493
|
+
|
1494
|
+
if result.returncode == 0:
|
1495
|
+
self.logger.info(f" ✅ Successfully injected {dep}")
|
1496
|
+
# Check if already injected (pipx will complain if package already exists)
|
1497
|
+
elif (
|
1498
|
+
"already satisfied" in result.stderr.lower()
|
1499
|
+
or "already installed" in result.stderr.lower()
|
1500
|
+
):
|
1501
|
+
self.logger.debug(f" {dep} already present in {service_name}")
|
1502
|
+
else:
|
1503
|
+
self.logger.error(f" Failed to inject {dep}: {result.stderr}")
|
1504
|
+
all_successful = False
|
1505
|
+
|
1506
|
+
except subprocess.TimeoutExpired:
|
1507
|
+
self.logger.error(f" Timeout while injecting {dep}")
|
1508
|
+
all_successful = False
|
1509
|
+
except Exception as e:
|
1510
|
+
self.logger.error(f" Error injecting {dep}: {e}")
|
1511
|
+
all_successful = False
|
1512
|
+
|
1513
|
+
return all_successful
|
1514
|
+
|
1261
1515
|
def _auto_reinstall_mcp_service(self, service_name: str) -> bool:
|
1262
1516
|
"""
|
1263
1517
|
Automatically reinstall an MCP service with missing dependencies.
|
@@ -1312,6 +1566,16 @@ class MCPConfigManager:
|
|
1312
1566
|
)
|
1313
1567
|
return False
|
1314
1568
|
|
1569
|
+
# Inject any missing dependencies that pipx doesn't handle automatically
|
1570
|
+
if service_name in self.SERVICE_MISSING_DEPENDENCIES:
|
1571
|
+
self.logger.info(
|
1572
|
+
f" → Fixing missing dependencies for {service_name}..."
|
1573
|
+
)
|
1574
|
+
if not self._inject_missing_dependencies(service_name):
|
1575
|
+
self.logger.warning(
|
1576
|
+
f"Failed to inject all dependencies for {service_name}, but continuing..."
|
1577
|
+
)
|
1578
|
+
|
1315
1579
|
# Verify the reinstall worked
|
1316
1580
|
self.logger.info(f" → Verifying {service_name} installation...")
|
1317
1581
|
issue = self._detect_service_issue(service_name)
|
@@ -1319,9 +1583,17 @@ class MCPConfigManager:
|
|
1319
1583
|
if issue is None:
|
1320
1584
|
self.logger.info(f" ✅ Successfully reinstalled {service_name}")
|
1321
1585
|
return True
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1586
|
+
|
1587
|
+
# If still has missing dependency issue after injection, log specific instructions
|
1588
|
+
if issue == "missing_dependency" and service_name == "mcp-ticketer":
|
1589
|
+
self.logger.error(
|
1590
|
+
f" {service_name} still has missing dependencies after injection. "
|
1591
|
+
f"Manual fix: pipx inject {service_name} gql"
|
1592
|
+
)
|
1593
|
+
else:
|
1594
|
+
self.logger.warning(
|
1595
|
+
f"Reinstalled {service_name} but still has issue: {issue}"
|
1596
|
+
)
|
1325
1597
|
return False
|
1326
1598
|
|
1327
1599
|
except subprocess.TimeoutExpired:
|
@@ -1439,3 +1711,21 @@ class MCPConfigManager:
|
|
1439
1711
|
|
1440
1712
|
# For other services, try pipx run
|
1441
1713
|
return None
|
1714
|
+
|
1715
|
+
def get_filtered_services(self) -> Dict[str, Dict]:
|
1716
|
+
"""Get all MCP service configurations filtered by startup configuration.
|
1717
|
+
|
1718
|
+
Returns:
|
1719
|
+
Dictionary of service configurations, filtered based on startup settings
|
1720
|
+
"""
|
1721
|
+
filtered_services = {}
|
1722
|
+
|
1723
|
+
for service_name in self.STATIC_MCP_CONFIGS:
|
1724
|
+
if self.should_enable_service(service_name):
|
1725
|
+
# Get the actual service configuration with proper paths
|
1726
|
+
service_config = self.get_static_service_config(service_name)
|
1727
|
+
if service_config:
|
1728
|
+
filtered_services[service_name] = service_config
|
1729
|
+
# Removed noisy debug logging that was called multiple times per startup
|
1730
|
+
|
1731
|
+
return filtered_services
|