claude-mpm 4.4.3__py3-none-any.whl → 4.4.5__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/agents/agent_loader.py +3 -2
- claude_mpm/agents/agent_loader_integration.py +2 -1
- claude_mpm/agents/async_agent_loader.py +2 -2
- claude_mpm/agents/base_agent_loader.py +2 -2
- claude_mpm/agents/frontmatter_validator.py +1 -0
- claude_mpm/agents/system_agent_config.py +2 -1
- claude_mpm/cli/commands/doctor.py +44 -5
- claude_mpm/cli/commands/mpm_init.py +116 -62
- claude_mpm/cli/parsers/configure_parser.py +3 -1
- claude_mpm/cli/startup_logging.py +1 -3
- claude_mpm/config/agent_config.py +1 -1
- claude_mpm/config/paths.py +2 -1
- claude_mpm/core/agent_name_normalizer.py +1 -0
- claude_mpm/core/config.py +2 -1
- claude_mpm/core/config_aliases.py +2 -1
- claude_mpm/core/file_utils.py +0 -1
- claude_mpm/core/framework/__init__.py +6 -6
- claude_mpm/core/framework/formatters/__init__.py +2 -2
- claude_mpm/core/framework/formatters/capability_generator.py +19 -8
- claude_mpm/core/framework/formatters/content_formatter.py +8 -3
- claude_mpm/core/framework/formatters/context_generator.py +7 -3
- claude_mpm/core/framework/loaders/__init__.py +3 -3
- claude_mpm/core/framework/loaders/agent_loader.py +7 -3
- claude_mpm/core/framework/loaders/file_loader.py +16 -6
- claude_mpm/core/framework/loaders/instruction_loader.py +16 -6
- claude_mpm/core/framework/loaders/packaged_loader.py +36 -12
- claude_mpm/core/framework/processors/__init__.py +2 -2
- claude_mpm/core/framework/processors/memory_processor.py +14 -6
- claude_mpm/core/framework/processors/metadata_processor.py +5 -5
- claude_mpm/core/framework/processors/template_processor.py +12 -6
- claude_mpm/core/framework_loader.py +44 -20
- claude_mpm/core/log_manager.py +2 -1
- claude_mpm/core/tool_access_control.py +1 -0
- claude_mpm/core/unified_agent_registry.py +2 -1
- claude_mpm/core/unified_paths.py +1 -0
- claude_mpm/experimental/cli_enhancements.py +1 -0
- claude_mpm/hooks/base_hook.py +1 -0
- claude_mpm/hooks/instruction_reinforcement.py +1 -0
- claude_mpm/hooks/kuzu_memory_hook.py +20 -13
- claude_mpm/hooks/validation_hooks.py +1 -1
- claude_mpm/scripts/mpm_doctor.py +1 -0
- claude_mpm/services/agents/loading/agent_profile_loader.py +1 -1
- claude_mpm/services/agents/loading/base_agent_manager.py +1 -1
- claude_mpm/services/agents/loading/framework_agent_loader.py +1 -1
- claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -0
- claude_mpm/services/agents/management/agent_management_service.py +1 -1
- claude_mpm/services/agents/memory/memory_categorization_service.py +0 -1
- claude_mpm/services/agents/memory/memory_file_service.py +6 -2
- claude_mpm/services/agents/memory/memory_format_service.py +0 -1
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +1 -1
- claude_mpm/services/async_session_logger.py +1 -1
- claude_mpm/services/claude_session_logger.py +1 -0
- claude_mpm/services/core/path_resolver.py +1 -0
- claude_mpm/services/diagnostics/checks/__init__.py +2 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +126 -25
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +451 -0
- claude_mpm/services/diagnostics/diagnostic_runner.py +3 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +259 -32
- claude_mpm/services/event_bus/direct_relay.py +2 -1
- claude_mpm/services/event_bus/event_bus.py +1 -0
- claude_mpm/services/event_bus/relay.py +3 -2
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +1 -1
- claude_mpm/services/infrastructure/daemon_manager.py +1 -1
- claude_mpm/services/mcp_config_manager.py +301 -54
- claude_mpm/services/mcp_gateway/core/process_pool.py +62 -23
- claude_mpm/services/mcp_gateway/tools/__init__.py +6 -5
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +3 -1
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +16 -31
- claude_mpm/services/memory/cache/simple_cache.py +1 -1
- claude_mpm/services/project/archive_manager.py +159 -96
- claude_mpm/services/project/documentation_manager.py +64 -45
- claude_mpm/services/project/enhanced_analyzer.py +132 -89
- claude_mpm/services/project/project_organizer.py +225 -131
- claude_mpm/services/response_tracker.py +1 -1
- claude_mpm/services/socketio/server/eventbus_integration.py +1 -1
- claude_mpm/services/unified/__init__.py +1 -1
- claude_mpm/services/unified/analyzer_strategies/__init__.py +3 -3
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +97 -53
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +81 -40
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +277 -178
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +196 -112
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +83 -49
- claude_mpm/services/unified/config_strategies/__init__.py +111 -126
- claude_mpm/services/unified/config_strategies/config_schema.py +157 -111
- claude_mpm/services/unified/config_strategies/context_strategy.py +91 -89
- claude_mpm/services/unified/config_strategies/error_handling_strategy.py +183 -173
- claude_mpm/services/unified/config_strategies/file_loader_strategy.py +160 -152
- claude_mpm/services/unified/config_strategies/unified_config_service.py +124 -112
- claude_mpm/services/unified/config_strategies/validation_strategy.py +298 -259
- claude_mpm/services/unified/deployment_strategies/__init__.py +7 -7
- claude_mpm/services/unified/deployment_strategies/base.py +24 -28
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +168 -88
- claude_mpm/services/unified/deployment_strategies/local.py +49 -34
- claude_mpm/services/unified/deployment_strategies/utils.py +39 -43
- claude_mpm/services/unified/deployment_strategies/vercel.py +30 -24
- claude_mpm/services/unified/interfaces.py +0 -26
- claude_mpm/services/unified/migration.py +17 -40
- claude_mpm/services/unified/strategies.py +9 -26
- claude_mpm/services/unified/unified_analyzer.py +48 -44
- claude_mpm/services/unified/unified_config.py +21 -19
- claude_mpm/services/unified/unified_deployment.py +21 -26
- claude_mpm/storage/state_storage.py +1 -0
- claude_mpm/utils/agent_dependency_loader.py +18 -6
- claude_mpm/utils/common.py +14 -12
- claude_mpm/utils/database_connector.py +15 -12
- claude_mpm/utils/error_handler.py +1 -0
- claude_mpm/utils/log_cleanup.py +1 -0
- claude_mpm/utils/path_operations.py +1 -0
- claude_mpm/utils/session_logging.py +1 -1
- claude_mpm/utils/subprocess_utils.py +1 -0
- claude_mpm/validation/agent_validator.py +1 -1
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.5.dist-info}/METADATA +35 -15
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.5.dist-info}/RECORD +118 -117
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.5.dist-info}/WHEEL +0 -0
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.5.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.5.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.5.dist-info}/top_level.txt +0 -0
@@ -11,6 +11,7 @@ MCP service installations.
|
|
11
11
|
|
12
12
|
import json
|
13
13
|
import subprocess
|
14
|
+
import sys
|
14
15
|
from datetime import datetime, timezone
|
15
16
|
from enum import Enum
|
16
17
|
from pathlib import Path
|
@@ -76,6 +77,7 @@ class MCPConfigManager:
|
|
76
77
|
|
77
78
|
# Check system PATH (including homebrew)
|
78
79
|
import shutil
|
80
|
+
|
79
81
|
system_path = shutil.which(service_name)
|
80
82
|
if system_path and system_path not in candidates:
|
81
83
|
candidates.append(system_path)
|
@@ -84,14 +86,13 @@ class MCPConfigManager:
|
|
84
86
|
for path in candidates:
|
85
87
|
try:
|
86
88
|
result = subprocess.run(
|
87
|
-
[path, "--help"],
|
88
|
-
capture_output=True,
|
89
|
-
text=True,
|
90
|
-
timeout=5
|
89
|
+
[path, "--help"], capture_output=True, text=True, timeout=5, check=False
|
91
90
|
)
|
92
91
|
# Check if this version has MCP support
|
93
92
|
if "claude" in result.stdout or "mcp" in result.stdout:
|
94
|
-
self.logger.debug(
|
93
|
+
self.logger.debug(
|
94
|
+
f"Found kuzu-memory with MCP support at {path}"
|
95
|
+
)
|
95
96
|
return path
|
96
97
|
except:
|
97
98
|
pass
|
@@ -125,7 +126,9 @@ class MCPConfigManager:
|
|
125
126
|
)
|
126
127
|
return local_path
|
127
128
|
|
128
|
-
self.logger.debug(
|
129
|
+
self.logger.debug(
|
130
|
+
f"Service {service_name} not found - will auto-install when needed"
|
131
|
+
)
|
129
132
|
return None
|
130
133
|
|
131
134
|
def _check_pipx_installation(self, service_name: str) -> Optional[str]:
|
@@ -192,58 +195,161 @@ class MCPConfigManager:
|
|
192
195
|
"""
|
193
196
|
Generate configuration for a specific MCP service.
|
194
197
|
|
198
|
+
Prefers 'pipx run' or 'uvx' commands over direct execution for better isolation.
|
199
|
+
|
195
200
|
Args:
|
196
201
|
service_name: Name of the MCP service
|
197
202
|
|
198
203
|
Returns:
|
199
204
|
Service configuration dict or None if service not found
|
200
205
|
"""
|
201
|
-
|
202
|
-
|
203
|
-
|
206
|
+
import shutil
|
207
|
+
|
208
|
+
# Check for pipx run first (preferred for isolation)
|
209
|
+
use_pipx_run = False
|
210
|
+
use_uvx = False
|
211
|
+
|
212
|
+
# Try pipx run test
|
213
|
+
if shutil.which("pipx"):
|
214
|
+
try:
|
215
|
+
result = subprocess.run(
|
216
|
+
["pipx", "run", service_name, "--version"],
|
217
|
+
capture_output=True,
|
218
|
+
text=True,
|
219
|
+
timeout=5,
|
220
|
+
check=False,
|
221
|
+
)
|
222
|
+
if result.returncode == 0 or "version" in result.stdout.lower():
|
223
|
+
use_pipx_run = True
|
224
|
+
self.logger.debug(f"Will use 'pipx run' for {service_name}")
|
225
|
+
except:
|
226
|
+
pass
|
227
|
+
|
228
|
+
# Try uvx if pipx run not available
|
229
|
+
if not use_pipx_run and shutil.which("uvx"):
|
230
|
+
try:
|
231
|
+
result = subprocess.run(
|
232
|
+
["uvx", service_name, "--version"],
|
233
|
+
capture_output=True,
|
234
|
+
text=True,
|
235
|
+
timeout=5,
|
236
|
+
check=False,
|
237
|
+
)
|
238
|
+
if result.returncode == 0 or "version" in result.stdout.lower():
|
239
|
+
use_uvx = True
|
240
|
+
self.logger.debug(f"Will use 'uvx' for {service_name}")
|
241
|
+
except:
|
242
|
+
pass
|
204
243
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
244
|
+
# If neither work, try to find direct path
|
245
|
+
service_path = None
|
246
|
+
if not use_pipx_run and not use_uvx:
|
247
|
+
service_path = self.detect_service_path(service_name)
|
248
|
+
if not service_path:
|
249
|
+
return None
|
250
|
+
|
251
|
+
# Build configuration
|
252
|
+
config = {"type": "stdio"}
|
209
253
|
|
210
254
|
# Service-specific configurations
|
211
255
|
if service_name == "mcp-vector-search":
|
212
|
-
|
213
|
-
"
|
214
|
-
"
|
215
|
-
|
216
|
-
|
256
|
+
if use_pipx_run:
|
257
|
+
config["command"] = "pipx"
|
258
|
+
config["args"] = [
|
259
|
+
"run",
|
260
|
+
"mcp-vector-search",
|
261
|
+
"-m",
|
262
|
+
"mcp_vector_search.mcp.server",
|
263
|
+
str(self.project_root),
|
264
|
+
]
|
265
|
+
elif use_uvx:
|
266
|
+
config["command"] = "uvx"
|
267
|
+
config["args"] = [
|
268
|
+
"mcp-vector-search",
|
269
|
+
"-m",
|
270
|
+
"mcp_vector_search.mcp.server",
|
271
|
+
str(self.project_root),
|
272
|
+
]
|
273
|
+
else:
|
274
|
+
config["command"] = service_path
|
275
|
+
config["args"] = [
|
276
|
+
"-m",
|
277
|
+
"mcp_vector_search.mcp.server",
|
278
|
+
str(self.project_root),
|
279
|
+
]
|
217
280
|
config["env"] = {}
|
281
|
+
|
218
282
|
elif service_name == "mcp-browser":
|
219
|
-
|
283
|
+
if use_pipx_run:
|
284
|
+
config["command"] = "pipx"
|
285
|
+
config["args"] = ["run", "mcp-browser", "mcp"]
|
286
|
+
elif use_uvx:
|
287
|
+
config["command"] = "uvx"
|
288
|
+
config["args"] = ["mcp-browser", "mcp"]
|
289
|
+
else:
|
290
|
+
config["command"] = service_path
|
291
|
+
config["args"] = ["mcp"]
|
220
292
|
config["env"] = {"MCP_BROWSER_HOME": str(Path.home() / ".mcp-browser")}
|
293
|
+
|
221
294
|
elif service_name == "mcp-ticketer":
|
222
|
-
|
295
|
+
if use_pipx_run:
|
296
|
+
config["command"] = "pipx"
|
297
|
+
config["args"] = ["run", "mcp-ticketer", "mcp"]
|
298
|
+
elif use_uvx:
|
299
|
+
config["command"] = "uvx"
|
300
|
+
config["args"] = ["mcp-ticketer", "mcp"]
|
301
|
+
else:
|
302
|
+
config["command"] = service_path
|
303
|
+
config["args"] = ["mcp"]
|
304
|
+
|
223
305
|
elif service_name == "kuzu-memory":
|
224
|
-
#
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
306
|
+
# Determine kuzu-memory command version
|
307
|
+
kuzu_args = []
|
308
|
+
test_cmd = None
|
309
|
+
|
310
|
+
if use_pipx_run:
|
311
|
+
test_cmd = ["pipx", "run", "kuzu-memory", "--help"]
|
312
|
+
elif use_uvx:
|
313
|
+
test_cmd = ["uvx", "kuzu-memory", "--help"]
|
314
|
+
elif service_path:
|
315
|
+
test_cmd = [service_path, "--help"]
|
316
|
+
|
317
|
+
if test_cmd:
|
318
|
+
try:
|
319
|
+
result = subprocess.run(
|
320
|
+
test_cmd, capture_output=True, text=True, timeout=10, check=False
|
321
|
+
)
|
322
|
+
if "claude" in result.stdout:
|
323
|
+
# v1.1.0+ with claude command
|
324
|
+
kuzu_args = ["claude", "mcp-server"]
|
325
|
+
else:
|
326
|
+
# v1.0.0 with serve command
|
327
|
+
kuzu_args = ["serve"]
|
328
|
+
except:
|
329
|
+
# Default to newer version command
|
330
|
+
kuzu_args = ["claude", "mcp-server"]
|
331
|
+
|
332
|
+
if use_pipx_run:
|
333
|
+
config["command"] = "pipx"
|
334
|
+
config["args"] = ["run", "kuzu-memory"] + kuzu_args
|
335
|
+
elif use_uvx:
|
336
|
+
config["command"] = "uvx"
|
337
|
+
config["args"] = ["kuzu-memory"] + kuzu_args
|
338
|
+
else:
|
339
|
+
config["command"] = service_path
|
340
|
+
config["args"] = kuzu_args
|
341
|
+
|
244
342
|
else:
|
245
343
|
# Generic config for unknown services
|
246
|
-
|
344
|
+
if use_pipx_run:
|
345
|
+
config["command"] = "pipx"
|
346
|
+
config["args"] = ["run", service_name]
|
347
|
+
elif use_uvx:
|
348
|
+
config["command"] = "uvx"
|
349
|
+
config["args"] = [service_name]
|
350
|
+
else:
|
351
|
+
config["command"] = service_path
|
352
|
+
config["args"] = []
|
247
353
|
|
248
354
|
return config
|
249
355
|
|
@@ -464,7 +570,7 @@ class MCPConfigManager:
|
|
464
570
|
|
465
571
|
def install_missing_services(self) -> Tuple[bool, str]:
|
466
572
|
"""
|
467
|
-
Install missing MCP services via pipx.
|
573
|
+
Install missing MCP services via pipx with verification and fallbacks.
|
468
574
|
|
469
575
|
Returns:
|
470
576
|
Tuple of (success, message)
|
@@ -481,22 +587,163 @@ class MCPConfigManager:
|
|
481
587
|
failed = []
|
482
588
|
|
483
589
|
for service_name in missing:
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
check=True,
|
491
|
-
)
|
492
|
-
installed.append(service_name)
|
493
|
-
self.logger.info(f"Successfully installed {service_name}")
|
494
|
-
except subprocess.CalledProcessError as e:
|
590
|
+
# Try pipx install first
|
591
|
+
success, method = self._install_service_with_fallback(service_name)
|
592
|
+
if success:
|
593
|
+
installed.append(f"{service_name} ({method})")
|
594
|
+
self.logger.info(f"Successfully installed {service_name} via {method}")
|
595
|
+
else:
|
495
596
|
failed.append(service_name)
|
496
|
-
self.logger.error(f"Failed to install {service_name}
|
597
|
+
self.logger.error(f"Failed to install {service_name}")
|
497
598
|
|
498
599
|
if failed:
|
499
600
|
return False, f"Failed to install: {', '.join(failed)}"
|
500
601
|
if installed:
|
501
602
|
return True, f"Successfully installed: {', '.join(installed)}"
|
502
603
|
return True, "No services needed installation"
|
604
|
+
|
605
|
+
def _install_service_with_fallback(self, service_name: str) -> Tuple[bool, str]:
|
606
|
+
"""
|
607
|
+
Install a service with multiple fallback methods.
|
608
|
+
|
609
|
+
Returns:
|
610
|
+
Tuple of (success, installation_method)
|
611
|
+
"""
|
612
|
+
import shutil
|
613
|
+
|
614
|
+
# Method 1: Try pipx install
|
615
|
+
if shutil.which("pipx"):
|
616
|
+
try:
|
617
|
+
self.logger.debug(f"Attempting to install {service_name} via pipx...")
|
618
|
+
result = subprocess.run(
|
619
|
+
["pipx", "install", service_name],
|
620
|
+
capture_output=True,
|
621
|
+
text=True,
|
622
|
+
timeout=120, # 2 minute timeout
|
623
|
+
check=False,
|
624
|
+
)
|
625
|
+
|
626
|
+
if result.returncode == 0:
|
627
|
+
# Verify installation worked
|
628
|
+
if self._verify_service_installed(service_name, "pipx"):
|
629
|
+
return True, "pipx"
|
630
|
+
|
631
|
+
self.logger.warning(
|
632
|
+
f"pipx install succeeded but verification failed for {service_name}"
|
633
|
+
)
|
634
|
+
else:
|
635
|
+
self.logger.debug(
|
636
|
+
f"pipx install failed: {result.stderr}"
|
637
|
+
)
|
638
|
+
except subprocess.TimeoutExpired:
|
639
|
+
self.logger.warning(f"pipx install timed out for {service_name}")
|
640
|
+
except Exception as e:
|
641
|
+
self.logger.debug(f"pipx install error: {e}")
|
642
|
+
|
643
|
+
# Method 2: Try uvx (if available)
|
644
|
+
if shutil.which("uvx"):
|
645
|
+
try:
|
646
|
+
self.logger.debug(f"Attempting to install {service_name} via uvx...")
|
647
|
+
result = subprocess.run(
|
648
|
+
["uvx", "install", service_name],
|
649
|
+
capture_output=True,
|
650
|
+
text=True,
|
651
|
+
timeout=120,
|
652
|
+
check=False,
|
653
|
+
)
|
654
|
+
|
655
|
+
if result.returncode == 0:
|
656
|
+
if self._verify_service_installed(service_name, "uvx"):
|
657
|
+
return True, "uvx"
|
658
|
+
except Exception as e:
|
659
|
+
self.logger.debug(f"uvx install error: {e}")
|
660
|
+
|
661
|
+
# Method 3: Try pip install --user
|
662
|
+
try:
|
663
|
+
self.logger.debug(f"Attempting to install {service_name} via pip --user...")
|
664
|
+
result = subprocess.run(
|
665
|
+
[sys.executable, "-m", "pip", "install", "--user", service_name],
|
666
|
+
capture_output=True,
|
667
|
+
text=True,
|
668
|
+
timeout=120,
|
669
|
+
check=False,
|
670
|
+
)
|
671
|
+
|
672
|
+
if result.returncode == 0:
|
673
|
+
if self._verify_service_installed(service_name, "pip"):
|
674
|
+
return True, "pip --user"
|
675
|
+
|
676
|
+
self.logger.warning(
|
677
|
+
f"pip install succeeded but verification failed for {service_name}"
|
678
|
+
)
|
679
|
+
except Exception as e:
|
680
|
+
self.logger.debug(f"pip install error: {e}")
|
681
|
+
|
682
|
+
return False, "none"
|
683
|
+
|
684
|
+
def _verify_service_installed(self, service_name: str, method: str) -> bool:
|
685
|
+
"""
|
686
|
+
Verify that a service was successfully installed and is functional.
|
687
|
+
|
688
|
+
Args:
|
689
|
+
service_name: Name of the service
|
690
|
+
method: Installation method used
|
691
|
+
|
692
|
+
Returns:
|
693
|
+
True if service is installed and functional
|
694
|
+
"""
|
695
|
+
import time
|
696
|
+
|
697
|
+
# Give the installation a moment to settle
|
698
|
+
time.sleep(1)
|
699
|
+
|
700
|
+
# Check if we can find the service
|
701
|
+
service_path = self.detect_service_path(service_name)
|
702
|
+
if not service_path:
|
703
|
+
# Try pipx run as fallback for pipx installations
|
704
|
+
if method == "pipx":
|
705
|
+
try:
|
706
|
+
result = subprocess.run(
|
707
|
+
["pipx", "run", service_name, "--version"],
|
708
|
+
capture_output=True,
|
709
|
+
text=True,
|
710
|
+
timeout=10,
|
711
|
+
check=False,
|
712
|
+
)
|
713
|
+
if result.returncode == 0 or "version" in result.stdout.lower():
|
714
|
+
self.logger.debug(f"{service_name} accessible via 'pipx run'")
|
715
|
+
return True
|
716
|
+
except:
|
717
|
+
pass
|
718
|
+
return False
|
719
|
+
|
720
|
+
# Try to verify it works
|
721
|
+
try:
|
722
|
+
# Different services may need different verification
|
723
|
+
test_commands = [
|
724
|
+
[service_path, "--version"],
|
725
|
+
[service_path, "--help"],
|
726
|
+
]
|
727
|
+
|
728
|
+
for cmd in test_commands:
|
729
|
+
result = subprocess.run(
|
730
|
+
cmd,
|
731
|
+
capture_output=True,
|
732
|
+
text=True,
|
733
|
+
timeout=10,
|
734
|
+
check=False,
|
735
|
+
)
|
736
|
+
|
737
|
+
output = (result.stdout + result.stderr).lower()
|
738
|
+
# Check for signs of success
|
739
|
+
if result.returncode == 0:
|
740
|
+
return True
|
741
|
+
# Some tools return non-zero but still work
|
742
|
+
elif any(indicator in output for indicator in ["version", "usage", "help", service_name.lower()]):
|
743
|
+
# Make sure it's not an error message
|
744
|
+
if not any(error in output for error in ["error", "not found", "traceback", "no such"]):
|
745
|
+
return True
|
746
|
+
except Exception as e:
|
747
|
+
self.logger.debug(f"Verification error for {service_name}: {e}")
|
748
|
+
|
749
|
+
return False
|
@@ -395,6 +395,7 @@ async def auto_initialize_vector_search():
|
|
395
395
|
try:
|
396
396
|
# Import MCPConfigManager to handle installation
|
397
397
|
from claude_mpm.services.mcp_config_manager import MCPConfigManager
|
398
|
+
|
398
399
|
config_manager = MCPConfigManager()
|
399
400
|
|
400
401
|
# Check if mcp-vector-search is already installed
|
@@ -411,7 +412,9 @@ async def auto_initialize_vector_search():
|
|
411
412
|
import subprocess
|
412
413
|
|
413
414
|
if not shutil.which("pipx"):
|
414
|
-
logger.warning(
|
415
|
+
logger.warning(
|
416
|
+
"⚠️ pipx not found. Please install pipx to enable automatic mcp-vector-search installation"
|
417
|
+
)
|
415
418
|
logger.info(" Install pipx with: python -m pip install --user pipx")
|
416
419
|
return
|
417
420
|
|
@@ -420,26 +423,34 @@ async def auto_initialize_vector_search():
|
|
420
423
|
["pipx", "install", "mcp-vector-search"],
|
421
424
|
capture_output=True,
|
422
425
|
text=True,
|
423
|
-
timeout=60 # 1 minute timeout for installation
|
426
|
+
timeout=60, check=False, # 1 minute timeout for installation
|
424
427
|
)
|
425
428
|
|
426
429
|
if result.returncode == 0:
|
427
430
|
logger.info("✅ mcp-vector-search installed successfully")
|
428
431
|
# Detect the newly installed path
|
429
|
-
vector_search_path = config_manager.detect_service_path(
|
432
|
+
vector_search_path = config_manager.detect_service_path(
|
433
|
+
"mcp-vector-search"
|
434
|
+
)
|
430
435
|
if not vector_search_path:
|
431
|
-
logger.warning(
|
436
|
+
logger.warning(
|
437
|
+
"mcp-vector-search installed but command not found in PATH"
|
438
|
+
)
|
432
439
|
return
|
433
440
|
|
434
441
|
# Update the Claude configuration to include the newly installed service
|
435
442
|
logger.info("📝 Updating Claude configuration...")
|
436
|
-
config_success, config_msg =
|
443
|
+
config_success, config_msg = (
|
444
|
+
config_manager.ensure_mcp_services_configured()
|
445
|
+
)
|
437
446
|
if config_success:
|
438
447
|
logger.info(f"✅ {config_msg}")
|
439
448
|
else:
|
440
449
|
logger.warning(f"⚠️ Configuration update issue: {config_msg}")
|
441
450
|
else:
|
442
|
-
logger.warning(
|
451
|
+
logger.warning(
|
452
|
+
f"Failed to install mcp-vector-search: {result.stderr}"
|
453
|
+
)
|
443
454
|
return
|
444
455
|
|
445
456
|
except subprocess.TimeoutExpired:
|
@@ -452,10 +463,14 @@ async def auto_initialize_vector_search():
|
|
452
463
|
# At this point, mcp-vector-search should be available
|
453
464
|
# Get the actual command to use
|
454
465
|
import shutil
|
466
|
+
|
455
467
|
vector_search_cmd = shutil.which("mcp-vector-search")
|
456
468
|
if not vector_search_cmd:
|
457
469
|
# Try pipx installation path as fallback
|
458
|
-
pipx_path =
|
470
|
+
pipx_path = (
|
471
|
+
Path.home()
|
472
|
+
/ ".local/pipx/venvs/mcp-vector-search/bin/mcp-vector-search"
|
473
|
+
)
|
459
474
|
if pipx_path.exists():
|
460
475
|
vector_search_cmd = str(pipx_path)
|
461
476
|
else:
|
@@ -477,10 +492,13 @@ async def auto_initialize_vector_search():
|
|
477
492
|
if chroma_db.exists() and chroma_db.stat().st_size > 0:
|
478
493
|
logger.info("✓ Vector search index is healthy and ready")
|
479
494
|
return
|
480
|
-
|
481
|
-
|
495
|
+
logger.info(
|
496
|
+
"⚠️ Vector search index may be corrupted, rebuilding..."
|
497
|
+
)
|
482
498
|
except Exception as e:
|
483
|
-
logger.debug(
|
499
|
+
logger.debug(
|
500
|
+
f"Vector search health check failed: {e}, will attempt to rebuild"
|
501
|
+
)
|
484
502
|
|
485
503
|
# Initialize or reinitialize the project
|
486
504
|
logger.info(f"🎯 Initializing vector search for project: {current_dir}")
|
@@ -488,12 +506,13 @@ async def auto_initialize_vector_search():
|
|
488
506
|
# Initialize the project (this creates the config)
|
489
507
|
# Note: mcp-vector-search operates on the current directory
|
490
508
|
import subprocess
|
509
|
+
|
491
510
|
proc = subprocess.run(
|
492
511
|
[vector_search_cmd, "init"],
|
493
512
|
capture_output=True,
|
494
513
|
text=True,
|
495
514
|
timeout=30,
|
496
|
-
cwd=str(current_dir) # Run in the project directory
|
515
|
+
cwd=str(current_dir), check=False, # Run in the project directory
|
497
516
|
)
|
498
517
|
|
499
518
|
if proc.returncode == 0:
|
@@ -508,29 +527,36 @@ async def auto_initialize_vector_search():
|
|
508
527
|
capture_output=True,
|
509
528
|
text=True,
|
510
529
|
timeout=300, # 5 minute timeout for indexing
|
511
|
-
cwd=str(current_dir) # Run in the project directory
|
530
|
+
cwd=str(current_dir), check=False, # Run in the project directory
|
512
531
|
)
|
513
532
|
if index_proc.returncode == 0:
|
514
533
|
logger.info("✅ Project indexing completed successfully")
|
515
534
|
# Parse output to show statistics if available
|
516
535
|
if "indexed" in index_proc.stdout.lower():
|
517
536
|
# Extract and log indexing statistics
|
518
|
-
lines = index_proc.stdout.strip().split(
|
537
|
+
lines = index_proc.stdout.strip().split("\n")
|
519
538
|
for line in lines:
|
520
539
|
if "indexed" in line.lower() or "files" in line.lower():
|
521
540
|
logger.info(f" {line.strip()}")
|
522
541
|
else:
|
523
|
-
logger.warning(
|
542
|
+
logger.warning(
|
543
|
+
f"⚠️ Project indexing failed: {index_proc.stderr}"
|
544
|
+
)
|
524
545
|
except subprocess.TimeoutExpired:
|
525
|
-
logger.warning(
|
546
|
+
logger.warning(
|
547
|
+
"⚠️ Project indexing timed out (will continue in background)"
|
548
|
+
)
|
526
549
|
except Exception as e:
|
527
550
|
logger.debug(f"Background indexing error (non-critical): {e}")
|
528
551
|
|
529
552
|
# Run indexing in background thread
|
530
553
|
import threading
|
554
|
+
|
531
555
|
index_thread = threading.Thread(target=background_index, daemon=True)
|
532
556
|
index_thread.start()
|
533
|
-
logger.info(
|
557
|
+
logger.info(
|
558
|
+
"📚 Background indexing started - vector search will be available shortly"
|
559
|
+
)
|
534
560
|
|
535
561
|
else:
|
536
562
|
logger.warning(f"⚠️ Vector search initialization failed: {proc.stderr}")
|
@@ -556,6 +582,7 @@ async def auto_initialize_kuzu_memory():
|
|
556
582
|
try:
|
557
583
|
# Import MCPConfigManager to handle installation
|
558
584
|
from claude_mpm.services.mcp_config_manager import MCPConfigManager
|
585
|
+
|
559
586
|
config_manager = MCPConfigManager()
|
560
587
|
|
561
588
|
# Check if kuzu-memory is already installed
|
@@ -572,7 +599,9 @@ async def auto_initialize_kuzu_memory():
|
|
572
599
|
import subprocess
|
573
600
|
|
574
601
|
if not shutil.which("pipx"):
|
575
|
-
logger.warning(
|
602
|
+
logger.warning(
|
603
|
+
"⚠️ pipx not found. Please install pipx to enable automatic kuzu-memory installation"
|
604
|
+
)
|
576
605
|
logger.info(" Install pipx with: python -m pip install --user pipx")
|
577
606
|
return
|
578
607
|
|
@@ -581,7 +610,7 @@ async def auto_initialize_kuzu_memory():
|
|
581
610
|
["pipx", "install", "kuzu-memory"],
|
582
611
|
capture_output=True,
|
583
612
|
text=True,
|
584
|
-
timeout=60 # 1 minute timeout for installation
|
613
|
+
timeout=60, check=False, # 1 minute timeout for installation
|
585
614
|
)
|
586
615
|
|
587
616
|
if result.returncode == 0:
|
@@ -589,12 +618,16 @@ async def auto_initialize_kuzu_memory():
|
|
589
618
|
# Detect the newly installed path
|
590
619
|
kuzu_memory_path = config_manager.detect_service_path("kuzu-memory")
|
591
620
|
if not kuzu_memory_path:
|
592
|
-
logger.warning(
|
621
|
+
logger.warning(
|
622
|
+
"kuzu-memory installed but command not found in PATH"
|
623
|
+
)
|
593
624
|
return
|
594
625
|
|
595
626
|
# Update the Claude configuration to include the newly installed service
|
596
627
|
logger.info("📝 Updating Claude configuration...")
|
597
|
-
config_success, config_msg =
|
628
|
+
config_success, config_msg = (
|
629
|
+
config_manager.ensure_mcp_services_configured()
|
630
|
+
)
|
598
631
|
if config_success:
|
599
632
|
logger.info(f"✅ {config_msg}")
|
600
633
|
else:
|
@@ -613,6 +646,7 @@ async def auto_initialize_kuzu_memory():
|
|
613
646
|
# At this point, kuzu-memory should be available
|
614
647
|
# Get the actual command to use
|
615
648
|
import shutil
|
649
|
+
|
616
650
|
kuzu_memory_cmd = shutil.which("kuzu-memory")
|
617
651
|
if not kuzu_memory_cmd:
|
618
652
|
# Try pipx installation path as fallback
|
@@ -629,18 +663,23 @@ async def auto_initialize_kuzu_memory():
|
|
629
663
|
|
630
664
|
# Check if database is already initialized
|
631
665
|
if kuzu_memories_dir.exists():
|
632
|
-
logger.debug(
|
666
|
+
logger.debug(
|
667
|
+
f"Kuzu-memory database already initialized at {kuzu_memories_dir}"
|
668
|
+
)
|
633
669
|
else:
|
634
|
-
logger.info(
|
670
|
+
logger.info(
|
671
|
+
f"🎯 Initializing kuzu-memory database for project: {current_dir}"
|
672
|
+
)
|
635
673
|
|
636
674
|
# Initialize the database in current project directory
|
637
675
|
import subprocess
|
676
|
+
|
638
677
|
proc = subprocess.run(
|
639
678
|
[kuzu_memory_cmd, "init"],
|
640
679
|
capture_output=True,
|
641
680
|
text=True,
|
642
681
|
timeout=30,
|
643
|
-
cwd=str(current_dir),
|
682
|
+
cwd=str(current_dir), check=False,
|
644
683
|
)
|
645
684
|
|
646
685
|
if proc.returncode == 0:
|
@@ -14,11 +14,12 @@ from .base_adapter import (
|
|
14
14
|
from .document_summarizer import DocumentSummarizerTool
|
15
15
|
from .kuzu_memory_service import (
|
16
16
|
KuzuMemoryService,
|
17
|
-
|
17
|
+
get_context,
|
18
18
|
recall_memories,
|
19
19
|
search_memories,
|
20
|
-
|
20
|
+
store_memory,
|
21
21
|
)
|
22
|
+
|
22
23
|
# Ticket tools removed - using mcp-ticketer instead
|
23
24
|
|
24
25
|
__all__ = [
|
@@ -26,10 +27,10 @@ __all__ = [
|
|
26
27
|
"CalculatorToolAdapter",
|
27
28
|
"DocumentSummarizerTool",
|
28
29
|
"EchoToolAdapter",
|
29
|
-
"SystemInfoToolAdapter",
|
30
30
|
"KuzuMemoryService",
|
31
|
-
"
|
31
|
+
"SystemInfoToolAdapter",
|
32
|
+
"get_context",
|
32
33
|
"recall_memories",
|
33
34
|
"search_memories",
|
34
|
-
"
|
35
|
+
"store_memory",
|
35
36
|
]
|