claude-mpm 4.0.17__py3-none-any.whl → 4.0.20__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.
Files changed (46) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__main__.py +4 -0
  3. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +38 -2
  4. claude_mpm/agents/OUTPUT_STYLE.md +84 -0
  5. claude_mpm/agents/templates/qa.json +24 -12
  6. claude_mpm/cli/__init__.py +85 -1
  7. claude_mpm/cli/__main__.py +4 -0
  8. claude_mpm/cli/commands/mcp_install_commands.py +62 -5
  9. claude_mpm/cli/commands/mcp_server_commands.py +60 -79
  10. claude_mpm/cli/commands/memory.py +32 -5
  11. claude_mpm/cli/commands/run.py +33 -6
  12. claude_mpm/cli/parsers/base_parser.py +5 -0
  13. claude_mpm/cli/parsers/run_parser.py +5 -0
  14. claude_mpm/cli/utils.py +17 -4
  15. claude_mpm/core/base_service.py +1 -1
  16. claude_mpm/core/config.py +70 -5
  17. claude_mpm/core/framework_loader.py +342 -31
  18. claude_mpm/core/interactive_session.py +55 -1
  19. claude_mpm/core/oneshot_session.py +7 -1
  20. claude_mpm/core/output_style_manager.py +468 -0
  21. claude_mpm/core/unified_paths.py +190 -21
  22. claude_mpm/hooks/claude_hooks/hook_handler.py +91 -16
  23. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +3 -0
  24. claude_mpm/init.py +1 -0
  25. claude_mpm/scripts/mcp_server.py +68 -0
  26. claude_mpm/scripts/mcp_wrapper.py +39 -0
  27. claude_mpm/services/agents/deployment/agent_deployment.py +151 -7
  28. claude_mpm/services/agents/deployment/agent_template_builder.py +37 -1
  29. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +441 -0
  30. claude_mpm/services/agents/memory/__init__.py +0 -2
  31. claude_mpm/services/agents/memory/agent_memory_manager.py +737 -43
  32. claude_mpm/services/agents/memory/content_manager.py +144 -14
  33. claude_mpm/services/agents/memory/template_generator.py +7 -354
  34. claude_mpm/services/mcp_gateway/core/singleton_manager.py +312 -0
  35. claude_mpm/services/mcp_gateway/core/startup_verification.py +315 -0
  36. claude_mpm/services/mcp_gateway/main.py +7 -0
  37. claude_mpm/services/mcp_gateway/server/stdio_server.py +184 -176
  38. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +453 -0
  39. claude_mpm/services/subprocess_launcher_service.py +5 -0
  40. {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/METADATA +1 -1
  41. {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/RECORD +45 -38
  42. {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/entry_points.txt +1 -0
  43. claude_mpm/services/agents/memory/analyzer.py +0 -430
  44. {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/WHEEL +0 -0
  45. {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/licenses/LICENSE +0 -0
  46. {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,312 @@
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 asyncio
19
+ import fcntl
20
+ import json
21
+ import os
22
+ import signal
23
+ import threading
24
+ import time
25
+ from pathlib import Path
26
+ from typing import Any, Dict, Optional
27
+
28
+ from claude_mpm.config.paths import paths
29
+ from claude_mpm.core.logger import get_logger
30
+
31
+
32
+ class MCPGatewayManager:
33
+ """
34
+ Singleton manager for MCP Gateway instances.
35
+
36
+ Ensures only one gateway instance is running per installation using
37
+ file-based locking and PID tracking.
38
+ """
39
+
40
+ _instance: Optional['MCPGatewayManager'] = None
41
+ _lock = threading.Lock()
42
+
43
+ def __new__(cls):
44
+ """Singleton pattern implementation."""
45
+ with cls._lock:
46
+ if cls._instance is None:
47
+ cls._instance = super().__new__(cls)
48
+ cls._instance._initialized = False
49
+ return cls._instance
50
+
51
+ def __init__(self):
52
+ """Initialize the gateway manager."""
53
+ if self._initialized:
54
+ return
55
+
56
+ self.logger = get_logger("MCPGatewayManager")
57
+ self._initialized = True
58
+
59
+ # Paths for coordination
60
+ self.mcp_dir = paths.claude_mpm_dir_hidden / "mcp"
61
+ self.lock_file = self.mcp_dir / "gateway.lock"
62
+ self.instance_file = self.mcp_dir / "gateway.json"
63
+
64
+ # Ensure directory exists
65
+ self.mcp_dir.mkdir(parents=True, exist_ok=True)
66
+
67
+ # Lock file handle
68
+ self._lock_fd: Optional[int] = None
69
+ self._current_instance: Optional[Dict[str, Any]] = None
70
+
71
+ # Setup cleanup handlers
72
+ self._setup_cleanup_handlers()
73
+
74
+ def _setup_cleanup_handlers(self):
75
+ """Setup signal handlers for cleanup on termination."""
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(self.lock_file, os.O_CREAT | os.O_WRONLY | os.O_TRUNC)
93
+
94
+ # Try to acquire exclusive lock (non-blocking)
95
+ fcntl.flock(self._lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
96
+
97
+ # Write current PID to lock file
98
+ os.write(self._lock_fd, str(os.getpid()).encode())
99
+ os.fsync(self._lock_fd)
100
+
101
+ self.logger.info(f"Acquired gateway lock (PID: {os.getpid()})")
102
+ return True
103
+
104
+ except (OSError, IOError) as e:
105
+ if self._lock_fd:
106
+ try:
107
+ os.close(self._lock_fd)
108
+ except:
109
+ pass
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 open(self.instance_file, 'r') 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
+ else:
160
+ # Stale instance file, remove it
161
+ self.instance_file.unlink()
162
+ return None
163
+
164
+ except (json.JSONDecodeError, OSError) as e:
165
+ self.logger.warning(f"Error reading instance file: {e}")
166
+ return None
167
+
168
+ def _is_pid_running(self, pid: int) -> bool:
169
+ """Check if a PID is currently running."""
170
+ try:
171
+ os.kill(pid, 0) # Signal 0 just checks if process exists
172
+ return True
173
+ except (OSError, ProcessLookupError):
174
+ return False
175
+
176
+ def register_instance(self, gateway_name: str, version: str) -> bool:
177
+ """
178
+ Register a new gateway instance.
179
+
180
+ Args:
181
+ gateway_name: Name of the gateway
182
+ version: Gateway version
183
+
184
+ Returns:
185
+ True if registration successful, False otherwise
186
+ """
187
+ if not self.acquire_lock():
188
+ return False
189
+
190
+ try:
191
+ instance_info = {
192
+ 'pid': os.getpid(),
193
+ 'gateway_name': gateway_name,
194
+ 'version': version,
195
+ 'started_at': time.time(),
196
+ 'lock_file': str(self.lock_file),
197
+ }
198
+
199
+ with open(self.instance_file, 'w') as f:
200
+ json.dump(instance_info, f, indent=2)
201
+
202
+ self._current_instance = instance_info
203
+ self.logger.info(f"Registered gateway instance: {gateway_name} v{version}")
204
+ return True
205
+
206
+ except Exception as e:
207
+ self.logger.error(f"Failed to register instance: {e}")
208
+ self.release_lock()
209
+ return False
210
+
211
+ def cleanup(self):
212
+ """Clean up gateway instance and release resources."""
213
+ try:
214
+ # Remove instance file
215
+ if self.instance_file.exists():
216
+ self.instance_file.unlink()
217
+
218
+ # Release lock
219
+ self.release_lock()
220
+
221
+ self._current_instance = None
222
+ self.logger.info("Gateway cleanup completed")
223
+
224
+ except Exception as e:
225
+ self.logger.warning(f"Error during cleanup: {e}")
226
+
227
+
228
+ # Global functions for easy access
229
+ _manager: Optional[MCPGatewayManager] = None
230
+
231
+
232
+ def get_gateway_manager() -> MCPGatewayManager:
233
+ """Get the global gateway manager instance."""
234
+ global _manager
235
+ if _manager is None:
236
+ _manager = MCPGatewayManager()
237
+ return _manager
238
+
239
+
240
+ def is_gateway_running() -> bool:
241
+ """Check if a gateway instance is currently running."""
242
+ return get_gateway_manager().is_gateway_running()
243
+
244
+
245
+ def get_gateway_status() -> Optional[Dict[str, Any]]:
246
+ """Get status of running gateway instance."""
247
+ return get_gateway_manager().get_running_instance_info()
248
+
249
+
250
+ async def start_global_gateway(gateway_name: str = "claude-mpm-mcp", version: str = "1.0.0") -> bool:
251
+ """
252
+ Start the global MCP gateway instance.
253
+
254
+ Args:
255
+ gateway_name: Name for the gateway
256
+ version: Gateway version
257
+
258
+ Returns:
259
+ True if started successfully, False otherwise
260
+ """
261
+ manager = get_gateway_manager()
262
+
263
+ # Check if already running
264
+ if manager.is_gateway_running():
265
+ instance_info = manager.get_running_instance_info()
266
+ manager.logger.info(f"Gateway already running (PID: {instance_info.get('pid')})")
267
+ return True
268
+
269
+ # Register new instance
270
+ if not manager.register_instance(gateway_name, version):
271
+ manager.logger.error("Failed to register gateway instance")
272
+ return False
273
+
274
+ # Import and start the gateway
275
+ try:
276
+ from ..main import MCPGatewayOrchestrator
277
+
278
+ orchestrator = MCPGatewayOrchestrator()
279
+
280
+ if not await orchestrator.initialize():
281
+ manager.logger.error("Failed to initialize gateway")
282
+ manager.cleanup()
283
+ return False
284
+
285
+ if not await orchestrator.start():
286
+ manager.logger.error("Failed to start gateway")
287
+ manager.cleanup()
288
+ return False
289
+
290
+ manager.logger.info("Global gateway started successfully")
291
+ return True
292
+
293
+ except Exception as e:
294
+ manager.logger.error(f"Error starting gateway: {e}")
295
+ manager.cleanup()
296
+ return False
297
+
298
+
299
+ async def run_global_gateway():
300
+ """Run the global MCP gateway until shutdown."""
301
+ manager = get_gateway_manager()
302
+
303
+ try:
304
+ from ..main import MCPGatewayOrchestrator
305
+
306
+ orchestrator = MCPGatewayOrchestrator()
307
+ await orchestrator.run()
308
+
309
+ except Exception as e:
310
+ manager.logger.error(f"Error running gateway: {e}")
311
+ finally:
312
+ manager.cleanup()
@@ -0,0 +1,315 @@
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
+ import asyncio
20
+ from pathlib import Path
21
+ from typing import Any, Dict, List, Optional, Tuple
22
+
23
+ from claude_mpm.config.paths import paths
24
+ from claude_mpm.core.logger import get_logger
25
+
26
+ from .singleton_manager import get_gateway_manager, is_gateway_running
27
+
28
+
29
+ class MCPGatewayStartupVerifier:
30
+ """
31
+ Verifies and configures MCP Gateway on startup.
32
+
33
+ Ensures the gateway is properly configured with essential tools and
34
+ provides diagnostic information about the gateway state.
35
+ """
36
+
37
+ def __init__(self):
38
+ """Initialize the startup verifier."""
39
+ self.logger = get_logger("MCPGatewayStartupVerifier")
40
+ self.config_dir = paths.claude_mpm_dir_hidden / "mcp"
41
+ self.config_file = self.config_dir / "gateway_config.json"
42
+
43
+ # Essential tools that should be available
44
+ self.essential_tools = [
45
+ "echo", # Basic diagnostic tool
46
+ "calculator", # Math operations
47
+ "system_info", # System diagnostics
48
+ "health_check", # Health diagnostics
49
+ "document_summarizer", # File summarizer
50
+ "ticket", # Ticket service (unified)
51
+ ]
52
+
53
+ async def verify_and_configure(self) -> Dict[str, Any]:
54
+ """
55
+ Verify MCP gateway configuration and configure if needed.
56
+
57
+ Returns:
58
+ Dictionary with verification results and status
59
+ """
60
+ self.logger.info("Starting MCP Gateway verification")
61
+
62
+ results = {
63
+ "gateway_configured": False,
64
+ "singleton_manager": False,
65
+ "essential_tools": {},
66
+ "configuration_created": False,
67
+ "warnings": [],
68
+ "errors": [],
69
+ }
70
+
71
+ try:
72
+ # 1. Verify singleton manager
73
+ results["singleton_manager"] = self._verify_singleton_manager()
74
+
75
+ # 2. Ensure configuration directory exists
76
+ self._ensure_config_directory()
77
+
78
+ # 3. Verify or create gateway configuration
79
+ config_created = await self._verify_gateway_configuration()
80
+ results["configuration_created"] = config_created
81
+
82
+ # 4. Verify essential tools
83
+ tools_status = await self._verify_essential_tools()
84
+ results["essential_tools"] = tools_status
85
+
86
+ # 5. Check overall gateway status
87
+ results["gateway_configured"] = self._assess_gateway_status(results)
88
+
89
+ # Log summary
90
+ self._log_verification_summary(results)
91
+
92
+ return results
93
+
94
+ except Exception as e:
95
+ self.logger.error(f"Error during MCP Gateway verification: {e}")
96
+ results["errors"].append(f"Verification failed: {e}")
97
+ return results
98
+
99
+ def _verify_singleton_manager(self) -> bool:
100
+ """Verify singleton manager is working."""
101
+ try:
102
+ manager = get_gateway_manager()
103
+ # Test basic functionality
104
+ running = is_gateway_running()
105
+ self.logger.debug(f"Singleton manager operational, gateway running: {running}")
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
+ with open(self.config_file, 'w') as f:
128
+ json.dump(default_config, f, indent=2)
129
+
130
+ self.logger.info(f"Created default gateway configuration: {self.config_file}")
131
+ return True
132
+
133
+ except Exception as e:
134
+ self.logger.warning(f"Failed to create configuration: {e}")
135
+ return False
136
+
137
+ def _create_default_configuration(self) -> Dict[str, Any]:
138
+ """Create default MCP gateway configuration."""
139
+ return {
140
+ "mcp": {
141
+ "server": {
142
+ "name": "claude-mpm-mcp-gateway",
143
+ "version": "1.0.0",
144
+ "description": "Claude MPM MCP Gateway with essential tools"
145
+ },
146
+ "tools": {
147
+ "enabled": True,
148
+ "auto_discover": True,
149
+ "timeout_default": 30,
150
+ "max_concurrent": 10,
151
+ "essential_tools": self.essential_tools
152
+ },
153
+ "logging": {
154
+ "level": "INFO",
155
+ "file": str(paths.logs_dir / "mcp_gateway.log")
156
+ },
157
+ "security": {
158
+ "validate_schemas": True,
159
+ "sanitize_inputs": True
160
+ }
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": ("..tools.document_summarizer", "DocumentSummarizerTool"),
210
+ "ticket": ("..tools.unified_ticket_tool", "UnifiedTicketTool"),
211
+ }
212
+
213
+ if tool_name not in tool_map:
214
+ return None
215
+
216
+ module_path, class_name = tool_map[tool_name]
217
+
218
+ try:
219
+ # Dynamic import
220
+ from importlib import import_module
221
+ module = import_module(module_path, package=__package__)
222
+ tool_class = getattr(module, class_name)
223
+ return tool_class()
224
+ except (ImportError, AttributeError) as e:
225
+ self.logger.debug(f"Could not import {tool_name}: {e}")
226
+ return None
227
+
228
+ def _assess_gateway_status(self, results: Dict[str, Any]) -> bool:
229
+ """Assess overall gateway configuration status."""
230
+ # Gateway is considered configured if:
231
+ # 1. Singleton manager works
232
+ # 2. At least basic tools are available
233
+ # 3. No critical errors
234
+
235
+ if not results["singleton_manager"]:
236
+ return False
237
+
238
+ if results["errors"]:
239
+ return False
240
+
241
+ # Check if at least basic diagnostic tools are available
242
+ essential_available = 0
243
+ for tool_name, status in results["essential_tools"].items():
244
+ if status.get("available", False):
245
+ essential_available += 1
246
+
247
+ # Consider configured if at least 3 essential tools are available
248
+ return essential_available >= 3
249
+
250
+ def _log_verification_summary(self, results: Dict[str, Any]):
251
+ """Log verification summary."""
252
+ if results["gateway_configured"]:
253
+ self.logger.info("✅ MCP Gateway verification completed successfully")
254
+ else:
255
+ self.logger.warning("⚠️ MCP Gateway verification completed with issues")
256
+
257
+ # Log tool status
258
+ available_tools = []
259
+ unavailable_tools = []
260
+
261
+ for tool_name, status in results["essential_tools"].items():
262
+ if status.get("available", False):
263
+ available_tools.append(tool_name)
264
+ else:
265
+ unavailable_tools.append(tool_name)
266
+
267
+ if available_tools:
268
+ self.logger.info(f"Available tools: {', '.join(available_tools)}")
269
+
270
+ if unavailable_tools:
271
+ self.logger.warning(f"Unavailable tools: {', '.join(unavailable_tools)}")
272
+
273
+ # Log warnings and errors
274
+ for warning in results.get("warnings", []):
275
+ self.logger.warning(warning)
276
+
277
+ for error in results.get("errors", []):
278
+ self.logger.error(error)
279
+
280
+
281
+ # Global verification function
282
+ async def verify_mcp_gateway_on_startup() -> Dict[str, Any]:
283
+ """
284
+ Verify MCP Gateway configuration on startup.
285
+
286
+ This function should be called during application startup to ensure
287
+ the MCP gateway is properly configured.
288
+
289
+ Returns:
290
+ Dictionary with verification results
291
+ """
292
+ verifier = MCPGatewayStartupVerifier()
293
+ return await verifier.verify_and_configure()
294
+
295
+
296
+ def is_mcp_gateway_configured() -> bool:
297
+ """
298
+ Quick check if MCP gateway appears to be configured.
299
+
300
+ Returns:
301
+ True if gateway appears configured, False otherwise
302
+ """
303
+ try:
304
+ # Check if singleton manager works
305
+ manager = get_gateway_manager()
306
+
307
+ # Check if config directory exists
308
+ config_dir = paths.claude_mpm_dir_hidden / "mcp"
309
+ if not config_dir.exists():
310
+ return False
311
+
312
+ return True
313
+
314
+ except Exception:
315
+ return False
@@ -256,6 +256,13 @@ class MCPGatewayOrchestrator:
256
256
  except Exception as e:
257
257
  self.logger.warning(f"Could not load basic tools: {e}")
258
258
 
259
+ # Optional: Health check tool
260
+ try:
261
+ from .tools.health_check_tool import HealthCheckTool
262
+ tools.append(HealthCheckTool())
263
+ except Exception as e:
264
+ self.logger.warning(f"Could not load health check tool: {e}")
265
+
259
266
  # Optional: Document summarizer
260
267
  if DocumentSummarizerTool is not None:
261
268
  try: