claude-mpm 5.1.9__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 (176) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +4 -0
  3. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +1 -1
  4. claude_mpm/agents/PM_INSTRUCTIONS.md +290 -34
  5. claude_mpm/agents/agent_loader.py +13 -44
  6. claude_mpm/agents/templates/circuit-breakers.md +138 -1
  7. claude_mpm/cli/__main__.py +4 -0
  8. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  9. claude_mpm/cli/commands/agent_state_manager.py +8 -17
  10. claude_mpm/cli/commands/agents.py +0 -31
  11. claude_mpm/cli/commands/auto_configure.py +210 -25
  12. claude_mpm/cli/commands/config.py +88 -2
  13. claude_mpm/cli/commands/configure.py +1097 -158
  14. claude_mpm/cli/commands/configure_agent_display.py +15 -6
  15. claude_mpm/cli/commands/mpm_init/core.py +160 -46
  16. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  17. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  18. claude_mpm/cli/commands/skills.py +214 -189
  19. claude_mpm/cli/commands/summarize.py +413 -0
  20. claude_mpm/cli/executor.py +11 -3
  21. claude_mpm/cli/parsers/agents_parser.py +0 -9
  22. claude_mpm/cli/parsers/auto_configure_parser.py +0 -138
  23. claude_mpm/cli/parsers/base_parser.py +5 -0
  24. claude_mpm/cli/parsers/config_parser.py +153 -83
  25. claude_mpm/cli/parsers/skills_parser.py +3 -2
  26. claude_mpm/cli/startup.py +550 -94
  27. claude_mpm/commands/mpm-config.md +265 -0
  28. claude_mpm/commands/mpm-help.md +14 -95
  29. claude_mpm/commands/mpm-organize.md +500 -0
  30. claude_mpm/config/agent_sources.py +27 -0
  31. claude_mpm/core/framework/formatters/content_formatter.py +3 -13
  32. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  33. claude_mpm/core/framework_loader.py +4 -2
  34. claude_mpm/core/logger.py +13 -0
  35. claude_mpm/core/socketio_pool.py +3 -3
  36. claude_mpm/core/unified_agent_registry.py +5 -15
  37. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  38. claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
  39. claude_mpm/hooks/claude_hooks/hook_handler.py +6 -0
  40. claude_mpm/hooks/claude_hooks/installer.py +33 -10
  41. claude_mpm/hooks/claude_hooks/memory_integration.py +26 -9
  42. claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
  43. claude_mpm/hooks/claude_hooks/services/connection_manager.py +4 -0
  44. claude_mpm/hooks/memory_integration_hook.py +46 -1
  45. claude_mpm/init.py +0 -19
  46. claude_mpm/scripts/claude-hook-handler.sh +58 -18
  47. claude_mpm/scripts/launch_monitor.py +93 -13
  48. claude_mpm/scripts/start_activity_logging.py +0 -0
  49. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  50. claude_mpm/services/agents/agent_review_service.py +280 -0
  51. claude_mpm/services/agents/deployment/agent_discovery_service.py +2 -3
  52. claude_mpm/services/agents/deployment/agent_template_builder.py +4 -2
  53. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +78 -9
  54. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +335 -53
  55. claude_mpm/services/agents/git_source_manager.py +34 -0
  56. claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
  57. claude_mpm/services/agents/sources/git_source_sync_service.py +8 -1
  58. claude_mpm/services/agents/toolchain_detector.py +10 -6
  59. claude_mpm/services/analysis/__init__.py +11 -1
  60. claude_mpm/services/analysis/clone_detector.py +1030 -0
  61. claude_mpm/services/command_deployment_service.py +81 -10
  62. claude_mpm/services/event_bus/config.py +3 -1
  63. claude_mpm/services/git/git_operations_service.py +93 -8
  64. claude_mpm/services/monitor/daemon.py +9 -2
  65. claude_mpm/services/monitor/daemon_manager.py +39 -3
  66. claude_mpm/services/monitor/server.py +225 -19
  67. claude_mpm/services/self_upgrade_service.py +120 -12
  68. claude_mpm/services/skills/__init__.py +3 -0
  69. claude_mpm/services/skills/git_skill_source_manager.py +32 -2
  70. claude_mpm/services/skills/selective_skill_deployer.py +704 -0
  71. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  72. claude_mpm/services/skills_deployer.py +126 -9
  73. claude_mpm/services/socketio/event_normalizer.py +15 -1
  74. claude_mpm/services/socketio/server/core.py +160 -21
  75. claude_mpm/services/version_control/git_operations.py +103 -0
  76. claude_mpm/utils/agent_filters.py +17 -44
  77. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/METADATA +47 -84
  78. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/RECORD +82 -161
  79. claude_mpm-5.4.22.dist-info/entry_points.txt +5 -0
  80. claude_mpm-5.4.22.dist-info/licenses/LICENSE +94 -0
  81. claude_mpm-5.4.22.dist-info/licenses/LICENSE-FAQ.md +153 -0
  82. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
  83. claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
  84. claude_mpm/agents/BASE_ENGINEER.md +0 -658
  85. claude_mpm/agents/BASE_OPS.md +0 -219
  86. claude_mpm/agents/BASE_PM.md +0 -480
  87. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
  88. claude_mpm/agents/BASE_QA.md +0 -167
  89. claude_mpm/agents/BASE_RESEARCH.md +0 -53
  90. claude_mpm/agents/base_agent.json +0 -31
  91. claude_mpm/agents/base_agent_loader.py +0 -601
  92. claude_mpm/cli/commands/agents_detect.py +0 -380
  93. claude_mpm/cli/commands/agents_recommend.py +0 -309
  94. claude_mpm/cli/ticket_cli.py +0 -35
  95. claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
  96. claude_mpm/commands/mpm-agents-detect.md +0 -177
  97. claude_mpm/commands/mpm-agents-list.md +0 -131
  98. claude_mpm/commands/mpm-agents-recommend.md +0 -223
  99. claude_mpm/commands/mpm-config-view.md +0 -150
  100. claude_mpm/commands/mpm-ticket-organize.md +0 -304
  101. claude_mpm/dashboard/analysis_runner.py +0 -455
  102. claude_mpm/dashboard/index.html +0 -13
  103. claude_mpm/dashboard/open_dashboard.py +0 -66
  104. claude_mpm/dashboard/static/css/activity.css +0 -1958
  105. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  106. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  107. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  108. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  109. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  110. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  111. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  112. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  113. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  114. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  115. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  116. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  117. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  118. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  119. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  120. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  121. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  122. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  123. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  124. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  125. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  126. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  127. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  128. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  129. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  130. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  131. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  132. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  133. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  134. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  135. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  136. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  137. claude_mpm/dashboard/templates/code_simple.html +0 -153
  138. claude_mpm/dashboard/templates/index.html +0 -606
  139. claude_mpm/dashboard/test_dashboard.html +0 -372
  140. claude_mpm/scripts/mcp_server.py +0 -75
  141. claude_mpm/scripts/mcp_wrapper.py +0 -39
  142. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  143. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  144. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  145. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  146. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  147. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  148. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  149. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  150. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  151. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  152. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
  153. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  154. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  155. claude_mpm/services/mcp_gateway/main.py +0 -589
  156. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  157. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  158. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  159. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  160. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  161. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  162. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  163. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  164. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  165. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  166. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  167. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  168. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  169. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  170. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  171. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  172. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  173. claude_mpm-5.1.9.dist-info/entry_points.txt +0 -10
  174. claude_mpm-5.1.9.dist-info/licenses/LICENSE +0 -21
  175. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/WHEEL +0 -0
  176. {claude_mpm-5.1.9.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