claude-mpm 4.4.6__py3-none-any.whl → 4.4.8__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/agents/templates/local_ops_agent.json +273 -0
- claude_mpm/cli/__init__.py +21 -0
- claude_mpm/cli/commands/verify.py +118 -0
- claude_mpm/cli/parsers/base_parser.py +5 -0
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
- claude_mpm/scripts/mcp_server.py +0 -0
- claude_mpm/scripts/start_activity_logging.py +0 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +10 -6
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +122 -0
- claude_mpm/services/diagnostics/diagnostic_runner.py +3 -0
- claude_mpm/services/mcp_service_verifier.py +690 -0
- {claude_mpm-4.4.6.dist-info → claude_mpm-4.4.8.dist-info}/METADATA +13 -1
- {claude_mpm-4.4.6.dist-info → claude_mpm-4.4.8.dist-info}/RECORD +28 -12
- {claude_mpm-4.4.6.dist-info → claude_mpm-4.4.8.dist-info}/WHEEL +0 -0
- {claude_mpm-4.4.6.dist-info → claude_mpm-4.4.8.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.4.6.dist-info → claude_mpm-4.4.8.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.4.6.dist-info → claude_mpm-4.4.8.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
4.4.
|
1
|
+
4.4.8
|
@@ -0,0 +1,273 @@
|
|
1
|
+
{
|
2
|
+
"name": "local-ops",
|
3
|
+
"display_name": "Local Operations Agent",
|
4
|
+
"description": "Specialized agent for managing local development deployments with authority over PM2, Docker, and native processes",
|
5
|
+
"version": "1.0.0",
|
6
|
+
"author": "Claude MPM",
|
7
|
+
"authority": {
|
8
|
+
"level": "deployment_manager",
|
9
|
+
"domains": [
|
10
|
+
"local_deployments",
|
11
|
+
"process_management",
|
12
|
+
"port_allocation",
|
13
|
+
"resource_monitoring",
|
14
|
+
"log_management"
|
15
|
+
]
|
16
|
+
},
|
17
|
+
"capabilities": {
|
18
|
+
"frameworks": {
|
19
|
+
"javascript": ["next.js", "react", "vue", "angular", "svelte", "nuxt", "gatsby", "vite"],
|
20
|
+
"python": ["django", "flask", "fastapi", "streamlit", "gradio"],
|
21
|
+
"ruby": ["rails", "sinatra"],
|
22
|
+
"php": ["laravel", "symfony"],
|
23
|
+
"static": ["hugo", "jekyll", "eleventy"]
|
24
|
+
},
|
25
|
+
"deployment_methods": {
|
26
|
+
"pm2": {
|
27
|
+
"description": "Node.js process manager for production deployments",
|
28
|
+
"commands": ["start", "stop", "restart", "status", "logs", "monit"],
|
29
|
+
"features": ["auto-restart", "clustering", "log-rotation", "monitoring"]
|
30
|
+
},
|
31
|
+
"docker": {
|
32
|
+
"description": "Container-based deployment for isolation",
|
33
|
+
"commands": ["build", "run", "stop", "logs", "exec"],
|
34
|
+
"features": ["isolation", "reproducibility", "multi-service"]
|
35
|
+
},
|
36
|
+
"native": {
|
37
|
+
"description": "Direct process management for simple cases",
|
38
|
+
"commands": ["start", "stop", "status"],
|
39
|
+
"features": ["lightweight", "direct-control"]
|
40
|
+
},
|
41
|
+
"systemd": {
|
42
|
+
"description": "Linux system service management",
|
43
|
+
"commands": ["start", "stop", "enable", "status"],
|
44
|
+
"features": ["boot-persistence", "system-integration"]
|
45
|
+
}
|
46
|
+
},
|
47
|
+
"monitoring": {
|
48
|
+
"health_checks": ["http", "tcp", "process"],
|
49
|
+
"metrics": ["cpu", "memory", "response_time", "error_rate"],
|
50
|
+
"alerts": ["crash", "high_memory", "port_conflict", "unhealthy"]
|
51
|
+
}
|
52
|
+
},
|
53
|
+
"metadata": {
|
54
|
+
"category": "operations",
|
55
|
+
"tags": ["deployment", "devops", "local", "process-management", "monitoring"],
|
56
|
+
"requirements": {
|
57
|
+
"optional": ["pm2", "docker", "nginx"],
|
58
|
+
"auto_install": ["pm2"]
|
59
|
+
}
|
60
|
+
},
|
61
|
+
"configuration": {
|
62
|
+
"default_port_range": [3000, 9999],
|
63
|
+
"deployment_directory": ".claude-mpm/deployments",
|
64
|
+
"log_directory": ".claude-mpm/logs",
|
65
|
+
"state_file": ".claude-mpm/deployment-state.json",
|
66
|
+
"health_check_interval": 30,
|
67
|
+
"auto_restart_attempts": 3,
|
68
|
+
"cleanup_on_exit": false
|
69
|
+
},
|
70
|
+
"commands": {
|
71
|
+
"deploy": {
|
72
|
+
"description": "Deploy application with optimal method",
|
73
|
+
"examples": [
|
74
|
+
"Deploy this Next.js app in production mode",
|
75
|
+
"Start the development server with hot reload",
|
76
|
+
"Deploy with PM2 for stability"
|
77
|
+
],
|
78
|
+
"workflow": [
|
79
|
+
"detect_framework",
|
80
|
+
"check_existing_deployments",
|
81
|
+
"allocate_port",
|
82
|
+
"build_if_needed",
|
83
|
+
"start_process",
|
84
|
+
"monitor_health",
|
85
|
+
"report_status"
|
86
|
+
]
|
87
|
+
},
|
88
|
+
"status": {
|
89
|
+
"description": "Check deployment status",
|
90
|
+
"provides": ["process_info", "port_mapping", "health_status", "resource_usage"]
|
91
|
+
},
|
92
|
+
"logs": {
|
93
|
+
"description": "Stream or fetch deployment logs",
|
94
|
+
"options": ["tail", "follow", "filter", "since"]
|
95
|
+
},
|
96
|
+
"stop": {
|
97
|
+
"description": "Gracefully stop deployments",
|
98
|
+
"options": ["force", "timeout", "cleanup"]
|
99
|
+
},
|
100
|
+
"scale": {
|
101
|
+
"description": "Scale deployments (PM2 cluster mode)",
|
102
|
+
"options": ["instances", "auto"]
|
103
|
+
}
|
104
|
+
},
|
105
|
+
"detection_patterns": {
|
106
|
+
"nextjs": {
|
107
|
+
"files": ["next.config.js", "next.config.mjs", "next.config.ts"],
|
108
|
+
"package_json": ["next"],
|
109
|
+
"commands": {
|
110
|
+
"dev": "next dev",
|
111
|
+
"build": "next build",
|
112
|
+
"start": "next start",
|
113
|
+
"export": "next export"
|
114
|
+
}
|
115
|
+
},
|
116
|
+
"react": {
|
117
|
+
"files": ["react-scripts", "vite.config.js", "webpack.config.js"],
|
118
|
+
"package_json": ["react", "react-dom"],
|
119
|
+
"commands": {
|
120
|
+
"dev": "npm start",
|
121
|
+
"build": "npm run build",
|
122
|
+
"serve": "serve -s build"
|
123
|
+
}
|
124
|
+
},
|
125
|
+
"vue": {
|
126
|
+
"files": ["vue.config.js", "vite.config.js"],
|
127
|
+
"package_json": ["vue", "@vue/cli-service"],
|
128
|
+
"commands": {
|
129
|
+
"dev": "npm run serve",
|
130
|
+
"build": "npm run build",
|
131
|
+
"preview": "npm run preview"
|
132
|
+
}
|
133
|
+
},
|
134
|
+
"python_django": {
|
135
|
+
"files": ["manage.py", "wsgi.py"],
|
136
|
+
"requirements": ["django"],
|
137
|
+
"commands": {
|
138
|
+
"dev": "python manage.py runserver",
|
139
|
+
"prod": "gunicorn wsgi:application"
|
140
|
+
}
|
141
|
+
},
|
142
|
+
"python_flask": {
|
143
|
+
"files": ["app.py", "wsgi.py"],
|
144
|
+
"requirements": ["flask"],
|
145
|
+
"commands": {
|
146
|
+
"dev": "flask run",
|
147
|
+
"prod": "gunicorn app:app"
|
148
|
+
}
|
149
|
+
}
|
150
|
+
},
|
151
|
+
"deployment_strategies": {
|
152
|
+
"production": {
|
153
|
+
"nextjs": {
|
154
|
+
"method": "pm2",
|
155
|
+
"steps": [
|
156
|
+
"npm install --production",
|
157
|
+
"npm run build",
|
158
|
+
"pm2 start npm --name '{app_name}' -- start"
|
159
|
+
],
|
160
|
+
"health_check": "http://localhost:{port}",
|
161
|
+
"environment": {
|
162
|
+
"NODE_ENV": "production"
|
163
|
+
}
|
164
|
+
},
|
165
|
+
"react": {
|
166
|
+
"method": "static",
|
167
|
+
"steps": [
|
168
|
+
"npm install",
|
169
|
+
"npm run build",
|
170
|
+
"pm2 serve build {port} --name '{app_name}'"
|
171
|
+
],
|
172
|
+
"health_check": "http://localhost:{port}"
|
173
|
+
},
|
174
|
+
"python": {
|
175
|
+
"method": "pm2",
|
176
|
+
"steps": [
|
177
|
+
"pip install -r requirements.txt",
|
178
|
+
"pm2 start gunicorn --name '{app_name}' -- app:app --bind 0.0.0.0:{port}"
|
179
|
+
],
|
180
|
+
"health_check": "http://localhost:{port}/health"
|
181
|
+
}
|
182
|
+
},
|
183
|
+
"development": {
|
184
|
+
"nextjs": {
|
185
|
+
"method": "pm2",
|
186
|
+
"steps": [
|
187
|
+
"npm install",
|
188
|
+
"pm2 start npm --name '{app_name}-dev' -- run dev"
|
189
|
+
],
|
190
|
+
"environment": {
|
191
|
+
"NODE_ENV": "development"
|
192
|
+
}
|
193
|
+
},
|
194
|
+
"react": {
|
195
|
+
"method": "pm2",
|
196
|
+
"steps": [
|
197
|
+
"npm install",
|
198
|
+
"pm2 start npm --name '{app_name}-dev' -- start"
|
199
|
+
]
|
200
|
+
}
|
201
|
+
},
|
202
|
+
"docker": {
|
203
|
+
"default": {
|
204
|
+
"method": "docker",
|
205
|
+
"steps": [
|
206
|
+
"docker build -t {app_name} .",
|
207
|
+
"docker run -d -p {port}:{container_port} --name {app_name} {app_name}"
|
208
|
+
],
|
209
|
+
"health_check": "docker exec {app_name} echo 'OK'"
|
210
|
+
}
|
211
|
+
}
|
212
|
+
},
|
213
|
+
"error_recovery": {
|
214
|
+
"port_conflict": {
|
215
|
+
"detection": "EADDRINUSE",
|
216
|
+
"action": "allocate_next_available_port"
|
217
|
+
},
|
218
|
+
"build_failure": {
|
219
|
+
"detection": "npm ERR!|ERROR|Failed",
|
220
|
+
"action": "report_error_and_suggest_fixes"
|
221
|
+
},
|
222
|
+
"crash_loop": {
|
223
|
+
"detection": "restart_count > 5",
|
224
|
+
"action": "stop_and_investigate_logs"
|
225
|
+
},
|
226
|
+
"out_of_memory": {
|
227
|
+
"detection": "JavaScript heap out of memory",
|
228
|
+
"action": "increase_memory_limit"
|
229
|
+
}
|
230
|
+
},
|
231
|
+
"security": {
|
232
|
+
"port_exposure": "localhost_only",
|
233
|
+
"process_isolation": "user_level",
|
234
|
+
"log_sanitization": true,
|
235
|
+
"secrets_handling": "environment_variables"
|
236
|
+
},
|
237
|
+
"integration": {
|
238
|
+
"hooks": {
|
239
|
+
"pre_deploy": "validate_requirements",
|
240
|
+
"post_deploy": "notify_status",
|
241
|
+
"pre_stop": "graceful_shutdown",
|
242
|
+
"on_crash": "auto_restart_with_backoff"
|
243
|
+
},
|
244
|
+
"monitoring": {
|
245
|
+
"export_metrics": true,
|
246
|
+
"prometheus_endpoint": "/metrics",
|
247
|
+
"health_endpoint": "/health"
|
248
|
+
}
|
249
|
+
},
|
250
|
+
"tools": [
|
251
|
+
"Bash",
|
252
|
+
"Read",
|
253
|
+
"Write",
|
254
|
+
"Edit"
|
255
|
+
],
|
256
|
+
"examples": [
|
257
|
+
{
|
258
|
+
"user": "Deploy my Next.js app",
|
259
|
+
"response": "I'll deploy your Next.js application using PM2 for stability. Let me detect your configuration and set it up...",
|
260
|
+
"actions": ["detect_framework", "build_production", "deploy_with_pm2", "monitor_health"]
|
261
|
+
},
|
262
|
+
{
|
263
|
+
"user": "Show me the status of all deployments",
|
264
|
+
"response": "Here's the status of all active deployments...",
|
265
|
+
"actions": ["list_pm2_processes", "check_docker_containers", "aggregate_status"]
|
266
|
+
},
|
267
|
+
{
|
268
|
+
"user": "The app keeps crashing, help me debug",
|
269
|
+
"response": "Let me check the logs and identify the issue...",
|
270
|
+
"actions": ["fetch_error_logs", "analyze_crash_pattern", "suggest_fixes"]
|
271
|
+
}
|
272
|
+
]
|
273
|
+
}
|
claude_mpm/cli/__init__.py
CHANGED
@@ -234,6 +234,19 @@ def _verify_mcp_gateway_startup():
|
|
234
234
|
DESIGN DECISION: This is non-blocking - failures are logged but don't prevent
|
235
235
|
startup to ensure claude-mpm remains functional even if MCP gateway has issues.
|
236
236
|
"""
|
237
|
+
# Quick verification of MCP services installation
|
238
|
+
try:
|
239
|
+
from ..services.mcp_service_verifier import verify_mcp_services_on_startup
|
240
|
+
from ..core.logger import get_logger
|
241
|
+
|
242
|
+
logger = get_logger("mcp_verify")
|
243
|
+
all_ok, message = verify_mcp_services_on_startup()
|
244
|
+
if not all_ok:
|
245
|
+
logger.warning(message)
|
246
|
+
except Exception:
|
247
|
+
# Non-critical - continue with startup
|
248
|
+
pass
|
249
|
+
|
237
250
|
try:
|
238
251
|
import asyncio
|
239
252
|
import time
|
@@ -449,6 +462,14 @@ def _execute_command(command: str, args) -> int:
|
|
449
462
|
# Convert CommandResult to exit code
|
450
463
|
return result.exit_code if result else 0
|
451
464
|
|
465
|
+
# Handle verify command with lazy import
|
466
|
+
if command == "verify":
|
467
|
+
# Lazy import to avoid loading unless needed
|
468
|
+
from .commands.verify import handle_verify
|
469
|
+
|
470
|
+
result = handle_verify(args)
|
471
|
+
return result if result is not None else 0
|
472
|
+
|
452
473
|
# Map stable commands to their implementations
|
453
474
|
command_map = {
|
454
475
|
CLICommands.RUN.value: run_session,
|
@@ -0,0 +1,118 @@
|
|
1
|
+
"""
|
2
|
+
Verify command for MCP service health checks.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import argparse
|
6
|
+
import sys
|
7
|
+
|
8
|
+
from ...core.logger import get_logger
|
9
|
+
from ...services.mcp_service_verifier import MCPServiceVerifier, ServiceStatus
|
10
|
+
|
11
|
+
|
12
|
+
def add_parser(subparsers) -> None:
|
13
|
+
"""Add the verify command parser."""
|
14
|
+
parser = subparsers.add_parser(
|
15
|
+
"verify",
|
16
|
+
help="Verify MCP services installation and configuration",
|
17
|
+
description="Performs comprehensive health checks on MCP services",
|
18
|
+
)
|
19
|
+
|
20
|
+
parser.add_argument(
|
21
|
+
"--fix",
|
22
|
+
action="store_true",
|
23
|
+
help="Attempt to automatically fix detected issues",
|
24
|
+
)
|
25
|
+
|
26
|
+
parser.add_argument(
|
27
|
+
"--service",
|
28
|
+
type=str,
|
29
|
+
help="Verify a specific service only",
|
30
|
+
choices=["mcp-vector-search", "mcp-browser", "mcp-ticketer", "kuzu-memory"],
|
31
|
+
)
|
32
|
+
|
33
|
+
parser.add_argument(
|
34
|
+
"--json",
|
35
|
+
action="store_true",
|
36
|
+
help="Output results in JSON format",
|
37
|
+
)
|
38
|
+
|
39
|
+
parser.set_defaults(func=handle_verify)
|
40
|
+
|
41
|
+
|
42
|
+
def handle_verify(args: argparse.Namespace) -> int:
|
43
|
+
"""
|
44
|
+
Handle the verify command.
|
45
|
+
|
46
|
+
Args:
|
47
|
+
args: Parsed command-line arguments
|
48
|
+
|
49
|
+
Returns:
|
50
|
+
Exit code (0 for success, non-zero for issues found)
|
51
|
+
"""
|
52
|
+
logger = get_logger(__name__)
|
53
|
+
verifier = MCPServiceVerifier()
|
54
|
+
|
55
|
+
try:
|
56
|
+
# Run verification
|
57
|
+
if args.service:
|
58
|
+
# Verify single service
|
59
|
+
logger.info(f"Verifying {args.service}...")
|
60
|
+
diagnostic = verifier._verify_service(args.service)
|
61
|
+
diagnostics = {args.service: diagnostic}
|
62
|
+
|
63
|
+
# Auto-fix if requested
|
64
|
+
if args.fix and diagnostic.fix_command and diagnostic.status != ServiceStatus.WORKING:
|
65
|
+
logger.info(f"Attempting to fix {args.service}...")
|
66
|
+
if verifier._attempt_auto_fix(args.service, diagnostic):
|
67
|
+
# Re-verify after fix
|
68
|
+
diagnostic = verifier._verify_service(args.service)
|
69
|
+
diagnostics = {args.service: diagnostic}
|
70
|
+
else:
|
71
|
+
# Verify all services
|
72
|
+
logger.info("Verifying all MCP services...")
|
73
|
+
diagnostics = verifier.verify_all_services(auto_fix=args.fix)
|
74
|
+
|
75
|
+
# Output results
|
76
|
+
if args.json:
|
77
|
+
import json
|
78
|
+
|
79
|
+
# Convert to JSON-serializable format
|
80
|
+
json_output = {}
|
81
|
+
for name, diag in diagnostics.items():
|
82
|
+
json_output[name] = {
|
83
|
+
"status": diag.status.value,
|
84
|
+
"message": diag.message,
|
85
|
+
"installed_path": diag.installed_path,
|
86
|
+
"configured_command": diag.configured_command,
|
87
|
+
"fix_command": diag.fix_command,
|
88
|
+
"details": diag.details,
|
89
|
+
}
|
90
|
+
print(json.dumps(json_output, indent=2))
|
91
|
+
else:
|
92
|
+
verifier.print_diagnostics(diagnostics)
|
93
|
+
|
94
|
+
# Determine exit code
|
95
|
+
all_working = all(
|
96
|
+
d.status == ServiceStatus.WORKING
|
97
|
+
for d in diagnostics.values()
|
98
|
+
)
|
99
|
+
|
100
|
+
if all_working:
|
101
|
+
logger.info("✅ All verified services are fully operational")
|
102
|
+
return 0
|
103
|
+
else:
|
104
|
+
issues_count = sum(
|
105
|
+
1 for d in diagnostics.values()
|
106
|
+
if d.status != ServiceStatus.WORKING
|
107
|
+
)
|
108
|
+
logger.warning(f"⚠️ {issues_count} service(s) have issues")
|
109
|
+
return 1
|
110
|
+
|
111
|
+
except Exception as e:
|
112
|
+
logger.error(f"Verification failed: {e}")
|
113
|
+
if args.json:
|
114
|
+
import json
|
115
|
+
print(json.dumps({"error": str(e)}, indent=2))
|
116
|
+
else:
|
117
|
+
print(f"\n❌ Verification failed: {e}")
|
118
|
+
return 2
|
@@ -422,6 +422,11 @@ def create_parser(
|
|
422
422
|
from ..commands.doctor import add_doctor_parser
|
423
423
|
|
424
424
|
add_doctor_parser(subparsers)
|
425
|
+
|
426
|
+
# Add verify command for MCP service verification
|
427
|
+
from ..commands.verify import add_parser as add_verify_parser
|
428
|
+
|
429
|
+
add_verify_parser(subparsers)
|
425
430
|
except ImportError:
|
426
431
|
# Commands module may not be available during testing or refactoring
|
427
432
|
pass
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
claude_mpm/scripts/mcp_server.py
CHANGED
File without changes
|
File without changes
|
@@ -369,12 +369,18 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
369
369
|
else "single"
|
370
370
|
)
|
371
371
|
|
372
|
+
# When using multi-source deployment, we've already determined which
|
373
|
+
# agents need updates. Don't re-check versions in single_agent_deployer.
|
374
|
+
# This prevents the issue where multi-source says "deploying 9 agents"
|
375
|
+
# but then all get skipped due to redundant version checks.
|
376
|
+
skip_version_check = use_multi_source and not force_rebuild
|
377
|
+
|
372
378
|
self.single_agent_deployer.deploy_single_agent(
|
373
379
|
template_file=template_file_path,
|
374
380
|
agents_dir=agents_dir,
|
375
381
|
base_agent_data=base_agent_data,
|
376
382
|
base_agent_version=base_agent_version,
|
377
|
-
force_rebuild=force_rebuild,
|
383
|
+
force_rebuild=force_rebuild or skip_version_check,
|
378
384
|
deployment_mode=deployment_mode,
|
379
385
|
results=results,
|
380
386
|
source_info=source_info,
|
@@ -817,11 +823,9 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
817
823
|
|
818
824
|
agents_to_deploy = filtered_agents
|
819
825
|
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
)
|
824
|
-
else:
|
826
|
+
# Don't log this redundant message - we already logged the upgrades above
|
827
|
+
# The "Deploying X agent upgrade(s)" message is sufficient
|
828
|
+
if not agents_to_deploy:
|
825
829
|
self.logger.debug(
|
826
830
|
f"All {len(comparison_results.get('up_to_date', []))} agents are up to date"
|
827
831
|
)
|
@@ -10,6 +10,7 @@ import subprocess
|
|
10
10
|
from pathlib import Path
|
11
11
|
from typing import Dict, List, Optional, Tuple
|
12
12
|
|
13
|
+
from claude_mpm.core.logger import get_logger
|
13
14
|
from ..models import DiagnosticResult, DiagnosticStatus
|
14
15
|
from .base_check import BaseDiagnosticCheck
|
15
16
|
|
@@ -17,6 +18,11 @@ from .base_check import BaseDiagnosticCheck
|
|
17
18
|
class MCPServicesCheck(BaseDiagnosticCheck):
|
18
19
|
"""Check MCP external services installation and health."""
|
19
20
|
|
21
|
+
def __init__(self, verbose: bool = False):
|
22
|
+
"""Initialize the MCP services check."""
|
23
|
+
super().__init__(verbose)
|
24
|
+
self.logger = get_logger(self.__class__.__name__)
|
25
|
+
|
20
26
|
# Define MCP services to check
|
21
27
|
MCP_SERVICES = {
|
22
28
|
"mcp-vector-search": {
|
@@ -71,6 +77,11 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
71
77
|
sub_results = []
|
72
78
|
services_status = {}
|
73
79
|
|
80
|
+
# Check for kuzu-memory configuration issues and offer auto-fix
|
81
|
+
kuzu_config_result = self._check_and_fix_kuzu_memory_config()
|
82
|
+
if kuzu_config_result:
|
83
|
+
sub_results.append(kuzu_config_result)
|
84
|
+
|
74
85
|
# Check each MCP service
|
75
86
|
for service_name, service_config in self.MCP_SERVICES.items():
|
76
87
|
service_result = self._check_service(service_name, service_config)
|
@@ -391,6 +402,117 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
391
402
|
|
392
403
|
return None
|
393
404
|
|
405
|
+
def _check_and_fix_kuzu_memory_config(self) -> Optional[DiagnosticResult]:
|
406
|
+
"""Check for incorrect kuzu-memory configuration in .claude.json and offer auto-fix."""
|
407
|
+
claude_config_path = Path.home() / ".claude.json"
|
408
|
+
|
409
|
+
if not claude_config_path.exists():
|
410
|
+
return None
|
411
|
+
|
412
|
+
try:
|
413
|
+
with open(claude_config_path) as f:
|
414
|
+
config = json.load(f)
|
415
|
+
|
416
|
+
mcp_servers = config.get("mcpServers", {})
|
417
|
+
kuzu_config = mcp_servers.get("kuzu-memory")
|
418
|
+
|
419
|
+
if not kuzu_config:
|
420
|
+
return None
|
421
|
+
|
422
|
+
# Check if kuzu-memory has incorrect args
|
423
|
+
args = kuzu_config.get("args", [])
|
424
|
+
needs_fix = False
|
425
|
+
fix_reason = ""
|
426
|
+
new_args = None
|
427
|
+
|
428
|
+
# Check for outdated configurations
|
429
|
+
if args == ["claude", "mcp-server"]:
|
430
|
+
needs_fix = True
|
431
|
+
fix_reason = "Outdated 'claude mcp-server' format"
|
432
|
+
new_args = ["mcp", "serve"]
|
433
|
+
elif args == ["serve"]:
|
434
|
+
needs_fix = True
|
435
|
+
fix_reason = "Legacy 'serve' format"
|
436
|
+
new_args = ["mcp", "serve"]
|
437
|
+
elif args == ["mcp-server"]:
|
438
|
+
needs_fix = True
|
439
|
+
fix_reason = "Incorrect 'mcp-server' format"
|
440
|
+
new_args = ["mcp", "serve"]
|
441
|
+
|
442
|
+
if needs_fix:
|
443
|
+
# Offer to auto-fix
|
444
|
+
self.logger.warning(
|
445
|
+
f"Found incorrect kuzu-memory configuration: {fix_reason}. "
|
446
|
+
f"Current args: {args}"
|
447
|
+
)
|
448
|
+
|
449
|
+
# Auto-fix the configuration
|
450
|
+
fixed = self._fix_kuzu_memory_args(claude_config_path, config, new_args)
|
451
|
+
|
452
|
+
if fixed:
|
453
|
+
return DiagnosticResult(
|
454
|
+
category="kuzu-memory Configuration Fix",
|
455
|
+
status=DiagnosticStatus.OK,
|
456
|
+
message="Fixed kuzu-memory configuration",
|
457
|
+
details={
|
458
|
+
"old_args": args,
|
459
|
+
"new_args": new_args,
|
460
|
+
"reason": fix_reason,
|
461
|
+
"auto_fixed": True,
|
462
|
+
},
|
463
|
+
)
|
464
|
+
else:
|
465
|
+
return DiagnosticResult(
|
466
|
+
category="kuzu-memory Configuration",
|
467
|
+
status=DiagnosticStatus.WARNING,
|
468
|
+
message="kuzu-memory has incorrect configuration",
|
469
|
+
details={
|
470
|
+
"current_args": args,
|
471
|
+
"correct_args": new_args,
|
472
|
+
"reason": fix_reason,
|
473
|
+
"auto_fix_failed": True,
|
474
|
+
},
|
475
|
+
fix_command="claude-mpm configure --mcp --fix-kuzu",
|
476
|
+
fix_description="Fix kuzu-memory configuration manually",
|
477
|
+
)
|
478
|
+
|
479
|
+
# Configuration is correct
|
480
|
+
return None
|
481
|
+
|
482
|
+
except (json.JSONDecodeError, Exception) as e:
|
483
|
+
self.logger.debug(f"Could not check kuzu-memory config: {e}")
|
484
|
+
return None
|
485
|
+
|
486
|
+
def _fix_kuzu_memory_args(self, config_path: Path, config: Dict, new_args: List[str]) -> bool:
|
487
|
+
"""Fix kuzu-memory args in the configuration."""
|
488
|
+
try:
|
489
|
+
# Save old args before updating
|
490
|
+
old_args = config["mcpServers"]["kuzu-memory"].get("args", [])
|
491
|
+
|
492
|
+
# Create backup
|
493
|
+
backup_path = config_path.with_suffix(".json.backup")
|
494
|
+
with open(backup_path, "w") as f:
|
495
|
+
json.dump(config, f, indent=2)
|
496
|
+
|
497
|
+
# Update the configuration
|
498
|
+
config["mcpServers"]["kuzu-memory"]["args"] = new_args
|
499
|
+
|
500
|
+
# Write updated configuration
|
501
|
+
with open(config_path, "w") as f:
|
502
|
+
json.dump(config, f, indent=2)
|
503
|
+
|
504
|
+
self.logger.info(
|
505
|
+
f"✅ Fixed kuzu-memory configuration in {config_path}\n"
|
506
|
+
f" Changed args from {old_args} to {new_args}\n"
|
507
|
+
f" Backup saved to {backup_path}"
|
508
|
+
)
|
509
|
+
|
510
|
+
return True
|
511
|
+
|
512
|
+
except Exception as e:
|
513
|
+
self.logger.error(f"Failed to fix kuzu-memory configuration: {e}")
|
514
|
+
return False
|
515
|
+
|
394
516
|
def _check_gateway_configuration(self) -> DiagnosticResult:
|
395
517
|
"""Check if MCP services are configured in the gateway."""
|
396
518
|
try:
|
@@ -214,6 +214,9 @@ class DiagnosticRunner:
|
|
214
214
|
"agents": AgentCheck,
|
215
215
|
"agent": AgentCheck,
|
216
216
|
"mcp": MCPCheck,
|
217
|
+
"mcp_services": MCPServicesCheck,
|
218
|
+
"mcp-services": MCPServicesCheck,
|
219
|
+
"external": MCPServicesCheck,
|
217
220
|
"monitor": MonitorCheck,
|
218
221
|
"monitoring": MonitorCheck,
|
219
222
|
"common": CommonIssuesCheck,
|