claude-mpm 5.1.9__py3-none-any.whl → 5.4.48__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (248) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +4 -0
  3. claude_mpm/agents/BASE_AGENT.md +164 -0
  4. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +1 -1
  5. claude_mpm/agents/MEMORY.md +1 -1
  6. claude_mpm/agents/PM_INSTRUCTIONS.md +843 -900
  7. claude_mpm/agents/WORKFLOW.md +5 -254
  8. claude_mpm/agents/agent_loader.py +13 -44
  9. claude_mpm/agents/base_agent.json +1 -1
  10. claude_mpm/agents/frontmatter_validator.py +2 -2
  11. claude_mpm/agents/templates/circuit-breakers.md +138 -1
  12. claude_mpm/cli/__main__.py +4 -0
  13. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  14. claude_mpm/cli/commands/agent_state_manager.py +18 -27
  15. claude_mpm/cli/commands/agents.py +9 -40
  16. claude_mpm/cli/commands/auto_configure.py +210 -25
  17. claude_mpm/cli/commands/config.py +88 -2
  18. claude_mpm/cli/commands/configure.py +1098 -159
  19. claude_mpm/cli/commands/configure_agent_display.py +25 -6
  20. claude_mpm/cli/commands/mpm_init/core.py +225 -46
  21. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  22. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  23. claude_mpm/cli/commands/postmortem.py +1 -1
  24. claude_mpm/cli/commands/profile.py +277 -0
  25. claude_mpm/cli/commands/skills.py +218 -197
  26. claude_mpm/cli/commands/summarize.py +413 -0
  27. claude_mpm/cli/executor.py +21 -3
  28. claude_mpm/cli/interactive/agent_wizard.py +2 -2
  29. claude_mpm/cli/parsers/agents_parser.py +0 -9
  30. claude_mpm/cli/parsers/auto_configure_parser.py +0 -138
  31. claude_mpm/cli/parsers/base_parser.py +12 -0
  32. claude_mpm/cli/parsers/config_parser.py +153 -83
  33. claude_mpm/cli/parsers/profile_parser.py +148 -0
  34. claude_mpm/cli/parsers/skills_parser.py +0 -5
  35. claude_mpm/cli/startup.py +876 -149
  36. claude_mpm/commands/mpm-config.md +28 -0
  37. claude_mpm/commands/mpm-doctor.md +9 -22
  38. claude_mpm/commands/mpm-help.md +5 -287
  39. claude_mpm/commands/mpm-init.md +81 -507
  40. claude_mpm/commands/mpm-monitor.md +15 -402
  41. claude_mpm/commands/mpm-organize.md +120 -0
  42. claude_mpm/commands/mpm-postmortem.md +6 -108
  43. claude_mpm/commands/mpm-session-resume.md +12 -363
  44. claude_mpm/commands/mpm-status.md +5 -69
  45. claude_mpm/commands/mpm-ticket-view.md +52 -495
  46. claude_mpm/commands/mpm-version.md +5 -107
  47. claude_mpm/config/agent_sources.py +27 -0
  48. claude_mpm/core/config.py +2 -4
  49. claude_mpm/core/framework/formatters/content_formatter.py +3 -13
  50. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  51. claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
  52. claude_mpm/core/framework_loader.py +4 -2
  53. claude_mpm/core/logger.py +13 -0
  54. claude_mpm/core/optimized_startup.py +59 -0
  55. claude_mpm/core/shared/config_loader.py +1 -1
  56. claude_mpm/core/socketio_pool.py +3 -3
  57. claude_mpm/core/unified_agent_registry.py +5 -15
  58. claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
  59. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +1 -0
  60. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +1 -0
  61. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +1 -0
  62. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +1 -0
  63. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +1 -0
  64. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +2 -0
  65. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DjhvlsAc.js +1 -0
  66. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/N4qtv3Hx.js +2 -0
  67. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uj46x2Wr.js +1 -0
  68. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +2 -0
  69. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +1 -0
  70. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.CAGBuiOw.js +1 -0
  71. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +1 -0
  72. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +10 -0
  73. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
  74. claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
  75. claude_mpm/dashboard/static/svelte-build/index.html +36 -0
  76. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  77. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  78. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  79. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  80. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  81. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  82. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  83. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  84. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  85. claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
  86. claude_mpm/hooks/claude_hooks/hook_handler.py +155 -1
  87. claude_mpm/hooks/claude_hooks/installer.py +33 -10
  88. claude_mpm/hooks/claude_hooks/memory_integration.py +26 -9
  89. claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
  90. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  91. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  92. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  93. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  94. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  95. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  96. claude_mpm/hooks/claude_hooks/services/connection_manager.py +30 -6
  97. claude_mpm/hooks/kuzu_memory_hook.py +5 -5
  98. claude_mpm/hooks/memory_integration_hook.py +46 -1
  99. claude_mpm/init.py +63 -19
  100. claude_mpm/models/git_repository.py +3 -3
  101. claude_mpm/scripts/claude-hook-handler.sh +58 -18
  102. claude_mpm/scripts/launch_monitor.py +93 -13
  103. claude_mpm/services/agents/agent_builder.py +3 -3
  104. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  105. claude_mpm/services/agents/agent_review_service.py +280 -0
  106. claude_mpm/services/agents/cache_git_manager.py +6 -6
  107. claude_mpm/services/agents/deployment/agent_deployment.py +29 -7
  108. claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -5
  109. claude_mpm/services/agents/deployment/agent_format_converter.py +23 -13
  110. claude_mpm/services/agents/deployment/agent_template_builder.py +32 -20
  111. claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
  112. claude_mpm/services/agents/deployment/async_agent_deployment.py +31 -27
  113. claude_mpm/services/agents/deployment/local_template_deployment.py +3 -1
  114. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +247 -35
  115. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +392 -87
  116. claude_mpm/services/agents/git_source_manager.py +53 -4
  117. claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
  118. claude_mpm/services/agents/recommender.py +5 -3
  119. claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
  120. claude_mpm/services/agents/sources/git_source_sync_service.py +120 -7
  121. claude_mpm/services/agents/startup_sync.py +22 -2
  122. claude_mpm/services/agents/toolchain_detector.py +10 -6
  123. claude_mpm/services/analysis/__init__.py +11 -1
  124. claude_mpm/services/analysis/clone_detector.py +1030 -0
  125. claude_mpm/services/command_deployment_service.py +81 -10
  126. claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
  127. claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
  128. claude_mpm/services/event_bus/config.py +3 -1
  129. claude_mpm/services/git/git_operations_service.py +101 -16
  130. claude_mpm/services/monitor/daemon.py +9 -2
  131. claude_mpm/services/monitor/daemon_manager.py +39 -3
  132. claude_mpm/services/monitor/management/lifecycle.py +8 -1
  133. claude_mpm/services/monitor/server.py +698 -22
  134. claude_mpm/services/pm_skills_deployer.py +711 -0
  135. claude_mpm/services/profile_manager.py +331 -0
  136. claude_mpm/services/self_upgrade_service.py +120 -12
  137. claude_mpm/services/skills/__init__.py +3 -0
  138. claude_mpm/services/skills/git_skill_source_manager.py +130 -2
  139. claude_mpm/services/skills/selective_skill_deployer.py +704 -0
  140. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  141. claude_mpm/services/skills_deployer.py +127 -9
  142. claude_mpm/services/socketio/dashboard_server.py +1 -0
  143. claude_mpm/services/socketio/event_normalizer.py +51 -6
  144. claude_mpm/services/socketio/server/core.py +386 -108
  145. claude_mpm/services/version_control/git_operations.py +103 -0
  146. claude_mpm/skills/skill_manager.py +92 -3
  147. claude_mpm/utils/agent_dependency_loader.py +14 -2
  148. claude_mpm/utils/agent_filters.py +17 -44
  149. claude_mpm/utils/migration.py +4 -4
  150. claude_mpm/utils/robust_installer.py +47 -3
  151. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.dist-info}/METADATA +53 -87
  152. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.dist-info}/RECORD +157 -197
  153. claude_mpm-5.4.48.dist-info/entry_points.txt +5 -0
  154. claude_mpm-5.4.48.dist-info/licenses/LICENSE +94 -0
  155. claude_mpm-5.4.48.dist-info/licenses/LICENSE-FAQ.md +153 -0
  156. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
  157. claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
  158. claude_mpm/agents/BASE_OPS.md +0 -219
  159. claude_mpm/agents/BASE_PM.md +0 -480
  160. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
  161. claude_mpm/agents/BASE_QA.md +0 -167
  162. claude_mpm/agents/BASE_RESEARCH.md +0 -53
  163. claude_mpm/agents/base_agent_loader.py +0 -601
  164. claude_mpm/cli/commands/agents_detect.py +0 -380
  165. claude_mpm/cli/commands/agents_recommend.py +0 -309
  166. claude_mpm/cli/ticket_cli.py +0 -35
  167. claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
  168. claude_mpm/commands/mpm-agents-detect.md +0 -177
  169. claude_mpm/commands/mpm-agents-list.md +0 -131
  170. claude_mpm/commands/mpm-agents-recommend.md +0 -223
  171. claude_mpm/commands/mpm-config-view.md +0 -150
  172. claude_mpm/commands/mpm-ticket-organize.md +0 -304
  173. claude_mpm/dashboard/analysis_runner.py +0 -455
  174. claude_mpm/dashboard/index.html +0 -13
  175. claude_mpm/dashboard/open_dashboard.py +0 -66
  176. claude_mpm/dashboard/static/css/activity.css +0 -1958
  177. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  178. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  179. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  180. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  181. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  182. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  183. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  184. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  185. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  186. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  187. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  188. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  189. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  190. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  191. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  192. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  193. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  194. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  195. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  196. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  197. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  198. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  199. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  200. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  201. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  202. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  203. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  204. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  205. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  206. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  207. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  208. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  209. claude_mpm/dashboard/templates/code_simple.html +0 -153
  210. claude_mpm/dashboard/templates/index.html +0 -606
  211. claude_mpm/dashboard/test_dashboard.html +0 -372
  212. claude_mpm/scripts/mcp_server.py +0 -75
  213. claude_mpm/scripts/mcp_wrapper.py +0 -39
  214. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  215. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  216. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  217. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  218. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  219. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  220. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  221. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  222. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  223. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  224. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
  225. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  226. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  227. claude_mpm/services/mcp_gateway/main.py +0 -589
  228. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  229. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  230. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  231. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  232. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  233. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  234. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  235. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  236. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  237. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  238. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  239. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  240. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  241. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  242. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  243. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  244. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  245. claude_mpm-5.1.9.dist-info/entry_points.txt +0 -10
  246. claude_mpm-5.1.9.dist-info/licenses/LICENSE +0 -21
  247. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.dist-info}/WHEEL +0 -0
  248. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.dist-info}/top_level.txt +0 -0
@@ -1,654 +0,0 @@
1
- """
2
- External MCP Services Integration
3
- ==================================
4
-
5
- Manages detection and setup of external MCP services like mcp-vector-search
6
- and mcp-browser. These services run as separate MCP servers in Claude Code,
7
- not as part of the Claude MPM MCP Gateway.
8
-
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.
21
- """
22
-
23
- import json
24
- import subprocess
25
- import sys
26
- from pathlib import Path
27
- from typing import Any, Dict, List
28
-
29
- from claude_mpm.services.mcp_gateway.tools.base_adapter import BaseToolAdapter
30
-
31
-
32
- class ExternalMCPService(BaseToolAdapter):
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
- """
39
-
40
- def __init__(self, service_name: str, package_name: str):
41
- """
42
- Initialize external MCP service.
43
-
44
- Args:
45
- service_name: Name of the service for MCP
46
- package_name: Python package name to install/run
47
- """
48
- # Import here to avoid circular imports
49
- from claude_mpm.services.mcp_gateway.core.interfaces import MCPToolDefinition
50
-
51
- # Create a basic tool definition for the service
52
- tool_def = MCPToolDefinition(
53
- name=service_name,
54
- description=f"External MCP service: {package_name}",
55
- input_schema={
56
- "type": "object",
57
- "properties": {},
58
- "required": [],
59
- },
60
- )
61
- super().__init__(tool_def)
62
- self.service_name = service_name
63
- self.package_name = package_name
64
- self.process = None
65
- self._is_installed = False
66
-
67
- async def invoke(self, invocation):
68
- """
69
- Invoke method required by BaseToolAdapter interface.
70
-
71
- This base implementation should be overridden by subclasses.
72
- """
73
- # Import here to avoid circular imports
74
- from claude_mpm.services.mcp_gateway.core.interfaces import MCPToolResult
75
-
76
- return MCPToolResult(
77
- success=False,
78
- error="invoke method not implemented in base ExternalMCPService",
79
- execution_time=0.0,
80
- )
81
-
82
- async def initialize(
83
- self, auto_install: bool = False, interactive: bool = False
84
- ) -> bool:
85
- """Initialize the external service.
86
-
87
- NOTE: Auto-installation is disabled by default (v4.9.0+). Users must
88
- manually install external services using pipx or pip.
89
-
90
- Args:
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
95
- """
96
- try:
97
- # Check if package is installed
98
- self._is_installed = await self._check_installation()
99
-
100
- if not self._is_installed and auto_install:
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}"
105
- )
106
- await self._install_package(interactive=interactive)
107
- self._is_installed = await self._check_installation()
108
-
109
- if not self._is_installed:
110
- self.logger.debug(
111
- f"{self.package_name} is not available. "
112
- f"Install manually with: pipx install {self.package_name}"
113
- )
114
- return False
115
-
116
- self.logger.info(f"{self.package_name} is available")
117
- return True
118
-
119
- except Exception as e:
120
- self.logger.error(f"Failed to initialize {self.service_name}: {e}")
121
- return False
122
-
123
- async def _check_installation(self) -> bool:
124
- """Check if the package is installed."""
125
- # First check if importable (faster and more reliable)
126
- import_name = self.package_name.replace("-", "_")
127
- try:
128
- import importlib.util
129
-
130
- spec = importlib.util.find_spec(import_name)
131
- if spec is not None:
132
- return True
133
- except (ImportError, ModuleNotFoundError, ValueError):
134
- pass
135
-
136
- # Fallback: try running as module
137
- try:
138
- result = subprocess.run(
139
- [sys.executable, "-m", import_name, "--help"],
140
- capture_output=True,
141
- text=True,
142
- timeout=5,
143
- check=False,
144
- )
145
- return result.returncode == 0
146
- except (
147
- subprocess.TimeoutExpired,
148
- FileNotFoundError,
149
- subprocess.CalledProcessError,
150
- ):
151
- return False
152
-
153
- async def _install_package(self, interactive: bool = True) -> bool:
154
- """Install the package using pip or pipx.
155
-
156
- Args:
157
- interactive: Whether to prompt user for installation method choice
158
- """
159
- try:
160
- install_method = None
161
-
162
- if interactive:
163
- # Show user-friendly installation prompt
164
- print(f"\n⚠️ {self.package_name} not found", file=sys.stderr)
165
- print(
166
- "This package enables enhanced functionality (optional).",
167
- file=sys.stderr,
168
- )
169
- print("\nInstallation options:", file=sys.stderr)
170
- print(
171
- "1. Install via pip (recommended for this project)", file=sys.stderr
172
- )
173
- print("2. Install via pipx (isolated, system-wide)", file=sys.stderr)
174
- print("3. Skip (continue without this package)", file=sys.stderr)
175
-
176
- try:
177
- choice = input("\nChoose option (1/2/3) [1]: ").strip() or "1"
178
- if choice == "1":
179
- install_method = "pip"
180
- elif choice == "2":
181
- install_method = "pipx"
182
- else:
183
- self.logger.info(
184
- f"Skipping installation of {self.package_name}"
185
- )
186
- return False
187
- except (EOFError, KeyboardInterrupt):
188
- print("\nInstallation cancelled", file=sys.stderr)
189
- return False
190
- else:
191
- # Non-interactive: default to pip
192
- install_method = "pip"
193
-
194
- # Install using selected method
195
- if install_method == "pip":
196
- return await self._install_via_pip()
197
- if install_method == "pipx":
198
- return await self._install_via_pipx()
199
-
200
- return False
201
-
202
- except Exception as e:
203
- self.logger.error(f"Error installing {self.package_name}: {e}")
204
- return False
205
-
206
- async def _install_via_pip(self) -> bool:
207
- """Install package via pip."""
208
- try:
209
- print(f"\n📦 Installing {self.package_name} via pip...", file=sys.stderr)
210
- result = subprocess.run(
211
- [sys.executable, "-m", "pip", "install", self.package_name],
212
- capture_output=True,
213
- text=True,
214
- timeout=120,
215
- check=False,
216
- )
217
-
218
- if result.returncode == 0:
219
- print(f"✓ Successfully installed {self.package_name}", file=sys.stderr)
220
- self.logger.info(f"Successfully installed {self.package_name} via pip")
221
- return True
222
-
223
- error_msg = result.stderr.strip() if result.stderr else "Unknown error"
224
- print(f"✗ Installation failed: {error_msg}", file=sys.stderr)
225
- self.logger.error(f"Failed to install {self.package_name}: {error_msg}")
226
- return False
227
-
228
- except subprocess.TimeoutExpired:
229
- print("✗ Installation timed out", file=sys.stderr)
230
- self.logger.error(f"Installation of {self.package_name} timed out")
231
- return False
232
- except Exception as e:
233
- print(f"✗ Installation error: {e}", file=sys.stderr)
234
- self.logger.error(f"Error installing {self.package_name}: {e}")
235
- return False
236
-
237
- async def _install_via_pipx(self) -> bool:
238
- """Install package via pipx."""
239
- try:
240
- # Check if pipx is available
241
- pipx_check = subprocess.run(
242
- ["pipx", "--version"],
243
- capture_output=True,
244
- text=True,
245
- timeout=5,
246
- check=False,
247
- )
248
-
249
- if pipx_check.returncode != 0:
250
- print("✗ pipx is not installed", file=sys.stderr)
251
- print("Install pipx first: python -m pip install pipx", file=sys.stderr)
252
- self.logger.error("pipx not available for installation")
253
- return False
254
-
255
- print(f"\n📦 Installing {self.package_name} via pipx...", file=sys.stderr)
256
- result = subprocess.run(
257
- ["pipx", "install", self.package_name],
258
- capture_output=True,
259
- text=True,
260
- timeout=120,
261
- check=False,
262
- )
263
-
264
- if result.returncode == 0:
265
- print(f"✓ Successfully installed {self.package_name}", file=sys.stderr)
266
- self.logger.info(f"Successfully installed {self.package_name} via pipx")
267
- return True
268
-
269
- error_msg = result.stderr.strip() if result.stderr else "Unknown error"
270
- print(f"✗ Installation failed: {error_msg}", file=sys.stderr)
271
- self.logger.error(f"Failed to install {self.package_name}: {error_msg}")
272
- return False
273
-
274
- except FileNotFoundError:
275
- print("✗ pipx command not found", file=sys.stderr)
276
- print("Install pipx first: python -m pip install pipx", file=sys.stderr)
277
- self.logger.error("pipx command not found")
278
- return False
279
- except subprocess.TimeoutExpired:
280
- print("✗ Installation timed out", file=sys.stderr)
281
- self.logger.error(f"Installation of {self.package_name} timed out")
282
- return False
283
- except Exception as e:
284
- print(f"✗ Installation error: {e}", file=sys.stderr)
285
- self.logger.error(f"Error installing {self.package_name}: {e}")
286
- return False
287
-
288
- def get_definition(self) -> Dict[str, Any]:
289
- """Get service definition for MCP registration."""
290
- return {
291
- "name": self.service_name,
292
- "description": f"External MCP service: {self.package_name}",
293
- "type": "external_service",
294
- "package": self.package_name,
295
- "installed": self._is_installed,
296
- }
297
-
298
-
299
- class MCPVectorSearchService(ExternalMCPService):
300
- """MCP Vector Search service integration."""
301
-
302
- def __init__(self):
303
- """Initialize MCP Vector Search service."""
304
- super().__init__("mcp-vector-search", "mcp-vector-search")
305
-
306
- def get_definition(self) -> Dict[str, Any]:
307
- """Get tool definition for MCP registration."""
308
- base_def = super().get_definition()
309
- base_def.update(
310
- {
311
- "description": "Semantic code search powered by vector embeddings",
312
- "tools": [
313
- {
314
- "name": "mcp__mcp-vector-search__search_code",
315
- "description": "Search for code using semantic similarity",
316
- "inputSchema": {
317
- "type": "object",
318
- "properties": {
319
- "query": {
320
- "type": "string",
321
- "description": "The search query",
322
- },
323
- "limit": {"type": "integer", "default": 10},
324
- "similarity_threshold": {
325
- "type": "number",
326
- "default": 0.3,
327
- },
328
- "language": {"type": "string"},
329
- "file_extensions": {
330
- "type": "array",
331
- "items": {"type": "string"},
332
- },
333
- "files": {"type": "string"},
334
- "class_name": {"type": "string"},
335
- "function_name": {"type": "string"},
336
- },
337
- "required": ["query"],
338
- },
339
- },
340
- {
341
- "name": "mcp__mcp-vector-search__search_similar",
342
- "description": "Find code similar to a specific file or function",
343
- "inputSchema": {
344
- "type": "object",
345
- "properties": {
346
- "file_path": {
347
- "type": "string",
348
- "description": "Path to the file",
349
- },
350
- "function_name": {"type": "string"},
351
- "limit": {"type": "integer", "default": 10},
352
- "similarity_threshold": {
353
- "type": "number",
354
- "default": 0.3,
355
- },
356
- },
357
- "required": ["file_path"],
358
- },
359
- },
360
- {
361
- "name": "mcp__mcp-vector-search__search_context",
362
- "description": "Search for code based on contextual description",
363
- "inputSchema": {
364
- "type": "object",
365
- "properties": {
366
- "description": {
367
- "type": "string",
368
- "description": "Contextual description",
369
- },
370
- "focus_areas": {
371
- "type": "array",
372
- "items": {"type": "string"},
373
- },
374
- "limit": {"type": "integer", "default": 10},
375
- },
376
- "required": ["description"],
377
- },
378
- },
379
- {
380
- "name": "mcp__mcp-vector-search__get_project_status",
381
- "description": "Get project indexing status and statistics",
382
- "inputSchema": {
383
- "type": "object",
384
- "properties": {},
385
- "required": [],
386
- },
387
- },
388
- {
389
- "name": "mcp__mcp-vector-search__index_project",
390
- "description": "Index or reindex the project codebase",
391
- "inputSchema": {
392
- "type": "object",
393
- "properties": {
394
- "force": {"type": "boolean", "default": False},
395
- "file_extensions": {
396
- "type": "array",
397
- "items": {"type": "string"},
398
- },
399
- },
400
- "required": [],
401
- },
402
- },
403
- ],
404
- }
405
- )
406
- return base_def
407
-
408
- async def invoke(self, tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
409
- """Invoke a tool from mcp-vector-search."""
410
- try:
411
- # Extract the actual tool name (remove prefix)
412
- actual_tool = tool_name.replace("mcp__mcp-vector-search__", "")
413
-
414
- # Prepare the command
415
- cmd = [
416
- sys.executable,
417
- "-m",
418
- "mcp_vector_search",
419
- "--tool",
420
- actual_tool,
421
- "--args",
422
- json.dumps(arguments),
423
- ]
424
-
425
- # Run the command
426
- result = subprocess.run(
427
- cmd,
428
- capture_output=True,
429
- text=True,
430
- timeout=30,
431
- cwd=Path.cwd(),
432
- check=False, # Use current working directory for project context
433
- )
434
-
435
- if result.returncode == 0:
436
- try:
437
- return json.loads(result.stdout)
438
- except json.JSONDecodeError:
439
- return {"result": result.stdout}
440
- else:
441
- return {"error": result.stderr or "Tool invocation failed"}
442
-
443
- except subprocess.TimeoutExpired:
444
- return {"error": "Tool invocation timed out"}
445
- except Exception as e:
446
- return {"error": str(e)}
447
-
448
-
449
- class MCPBrowserService(ExternalMCPService):
450
- """MCP Browser service integration."""
451
-
452
- def __init__(self):
453
- """Initialize MCP Browser service."""
454
- super().__init__("mcp-browser", "mcp-browser")
455
-
456
- def get_definition(self) -> Dict[str, Any]:
457
- """Get tool definition for MCP registration."""
458
- base_def = super().get_definition()
459
- base_def.update(
460
- {
461
- "description": "Web browsing and content extraction capabilities",
462
- "tools": [
463
- {
464
- "name": "mcp__mcp-browser__browse",
465
- "description": "Browse a webpage and extract content",
466
- "inputSchema": {
467
- "type": "object",
468
- "properties": {
469
- "url": {
470
- "type": "string",
471
- "description": "URL to browse",
472
- },
473
- "extract": {
474
- "type": "string",
475
- "description": "What to extract",
476
- },
477
- },
478
- "required": ["url"],
479
- },
480
- },
481
- {
482
- "name": "mcp__mcp-browser__search",
483
- "description": "Search the web",
484
- "inputSchema": {
485
- "type": "object",
486
- "properties": {
487
- "query": {
488
- "type": "string",
489
- "description": "Search query",
490
- },
491
- "num_results": {"type": "integer", "default": 10},
492
- },
493
- "required": ["query"],
494
- },
495
- },
496
- {
497
- "name": "mcp__mcp-browser__screenshot",
498
- "description": "Take a screenshot of a webpage",
499
- "inputSchema": {
500
- "type": "object",
501
- "properties": {
502
- "url": {
503
- "type": "string",
504
- "description": "URL to screenshot",
505
- },
506
- "full_page": {"type": "boolean", "default": False},
507
- },
508
- "required": ["url"],
509
- },
510
- },
511
- ],
512
- }
513
- )
514
- return base_def
515
-
516
- async def invoke(self, tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
517
- """Invoke a tool from mcp-browser."""
518
- try:
519
- # Extract the actual tool name (remove prefix)
520
- actual_tool = tool_name.replace("mcp__mcp-browser__", "")
521
-
522
- # Prepare the command
523
- cmd = [
524
- sys.executable,
525
- "-m",
526
- "mcp_browser",
527
- "--tool",
528
- actual_tool,
529
- "--args",
530
- json.dumps(arguments),
531
- ]
532
-
533
- # Run the command
534
- result = subprocess.run(
535
- cmd, capture_output=True, text=True, timeout=30, check=False
536
- )
537
-
538
- if result.returncode == 0:
539
- try:
540
- return json.loads(result.stdout)
541
- except json.JSONDecodeError:
542
- return {"result": result.stdout}
543
- else:
544
- return {"error": result.stderr or "Tool invocation failed"}
545
-
546
- except subprocess.TimeoutExpired:
547
- return {"error": "Tool invocation timed out"}
548
- except Exception as e:
549
- return {"error": str(e)}
550
-
551
-
552
- class ExternalMCPServiceManager:
553
- """Manager for external MCP services.
554
-
555
- This manager is responsible for detecting (but NOT installing) Python packages
556
- for external MCP services. The actual registration of these services happens
557
- in Claude Code configuration as separate MCP servers.
558
-
559
- IMPORTANT: As of v4.9.0, this manager NO LONGER auto-installs missing services.
560
- Users must manually install external services using pipx or pip:
561
- - pipx install mcp-vector-search
562
- - pipx install mcp-browser
563
- - pipx install kuzu-memory
564
- - pipx install mcp-ticketer
565
-
566
- Note: This class is maintained for backward compatibility and service detection.
567
- The actual tool registration is handled by separate MCP server instances in
568
- Claude Code.
569
- """
570
-
571
- def __init__(self):
572
- """Initialize the service manager."""
573
- self.services: List[ExternalMCPService] = []
574
- self.logger = None
575
-
576
- async def initialize_services(self) -> List[ExternalMCPService]:
577
- """Initialize all external MCP services.
578
-
579
- This method checks if external service packages are already installed
580
- and registers them if available. It does NOT auto-install missing services.
581
-
582
- External MCP services (mcp-vector-search, mcp-browser, kuzu-memory, mcp-ticketer)
583
- must be manually installed by users. This gives users explicit control over
584
- which services they want to use.
585
-
586
- Installation instructions:
587
- - mcp-vector-search: pipx install mcp-vector-search
588
- - mcp-browser: pipx install mcp-browser
589
- - kuzu-memory: pipx install kuzu-memory
590
- - mcp-ticketer: pipx install mcp-ticketer
591
-
592
- Services run as separate MCP servers in Claude Code, not as tools within
593
- the gateway.
594
- """
595
- # Create service instances
596
- # Note: kuzu-memory and mcp-ticketer are configured via MCPConfigManager
597
- # and run as separate MCP servers. They don't need to be included here
598
- # since they're already set up through the MCP config.
599
- services = [MCPVectorSearchService(), MCPBrowserService()]
600
-
601
- # Initialize each service (check if installed, but DO NOT auto-install)
602
- initialized_services = []
603
- for service in services:
604
- try:
605
- # Pass auto_install=False to prevent automatic installation
606
- if await service.initialize(auto_install=False, interactive=False):
607
- initialized_services.append(service)
608
- if self.logger:
609
- self.logger.info(
610
- f"Initialized external service: {service.service_name}"
611
- )
612
- elif self.logger:
613
- self.logger.debug(
614
- f"Service not available (optional): {service.service_name}. "
615
- f"Install manually with: pipx install {service.package_name}"
616
- )
617
- except Exception as e:
618
- if self.logger:
619
- self.logger.error(f"Error initializing {service.service_name}: {e}")
620
-
621
- self.services = initialized_services
622
- return initialized_services
623
-
624
- def get_all_tools(self) -> List[Dict[str, Any]]:
625
- """Get all tool definitions from external services."""
626
- all_tools = []
627
- for service in self.services:
628
- service_def = service.get_definition()
629
- if "tools" in service_def:
630
- all_tools.extend(service_def["tools"])
631
- return all_tools
632
-
633
- async def invoke_tool(
634
- self, tool_name: str, arguments: Dict[str, Any]
635
- ) -> Dict[str, Any]:
636
- """Invoke a tool from any registered external service."""
637
- # Find the service that handles this tool
638
- for service in self.services:
639
- if tool_name.startswith(f"mcp__{service.service_name}__"):
640
- if isinstance(service, (MCPVectorSearchService, MCPBrowserService)):
641
- return await service.invoke(tool_name, arguments)
642
-
643
- return {"error": f"No service found for tool: {tool_name}"}
644
-
645
- async def shutdown(self):
646
- """Shutdown all external services."""
647
- for service in self.services:
648
- try:
649
- await service.shutdown()
650
- except Exception as e:
651
- if self.logger:
652
- self.logger.warning(
653
- f"Error shutting down {service.service_name}: {e}"
654
- )