claude-mpm 4.4.11__py3-none-any.whl → 4.5.0__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.
@@ -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": ["python", "-m", "mcp_vector_search.mcp.server"], # Command to run as MCP server
41
- "pipx_mcp_command": ["pipx", "run", "--spec", "mcp-vector-search", "python", "-m", "mcp_vector_search.mcp.server"],
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": ["kuzu-memory", "mcp", "serve"], # v1.1.0+ uses 'mcp serve' args
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 fix_message and fix_message != "All MCP services are functioning correctly":
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=DiagnosticStatus.OK if fix_success else DiagnosticStatus.WARNING,
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 = mcp_manager.ensure_mcp_services_configured()
105
- if config_message and config_message != "All MCP services already configured correctly":
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=DiagnosticStatus.OK if config_success else DiagnosticStatus.WARNING,
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(s.get("tools_discovered", 0) for s in services_status.values())
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(tools_response_line.decode())
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"] = f"MCP error: {response['error'].get('message', 'Unknown 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('utf-8', errors='ignore')[:200]
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"] = f"Connection timeout (5s). Server output: {stderr_output}"
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('utf-8', errors='ignore')[:200]
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"] = f"Invalid JSON response: {str(e)}. Server error: {stderr_output}"
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: {str(e)}"
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: {str(e)}"
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: {str(e)}"
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=DiagnosticStatus.OK if connection_info.get("connected") else DiagnosticStatus.WARNING,
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(claude_config_path, config, correct_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=f"Fixed kuzu-memory configuration to use correct args",
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
- else:
790
- return DiagnosticResult(
791
- category="kuzu-memory Configuration",
792
- status=DiagnosticStatus.WARNING,
793
- message="kuzu-memory has incorrect configuration",
794
- details={
795
- "current_args": args,
796
- "correct_args": correct_args,
797
- "reason": fix_reason,
798
- "auto_fix_failed": True,
799
- },
800
- fix_command="claude-mpm configure --mcp --fix-kuzu",
801
- fix_description="Fix kuzu-memory configuration manually",
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(self, config_path: Path, config: Dict, new_args: List[str]) -> bool:
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 = verify_config.get("mcpServers", {}).get("kuzu-memory", {}).get("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(
@@ -871,38 +923,53 @@ class MCPServicesCheck(BaseDiagnosticCheck):
871
923
  def _check_gateway_configuration(self) -> DiagnosticResult:
872
924
  """Check if MCP services are configured in the gateway."""
873
925
  try:
874
- # Check MCP config file
875
- config_dir = Path.home() / ".claude" / "mcp"
876
- config_file = config_dir / "config.json"
926
+ # Check Claude config file (the correct location for Claude Code)
927
+ config_file = Path.home() / ".claude.json"
877
928
 
878
929
  if not config_file.exists():
879
930
  return DiagnosticResult(
880
931
  category="MCP Gateway Configuration",
881
932
  status=DiagnosticStatus.WARNING,
882
- message="MCP configuration file not found",
933
+ message="Claude configuration file not found",
883
934
  details={"config_path": str(config_file), "exists": False},
884
935
  fix_command="claude-mpm configure --mcp",
885
- fix_description="Initialize MCP configuration",
936
+ fix_description="Initialize Claude configuration",
886
937
  )
887
938
 
888
939
  with open(config_file) as f:
889
940
  config = json.load(f)
890
941
 
891
- # Check for external services configuration
892
- external_services = config.get("external_services", {})
942
+ # Get the current project configuration
943
+ from pathlib import Path
944
+
945
+ current_project = str(Path.cwd())
946
+
947
+ # Check if current project has MCP servers configured
948
+ projects = config.get("projects", {})
949
+ if current_project not in projects:
950
+ return DiagnosticResult(
951
+ category="MCP Gateway Configuration",
952
+ status=DiagnosticStatus.WARNING,
953
+ message="Current project not configured in Claude",
954
+ details={
955
+ "config_path": str(config_file),
956
+ "project": current_project,
957
+ },
958
+ fix_command="claude-mpm configure --mcp",
959
+ fix_description="Configure MCP services for current project",
960
+ )
961
+
962
+ project_config = projects[current_project]
963
+ mcp_servers = project_config.get("mcpServers", {})
964
+
893
965
  configured_services = []
894
966
  missing_services = []
895
967
 
896
968
  for service_name in self.MCP_SERVICES:
897
- if service_name in external_services:
969
+ if service_name in mcp_servers:
898
970
  configured_services.append(service_name)
899
971
  else:
900
- # Also check if it's in the services list directly
901
- services = config.get("services", [])
902
- if any(s.get("name") == service_name for s in services):
903
- configured_services.append(service_name)
904
- else:
905
- missing_services.append(service_name)
972
+ missing_services.append(service_name)
906
973
 
907
974
  details = {
908
975
  "config_path": str(config_file),
@@ -975,11 +1042,14 @@ class MCPServicesCheck(BaseDiagnosticCheck):
975
1042
  )
976
1043
 
977
1044
  if inject_result.returncode == 0:
978
- self.logger.info("✅ Successfully injected gql dependency into mcp-ticketer")
1045
+ self.logger.info(
1046
+ "✅ Successfully injected gql dependency into mcp-ticketer"
1047
+ )
979
1048
  return True
980
- else:
981
- self.logger.warning(f"Failed to inject gql dependency: {inject_result.stderr}")
982
- return False
1049
+ self.logger.warning(
1050
+ f"Failed to inject gql dependency: {inject_result.stderr}"
1051
+ )
1052
+ return False
983
1053
 
984
1054
  # Dependency already present
985
1055
  return False