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.
- claude_mpm/VERSION +1 -1
- claude_mpm/__main__.py +4 -0
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +38 -2
- claude_mpm/agents/OUTPUT_STYLE.md +84 -0
- claude_mpm/agents/templates/qa.json +24 -12
- claude_mpm/cli/__init__.py +85 -1
- claude_mpm/cli/__main__.py +4 -0
- claude_mpm/cli/commands/mcp_install_commands.py +62 -5
- claude_mpm/cli/commands/mcp_server_commands.py +60 -79
- claude_mpm/cli/commands/memory.py +32 -5
- claude_mpm/cli/commands/run.py +33 -6
- claude_mpm/cli/parsers/base_parser.py +5 -0
- claude_mpm/cli/parsers/run_parser.py +5 -0
- claude_mpm/cli/utils.py +17 -4
- claude_mpm/core/base_service.py +1 -1
- claude_mpm/core/config.py +70 -5
- claude_mpm/core/framework_loader.py +342 -31
- claude_mpm/core/interactive_session.py +55 -1
- claude_mpm/core/oneshot_session.py +7 -1
- claude_mpm/core/output_style_manager.py +468 -0
- claude_mpm/core/unified_paths.py +190 -21
- claude_mpm/hooks/claude_hooks/hook_handler.py +91 -16
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +3 -0
- claude_mpm/init.py +1 -0
- claude_mpm/scripts/mcp_server.py +68 -0
- claude_mpm/scripts/mcp_wrapper.py +39 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +151 -7
- claude_mpm/services/agents/deployment/agent_template_builder.py +37 -1
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +441 -0
- claude_mpm/services/agents/memory/__init__.py +0 -2
- claude_mpm/services/agents/memory/agent_memory_manager.py +737 -43
- claude_mpm/services/agents/memory/content_manager.py +144 -14
- claude_mpm/services/agents/memory/template_generator.py +7 -354
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +312 -0
- claude_mpm/services/mcp_gateway/core/startup_verification.py +315 -0
- claude_mpm/services/mcp_gateway/main.py +7 -0
- claude_mpm/services/mcp_gateway/server/stdio_server.py +184 -176
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +453 -0
- claude_mpm/services/subprocess_launcher_service.py +5 -0
- {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/METADATA +1 -1
- {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/RECORD +45 -38
- {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/entry_points.txt +1 -0
- claude_mpm/services/agents/memory/analyzer.py +0 -430
- {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/WHEEL +0 -0
- {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Health Check Tool for MCP Gateway
|
|
3
|
+
=================================
|
|
4
|
+
|
|
5
|
+
Provides comprehensive health checking and diagnostic capabilities for the
|
|
6
|
+
MCP Gateway and claude-mpm system.
|
|
7
|
+
|
|
8
|
+
WHY: A dedicated health check tool provides centralized diagnostics for
|
|
9
|
+
troubleshooting and system monitoring, essential for maintaining a reliable
|
|
10
|
+
MCP gateway service.
|
|
11
|
+
|
|
12
|
+
DESIGN DECISIONS:
|
|
13
|
+
- Comprehensive system checks (gateway, tools, configuration)
|
|
14
|
+
- Structured output for easy parsing
|
|
15
|
+
- Non-blocking checks with timeouts
|
|
16
|
+
- Detailed error reporting for troubleshooting
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import asyncio
|
|
20
|
+
import os
|
|
21
|
+
import platform
|
|
22
|
+
import psutil
|
|
23
|
+
import sys
|
|
24
|
+
from datetime import datetime
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from typing import Any, Dict, List, Optional
|
|
27
|
+
|
|
28
|
+
from claude_mpm.config.paths import paths
|
|
29
|
+
from claude_mpm.core.logger import get_logger
|
|
30
|
+
from claude_mpm.services.mcp_gateway.core.interfaces import (
|
|
31
|
+
MCPToolDefinition,
|
|
32
|
+
MCPToolInvocation,
|
|
33
|
+
MCPToolResult,
|
|
34
|
+
)
|
|
35
|
+
from claude_mpm.services.mcp_gateway.tools.base_adapter import BaseToolAdapter
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class HealthCheckTool(BaseToolAdapter):
|
|
39
|
+
"""
|
|
40
|
+
Comprehensive health check tool for MCP Gateway diagnostics.
|
|
41
|
+
|
|
42
|
+
Provides system health, gateway status, tool availability, and
|
|
43
|
+
configuration validation checks.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self):
|
|
47
|
+
"""Initialize the health check tool."""
|
|
48
|
+
definition = MCPToolDefinition(
|
|
49
|
+
name="health_check",
|
|
50
|
+
description="Comprehensive health check and diagnostics for MCP Gateway and claude-mpm system",
|
|
51
|
+
input_schema={
|
|
52
|
+
"type": "object",
|
|
53
|
+
"properties": {
|
|
54
|
+
"check_type": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"enum": ["all", "system", "gateway", "tools", "config"],
|
|
57
|
+
"description": "Type of health check to perform",
|
|
58
|
+
"default": "all",
|
|
59
|
+
},
|
|
60
|
+
"detailed": {
|
|
61
|
+
"type": "boolean",
|
|
62
|
+
"description": "Include detailed diagnostic information",
|
|
63
|
+
"default": False,
|
|
64
|
+
},
|
|
65
|
+
"timeout": {
|
|
66
|
+
"type": "integer",
|
|
67
|
+
"description": "Timeout for checks in seconds",
|
|
68
|
+
"default": 30,
|
|
69
|
+
"minimum": 5,
|
|
70
|
+
"maximum": 120,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
"required": [],
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
super().__init__(definition)
|
|
77
|
+
self.logger = get_logger("HealthCheckTool")
|
|
78
|
+
|
|
79
|
+
async def invoke(self, invocation: MCPToolInvocation) -> MCPToolResult:
|
|
80
|
+
"""
|
|
81
|
+
Perform health checks based on the requested type.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
invocation: Tool invocation request
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Tool execution result with health check results
|
|
88
|
+
"""
|
|
89
|
+
start_time = datetime.now()
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
# Get parameters
|
|
93
|
+
check_type = invocation.parameters.get("check_type", "all")
|
|
94
|
+
detailed = invocation.parameters.get("detailed", False)
|
|
95
|
+
timeout = invocation.parameters.get("timeout", 30)
|
|
96
|
+
|
|
97
|
+
# Perform health checks
|
|
98
|
+
results = await self._perform_health_checks(check_type, detailed, timeout)
|
|
99
|
+
|
|
100
|
+
# Calculate execution time
|
|
101
|
+
execution_time = (datetime.now() - start_time).total_seconds()
|
|
102
|
+
|
|
103
|
+
# Update metrics
|
|
104
|
+
self._update_metrics(True, execution_time)
|
|
105
|
+
|
|
106
|
+
return MCPToolResult(
|
|
107
|
+
success=True,
|
|
108
|
+
data=results,
|
|
109
|
+
execution_time=execution_time,
|
|
110
|
+
metadata={
|
|
111
|
+
"tool": "health_check",
|
|
112
|
+
"check_type": check_type,
|
|
113
|
+
"detailed": detailed,
|
|
114
|
+
},
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
except Exception as e:
|
|
118
|
+
execution_time = (datetime.now() - start_time).total_seconds()
|
|
119
|
+
self._update_metrics(False, execution_time)
|
|
120
|
+
|
|
121
|
+
return MCPToolResult(
|
|
122
|
+
success=False,
|
|
123
|
+
error=f"Health check failed: {str(e)}",
|
|
124
|
+
execution_time=execution_time,
|
|
125
|
+
metadata={"tool": "health_check", "error": str(e)},
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
async def _perform_health_checks(
|
|
129
|
+
self, check_type: str, detailed: bool, timeout: int
|
|
130
|
+
) -> Dict[str, Any]:
|
|
131
|
+
"""Perform the requested health checks."""
|
|
132
|
+
results = {
|
|
133
|
+
"timestamp": datetime.now().isoformat(),
|
|
134
|
+
"check_type": check_type,
|
|
135
|
+
"detailed": detailed,
|
|
136
|
+
"overall_status": "unknown",
|
|
137
|
+
"checks": {},
|
|
138
|
+
"summary": {},
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
# Determine which checks to run
|
|
142
|
+
checks_to_run = []
|
|
143
|
+
if check_type == "all":
|
|
144
|
+
checks_to_run = ["system", "gateway", "tools", "config"]
|
|
145
|
+
else:
|
|
146
|
+
checks_to_run = [check_type]
|
|
147
|
+
|
|
148
|
+
# Run checks with timeout
|
|
149
|
+
try:
|
|
150
|
+
check_tasks = []
|
|
151
|
+
for check in checks_to_run:
|
|
152
|
+
if check == "system":
|
|
153
|
+
check_tasks.append(self._check_system_health(detailed))
|
|
154
|
+
elif check == "gateway":
|
|
155
|
+
check_tasks.append(self._check_gateway_health(detailed))
|
|
156
|
+
elif check == "tools":
|
|
157
|
+
check_tasks.append(self._check_tools_health(detailed))
|
|
158
|
+
elif check == "config":
|
|
159
|
+
check_tasks.append(self._check_config_health(detailed))
|
|
160
|
+
|
|
161
|
+
# Run all checks concurrently with timeout
|
|
162
|
+
check_results = await asyncio.wait_for(
|
|
163
|
+
asyncio.gather(*check_tasks, return_exceptions=True),
|
|
164
|
+
timeout=timeout
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Process results
|
|
168
|
+
for i, check_name in enumerate(checks_to_run):
|
|
169
|
+
if i < len(check_results):
|
|
170
|
+
if isinstance(check_results[i], Exception):
|
|
171
|
+
results["checks"][check_name] = {
|
|
172
|
+
"status": "error",
|
|
173
|
+
"error": str(check_results[i]),
|
|
174
|
+
}
|
|
175
|
+
else:
|
|
176
|
+
results["checks"][check_name] = check_results[i]
|
|
177
|
+
else:
|
|
178
|
+
results["checks"][check_name] = {
|
|
179
|
+
"status": "timeout",
|
|
180
|
+
"error": "Check timed out",
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
except asyncio.TimeoutError:
|
|
184
|
+
results["checks"]["timeout"] = {
|
|
185
|
+
"status": "error",
|
|
186
|
+
"error": f"Health checks timed out after {timeout} seconds",
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
# Calculate overall status and summary
|
|
190
|
+
results["overall_status"] = self._calculate_overall_status(results["checks"])
|
|
191
|
+
results["summary"] = self._generate_summary(results["checks"])
|
|
192
|
+
|
|
193
|
+
return results
|
|
194
|
+
|
|
195
|
+
async def _check_system_health(self, detailed: bool) -> Dict[str, Any]:
|
|
196
|
+
"""Check system health (CPU, memory, disk, etc.)."""
|
|
197
|
+
check_result = {
|
|
198
|
+
"status": "healthy",
|
|
199
|
+
"checks": {},
|
|
200
|
+
"warnings": [],
|
|
201
|
+
"errors": [],
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
try:
|
|
205
|
+
# Basic system info
|
|
206
|
+
check_result["checks"]["platform"] = {
|
|
207
|
+
"system": platform.system(),
|
|
208
|
+
"release": platform.release(),
|
|
209
|
+
"python_version": sys.version,
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
# Memory check
|
|
213
|
+
memory = psutil.virtual_memory()
|
|
214
|
+
memory_usage = memory.percent
|
|
215
|
+
check_result["checks"]["memory"] = {
|
|
216
|
+
"usage_percent": memory_usage,
|
|
217
|
+
"available_gb": round(memory.available / (1024**3), 2),
|
|
218
|
+
"total_gb": round(memory.total / (1024**3), 2),
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if memory_usage > 90:
|
|
222
|
+
check_result["errors"].append("High memory usage detected")
|
|
223
|
+
check_result["status"] = "unhealthy"
|
|
224
|
+
elif memory_usage > 80:
|
|
225
|
+
check_result["warnings"].append("Elevated memory usage")
|
|
226
|
+
|
|
227
|
+
# CPU check
|
|
228
|
+
cpu_usage = psutil.cpu_percent(interval=1)
|
|
229
|
+
check_result["checks"]["cpu"] = {
|
|
230
|
+
"usage_percent": cpu_usage,
|
|
231
|
+
"count": psutil.cpu_count(),
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if cpu_usage > 95:
|
|
235
|
+
check_result["errors"].append("High CPU usage detected")
|
|
236
|
+
check_result["status"] = "unhealthy"
|
|
237
|
+
elif cpu_usage > 80:
|
|
238
|
+
check_result["warnings"].append("Elevated CPU usage")
|
|
239
|
+
|
|
240
|
+
# Disk check for claude-mpm data directory
|
|
241
|
+
if paths.data_dir.exists():
|
|
242
|
+
disk_usage = psutil.disk_usage(str(paths.data_dir))
|
|
243
|
+
disk_usage_percent = (disk_usage.used / disk_usage.total) * 100
|
|
244
|
+
check_result["checks"]["disk"] = {
|
|
245
|
+
"usage_percent": round(disk_usage_percent, 2),
|
|
246
|
+
"free_gb": round(disk_usage.free / (1024**3), 2),
|
|
247
|
+
"total_gb": round(disk_usage.total / (1024**3), 2),
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if disk_usage_percent > 95:
|
|
251
|
+
check_result["errors"].append("Disk space critically low")
|
|
252
|
+
check_result["status"] = "unhealthy"
|
|
253
|
+
elif disk_usage_percent > 85:
|
|
254
|
+
check_result["warnings"].append("Disk space running low")
|
|
255
|
+
|
|
256
|
+
# Process check
|
|
257
|
+
current_process = psutil.Process()
|
|
258
|
+
check_result["checks"]["process"] = {
|
|
259
|
+
"pid": current_process.pid,
|
|
260
|
+
"memory_mb": round(current_process.memory_info().rss / (1024**2), 2),
|
|
261
|
+
"cpu_percent": current_process.cpu_percent(),
|
|
262
|
+
"threads": current_process.num_threads(),
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
except Exception as e:
|
|
266
|
+
check_result["status"] = "error"
|
|
267
|
+
check_result["errors"].append(f"System health check failed: {e}")
|
|
268
|
+
|
|
269
|
+
return check_result
|
|
270
|
+
|
|
271
|
+
async def _check_gateway_health(self, detailed: bool) -> Dict[str, Any]:
|
|
272
|
+
"""Check MCP Gateway health."""
|
|
273
|
+
check_result = {
|
|
274
|
+
"status": "healthy",
|
|
275
|
+
"checks": {},
|
|
276
|
+
"warnings": [],
|
|
277
|
+
"errors": [],
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
try:
|
|
281
|
+
# Check singleton manager
|
|
282
|
+
from ..core.singleton_manager import get_gateway_manager, is_gateway_running
|
|
283
|
+
|
|
284
|
+
manager = get_gateway_manager()
|
|
285
|
+
check_result["checks"]["singleton_manager"] = {
|
|
286
|
+
"available": True,
|
|
287
|
+
"gateway_running": is_gateway_running(),
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
# Check gateway instance info
|
|
291
|
+
instance_info = manager.get_running_instance_info()
|
|
292
|
+
if instance_info:
|
|
293
|
+
check_result["checks"]["instance"] = instance_info
|
|
294
|
+
else:
|
|
295
|
+
check_result["warnings"].append("No gateway instance currently running")
|
|
296
|
+
|
|
297
|
+
# Check MCP directories
|
|
298
|
+
mcp_dir = paths.data_dir / "mcp"
|
|
299
|
+
check_result["checks"]["directories"] = {
|
|
300
|
+
"mcp_dir_exists": mcp_dir.exists(),
|
|
301
|
+
"mcp_dir_writable": mcp_dir.exists() and os.access(mcp_dir, os.W_OK),
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if not mcp_dir.exists():
|
|
305
|
+
check_result["errors"].append("MCP directory does not exist")
|
|
306
|
+
check_result["status"] = "unhealthy"
|
|
307
|
+
|
|
308
|
+
except Exception as e:
|
|
309
|
+
check_result["status"] = "error"
|
|
310
|
+
check_result["errors"].append(f"Gateway health check failed: {e}")
|
|
311
|
+
|
|
312
|
+
return check_result
|
|
313
|
+
|
|
314
|
+
async def _check_tools_health(self, detailed: bool) -> Dict[str, Any]:
|
|
315
|
+
"""Check MCP tools health."""
|
|
316
|
+
check_result = {
|
|
317
|
+
"status": "healthy",
|
|
318
|
+
"checks": {},
|
|
319
|
+
"warnings": [],
|
|
320
|
+
"errors": [],
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
try:
|
|
324
|
+
# Try to import and check tool registry
|
|
325
|
+
from ..registry.tool_registry import ToolRegistry
|
|
326
|
+
|
|
327
|
+
registry = ToolRegistry()
|
|
328
|
+
await registry.initialize()
|
|
329
|
+
|
|
330
|
+
# Get tool list
|
|
331
|
+
tools = registry.list_tools()
|
|
332
|
+
check_result["checks"]["tool_count"] = len(tools)
|
|
333
|
+
check_result["checks"]["tools"] = [tool.name for tool in tools]
|
|
334
|
+
|
|
335
|
+
# Check essential tools
|
|
336
|
+
essential_tools = ["echo", "calculator", "system_info"]
|
|
337
|
+
available_essential = []
|
|
338
|
+
missing_essential = []
|
|
339
|
+
|
|
340
|
+
for tool_name in essential_tools:
|
|
341
|
+
if registry.get_tool(tool_name):
|
|
342
|
+
available_essential.append(tool_name)
|
|
343
|
+
else:
|
|
344
|
+
missing_essential.append(tool_name)
|
|
345
|
+
|
|
346
|
+
check_result["checks"]["essential_tools"] = {
|
|
347
|
+
"available": available_essential,
|
|
348
|
+
"missing": missing_essential,
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if missing_essential:
|
|
352
|
+
check_result["warnings"].append(f"Missing essential tools: {missing_essential}")
|
|
353
|
+
|
|
354
|
+
if len(available_essential) == 0:
|
|
355
|
+
check_result["status"] = "unhealthy"
|
|
356
|
+
check_result["errors"].append("No essential tools available")
|
|
357
|
+
|
|
358
|
+
except Exception as e:
|
|
359
|
+
check_result["status"] = "error"
|
|
360
|
+
check_result["errors"].append(f"Tools health check failed: {e}")
|
|
361
|
+
|
|
362
|
+
return check_result
|
|
363
|
+
|
|
364
|
+
async def _check_config_health(self, detailed: bool) -> Dict[str, Any]:
|
|
365
|
+
"""Check configuration health."""
|
|
366
|
+
check_result = {
|
|
367
|
+
"status": "healthy",
|
|
368
|
+
"checks": {},
|
|
369
|
+
"warnings": [],
|
|
370
|
+
"errors": [],
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
try:
|
|
374
|
+
# Check configuration files
|
|
375
|
+
config_dir = paths.data_dir / "mcp"
|
|
376
|
+
config_file = config_dir / "gateway_config.json"
|
|
377
|
+
|
|
378
|
+
check_result["checks"]["config_dir"] = config_dir.exists()
|
|
379
|
+
check_result["checks"]["config_file"] = config_file.exists()
|
|
380
|
+
|
|
381
|
+
if config_file.exists():
|
|
382
|
+
# Try to load configuration
|
|
383
|
+
import json
|
|
384
|
+
with open(config_file, 'r') as f:
|
|
385
|
+
config_data = json.load(f)
|
|
386
|
+
|
|
387
|
+
check_result["checks"]["config_valid"] = True
|
|
388
|
+
if detailed:
|
|
389
|
+
check_result["checks"]["config_content"] = config_data
|
|
390
|
+
else:
|
|
391
|
+
check_result["warnings"].append("Gateway configuration file not found")
|
|
392
|
+
|
|
393
|
+
# Check paths
|
|
394
|
+
check_result["checks"]["paths"] = {
|
|
395
|
+
"data_dir": str(paths.data_dir),
|
|
396
|
+
"logs_dir": str(paths.logs_dir),
|
|
397
|
+
"data_dir_exists": paths.data_dir.exists(),
|
|
398
|
+
"logs_dir_exists": paths.logs_dir.exists(),
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
except Exception as e:
|
|
402
|
+
check_result["status"] = "error"
|
|
403
|
+
check_result["errors"].append(f"Config health check failed: {e}")
|
|
404
|
+
|
|
405
|
+
return check_result
|
|
406
|
+
|
|
407
|
+
def _calculate_overall_status(self, checks: Dict[str, Any]) -> str:
|
|
408
|
+
"""Calculate overall health status from individual checks."""
|
|
409
|
+
if not checks:
|
|
410
|
+
return "unknown"
|
|
411
|
+
|
|
412
|
+
statuses = [check.get("status", "unknown") for check in checks.values()]
|
|
413
|
+
|
|
414
|
+
if "error" in statuses:
|
|
415
|
+
return "error"
|
|
416
|
+
elif "unhealthy" in statuses:
|
|
417
|
+
return "unhealthy"
|
|
418
|
+
elif "warning" in statuses:
|
|
419
|
+
return "warning"
|
|
420
|
+
elif all(status == "healthy" for status in statuses):
|
|
421
|
+
return "healthy"
|
|
422
|
+
else:
|
|
423
|
+
return "unknown"
|
|
424
|
+
|
|
425
|
+
def _generate_summary(self, checks: Dict[str, Any]) -> Dict[str, Any]:
|
|
426
|
+
"""Generate a summary of health check results."""
|
|
427
|
+
summary = {
|
|
428
|
+
"total_checks": len(checks),
|
|
429
|
+
"healthy": 0,
|
|
430
|
+
"warnings": 0,
|
|
431
|
+
"errors": 0,
|
|
432
|
+
"issues": [],
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
for check_name, check_result in checks.items():
|
|
436
|
+
status = check_result.get("status", "unknown")
|
|
437
|
+
|
|
438
|
+
if status == "healthy":
|
|
439
|
+
summary["healthy"] += 1
|
|
440
|
+
elif status in ["warning", "unhealthy"]:
|
|
441
|
+
summary["warnings"] += 1
|
|
442
|
+
# Collect warning messages
|
|
443
|
+
warnings = check_result.get("warnings", [])
|
|
444
|
+
for warning in warnings:
|
|
445
|
+
summary["issues"].append(f"{check_name}: {warning}")
|
|
446
|
+
elif status == "error":
|
|
447
|
+
summary["errors"] += 1
|
|
448
|
+
# Collect error messages
|
|
449
|
+
errors = check_result.get("errors", [])
|
|
450
|
+
for error in errors:
|
|
451
|
+
summary["issues"].append(f"{check_name}: {error}")
|
|
452
|
+
|
|
453
|
+
return summary
|
|
@@ -313,4 +313,9 @@ class SubprocessLauncherService(BaseService, SubprocessLauncherInterface):
|
|
|
313
313
|
env = os.environ.copy()
|
|
314
314
|
if base_env:
|
|
315
315
|
env.update(base_env)
|
|
316
|
+
|
|
317
|
+
# Disable telemetry for Claude Code subprocesses
|
|
318
|
+
# This ensures Claude Code doesn't send telemetry data during runtime
|
|
319
|
+
env["DISABLE_TELEMETRY"] = "1"
|
|
320
|
+
|
|
316
321
|
return env
|