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