claude-mpm 4.4.12__py3-none-any.whl → 4.5.1__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/web_qa.json +85 -6
- claude_mpm/cli/__init__.py +2 -2
- claude_mpm/cli/commands/mcp_setup_external.py +1 -3
- claude_mpm/cli/commands/verify.py +13 -12
- claude_mpm/services/diagnostics/checks/claude_code_check.py +4 -4
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +114 -57
- claude_mpm/services/mcp_config_manager.py +134 -50
- claude_mpm/services/mcp_service_verifier.py +84 -46
- {claude_mpm-4.4.12.dist-info → claude_mpm-4.5.1.dist-info}/METADATA +1 -1
- {claude_mpm-4.4.12.dist-info → claude_mpm-4.5.1.dist-info}/RECORD +15 -15
- {claude_mpm-4.4.12.dist-info → claude_mpm-4.5.1.dist-info}/WHEEL +0 -0
- {claude_mpm-4.4.12.dist-info → claude_mpm-4.5.1.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.4.12.dist-info → claude_mpm-4.5.1.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.4.12.dist-info → claude_mpm-4.5.1.dist-info}/top_level.txt +0 -0
@@ -13,6 +13,7 @@ from pathlib import Path
|
|
13
13
|
from typing import Dict, List, Optional, Tuple
|
14
14
|
|
15
15
|
from claude_mpm.core.logger import get_logger
|
16
|
+
|
16
17
|
from ..models import DiagnosticResult, DiagnosticStatus
|
17
18
|
from .base_check import BaseDiagnosticCheck
|
18
19
|
|
@@ -37,8 +38,20 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
37
38
|
"check_health": True,
|
38
39
|
"health_command": ["mcp-vector-search", "--version"],
|
39
40
|
"pipx_run_command": ["pipx", "run", "mcp-vector-search", "--version"],
|
40
|
-
"mcp_command": [
|
41
|
-
|
41
|
+
"mcp_command": [
|
42
|
+
"python",
|
43
|
+
"-m",
|
44
|
+
"mcp_vector_search.mcp.server",
|
45
|
+
], # Command to run as MCP server
|
46
|
+
"pipx_mcp_command": [
|
47
|
+
"pipx",
|
48
|
+
"run",
|
49
|
+
"--spec",
|
50
|
+
"mcp-vector-search",
|
51
|
+
"python",
|
52
|
+
"-m",
|
53
|
+
"mcp_vector_search.mcp.server",
|
54
|
+
],
|
42
55
|
},
|
43
56
|
"mcp-browser": {
|
44
57
|
"package": "mcp-browser",
|
@@ -65,7 +78,11 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
65
78
|
"check_health": True, # v1.1.0+ has version command
|
66
79
|
"health_command": ["kuzu-memory", "--version"],
|
67
80
|
"pipx_run_command": ["pipx", "run", "kuzu-memory", "--version"],
|
68
|
-
"mcp_command": [
|
81
|
+
"mcp_command": [
|
82
|
+
"kuzu-memory",
|
83
|
+
"mcp",
|
84
|
+
"serve",
|
85
|
+
], # v1.1.0+ uses 'mcp serve' args
|
69
86
|
},
|
70
87
|
}
|
71
88
|
|
@@ -86,28 +103,43 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
86
103
|
|
87
104
|
# Use MCPConfigManager to detect and fix corrupted installations
|
88
105
|
from claude_mpm.services.mcp_config_manager import MCPConfigManager
|
106
|
+
|
89
107
|
mcp_manager = MCPConfigManager()
|
90
108
|
|
91
109
|
# Run comprehensive fix for all MCP service issues
|
92
110
|
fix_success, fix_message = mcp_manager.fix_mcp_service_issues()
|
93
|
-
if
|
111
|
+
if (
|
112
|
+
fix_message
|
113
|
+
and fix_message != "All MCP services are functioning correctly"
|
114
|
+
):
|
94
115
|
# Create diagnostic result for the fixes
|
95
116
|
fix_result = DiagnosticResult(
|
96
117
|
category="MCP Service Fixes",
|
97
|
-
status=
|
118
|
+
status=(
|
119
|
+
DiagnosticStatus.OK if fix_success else DiagnosticStatus.WARNING
|
120
|
+
),
|
98
121
|
message=fix_message,
|
99
|
-
details={"auto_fix_applied": True}
|
122
|
+
details={"auto_fix_applied": True},
|
100
123
|
)
|
101
124
|
sub_results.append(fix_result)
|
102
125
|
|
103
126
|
# Also ensure configurations are updated for all projects
|
104
|
-
config_success, config_message =
|
105
|
-
|
127
|
+
config_success, config_message = (
|
128
|
+
mcp_manager.ensure_mcp_services_configured()
|
129
|
+
)
|
130
|
+
if (
|
131
|
+
config_message
|
132
|
+
and config_message != "All MCP services already configured correctly"
|
133
|
+
):
|
106
134
|
config_result = DiagnosticResult(
|
107
135
|
category="MCP Configuration Update",
|
108
|
-
status=
|
136
|
+
status=(
|
137
|
+
DiagnosticStatus.OK
|
138
|
+
if config_success
|
139
|
+
else DiagnosticStatus.WARNING
|
140
|
+
),
|
109
141
|
message=config_message,
|
110
|
-
details={"auto_config_applied": True}
|
142
|
+
details={"auto_config_applied": True},
|
111
143
|
)
|
112
144
|
sub_results.append(config_result)
|
113
145
|
|
@@ -149,7 +181,9 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
149
181
|
total_services = len(self.MCP_SERVICES)
|
150
182
|
|
151
183
|
# Calculate total tools discovered
|
152
|
-
total_tools = sum(
|
184
|
+
total_tools = sum(
|
185
|
+
s.get("tools_discovered", 0) for s in services_status.values()
|
186
|
+
)
|
153
187
|
|
154
188
|
details["services"] = services_status
|
155
189
|
details["installed_count"] = installed_count
|
@@ -201,9 +235,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
201
235
|
details={"error": str(e)},
|
202
236
|
)
|
203
237
|
|
204
|
-
async def _test_mcp_connection(
|
205
|
-
self, service_name: str, command: List[str]
|
206
|
-
) -> Dict:
|
238
|
+
async def _test_mcp_connection(self, service_name: str, command: List[str]) -> Dict:
|
207
239
|
"""Test MCP server connection by sending JSON-RPC requests."""
|
208
240
|
result = {
|
209
241
|
"connected": False,
|
@@ -234,12 +266,9 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
234
266
|
"params": {
|
235
267
|
"protocolVersion": "2024-11-05",
|
236
268
|
"capabilities": {},
|
237
|
-
"clientInfo": {
|
238
|
-
"name": "mpm-doctor",
|
239
|
-
"version": "1.0.0"
|
240
|
-
}
|
269
|
+
"clientInfo": {"name": "mpm-doctor", "version": "1.0.0"},
|
241
270
|
},
|
242
|
-
"id": 1
|
271
|
+
"id": 1,
|
243
272
|
}
|
244
273
|
|
245
274
|
# Send initialize request
|
@@ -260,7 +289,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
260
289
|
response_text = response_line.decode().strip()
|
261
290
|
|
262
291
|
# Skip empty lines or non-JSON lines
|
263
|
-
while response_text and not response_text.startswith(
|
292
|
+
while response_text and not response_text.startswith("{"):
|
264
293
|
# Try to read the next line
|
265
294
|
try:
|
266
295
|
response_line = await asyncio.wait_for(
|
@@ -273,7 +302,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
273
302
|
except asyncio.TimeoutError:
|
274
303
|
break
|
275
304
|
|
276
|
-
if not response_text or not response_text.startswith(
|
305
|
+
if not response_text or not response_text.startswith("{"):
|
277
306
|
result["error"] = "No valid JSON response received"
|
278
307
|
return result
|
279
308
|
|
@@ -289,7 +318,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
289
318
|
"jsonrpc": "2.0",
|
290
319
|
"method": "tools/list",
|
291
320
|
"params": {},
|
292
|
-
"id": 2
|
321
|
+
"id": 2,
|
293
322
|
}
|
294
323
|
|
295
324
|
request_line = json.dumps(tools_request) + "\n"
|
@@ -303,7 +332,9 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
303
332
|
)
|
304
333
|
|
305
334
|
if tools_response_line:
|
306
|
-
tools_response = json.loads(
|
335
|
+
tools_response = json.loads(
|
336
|
+
tools_response_line.decode()
|
337
|
+
)
|
307
338
|
if "result" in tools_response:
|
308
339
|
tools = tools_response["result"].get("tools", [])
|
309
340
|
result["tools_count"] = len(tools)
|
@@ -320,7 +351,9 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
320
351
|
pass
|
321
352
|
|
322
353
|
elif "error" in response:
|
323
|
-
result["error"] =
|
354
|
+
result["error"] = (
|
355
|
+
f"MCP error: {response['error'].get('message', 'Unknown error')}"
|
356
|
+
)
|
324
357
|
else:
|
325
358
|
result["error"] = "Invalid JSON-RPC response format"
|
326
359
|
|
@@ -333,12 +366,16 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
333
366
|
process.stderr.read(1000), timeout=0.5
|
334
367
|
)
|
335
368
|
if stderr_data:
|
336
|
-
stderr_output = stderr_data.decode(
|
369
|
+
stderr_output = stderr_data.decode(
|
370
|
+
"utf-8", errors="ignore"
|
371
|
+
)[:200]
|
337
372
|
except:
|
338
373
|
pass
|
339
374
|
|
340
375
|
if stderr_output:
|
341
|
-
result["error"] =
|
376
|
+
result["error"] = (
|
377
|
+
f"Connection timeout (5s). Server output: {stderr_output}"
|
378
|
+
)
|
342
379
|
else:
|
343
380
|
result["error"] = "Connection timeout (5s)"
|
344
381
|
|
@@ -351,21 +388,25 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
351
388
|
process.stderr.read(1000), timeout=0.5
|
352
389
|
)
|
353
390
|
if stderr_data:
|
354
|
-
stderr_output = stderr_data.decode(
|
391
|
+
stderr_output = stderr_data.decode(
|
392
|
+
"utf-8", errors="ignore"
|
393
|
+
)[:200]
|
355
394
|
except:
|
356
395
|
pass
|
357
396
|
|
358
397
|
if stderr_output:
|
359
|
-
result["error"] =
|
398
|
+
result["error"] = (
|
399
|
+
f"Invalid JSON response: {e!s}. Server error: {stderr_output}"
|
400
|
+
)
|
360
401
|
else:
|
361
|
-
result["error"] = f"Invalid JSON response: {
|
402
|
+
result["error"] = f"Invalid JSON response: {e!s}"
|
362
403
|
|
363
404
|
except FileNotFoundError:
|
364
405
|
result["error"] = f"Command not found: {command[0]}"
|
365
406
|
except PermissionError:
|
366
407
|
result["error"] = f"Permission denied: {command[0]}"
|
367
408
|
except Exception as e:
|
368
|
-
result["error"] = f"Connection failed: {
|
409
|
+
result["error"] = f"Connection failed: {e!s}"
|
369
410
|
finally:
|
370
411
|
# Clean up process
|
371
412
|
if process:
|
@@ -487,12 +528,12 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
487
528
|
"response_time_ms": connection_result["response_time"],
|
488
529
|
"tools_discovered": connection_result["tools_count"],
|
489
530
|
"tools_sample": connection_result["tools"],
|
490
|
-
"error": connection_result["error"]
|
531
|
+
"error": connection_result["error"],
|
491
532
|
}
|
492
533
|
except Exception as e:
|
493
534
|
details["connection_test"] = {
|
494
535
|
"connected": False,
|
495
|
-
"error": f"Test failed: {
|
536
|
+
"error": f"Test failed: {e!s}",
|
496
537
|
}
|
497
538
|
|
498
539
|
# Determine status
|
@@ -520,7 +561,11 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
520
561
|
|
521
562
|
return DiagnosticResult(
|
522
563
|
category=f"MCP Service: {service_name}",
|
523
|
-
status=
|
564
|
+
status=(
|
565
|
+
DiagnosticStatus.OK
|
566
|
+
if connection_info.get("connected")
|
567
|
+
else DiagnosticStatus.WARNING
|
568
|
+
),
|
524
569
|
message=message,
|
525
570
|
details=details,
|
526
571
|
)
|
@@ -772,13 +817,15 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
772
817
|
)
|
773
818
|
|
774
819
|
# Auto-fix the configuration
|
775
|
-
fixed = self._fix_kuzu_memory_args(
|
820
|
+
fixed = self._fix_kuzu_memory_args(
|
821
|
+
claude_config_path, config, correct_args
|
822
|
+
)
|
776
823
|
|
777
824
|
if fixed:
|
778
825
|
return DiagnosticResult(
|
779
826
|
category="kuzu-memory Configuration Fix",
|
780
827
|
status=DiagnosticStatus.OK,
|
781
|
-
message=
|
828
|
+
message="Fixed kuzu-memory configuration to use correct args",
|
782
829
|
details={
|
783
830
|
"old_args": args,
|
784
831
|
"new_args": correct_args,
|
@@ -786,20 +833,19 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
786
833
|
"auto_fixed": True,
|
787
834
|
},
|
788
835
|
)
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
)
|
836
|
+
return DiagnosticResult(
|
837
|
+
category="kuzu-memory Configuration",
|
838
|
+
status=DiagnosticStatus.WARNING,
|
839
|
+
message="kuzu-memory has incorrect configuration",
|
840
|
+
details={
|
841
|
+
"current_args": args,
|
842
|
+
"correct_args": correct_args,
|
843
|
+
"reason": fix_reason,
|
844
|
+
"auto_fix_failed": True,
|
845
|
+
},
|
846
|
+
fix_command="claude-mpm configure --mcp --fix-kuzu",
|
847
|
+
fix_description="Fix kuzu-memory configuration manually",
|
848
|
+
)
|
803
849
|
|
804
850
|
# Configuration is correct - args match ["mcp", "serve"]
|
805
851
|
return None
|
@@ -808,7 +854,9 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
808
854
|
self.logger.debug(f"Could not check kuzu-memory config: {e}")
|
809
855
|
return None
|
810
856
|
|
811
|
-
def _fix_kuzu_memory_args(
|
857
|
+
def _fix_kuzu_memory_args(
|
858
|
+
self, config_path: Path, config: Dict, new_args: List[str]
|
859
|
+
) -> bool:
|
812
860
|
"""Fix kuzu-memory args in the configuration."""
|
813
861
|
try:
|
814
862
|
# Save old args before updating
|
@@ -842,7 +890,11 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
842
890
|
# Verify the file was written correctly
|
843
891
|
with open(config_path) as f:
|
844
892
|
verify_config = json.load(f)
|
845
|
-
verify_args =
|
893
|
+
verify_args = (
|
894
|
+
verify_config.get("mcpServers", {})
|
895
|
+
.get("kuzu-memory", {})
|
896
|
+
.get("args", [])
|
897
|
+
)
|
846
898
|
|
847
899
|
if verify_args != new_args:
|
848
900
|
self.logger.error(
|
@@ -889,7 +941,6 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
889
941
|
|
890
942
|
# Get the current project configuration
|
891
943
|
from pathlib import Path
|
892
|
-
import os
|
893
944
|
|
894
945
|
current_project = str(Path.cwd())
|
895
946
|
|
@@ -900,7 +951,10 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
900
951
|
category="MCP Gateway Configuration",
|
901
952
|
status=DiagnosticStatus.WARNING,
|
902
953
|
message="Current project not configured in Claude",
|
903
|
-
details={
|
954
|
+
details={
|
955
|
+
"config_path": str(config_file),
|
956
|
+
"project": current_project,
|
957
|
+
},
|
904
958
|
fix_command="claude-mpm configure --mcp",
|
905
959
|
fix_description="Configure MCP services for current project",
|
906
960
|
)
|
@@ -988,11 +1042,14 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
988
1042
|
)
|
989
1043
|
|
990
1044
|
if inject_result.returncode == 0:
|
991
|
-
self.logger.info(
|
1045
|
+
self.logger.info(
|
1046
|
+
"✅ Successfully injected gql dependency into mcp-ticketer"
|
1047
|
+
)
|
992
1048
|
return True
|
993
|
-
|
994
|
-
|
995
|
-
|
1049
|
+
self.logger.warning(
|
1050
|
+
f"Failed to inject gql dependency: {inject_result.stderr}"
|
1051
|
+
)
|
1052
|
+
return False
|
996
1053
|
|
997
1054
|
# Dependency already present
|
998
1055
|
return False
|