claude-mpm 5.1.8__py3-none-any.whl → 5.4.22__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 (191) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +4 -0
  3. claude_mpm/agents/{PM_INSTRUCTIONS_TEACH.md → CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md} +721 -41
  4. claude_mpm/agents/PM_INSTRUCTIONS.md +290 -34
  5. claude_mpm/agents/agent_loader.py +13 -44
  6. claude_mpm/agents/frontmatter_validator.py +68 -0
  7. claude_mpm/agents/templates/circuit-breakers.md +138 -1
  8. claude_mpm/cli/__main__.py +4 -0
  9. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  10. claude_mpm/cli/commands/agent_state_manager.py +8 -17
  11. claude_mpm/cli/commands/agents.py +169 -31
  12. claude_mpm/cli/commands/auto_configure.py +210 -25
  13. claude_mpm/cli/commands/config.py +88 -2
  14. claude_mpm/cli/commands/configure.py +1111 -161
  15. claude_mpm/cli/commands/configure_agent_display.py +15 -6
  16. claude_mpm/cli/commands/mpm_init/core.py +160 -46
  17. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  18. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  19. claude_mpm/cli/commands/skills.py +214 -189
  20. claude_mpm/cli/commands/summarize.py +413 -0
  21. claude_mpm/cli/executor.py +11 -3
  22. claude_mpm/cli/parsers/agents_parser.py +54 -9
  23. claude_mpm/cli/parsers/auto_configure_parser.py +0 -138
  24. claude_mpm/cli/parsers/base_parser.py +5 -0
  25. claude_mpm/cli/parsers/config_parser.py +153 -83
  26. claude_mpm/cli/parsers/skills_parser.py +3 -2
  27. claude_mpm/cli/startup.py +550 -94
  28. claude_mpm/commands/mpm-config.md +265 -0
  29. claude_mpm/commands/mpm-help.md +14 -95
  30. claude_mpm/commands/mpm-organize.md +500 -0
  31. claude_mpm/config/agent_sources.py +27 -0
  32. claude_mpm/core/framework/formatters/content_formatter.py +3 -13
  33. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  34. claude_mpm/core/framework_loader.py +4 -2
  35. claude_mpm/core/logger.py +13 -0
  36. claude_mpm/core/output_style_manager.py +173 -43
  37. claude_mpm/core/socketio_pool.py +3 -3
  38. claude_mpm/core/unified_agent_registry.py +134 -16
  39. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  40. claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
  41. claude_mpm/hooks/claude_hooks/hook_handler.py +6 -0
  42. claude_mpm/hooks/claude_hooks/installer.py +33 -10
  43. claude_mpm/hooks/claude_hooks/memory_integration.py +26 -9
  44. claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
  45. claude_mpm/hooks/claude_hooks/services/connection_manager.py +4 -0
  46. claude_mpm/hooks/memory_integration_hook.py +46 -1
  47. claude_mpm/init.py +0 -19
  48. claude_mpm/models/agent_definition.py +7 -0
  49. claude_mpm/scripts/claude-hook-handler.sh +58 -18
  50. claude_mpm/scripts/launch_monitor.py +93 -13
  51. claude_mpm/scripts/start_activity_logging.py +0 -0
  52. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  53. claude_mpm/services/agents/agent_review_service.py +280 -0
  54. claude_mpm/services/agents/deployment/agent_discovery_service.py +2 -3
  55. claude_mpm/services/agents/deployment/agent_template_builder.py +4 -2
  56. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +188 -12
  57. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +531 -55
  58. claude_mpm/services/agents/git_source_manager.py +34 -0
  59. claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
  60. claude_mpm/services/agents/sources/git_source_sync_service.py +8 -1
  61. claude_mpm/services/agents/toolchain_detector.py +10 -6
  62. claude_mpm/services/analysis/__init__.py +11 -1
  63. claude_mpm/services/analysis/clone_detector.py +1030 -0
  64. claude_mpm/services/command_deployment_service.py +81 -10
  65. claude_mpm/services/event_bus/config.py +3 -1
  66. claude_mpm/services/git/git_operations_service.py +93 -8
  67. claude_mpm/services/monitor/daemon.py +9 -2
  68. claude_mpm/services/monitor/daemon_manager.py +39 -3
  69. claude_mpm/services/monitor/server.py +225 -19
  70. claude_mpm/services/self_upgrade_service.py +120 -12
  71. claude_mpm/services/skills/__init__.py +3 -0
  72. claude_mpm/services/skills/git_skill_source_manager.py +32 -2
  73. claude_mpm/services/skills/selective_skill_deployer.py +704 -0
  74. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  75. claude_mpm/services/skills_deployer.py +126 -9
  76. claude_mpm/services/socketio/event_normalizer.py +15 -1
  77. claude_mpm/services/socketio/server/core.py +160 -21
  78. claude_mpm/services/version_control/git_operations.py +103 -0
  79. claude_mpm/utils/agent_filters.py +17 -44
  80. {claude_mpm-5.1.8.dist-info → claude_mpm-5.4.22.dist-info}/METADATA +47 -84
  81. {claude_mpm-5.1.8.dist-info → claude_mpm-5.4.22.dist-info}/RECORD +86 -176
  82. claude_mpm-5.4.22.dist-info/entry_points.txt +5 -0
  83. claude_mpm-5.4.22.dist-info/licenses/LICENSE +94 -0
  84. claude_mpm-5.4.22.dist-info/licenses/LICENSE-FAQ.md +153 -0
  85. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
  86. claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
  87. claude_mpm/agents/BASE_ENGINEER.md +0 -658
  88. claude_mpm/agents/BASE_OPS.md +0 -219
  89. claude_mpm/agents/BASE_PM.md +0 -480
  90. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
  91. claude_mpm/agents/BASE_QA.md +0 -167
  92. claude_mpm/agents/BASE_RESEARCH.md +0 -53
  93. claude_mpm/agents/base_agent.json +0 -31
  94. claude_mpm/agents/base_agent_loader.py +0 -601
  95. claude_mpm/cli/commands/agents_detect.py +0 -380
  96. claude_mpm/cli/commands/agents_recommend.py +0 -309
  97. claude_mpm/cli/ticket_cli.py +0 -35
  98. claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
  99. claude_mpm/commands/mpm-agents-detect.md +0 -177
  100. claude_mpm/commands/mpm-agents-list.md +0 -131
  101. claude_mpm/commands/mpm-agents-recommend.md +0 -223
  102. claude_mpm/commands/mpm-config-view.md +0 -150
  103. claude_mpm/commands/mpm-ticket-organize.md +0 -304
  104. claude_mpm/dashboard/analysis_runner.py +0 -455
  105. claude_mpm/dashboard/index.html +0 -13
  106. claude_mpm/dashboard/open_dashboard.py +0 -66
  107. claude_mpm/dashboard/static/css/activity.css +0 -1958
  108. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  109. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  110. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  111. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  112. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  113. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  114. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  115. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  116. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  117. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  118. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  119. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  120. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  121. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  122. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  123. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  124. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  125. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  126. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  127. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  128. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  129. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  130. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  131. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  132. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  133. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  134. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  135. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  136. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  137. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  138. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  139. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  140. claude_mpm/dashboard/templates/code_simple.html +0 -153
  141. claude_mpm/dashboard/templates/index.html +0 -606
  142. claude_mpm/dashboard/test_dashboard.html +0 -372
  143. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  144. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  145. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  146. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  147. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  148. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  149. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  150. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  151. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  152. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  153. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  154. claude_mpm/scripts/mcp_server.py +0 -75
  155. claude_mpm/scripts/mcp_wrapper.py +0 -39
  156. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  157. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  158. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  159. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  160. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  161. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  162. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  163. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  164. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  165. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  166. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
  167. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  168. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  169. claude_mpm/services/mcp_gateway/main.py +0 -589
  170. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  171. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  172. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  173. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  174. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  175. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  176. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  177. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  178. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  179. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  180. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  181. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  182. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  183. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  184. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  185. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  186. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  187. claude_mpm-5.1.8.dist-info/entry_points.txt +0 -10
  188. claude_mpm-5.1.8.dist-info/licenses/LICENSE +0 -21
  189. /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
  190. {claude_mpm-5.1.8.dist-info → claude_mpm-5.4.22.dist-info}/WHEEL +0 -0
  191. {claude_mpm-5.1.8.dist-info → claude_mpm-5.4.22.dist-info}/top_level.txt +0 -0
@@ -1,315 +0,0 @@
1
- """
2
- MCP Gateway Singleton Manager
3
- ============================
4
-
5
- Provides singleton management for MCP Gateway instances to ensure only one
6
- gateway is running per claude-mpm installation.
7
-
8
- WHY: MCP gateways are stdio-based protocol handlers that should have only one
9
- instance per installation to avoid conflicts and resource contention.
10
-
11
- DESIGN DECISIONS:
12
- - File-based locking for cross-process coordination
13
- - PID tracking for instance validation
14
- - Automatic cleanup on process termination
15
- - Thread-safe singleton pattern
16
- """
17
-
18
- import contextlib
19
- import fcntl
20
- import json
21
- import os
22
- import signal
23
- import threading
24
- import time
25
- from typing import Any, Dict, Optional
26
-
27
- from claude_mpm.config.paths import paths
28
- from claude_mpm.core.logger import get_logger
29
-
30
-
31
- class MCPGatewayManager:
32
- """
33
- Singleton manager for MCP Gateway instances.
34
-
35
- Ensures only one gateway instance is running per installation using
36
- file-based locking and PID tracking.
37
- """
38
-
39
- _instance: Optional["MCPGatewayManager"] = None
40
- _lock = threading.Lock()
41
-
42
- def __new__(cls):
43
- """Singleton pattern implementation."""
44
- with cls._lock:
45
- if cls._instance is None:
46
- cls._instance = super().__new__(cls)
47
- cls._instance._initialized = False
48
- return cls._instance
49
-
50
- def __init__(self):
51
- """Initialize the gateway manager."""
52
- if self._initialized:
53
- return
54
-
55
- self.logger = get_logger("MCPGatewayManager")
56
- self._initialized = True
57
-
58
- # Paths for coordination
59
- self.mcp_dir = paths.claude_mpm_dir_hidden / "mcp"
60
- self.lock_file = self.mcp_dir / "gateway.lock"
61
- self.instance_file = self.mcp_dir / "gateway.json"
62
-
63
- # Ensure directory exists
64
- self.mcp_dir.mkdir(parents=True, exist_ok=True)
65
-
66
- # Lock file handle
67
- self._lock_fd: Optional[int] = None
68
- self._current_instance: Optional[Dict[str, Any]] = None
69
-
70
- # Setup cleanup handlers
71
- self._setup_cleanup_handlers()
72
-
73
- def _setup_cleanup_handlers(self):
74
- """Setup signal handlers for cleanup on termination."""
75
-
76
- def cleanup_handler(signum, frame):
77
- self.logger.info(f"Received signal {signum}, cleaning up gateway")
78
- self.cleanup()
79
-
80
- signal.signal(signal.SIGTERM, cleanup_handler)
81
- signal.signal(signal.SIGINT, cleanup_handler)
82
-
83
- def acquire_lock(self) -> bool:
84
- """
85
- Acquire exclusive lock for gateway instance.
86
-
87
- Returns:
88
- True if lock acquired successfully, False otherwise
89
- """
90
- try:
91
- # Open lock file
92
- self._lock_fd = os.open(
93
- self.lock_file, os.O_CREAT | os.O_WRONLY | os.O_TRUNC
94
- )
95
-
96
- # Try to acquire exclusive lock (non-blocking)
97
- fcntl.flock(self._lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
98
-
99
- # Write current PID to lock file
100
- os.write(self._lock_fd, str(os.getpid()).encode())
101
- os.fsync(self._lock_fd)
102
-
103
- self.logger.info(f"Acquired gateway lock (PID: {os.getpid()})")
104
- return True
105
-
106
- except OSError as e:
107
- if self._lock_fd:
108
- with contextlib.suppress(Exception):
109
- os.close(self._lock_fd)
110
- self._lock_fd = None
111
-
112
- self.logger.debug(f"Failed to acquire gateway lock: {e}")
113
- return False
114
-
115
- def release_lock(self):
116
- """Release the gateway lock."""
117
- if self._lock_fd:
118
- try:
119
- fcntl.flock(self._lock_fd, fcntl.LOCK_UN)
120
- os.close(self._lock_fd)
121
- self._lock_fd = None
122
-
123
- # Remove lock file
124
- if self.lock_file.exists():
125
- self.lock_file.unlink()
126
-
127
- self.logger.info("Released gateway lock")
128
- except Exception as e:
129
- self.logger.warning(f"Error releasing lock: {e}")
130
-
131
- def is_gateway_running(self) -> bool:
132
- """
133
- Check if a gateway instance is currently running.
134
-
135
- Returns:
136
- True if gateway is running, False otherwise
137
- """
138
- instance_info = self.get_running_instance_info()
139
- return instance_info is not None
140
-
141
- def get_running_instance_info(self) -> Optional[Dict[str, Any]]:
142
- """
143
- Get information about the currently running gateway instance.
144
-
145
- Returns:
146
- Instance information dict or None if no instance running
147
- """
148
- if not self.instance_file.exists():
149
- return None
150
-
151
- try:
152
- with self.instance_file.open() as f:
153
- instance_info = json.load(f)
154
-
155
- # Validate PID is still running
156
- pid = instance_info.get("pid")
157
- if pid and self._is_pid_running(pid):
158
- return instance_info
159
- # Stale instance file, remove it
160
- self.instance_file.unlink()
161
- return None
162
-
163
- except (json.JSONDecodeError, OSError) as e:
164
- self.logger.warning(f"Error reading instance file: {e}")
165
- return None
166
-
167
- def _is_pid_running(self, pid: int) -> bool:
168
- """Check if a PID is currently running."""
169
- try:
170
- os.kill(pid, 0) # Signal 0 just checks if process exists
171
- return True
172
- except (OSError, ProcessLookupError):
173
- return False
174
-
175
- def register_instance(self, gateway_name: str, version: str) -> bool:
176
- """
177
- Register a new gateway instance.
178
-
179
- Args:
180
- gateway_name: Name of the gateway
181
- version: Gateway version
182
-
183
- Returns:
184
- True if registration successful, False otherwise
185
- """
186
- if not self.acquire_lock():
187
- return False
188
-
189
- try:
190
- instance_info = {
191
- "pid": os.getpid(),
192
- "gateway_name": gateway_name,
193
- "version": version,
194
- "started_at": time.time(),
195
- "lock_file": str(self.lock_file),
196
- }
197
-
198
- with self.instance_file.open("w") as f:
199
- json.dump(instance_info, f, indent=2)
200
-
201
- self._current_instance = instance_info
202
- self.logger.info(f"Registered gateway instance: {gateway_name} v{version}")
203
- return True
204
-
205
- except Exception as e:
206
- self.logger.error(f"Failed to register instance: {e}")
207
- self.release_lock()
208
- return False
209
-
210
- def cleanup(self):
211
- """Clean up gateway instance and release resources."""
212
- try:
213
- # Remove instance file
214
- if self.instance_file.exists():
215
- self.instance_file.unlink()
216
-
217
- # Release lock
218
- self.release_lock()
219
-
220
- self._current_instance = None
221
- self.logger.info("Gateway cleanup completed")
222
-
223
- except Exception as e:
224
- self.logger.warning(f"Error during cleanup: {e}")
225
-
226
-
227
- # Global functions for easy access
228
- _manager: Optional[MCPGatewayManager] = None
229
-
230
-
231
- def get_gateway_manager() -> MCPGatewayManager:
232
- """Get the global gateway manager instance."""
233
- global _manager
234
- if _manager is None:
235
- _manager = MCPGatewayManager()
236
- return _manager
237
-
238
-
239
- def is_gateway_running() -> bool:
240
- """Check if a gateway instance is currently running."""
241
- return get_gateway_manager().is_gateway_running()
242
-
243
-
244
- def get_gateway_status() -> Optional[Dict[str, Any]]:
245
- """Get status of running gateway instance."""
246
- return get_gateway_manager().get_running_instance_info()
247
-
248
-
249
- async def start_global_gateway(
250
- gateway_name: str = "claude-mpm-mcp", version: str = "1.0.0"
251
- ) -> bool:
252
- """
253
- Start the global MCP gateway instance.
254
-
255
- Args:
256
- gateway_name: Name for the gateway
257
- version: Gateway version
258
-
259
- Returns:
260
- True if started successfully, False otherwise
261
- """
262
- manager = get_gateway_manager()
263
-
264
- # Check if already running
265
- if manager.is_gateway_running():
266
- instance_info = manager.get_running_instance_info()
267
- manager.logger.info(
268
- f"Gateway already running (PID: {instance_info.get('pid')})"
269
- )
270
- return True
271
-
272
- # Register new instance
273
- if not manager.register_instance(gateway_name, version):
274
- manager.logger.error("Failed to register gateway instance")
275
- return False
276
-
277
- # Import and start the gateway
278
- try:
279
- from ..main import MCPGatewayOrchestrator
280
-
281
- orchestrator = MCPGatewayOrchestrator()
282
-
283
- if not await orchestrator.initialize():
284
- manager.logger.error("Failed to initialize gateway")
285
- manager.cleanup()
286
- return False
287
-
288
- if not await orchestrator.start():
289
- manager.logger.error("Failed to start gateway")
290
- manager.cleanup()
291
- return False
292
-
293
- manager.logger.info("Global gateway started successfully")
294
- return True
295
-
296
- except Exception as e:
297
- manager.logger.error(f"Error starting gateway: {e}")
298
- manager.cleanup()
299
- return False
300
-
301
-
302
- async def run_global_gateway():
303
- """Run the global MCP gateway until shutdown."""
304
- manager = get_gateway_manager()
305
-
306
- try:
307
- from ..main import MCPGatewayOrchestrator
308
-
309
- orchestrator = MCPGatewayOrchestrator()
310
- await orchestrator.run()
311
-
312
- except Exception as e:
313
- manager.logger.error(f"Error running gateway: {e}")
314
- finally:
315
- manager.cleanup()
@@ -1,316 +0,0 @@
1
- """
2
- MCP Gateway Startup Verification
3
- ================================
4
-
5
- Provides startup verification and installation for MCP Gateway configuration.
6
- Ensures the gateway is properly configured with essential tools on startup.
7
-
8
- WHY: The MCP gateway should be automatically configured and verified on startup
9
- to provide a seamless experience with diagnostic tools, file summarizer, and
10
- ticket service.
11
-
12
- DESIGN DECISIONS:
13
- - Automatic configuration detection and installation
14
- - Essential tools verification (diagnostics, file summarizer, ticket service)
15
- - Graceful fallback when tools are unavailable
16
- - Non-blocking startup (warnings instead of failures)
17
- """
18
-
19
- from typing import Any, Dict
20
-
21
- from claude_mpm.config.paths import paths
22
- from claude_mpm.core.logger import get_logger
23
-
24
- from .singleton_manager import get_gateway_manager, is_gateway_running
25
-
26
-
27
- class MCPGatewayStartupVerifier:
28
- """
29
- Verifies and configures MCP Gateway on startup.
30
-
31
- Ensures the gateway is properly configured with essential tools and
32
- provides diagnostic information about the gateway state.
33
- """
34
-
35
- def __init__(self):
36
- """Initialize the startup verifier."""
37
- self.logger = get_logger("MCPGatewayStartupVerifier")
38
- self.config_dir = paths.claude_mpm_dir_hidden / "mcp"
39
- self.config_file = self.config_dir / "gateway_config.json"
40
-
41
- # Essential tools that should be available
42
- self.essential_tools = [
43
- "echo", # Basic diagnostic tool
44
- "calculator", # Math operations
45
- "system_info", # System diagnostics
46
- "health_check", # Health diagnostics
47
- "document_summarizer", # File summarizer
48
- # Ticket functionality now provided by mcp-ticketer
49
- ]
50
-
51
- async def verify_and_configure(self) -> Dict[str, Any]:
52
- """
53
- Verify MCP gateway configuration and configure if needed.
54
-
55
- Returns:
56
- Dictionary with verification results and status
57
- """
58
- self.logger.info("Starting MCP Gateway verification")
59
-
60
- results = {
61
- "gateway_configured": False,
62
- "singleton_manager": False,
63
- "essential_tools": {},
64
- "configuration_created": False,
65
- "warnings": [],
66
- "errors": [],
67
- }
68
-
69
- try:
70
- # 1. Verify singleton manager
71
- results["singleton_manager"] = self._verify_singleton_manager()
72
-
73
- # 2. Ensure configuration directory exists
74
- self._ensure_config_directory()
75
-
76
- # 3. Verify or create gateway configuration
77
- config_created = await self._verify_gateway_configuration()
78
- results["configuration_created"] = config_created
79
-
80
- # 4. Verify essential tools
81
- tools_status = await self._verify_essential_tools()
82
- results["essential_tools"] = tools_status
83
-
84
- # 5. Check overall gateway status
85
- results["gateway_configured"] = self._assess_gateway_status(results)
86
-
87
- # Log summary
88
- self._log_verification_summary(results)
89
-
90
- return results
91
-
92
- except Exception as e:
93
- self.logger.error(f"Error during MCP Gateway verification: {e}")
94
- results["errors"].append(f"Verification failed: {e}")
95
- return results
96
-
97
- def _verify_singleton_manager(self) -> bool:
98
- """Verify singleton manager is working."""
99
- try:
100
- get_gateway_manager()
101
- # Test basic functionality
102
- running = is_gateway_running()
103
- self.logger.debug(
104
- f"Singleton manager operational, gateway running: {running}"
105
- )
106
- return True
107
- except Exception as e:
108
- self.logger.warning(f"Singleton manager issue: {e}")
109
- return False
110
-
111
- def _ensure_config_directory(self):
112
- """Ensure MCP configuration directory exists."""
113
- self.config_dir.mkdir(parents=True, exist_ok=True)
114
- self.logger.debug(f"MCP config directory: {self.config_dir}")
115
-
116
- async def _verify_gateway_configuration(self) -> bool:
117
- """Verify or create gateway configuration."""
118
- if self.config_file.exists():
119
- self.logger.debug("Gateway configuration file exists")
120
- return False
121
-
122
- # Create default configuration
123
- default_config = self._create_default_configuration()
124
-
125
- try:
126
- import json
127
-
128
- with self.config_file.open("w") as f:
129
- json.dump(default_config, f, indent=2)
130
-
131
- self.logger.info(
132
- f"Created default gateway configuration: {self.config_file}"
133
- )
134
- return True
135
-
136
- except Exception as e:
137
- self.logger.warning(f"Failed to create configuration: {e}")
138
- return False
139
-
140
- def _create_default_configuration(self) -> Dict[str, Any]:
141
- """Create default MCP gateway configuration."""
142
- return {
143
- "mcp": {
144
- "server": {
145
- "name": "claude-mpm-mcp-gateway",
146
- "version": "1.0.0",
147
- "description": "Claude MPM MCP Gateway with essential tools",
148
- },
149
- "tools": {
150
- "enabled": True,
151
- "auto_discover": True,
152
- "timeout_default": 30,
153
- "max_concurrent": 10,
154
- "essential_tools": self.essential_tools,
155
- },
156
- "logging": {
157
- "level": "INFO",
158
- "file": str(paths.logs_dir / "mcp_gateway.log"),
159
- },
160
- "security": {"validate_schemas": True, "sanitize_inputs": True},
161
- }
162
- }
163
-
164
- async def _verify_essential_tools(self) -> Dict[str, Dict[str, Any]]:
165
- """Verify essential tools are available."""
166
- tools_status = {}
167
-
168
- for tool_name in self.essential_tools:
169
- status = await self._verify_tool(tool_name)
170
- tools_status[tool_name] = status
171
-
172
- return tools_status
173
-
174
- async def _verify_tool(self, tool_name: str) -> Dict[str, Any]:
175
- """Verify a specific tool is available."""
176
- status = {
177
- "available": False,
178
- "initialized": False,
179
- "error": None,
180
- }
181
-
182
- try:
183
- # Try to import and initialize the tool
184
- tool_instance = await self._create_tool_instance(tool_name)
185
-
186
- if tool_instance:
187
- status["available"] = True
188
-
189
- # Try to initialize
190
- if hasattr(tool_instance, "initialize"):
191
- initialized = await tool_instance.initialize()
192
- status["initialized"] = initialized
193
- else:
194
- status["initialized"] = True
195
-
196
- except Exception as e:
197
- status["error"] = str(e)
198
- self.logger.debug(f"Tool {tool_name} verification failed: {e}")
199
-
200
- return status
201
-
202
- async def _create_tool_instance(self, tool_name: str):
203
- """Create an instance of the specified tool."""
204
- tool_map = {
205
- "echo": ("..tools.base_adapter", "EchoToolAdapter"),
206
- "calculator": ("..tools.base_adapter", "CalculatorToolAdapter"),
207
- "system_info": ("..tools.base_adapter", "SystemInfoToolAdapter"),
208
- "health_check": ("..tools.health_check_tool", "HealthCheckTool"),
209
- "document_summarizer": (
210
- "..tools.document_summarizer",
211
- "DocumentSummarizerTool",
212
- ),
213
- # Ticket functionality now provided by mcp-ticketer
214
- }
215
-
216
- if tool_name not in tool_map:
217
- return None
218
-
219
- module_path, class_name = tool_map[tool_name]
220
-
221
- try:
222
- # Dynamic import
223
- from importlib import import_module
224
-
225
- module = import_module(module_path, package=__package__)
226
- tool_class = getattr(module, class_name)
227
- return tool_class()
228
- except (ImportError, AttributeError) as e:
229
- self.logger.debug(f"Could not import {tool_name}: {e}")
230
- return None
231
-
232
- def _assess_gateway_status(self, results: Dict[str, Any]) -> bool:
233
- """Assess overall gateway configuration status."""
234
- # Gateway is considered configured if:
235
- # 1. Singleton manager works
236
- # 2. At least basic tools are available
237
- # 3. No critical errors
238
-
239
- if not results["singleton_manager"]:
240
- return False
241
-
242
- if results["errors"]:
243
- return False
244
-
245
- # Check if at least basic diagnostic tools are available
246
- essential_available = 0
247
- for _tool_name, status in results["essential_tools"].items():
248
- if status.get("available", False):
249
- essential_available += 1
250
-
251
- # Consider configured if at least 3 essential tools are available
252
- return essential_available >= 3
253
-
254
- def _log_verification_summary(self, results: Dict[str, Any]):
255
- """Log verification summary."""
256
- if results["gateway_configured"]:
257
- self.logger.info("✅ MCP Gateway verification completed successfully")
258
- else:
259
- self.logger.warning("⚠️ MCP Gateway verification completed with issues")
260
-
261
- # Log tool status
262
- available_tools = []
263
- unavailable_tools = []
264
-
265
- for tool_name, status in results["essential_tools"].items():
266
- if status.get("available", False):
267
- available_tools.append(tool_name)
268
- else:
269
- unavailable_tools.append(tool_name)
270
-
271
- if available_tools:
272
- self.logger.info(f"Available tools: {', '.join(available_tools)}")
273
-
274
- if unavailable_tools:
275
- self.logger.warning(f"Unavailable tools: {', '.join(unavailable_tools)}")
276
-
277
- # Log warnings and errors
278
- for warning in results.get("warnings", []):
279
- self.logger.warning(warning)
280
-
281
- for error in results.get("errors", []):
282
- self.logger.error(error)
283
-
284
-
285
- # Global verification function
286
- async def verify_mcp_gateway_on_startup() -> Dict[str, Any]:
287
- """
288
- Verify MCP Gateway configuration on startup.
289
-
290
- This function should be called during application startup to ensure
291
- the MCP gateway is properly configured.
292
-
293
- Returns:
294
- Dictionary with verification results
295
- """
296
- verifier = MCPGatewayStartupVerifier()
297
- return await verifier.verify_and_configure()
298
-
299
-
300
- def is_mcp_gateway_configured() -> bool:
301
- """
302
- Quick check if MCP gateway appears to be configured.
303
-
304
- Returns:
305
- True if gateway appears configured, False otherwise
306
- """
307
- try:
308
- # Check if singleton manager works
309
- get_gateway_manager()
310
-
311
- # Check if config directory exists
312
- config_dir = paths.claude_mpm_dir_hidden / "mcp"
313
- return config_dir.exists()
314
-
315
- except Exception:
316
- return False