claude-mpm 4.4.8__py3-none-any.whl → 4.4.9__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 (24) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/scripts/mcp_server.py +0 -0
  3. claude_mpm/scripts/start_activity_logging.py +0 -0
  4. claude_mpm/services/diagnostics/checks/mcp_services_check.py +393 -27
  5. claude_mpm/services/mcp_config_manager.py +187 -42
  6. {claude_mpm-4.4.8.dist-info → claude_mpm-4.4.9.dist-info}/METADATA +1 -1
  7. {claude_mpm-4.4.8.dist-info → claude_mpm-4.4.9.dist-info}/RECORD +9 -22
  8. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  9. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  10. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  11. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-313.pyc +0 -0
  12. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  13. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  14. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  15. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  16. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-313.pyc +0 -0
  17. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  18. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  19. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  20. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  21. {claude_mpm-4.4.8.dist-info → claude_mpm-4.4.9.dist-info}/WHEEL +0 -0
  22. {claude_mpm-4.4.8.dist-info → claude_mpm-4.4.9.dist-info}/entry_points.txt +0 -0
  23. {claude_mpm-4.4.8.dist-info → claude_mpm-4.4.9.dist-info}/licenses/LICENSE +0 -0
  24. {claude_mpm-4.4.8.dist-info → claude_mpm-4.4.9.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION CHANGED
@@ -1 +1 @@
1
- 4.4.8
1
+ 4.4.9
File without changes
File without changes
@@ -5,8 +5,10 @@ WHY: Verify that MCP services (mcp-vector-search, mcp-browser, mcp-ticketer, kuz
5
5
  are properly installed and accessible for enhanced Claude Code capabilities.
6
6
  """
7
7
 
8
+ import asyncio
8
9
  import json
9
10
  import subprocess
11
+ import time
10
12
  from pathlib import Path
11
13
  from typing import Dict, List, Optional, Tuple
12
14
 
@@ -35,6 +37,8 @@ class MCPServicesCheck(BaseDiagnosticCheck):
35
37
  "check_health": True,
36
38
  "health_command": ["mcp-vector-search", "--version"],
37
39
  "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"],
38
42
  },
39
43
  "mcp-browser": {
40
44
  "package": "mcp-browser",
@@ -43,6 +47,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
43
47
  "check_health": True,
44
48
  "health_command": ["mcp-browser", "--version"],
45
49
  "pipx_run_command": ["pipx", "run", "mcp-browser", "--version"],
50
+ "mcp_command": ["mcp-browser", "mcp"], # Command to run as MCP server
46
51
  },
47
52
  "mcp-ticketer": {
48
53
  "package": "mcp-ticketer",
@@ -51,6 +56,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
51
56
  "check_health": True,
52
57
  "health_command": ["mcp-ticketer", "--version"],
53
58
  "pipx_run_command": ["pipx", "run", "mcp-ticketer", "--version"],
59
+ "mcp_command": ["mcp-ticketer", "mcp"], # Command to run as MCP server
54
60
  },
55
61
  "kuzu-memory": {
56
62
  "package": "kuzu-memory",
@@ -59,6 +65,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
59
65
  "check_health": True, # v1.1.0+ has version command
60
66
  "health_command": ["kuzu-memory", "--version"],
61
67
  "pipx_run_command": ["pipx", "run", "kuzu-memory", "--version"],
68
+ "mcp_command": ["kuzu-memory", "mcp", "serve"], # v1.1.0+ uses 'mcp serve' args
62
69
  },
63
70
  }
64
71
 
@@ -86,11 +93,20 @@ class MCPServicesCheck(BaseDiagnosticCheck):
86
93
  for service_name, service_config in self.MCP_SERVICES.items():
87
94
  service_result = self._check_service(service_name, service_config)
88
95
  sub_results.append(service_result)
96
+
97
+ # Extract connection test info if available
98
+ connection_test = service_result.details.get("connection_test", {})
99
+
89
100
  services_status[service_name] = {
90
101
  "status": service_result.status.value,
91
102
  "installed": service_result.details.get("installed", False),
92
103
  "accessible": service_result.details.get("accessible", False),
93
104
  "version": service_result.details.get("version"),
105
+ "connection_tested": bool(connection_test),
106
+ "connected": connection_test.get("connected", False),
107
+ "response_time_ms": connection_test.get("response_time_ms"),
108
+ "tools_discovered": connection_test.get("tools_discovered", 0),
109
+ "connection_error": connection_test.get("error"),
94
110
  }
95
111
 
96
112
  # Check MCP gateway configuration for services
@@ -102,12 +118,18 @@ class MCPServicesCheck(BaseDiagnosticCheck):
102
118
  accessible_count = sum(
103
119
  1 for s in services_status.values() if s["accessible"]
104
120
  )
121
+ connected_count = sum(1 for s in services_status.values() if s["connected"])
105
122
  total_services = len(self.MCP_SERVICES)
106
123
 
124
+ # Calculate total tools discovered
125
+ total_tools = sum(s.get("tools_discovered", 0) for s in services_status.values())
126
+
107
127
  details["services"] = services_status
108
128
  details["installed_count"] = installed_count
109
129
  details["accessible_count"] = accessible_count
130
+ details["connected_count"] = connected_count
110
131
  details["total_services"] = total_services
132
+ details["total_tools_discovered"] = total_tools
111
133
  details["gateway_configured"] = gateway_result.status == DiagnosticStatus.OK
112
134
 
113
135
  # Determine overall status
@@ -120,6 +142,12 @@ class MCPServicesCheck(BaseDiagnosticCheck):
120
142
  elif installed_count == 0:
121
143
  status = DiagnosticStatus.WARNING
122
144
  message = "No MCP services installed"
145
+ elif connected_count == total_services:
146
+ status = DiagnosticStatus.OK
147
+ message = f"All {total_services} MCP services connected ({total_tools} tools available)"
148
+ elif connected_count > 0:
149
+ status = DiagnosticStatus.WARNING
150
+ message = f"{connected_count}/{total_services} MCP services connected, {installed_count} installed"
123
151
  elif accessible_count < installed_count:
124
152
  status = DiagnosticStatus.WARNING
125
153
  message = f"{installed_count}/{total_services} services installed, {accessible_count} accessible"
@@ -127,8 +155,8 @@ class MCPServicesCheck(BaseDiagnosticCheck):
127
155
  status = DiagnosticStatus.WARNING
128
156
  message = f"{installed_count}/{total_services} MCP services installed"
129
157
  else:
130
- status = DiagnosticStatus.OK
131
- message = f"All {total_services} MCP services installed and accessible"
158
+ status = DiagnosticStatus.WARNING
159
+ message = f"All {total_services} MCP services installed but connections not tested"
132
160
 
133
161
  return DiagnosticResult(
134
162
  category=self.category,
@@ -146,6 +174,185 @@ class MCPServicesCheck(BaseDiagnosticCheck):
146
174
  details={"error": str(e)},
147
175
  )
148
176
 
177
+ async def _test_mcp_connection(
178
+ self, service_name: str, command: List[str]
179
+ ) -> Dict:
180
+ """Test MCP server connection by sending JSON-RPC requests."""
181
+ result = {
182
+ "connected": False,
183
+ "response_time": None,
184
+ "tools_count": 0,
185
+ "tools": [],
186
+ "error": None,
187
+ }
188
+
189
+ process = None
190
+ try:
191
+ # Start the MCP server process
192
+ start_time = time.time()
193
+ process = await asyncio.create_subprocess_exec(
194
+ *command,
195
+ stdin=asyncio.subprocess.PIPE,
196
+ stdout=asyncio.subprocess.PIPE,
197
+ stderr=asyncio.subprocess.PIPE,
198
+ )
199
+
200
+ # Give the server a moment to initialize
201
+ await asyncio.sleep(0.1)
202
+
203
+ # Prepare initialize request
204
+ init_request = {
205
+ "jsonrpc": "2.0",
206
+ "method": "initialize",
207
+ "params": {
208
+ "protocolVersion": "2024-11-05",
209
+ "capabilities": {},
210
+ "clientInfo": {
211
+ "name": "mpm-doctor",
212
+ "version": "1.0.0"
213
+ }
214
+ },
215
+ "id": 1
216
+ }
217
+
218
+ # Send initialize request
219
+ request_line = json.dumps(init_request) + "\n"
220
+ process.stdin.write(request_line.encode())
221
+ await process.stdin.drain()
222
+
223
+ # Read response with timeout
224
+ try:
225
+ response_line = await asyncio.wait_for(
226
+ process.stdout.readline(), timeout=5.0
227
+ )
228
+ response_time = time.time() - start_time
229
+
230
+ if response_line:
231
+ # Some MCP servers may output non-JSON before the actual response
232
+ # Try to find JSON in the response
233
+ response_text = response_line.decode().strip()
234
+
235
+ # Skip empty lines or non-JSON lines
236
+ while response_text and not response_text.startswith('{'):
237
+ # Try to read the next line
238
+ try:
239
+ response_line = await asyncio.wait_for(
240
+ process.stdout.readline(), timeout=1.0
241
+ )
242
+ if response_line:
243
+ response_text = response_line.decode().strip()
244
+ else:
245
+ break
246
+ except asyncio.TimeoutError:
247
+ break
248
+
249
+ if not response_text or not response_text.startswith('{'):
250
+ result["error"] = "No valid JSON response received"
251
+ return result
252
+
253
+ response = json.loads(response_text)
254
+
255
+ # Check for valid JSON-RPC response
256
+ if "result" in response and response.get("id") == 1:
257
+ result["connected"] = True
258
+ result["response_time"] = round(response_time * 1000, 2) # ms
259
+
260
+ # Send tools/list request
261
+ tools_request = {
262
+ "jsonrpc": "2.0",
263
+ "method": "tools/list",
264
+ "params": {},
265
+ "id": 2
266
+ }
267
+
268
+ request_line = json.dumps(tools_request) + "\n"
269
+ process.stdin.write(request_line.encode())
270
+ await process.stdin.drain()
271
+
272
+ # Read tools response
273
+ try:
274
+ tools_response_line = await asyncio.wait_for(
275
+ process.stdout.readline(), timeout=3.0
276
+ )
277
+
278
+ if tools_response_line:
279
+ tools_response = json.loads(tools_response_line.decode())
280
+ if "result" in tools_response:
281
+ tools = tools_response["result"].get("tools", [])
282
+ result["tools_count"] = len(tools)
283
+ # Store first 5 tool names for display
284
+ result["tools"] = [
285
+ tool.get("name", "unknown")
286
+ for tool in tools[:5]
287
+ ]
288
+ except asyncio.TimeoutError:
289
+ # Connection successful but tools query timed out
290
+ pass
291
+ except (json.JSONDecodeError, KeyError):
292
+ # Connection successful but tools response invalid
293
+ pass
294
+
295
+ elif "error" in response:
296
+ result["error"] = f"MCP error: {response['error'].get('message', 'Unknown error')}"
297
+ else:
298
+ result["error"] = "Invalid JSON-RPC response format"
299
+
300
+ except asyncio.TimeoutError:
301
+ # Try to get any error output from stderr
302
+ stderr_output = ""
303
+ if process and process.stderr:
304
+ try:
305
+ stderr_data = await asyncio.wait_for(
306
+ process.stderr.read(1000), timeout=0.5
307
+ )
308
+ if stderr_data:
309
+ stderr_output = stderr_data.decode('utf-8', errors='ignore')[:200]
310
+ except:
311
+ pass
312
+
313
+ if stderr_output:
314
+ result["error"] = f"Connection timeout (5s). Server output: {stderr_output}"
315
+ else:
316
+ result["error"] = "Connection timeout (5s)"
317
+
318
+ except json.JSONDecodeError as e:
319
+ # Try to get stderr for more context
320
+ stderr_output = ""
321
+ if process and process.stderr:
322
+ try:
323
+ stderr_data = await asyncio.wait_for(
324
+ process.stderr.read(1000), timeout=0.5
325
+ )
326
+ if stderr_data:
327
+ stderr_output = stderr_data.decode('utf-8', errors='ignore')[:200]
328
+ except:
329
+ pass
330
+
331
+ if stderr_output:
332
+ result["error"] = f"Invalid JSON response: {str(e)}. Server error: {stderr_output}"
333
+ else:
334
+ result["error"] = f"Invalid JSON response: {str(e)}"
335
+
336
+ except FileNotFoundError:
337
+ result["error"] = f"Command not found: {command[0]}"
338
+ except PermissionError:
339
+ result["error"] = f"Permission denied: {command[0]}"
340
+ except Exception as e:
341
+ result["error"] = f"Connection failed: {str(e)}"
342
+ finally:
343
+ # Clean up process
344
+ if process:
345
+ try:
346
+ process.terminate()
347
+ await asyncio.wait_for(process.wait(), timeout=2.0)
348
+ except asyncio.TimeoutError:
349
+ process.kill()
350
+ await process.wait()
351
+ except Exception:
352
+ pass
353
+
354
+ return result
355
+
149
356
  def _check_service(self, service_name: str, config: Dict) -> DiagnosticResult:
150
357
  """Check a specific MCP service."""
151
358
  details = {"service": service_name}
@@ -156,6 +363,12 @@ class MCPServicesCheck(BaseDiagnosticCheck):
156
363
  if pipx_path:
157
364
  details["pipx_path"] = pipx_path
158
365
 
366
+ # Special check for mcp-ticketer: ensure gql dependency
367
+ if service_name == "mcp-ticketer" and pipx_installed:
368
+ gql_fixed = self._ensure_mcp_ticketer_gql_dependency()
369
+ if gql_fixed:
370
+ details["gql_dependency_fixed"] = True
371
+
159
372
  # Check if accessible in PATH
160
373
  accessible, command_path = self._check_command_accessible(config["command"])
161
374
  details["accessible"] = accessible
@@ -197,6 +410,64 @@ class MCPServicesCheck(BaseDiagnosticCheck):
197
410
  details["version"] = version
198
411
  break
199
412
 
413
+ # Test MCP connection if installed (accessible or pipx) and has mcp_command
414
+ if (accessible or pipx_installed) and "mcp_command" in config:
415
+ # Determine which command to use for MCP connection test
416
+ mcp_command = None
417
+ if pipx_installed and not accessible:
418
+ # Service is installed via pipx but not in PATH
419
+ if "pipx_mcp_command" in config:
420
+ # Use special pipx MCP command if available (e.g., for mcp-vector-search)
421
+ mcp_command = config["pipx_mcp_command"]
422
+ else:
423
+ # Build pipx run command based on package
424
+ base_cmd = config["mcp_command"]
425
+ if len(base_cmd) > 0 and base_cmd[0] == config["package"]:
426
+ # Simple case where first command is the package name
427
+ mcp_command = ["pipx", "run", config["package"]] + base_cmd[1:]
428
+ else:
429
+ # Complex case - just try running the package with mcp arg
430
+ mcp_command = ["pipx", "run", config["package"], "mcp"]
431
+ elif details.get("accessible_via_pipx_run"):
432
+ # Use pipx run for the MCP command
433
+ if "pipx_mcp_command" in config:
434
+ # Use special pipx MCP command if available (e.g., for mcp-vector-search)
435
+ mcp_command = config["pipx_mcp_command"]
436
+ else:
437
+ # Build pipx run command
438
+ base_cmd = config["mcp_command"]
439
+ if service_name == "kuzu-memory":
440
+ # Special case for kuzu-memory with args
441
+ mcp_command = ["pipx", "run", base_cmd[0]] + base_cmd[1:]
442
+ else:
443
+ mcp_command = ["pipx", "run"] + base_cmd
444
+ else:
445
+ mcp_command = config["mcp_command"]
446
+
447
+ if mcp_command:
448
+ # Run async connection test
449
+ try:
450
+ loop = asyncio.new_event_loop()
451
+ asyncio.set_event_loop(loop)
452
+ connection_result = loop.run_until_complete(
453
+ self._test_mcp_connection(service_name, mcp_command)
454
+ )
455
+ loop.close()
456
+
457
+ # Add connection test results to details
458
+ details["connection_test"] = {
459
+ "connected": connection_result["connected"],
460
+ "response_time_ms": connection_result["response_time"],
461
+ "tools_discovered": connection_result["tools_count"],
462
+ "tools_sample": connection_result["tools"],
463
+ "error": connection_result["error"]
464
+ }
465
+ except Exception as e:
466
+ details["connection_test"] = {
467
+ "connected": False,
468
+ "error": f"Test failed: {str(e)}"
469
+ }
470
+
200
471
  # Determine status
201
472
  if not (pipx_installed or accessible):
202
473
  return DiagnosticResult(
@@ -211,10 +482,19 @@ class MCPServicesCheck(BaseDiagnosticCheck):
211
482
  if pipx_installed and not accessible:
212
483
  # Check if pipx run works
213
484
  if details.get("pipx_run_available"):
485
+ # Include connection test info if available
486
+ connection_info = details.get("connection_test", {})
487
+ if connection_info.get("connected"):
488
+ message = f"Installed via pipx, connection OK ({connection_info.get('tools_discovered', 0)} tools)"
489
+ elif connection_info.get("error"):
490
+ message = f"Installed via pipx, connection failed: {connection_info['error']}"
491
+ else:
492
+ message = "Installed via pipx (use 'pipx run' to execute)"
493
+
214
494
  return DiagnosticResult(
215
495
  category=f"MCP Service: {service_name}",
216
- status=DiagnosticStatus.OK,
217
- message="Installed via pipx (use 'pipx run' to execute)",
496
+ status=DiagnosticStatus.OK if connection_info.get("connected") else DiagnosticStatus.WARNING,
497
+ message=message,
218
498
  details=details,
219
499
  )
220
500
  return DiagnosticResult(
@@ -226,10 +506,26 @@ class MCPServicesCheck(BaseDiagnosticCheck):
226
506
  fix_description="Ensure pipx bin directory is in PATH",
227
507
  )
228
508
 
509
+ # Service is accessible - check connection test results
510
+ connection_info = details.get("connection_test", {})
511
+ if connection_info:
512
+ if connection_info.get("connected"):
513
+ response_time = connection_info.get("response_time_ms")
514
+ tools_count = connection_info.get("tools_discovered", 0)
515
+ message = f"Installed, accessible, connection OK ({tools_count} tools, {response_time}ms)"
516
+ status = DiagnosticStatus.OK
517
+ else:
518
+ error = connection_info.get("error", "Unknown error")
519
+ message = f"Installed but connection failed: {error}"
520
+ status = DiagnosticStatus.WARNING
521
+ else:
522
+ message = "Installed and accessible"
523
+ status = DiagnosticStatus.OK
524
+
229
525
  return DiagnosticResult(
230
526
  category=f"MCP Service: {service_name}",
231
- status=DiagnosticStatus.OK,
232
- message="Installed and accessible",
527
+ status=status,
528
+ message=message,
233
529
  details=details,
234
530
  )
235
531
 
@@ -423,40 +719,42 @@ class MCPServicesCheck(BaseDiagnosticCheck):
423
719
  args = kuzu_config.get("args", [])
424
720
  needs_fix = False
425
721
  fix_reason = ""
426
- new_args = None
722
+ # The correct args for kuzu-memory v1.1.0+ are ["mcp", "serve"]
723
+ correct_args = ["mcp", "serve"]
427
724
 
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"]:
725
+ # Check for any configuration that is NOT the correct one
726
+ if args != correct_args:
434
727
  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"]
728
+ # Identify the specific issue
729
+ if args == ["claude", "mcp-server"]:
730
+ fix_reason = "Outdated 'claude mcp-server' format (pre-v1.1.0)"
731
+ elif args == ["serve"]:
732
+ fix_reason = "Legacy 'serve' format"
733
+ elif args == ["mcp-server"]:
734
+ fix_reason = "Incorrect 'mcp-server' format"
735
+ elif args == []:
736
+ fix_reason = "Empty args list"
737
+ else:
738
+ fix_reason = f"Incorrect args format: {args}"
441
739
 
442
740
  if needs_fix:
443
- # Offer to auto-fix
741
+ # Log the issue for debugging
444
742
  self.logger.warning(
445
743
  f"Found incorrect kuzu-memory configuration: {fix_reason}. "
446
- f"Current args: {args}"
744
+ f"Current args: {args}, should be: {correct_args}"
447
745
  )
448
746
 
449
747
  # Auto-fix the configuration
450
- fixed = self._fix_kuzu_memory_args(claude_config_path, config, new_args)
748
+ fixed = self._fix_kuzu_memory_args(claude_config_path, config, correct_args)
451
749
 
452
750
  if fixed:
453
751
  return DiagnosticResult(
454
752
  category="kuzu-memory Configuration Fix",
455
753
  status=DiagnosticStatus.OK,
456
- message="Fixed kuzu-memory configuration",
754
+ message=f"Fixed kuzu-memory configuration to use correct args",
457
755
  details={
458
756
  "old_args": args,
459
- "new_args": new_args,
757
+ "new_args": correct_args,
460
758
  "reason": fix_reason,
461
759
  "auto_fixed": True,
462
760
  },
@@ -468,7 +766,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
468
766
  message="kuzu-memory has incorrect configuration",
469
767
  details={
470
768
  "current_args": args,
471
- "correct_args": new_args,
769
+ "correct_args": correct_args,
472
770
  "reason": fix_reason,
473
771
  "auto_fix_failed": True,
474
772
  },
@@ -476,7 +774,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
476
774
  fix_description="Fix kuzu-memory configuration manually",
477
775
  )
478
776
 
479
- # Configuration is correct
777
+ # Configuration is correct - args match ["mcp", "serve"]
480
778
  return None
481
779
 
482
780
  except (json.JSONDecodeError, Exception) as e:
@@ -489,18 +787,48 @@ class MCPServicesCheck(BaseDiagnosticCheck):
489
787
  # Save old args before updating
490
788
  old_args = config["mcpServers"]["kuzu-memory"].get("args", [])
491
789
 
790
+ # Log the exact change we're about to make
791
+ self.logger.debug(
792
+ f"Fixing kuzu-memory args: old={old_args}, new={new_args}"
793
+ )
794
+
492
795
  # Create backup
493
796
  backup_path = config_path.with_suffix(".json.backup")
494
797
  with open(backup_path, "w") as f:
495
798
  json.dump(config, f, indent=2)
496
799
 
497
- # Update the configuration
800
+ # Update the configuration - ensure we're setting the exact new_args
498
801
  config["mcpServers"]["kuzu-memory"]["args"] = new_args
499
802
 
803
+ # Verify the update in memory before writing
804
+ if config["mcpServers"]["kuzu-memory"]["args"] != new_args:
805
+ self.logger.error(
806
+ f"Failed to update args in memory! "
807
+ f"Expected {new_args}, got {config['mcpServers']['kuzu-memory']['args']}"
808
+ )
809
+ return False
810
+
500
811
  # Write updated configuration
501
812
  with open(config_path, "w") as f:
502
813
  json.dump(config, f, indent=2)
503
814
 
815
+ # Verify the file was written correctly
816
+ with open(config_path) as f:
817
+ verify_config = json.load(f)
818
+ verify_args = verify_config.get("mcpServers", {}).get("kuzu-memory", {}).get("args", [])
819
+
820
+ if verify_args != new_args:
821
+ self.logger.error(
822
+ f"Configuration write verification failed! "
823
+ f"Expected {new_args}, got {verify_args}"
824
+ )
825
+ # Restore backup
826
+ with open(backup_path) as bf:
827
+ backup_config = json.load(bf)
828
+ with open(config_path, "w") as f:
829
+ json.dump(backup_config, f, indent=2)
830
+ return False
831
+
504
832
  self.logger.info(
505
833
  f"✅ Fixed kuzu-memory configuration in {config_path}\n"
506
834
  f" Changed args from {old_args} to {new_args}\n"
@@ -594,3 +922,41 @@ class MCPServicesCheck(BaseDiagnosticCheck):
594
922
  message=f"Could not check configuration: {e!s}",
595
923
  details={"error": str(e)},
596
924
  )
925
+
926
+ def _ensure_mcp_ticketer_gql_dependency(self) -> bool:
927
+ """Ensure mcp-ticketer has the gql dependency injected."""
928
+ try:
929
+ # First check if mcp-ticketer can import gql
930
+ result = subprocess.run(
931
+ ["pipx", "run", "--spec", "mcp-ticketer", "python", "-c", "import gql"],
932
+ capture_output=True,
933
+ text=True,
934
+ timeout=5,
935
+ check=False,
936
+ )
937
+
938
+ # If import fails, inject the dependency
939
+ if result.returncode != 0:
940
+ self.logger.info("🔧 mcp-ticketer missing gql dependency, fixing...")
941
+
942
+ inject_result = subprocess.run(
943
+ ["pipx", "inject", "mcp-ticketer", "gql"],
944
+ capture_output=True,
945
+ text=True,
946
+ timeout=30,
947
+ check=False,
948
+ )
949
+
950
+ if inject_result.returncode == 0:
951
+ self.logger.info("✅ Successfully injected gql dependency into mcp-ticketer")
952
+ return True
953
+ else:
954
+ self.logger.warning(f"Failed to inject gql dependency: {inject_result.stderr}")
955
+ return False
956
+
957
+ # Dependency already present
958
+ return False
959
+
960
+ except Exception as e:
961
+ self.logger.debug(f"Could not check/fix mcp-ticketer gql dependency: {e}")
962
+ return False
@@ -41,6 +41,33 @@ class MCPConfigManager:
41
41
  "kuzu-memory",
42
42
  }
43
43
 
44
+ # Static known-good MCP service configurations
45
+ # These are the correct, tested configurations that work reliably
46
+ STATIC_MCP_CONFIGS = {
47
+ "kuzu-memory": {
48
+ "type": "stdio",
49
+ "command": "pipx",
50
+ "args": ["run", "kuzu-memory", "mcp", "serve"]
51
+ },
52
+ "mcp-ticketer": {
53
+ "type": "stdio",
54
+ "command": "pipx",
55
+ "args": ["run", "mcp-ticketer", "mcp"]
56
+ },
57
+ "mcp-browser": {
58
+ "type": "stdio",
59
+ "command": "pipx",
60
+ "args": ["run", "mcp-browser", "mcp"],
61
+ "env": {"MCP_BROWSER_HOME": str(Path.home() / ".mcp-browser")}
62
+ },
63
+ "mcp-vector-search": {
64
+ "type": "stdio",
65
+ "command": "pipx",
66
+ "args": ["run", "mcp-vector-search", "-m", "mcp_vector_search.mcp.server", "{project_root}"],
67
+ "env": {}
68
+ }
69
+ }
70
+
44
71
  def __init__(self):
45
72
  """Initialize the MCP configuration manager."""
46
73
  self.logger = get_logger(__name__)
@@ -195,11 +222,36 @@ class MCPConfigManager:
195
222
 
196
223
  return None
197
224
 
225
+ def get_static_service_config(self, service_name: str) -> Optional[Dict]:
226
+ """
227
+ Get the static, known-good configuration for an MCP service.
228
+
229
+ Args:
230
+ service_name: Name of the MCP service
231
+
232
+ Returns:
233
+ Static service configuration dict or None if service not known
234
+ """
235
+ if service_name not in self.STATIC_MCP_CONFIGS:
236
+ return None
237
+
238
+ config = self.STATIC_MCP_CONFIGS[service_name].copy()
239
+
240
+ # Special handling for mcp-vector-search: replace {project_root} placeholder
241
+ if service_name == "mcp-vector-search":
242
+ config["args"] = [
243
+ arg.replace("{project_root}", str(self.project_root)) if "{project_root}" in arg else arg
244
+ for arg in config["args"]
245
+ ]
246
+
247
+ return config
248
+
198
249
  def generate_service_config(self, service_name: str) -> Optional[Dict]:
199
250
  """
200
251
  Generate configuration for a specific MCP service.
201
252
 
202
- Prefers 'pipx run' or 'uvx' commands over direct execution for better isolation.
253
+ Prefers static configurations over detection. Falls back to detection
254
+ only for unknown services.
203
255
 
204
256
  Args:
205
257
  service_name: Name of the MCP service
@@ -207,6 +259,12 @@ class MCPConfigManager:
207
259
  Returns:
208
260
  Service configuration dict or None if service not found
209
261
  """
262
+ # First try to get static configuration
263
+ static_config = self.get_static_service_config(service_name)
264
+ if static_config:
265
+ return static_config
266
+
267
+ # Fall back to detection-based configuration for unknown services
210
268
  import shutil
211
269
 
212
270
  # Check for pipx run first (preferred for isolation)
@@ -308,7 +366,7 @@ class MCPConfigManager:
308
366
 
309
367
  elif service_name == "kuzu-memory":
310
368
  # Determine kuzu-memory command version
311
- kuzu_args = ["mcp", "serve"] # Default to the correct modern format
369
+ kuzu_args = ["mcp", "serve"] # Default to the standard v1.1.0+ format
312
370
  test_cmd = None
313
371
 
314
372
  if use_pipx_run:
@@ -330,20 +388,21 @@ class MCPConfigManager:
330
388
  # Check for MCP support in help output
331
389
  help_output = result.stdout.lower() + result.stderr.lower()
332
390
 
333
- # Modern version detection - look for "mcp serve" command
391
+ # Standard version detection - look for "mcp serve" command (v1.1.0+)
392
+ # This is the correct format for kuzu-memory v1.1.0 and later
334
393
  if "mcp serve" in help_output or ("mcp" in help_output and "serve" in help_output):
335
- # Modern version with mcp serve command
394
+ # Standard v1.1.0+ version with mcp serve command
336
395
  kuzu_args = ["mcp", "serve"]
337
396
  # Legacy version detection - only "serve" without "mcp"
338
397
  elif "serve" in help_output and "mcp" not in help_output:
339
398
  # Very old version that only has serve command
340
399
  kuzu_args = ["serve"]
341
- # Note: "claude mcp-server" format is deprecated and not used
342
400
  else:
343
- # Default to the correct modern format
401
+ # Default to the standard mcp serve format (v1.1.0+)
402
+ # Note: "claude mcp-server" format is deprecated and does not work
344
403
  kuzu_args = ["mcp", "serve"]
345
404
  except Exception:
346
- # Default to the correct mcp serve command on any error
405
+ # Default to the standard mcp serve command on any error
347
406
  kuzu_args = ["mcp", "serve"]
348
407
 
349
408
  if use_pipx_run:
@@ -371,17 +430,20 @@ class MCPConfigManager:
371
430
 
372
431
  def ensure_mcp_services_configured(self) -> Tuple[bool, str]:
373
432
  """
374
- Ensure MCP services are configured in ~/.claude.json on startup.
433
+ Ensure MCP services are configured correctly in ~/.claude.json on startup.
375
434
 
376
- This method checks if the core MCP services are configured in the
377
- current project's mcpServers section and automatically adds them if missing.
435
+ This method checks ALL projects in ~/.claude.json and ensures each has
436
+ the correct, static MCP service configurations. It will:
437
+ 1. Add missing services
438
+ 2. Fix incorrect configurations
439
+ 3. Update all projects, not just the current one
378
440
 
379
441
  Returns:
380
442
  Tuple of (success, message)
381
443
  """
382
444
  updated = False
445
+ fixed_services = []
383
446
  added_services = []
384
- project_key = str(self.project_root)
385
447
 
386
448
  # Load existing Claude config or create minimal structure
387
449
  claude_config = {}
@@ -396,9 +458,23 @@ class MCPConfigManager:
396
458
  # Ensure projects structure exists
397
459
  if "projects" not in claude_config:
398
460
  claude_config["projects"] = {}
461
+ updated = True
399
462
 
400
- if project_key not in claude_config["projects"]:
401
- claude_config["projects"][project_key] = {
463
+ # Check and fix mcp-ticketer dependencies ONCE before processing projects
464
+ # This avoids running the same pipx inject command 100+ times
465
+ mcp_ticketer_fixed = False
466
+ if "mcp-ticketer" in self.PIPX_SERVICES:
467
+ mcp_ticketer_fixed = self._check_and_fix_mcp_ticketer_dependencies()
468
+
469
+ # Process ALL projects in the config, not just current one
470
+ projects_to_update = list(claude_config.get("projects", {}).keys())
471
+
472
+ # Also add the current project if not in list
473
+ current_project_key = str(self.project_root)
474
+ if current_project_key not in projects_to_update:
475
+ projects_to_update.append(current_project_key)
476
+ # Initialize new project structure
477
+ claude_config["projects"][current_project_key] = {
402
478
  "allowedTools": [],
403
479
  "history": [],
404
480
  "mcpContextUris": [],
@@ -412,29 +488,51 @@ class MCPConfigManager:
412
488
  }
413
489
  updated = True
414
490
 
415
- # Get the project's mcpServers section
416
- project_config = claude_config["projects"][project_key]
417
- if "mcpServers" not in project_config:
418
- project_config["mcpServers"] = {}
419
- updated = True
420
-
421
- # Check each service and add if missing
422
- for service_name in self.PIPX_SERVICES:
423
- if service_name not in project_config["mcpServers"]:
424
- # Try to detect and configure the service
425
- service_path = self.detect_service_path(service_name)
426
- if service_path:
427
- config = self.generate_service_config(service_name)
428
- if config:
429
- project_config["mcpServers"][service_name] = config
430
- added_services.append(service_name)
431
- updated = True
432
- self.logger.debug(
433
- f"Added MCP service to config: {service_name}"
434
- )
491
+ # Update each project's MCP configurations
492
+ for project_key in projects_to_update:
493
+ project_config = claude_config["projects"][project_key]
494
+
495
+ # Ensure mcpServers section exists
496
+ if "mcpServers" not in project_config:
497
+ project_config["mcpServers"] = {}
498
+ updated = True
499
+
500
+ # Check and fix each service configuration
501
+ for service_name in self.PIPX_SERVICES:
502
+ # Get the correct static configuration
503
+ correct_config = self.STATIC_MCP_CONFIGS[service_name].copy()
504
+
505
+ # Special handling for mcp-vector-search: replace {project_root} placeholder
506
+ if service_name == "mcp-vector-search":
507
+ # Use the project key as the project root for each project
508
+ correct_config["args"] = [
509
+ arg.replace("{project_root}", project_key) if "{project_root}" in arg else arg
510
+ for arg in correct_config["args"]
511
+ ]
512
+
513
+ # Check if service exists and has correct configuration
514
+ existing_config = project_config["mcpServers"].get(service_name)
515
+
516
+ # Determine if we need to update
517
+ needs_update = False
518
+ if not existing_config:
519
+ # Service is missing
520
+ needs_update = True
521
+ added_services.append(f"{service_name} in {Path(project_key).name}")
435
522
  else:
523
+ # Service exists, check if configuration is correct
524
+ # Compare command and args (the most critical parts)
525
+ if (existing_config.get("command") != correct_config.get("command") or
526
+ existing_config.get("args") != correct_config.get("args")):
527
+ needs_update = True
528
+ fixed_services.append(f"{service_name} in {Path(project_key).name}")
529
+
530
+ # Update configuration if needed
531
+ if needs_update:
532
+ project_config["mcpServers"][service_name] = correct_config
533
+ updated = True
436
534
  self.logger.debug(
437
- f"MCP service {service_name} not found for auto-configuration"
535
+ f"Updated MCP service config for {service_name} in project {Path(project_key).name}"
438
536
  )
439
537
 
440
538
  # Write updated config if changes were made
@@ -448,7 +546,6 @@ class MCPConfigManager:
448
546
  f".backup.{datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')}.json"
449
547
  )
450
548
  import shutil
451
-
452
549
  shutil.copy2(self.claude_config_path, backup_path)
453
550
  self.logger.debug(f"Created backup: {backup_path}")
454
551
 
@@ -456,18 +553,20 @@ class MCPConfigManager:
456
553
  with open(self.claude_config_path, "w") as f:
457
554
  json.dump(claude_config, f, indent=2)
458
555
 
556
+ messages = []
459
557
  if added_services:
460
- message = (
461
- f"Auto-configured MCP services: {', '.join(added_services)}"
462
- )
463
- # Don't log here - let the caller handle logging to avoid duplicates
464
- return True, message
465
- return True, "All MCP services already configured"
558
+ messages.append(f"Added MCP services: {', '.join(added_services[:3])}")
559
+ if fixed_services:
560
+ messages.append(f"Fixed MCP services: {', '.join(fixed_services[:3])}")
561
+
562
+ if messages:
563
+ return True, "; ".join(messages)
564
+ return True, "All MCP services already configured correctly"
466
565
  except Exception as e:
467
566
  self.logger.error(f"Failed to write Claude config: {e}")
468
567
  return False, f"Failed to write configuration: {e}"
469
568
 
470
- return True, "All MCP services already configured"
569
+ return True, "All MCP services already configured correctly"
471
570
 
472
571
  def update_mcp_config(self, force_pipx: bool = True) -> Tuple[bool, str]:
473
572
  """
@@ -695,6 +794,49 @@ class MCPConfigManager:
695
794
 
696
795
  return False, "none"
697
796
 
797
+ def _check_and_fix_mcp_ticketer_dependencies(self) -> bool:
798
+ """Check and fix mcp-ticketer missing gql dependency.
799
+
800
+ Note: This is a workaround for mcp-ticketer <= 0.1.8 which is missing
801
+ the gql dependency in its package metadata. Future versions (> 0.1.8)
802
+ should include 'gql[httpx]>=3.0.0' as a dependency, making this fix
803
+ unnecessary. We keep this for backward compatibility with older versions.
804
+ """
805
+ try:
806
+ # Test if gql is available in mcp-ticketer's environment
807
+ test_result = subprocess.run(
808
+ ["pipx", "run", "--spec", "mcp-ticketer", "python", "-c", "import gql"],
809
+ capture_output=True,
810
+ text=True,
811
+ timeout=5,
812
+ check=False,
813
+ )
814
+
815
+ # If import fails, inject the dependency
816
+ if test_result.returncode != 0:
817
+ self.logger.info("🔧 mcp-ticketer missing gql dependency, fixing...")
818
+
819
+ inject_result = subprocess.run(
820
+ ["pipx", "inject", "mcp-ticketer", "gql"],
821
+ capture_output=True,
822
+ text=True,
823
+ timeout=30,
824
+ check=False,
825
+ )
826
+
827
+ if inject_result.returncode == 0:
828
+ self.logger.info("✅ Successfully injected gql dependency into mcp-ticketer")
829
+ return True
830
+ else:
831
+ self.logger.warning(f"Failed to inject gql: {inject_result.stderr}")
832
+ return False
833
+
834
+ return False
835
+
836
+ except Exception as e:
837
+ self.logger.debug(f"Could not check/fix mcp-ticketer dependencies: {e}")
838
+ return False
839
+
698
840
  def _verify_service_installed(self, service_name: str, method: str) -> bool:
699
841
  """
700
842
  Verify that a service was successfully installed and is functional.
@@ -711,6 +853,9 @@ class MCPConfigManager:
711
853
  # Give the installation a moment to settle
712
854
  time.sleep(1)
713
855
 
856
+ # Note: mcp-ticketer dependency fix is now handled once in ensure_mcp_services_configured()
857
+ # to avoid running the same pipx inject command multiple times
858
+
714
859
  # Check if we can find the service
715
860
  service_path = self.detect_service_path(service_name)
716
861
  if not service_path:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-mpm
3
- Version: 4.4.8
3
+ Version: 4.4.9
4
4
  Summary: Claude Multi-Agent Project Manager - Orchestrate Claude with agent delegation and ticket tracking
5
5
  Author-email: Bob Matsuoka <bob@matsuoka.com>
6
6
  Maintainer: Claude MPM Team
@@ -1,5 +1,5 @@
1
1
  claude_mpm/BUILD_NUMBER,sha256=toytnNjkIKPgQaGwDqQdC1rpNTAdSEc6Vja50d7Ovug,4
2
- claude_mpm/VERSION,sha256=6Oq7YOKP_W7Ec69SP5f3Of7vy8xtRJusLVAXeC2etgg,6
2
+ claude_mpm/VERSION,sha256=k8-5xMoy2UlbzZa4MVGVy1j0Ym4rIg_47OvMJE5eNKg,6
3
3
  claude_mpm/__init__.py,sha256=lyTZAYGH4DTaFGLRNWJKk5Q5oTjzN5I6AXmfVX-Jff0,1512
4
4
  claude_mpm/__main__.py,sha256=Ro5UBWBoQaSAIoSqWAr7zkbLyvi4sSy28WShqAhKJG0,723
5
5
  claude_mpm/constants.py,sha256=cChN3myrAcF3jC-6DvHnBFTEnwlDk-TAsIXPvUZr_yw,5953
@@ -383,25 +383,12 @@ claude_mpm/hooks/claude_hooks/installer.py,sha256=4QUZcnQWlQDH11BypvYs_JpQ-ojRF0
383
383
  claude_mpm/hooks/claude_hooks/memory_integration.py,sha256=9PogijgklM7iSNRTK2Sdz3s9AOlxL8Rt5axtU71-J2M,8866
384
384
  claude_mpm/hooks/claude_hooks/response_tracking.py,sha256=eMixPs2fOAqLn9WysTPgfIwwjwzZTdnAn0jpDjlOS6U,15024
385
385
  claude_mpm/hooks/claude_hooks/tool_analysis.py,sha256=t4CFrLldjD-akBY52su6Cl0cBmZCKRZbGbIE-Yg2Hhk,7896
386
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc,sha256=bRXmkaVByqCv8qmFpfSJOEeKUOzLNgFVgFc5GVZ1oGo,322
387
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc,sha256=W3pUVNdaDSE0ndQmQUSnGlBWaxOVnvjvZxCcmjLag-0,28937
388
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc,sha256=HTbwMv0BW8gMg9DxzH6UbEPRPZ8a0e_R7sNaRMJ6I1w,21336
389
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-313.pyc,sha256=xUXfgAB_3qc52cTpL8UgsO_xaplQ0v-yhI4pJLUBDF8,29046
390
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc,sha256=qhDYC403V_m8NG2LHMXtcK_SH3qMuPADSp0sJ9D6YTo,8895
391
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc,sha256=MuqyPvXZuFph_xp1j1vtNLqLKRJT51oI2AmZ3SEn69c,14213
392
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc,sha256=OsljgmGIgSmKTyVRRrJmKvmoHdQFq0kBBGTRUrAdnrI,9137
393
386
  claude_mpm/hooks/claude_hooks/services/__init__.py,sha256=OIYOKsUNw1BHYawOCp-KFK5kmQKuj92cCqCEPO0nwo0,585
394
387
  claude_mpm/hooks/claude_hooks/services/connection_manager.py,sha256=6GSZ6lB0j6u7jE-w9ay-57YX-VVBFGfK_wmZNNM6hxE,8280
395
388
  claude_mpm/hooks/claude_hooks/services/connection_manager_http.py,sha256=sEIWyV-3pfS6VM6_jQgnrKiepD-6DLrk5rr_KU80kHo,8832
396
389
  claude_mpm/hooks/claude_hooks/services/duplicate_detector.py,sha256=Fh9LmEMsVmQM9t0U1v2l_fuBwvNpVkl_0EF8Wu5KLHQ,3882
397
390
  claude_mpm/hooks/claude_hooks/services/state_manager.py,sha256=l50dVFt6eafeLCjlu6TH3qqt8tTBEof971U1OlrUxZ8,11166
398
391
  claude_mpm/hooks/claude_hooks/services/subagent_processor.py,sha256=f7a_vgo_kuG9MalDTka2UPXwDyCkqNgCvG8i1hp0oRo,15263
399
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc,sha256=bk04TMdGscqiVyfzkby3gPrW6DcSqzHJOCnclbO5Qrs,565
400
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-313.pyc,sha256=xEEOqJmCwu_rOaZTzfYdLQtamwrlvn_Uv4M9GcTJeIA,8713
401
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc,sha256=HvK-KUCBSg8J_0UTxNd98pMVa68_NYRkyoTgdATD_n4,9967
402
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc,sha256=87ROjkadSdZB1L24rVdjdOHggYRfaGZ2v0nnhLlUnJk,4722
403
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc,sha256=QoAQvuBn4xDlo_YXuHNgJ3vqXLhp4KzKunTOf9crJ70,11772
404
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc,sha256=svC-kX-MYT5YIbWrY_aNf-Biz-JL49noI--1D-dSx_Q,13821
405
392
  claude_mpm/models/__init__.py,sha256=GADyLAiF-1VVghYKI7Urz3zSnf2sjJUXlOEMuCOs7_g,468
406
393
  claude_mpm/models/agent_definition.py,sha256=LC7EwihixF2Gw4QqOxiCNchsEzzyQJPR6AgPrcTrWTM,6674
407
394
  claude_mpm/models/agent_session.py,sha256=BEI9RsCnnSVMF3oOzWKY3a_NEmWI33J2nU_2O-WEyJQ,20027
@@ -424,7 +411,7 @@ claude_mpm/services/event_aggregator.py,sha256=DDcehIZVpiEDzs9o18gDZyvjMBHCq2H8H
424
411
  claude_mpm/services/exceptions.py,sha256=5lVZETr_6-xk0ItH7BTfYUiX5RlckS1e8ah_UalYG9c,26475
425
412
  claude_mpm/services/hook_installer_service.py,sha256=z3kKeriEY1Y9bFesuGlHBxhCtc0Wzd3Zv02k2_rEyGo,19727
426
413
  claude_mpm/services/hook_service.py,sha256=rZnMn_4qxX5g9KAn0IQdoG50WmySNfsTmfG0XHuRHXk,15737
427
- claude_mpm/services/mcp_config_manager.py,sha256=bMV3vkDahRjH94Ht91sUg1Ha8H1Sz63Q1AM9SddfvBY,28957
414
+ claude_mpm/services/mcp_config_manager.py,sha256=iEBnl9rPFR-PccOol8ZdufyCvhZvVN47wYtTteQVpBY,35112
428
415
  claude_mpm/services/mcp_service_verifier.py,sha256=ngiegCngX18AFehfyJdvqQAvscoIBvFN_DeOoGTjxj0,25164
429
416
  claude_mpm/services/memory_hook_service.py,sha256=pRlTClkRcw30Jhwbha4BC8IMdzKZxF8aWqf52JlntgY,11600
430
417
  claude_mpm/services/monitor_build_service.py,sha256=8gWR9CaqgXdG6-OjOFXGpk28GCcJTlHhojkUYnMCebI,12160
@@ -573,7 +560,7 @@ claude_mpm/services/diagnostics/checks/filesystem_check.py,sha256=V5HoHDYlSuoK2l
573
560
  claude_mpm/services/diagnostics/checks/installation_check.py,sha256=WoTt15R8Wg-6k2JZFAtmffFuih1AIyCX71QOHEFH-Ro,19562
574
561
  claude_mpm/services/diagnostics/checks/instructions_check.py,sha256=VbgBorl0RpFvxKQ_SC1gibTmGSiXaKSp-vVZt6hbH1g,16290
575
562
  claude_mpm/services/diagnostics/checks/mcp_check.py,sha256=SftuhP70abopyMD8GlLA_K3XHEYnBAeITggUQI0cYP4,12173
576
- claude_mpm/services/diagnostics/checks/mcp_services_check.py,sha256=osS6J9UbGkwgUs1vxlMfUL4cGlUAkUH4CuatWY8sAIo,23794
563
+ claude_mpm/services/diagnostics/checks/mcp_services_check.py,sha256=SrHdpRbEc62e7mkfC_m8aGFtQIkhNOmiiCQZ78MME98,40800
577
564
  claude_mpm/services/diagnostics/checks/monitor_check.py,sha256=NUx5G1yjHWlukZmwhUz4o8STRWgsQEx01YjIMReNC0A,10096
578
565
  claude_mpm/services/diagnostics/checks/startup_log_check.py,sha256=DrXdml2rHvmhFBdb_sntE3xmwaP_DZIKjdVbCn8Dy7E,12258
579
566
  claude_mpm/services/event_bus/__init__.py,sha256=ETCo4a6puIeyVWAv55uCDjjhzNyUwbVAHEcAVkVapx8,688
@@ -786,9 +773,9 @@ claude_mpm/utils/subprocess_utils.py,sha256=D0izRT8anjiUb_JG72zlJR_JAw1cDkb7kalN
786
773
  claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
787
774
  claude_mpm/validation/agent_validator.py,sha256=Nm2WmcbCb0EwOG4nFcikc3wVdiiAfjGBBI3YoR6ainQ,20915
788
775
  claude_mpm/validation/frontmatter_validator.py,sha256=IDBOCBweO6umydSnUJjBh81sKk3cy9hRFYm61DCiXbI,7020
789
- claude_mpm-4.4.8.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
790
- claude_mpm-4.4.8.dist-info/METADATA,sha256=Mai4bOdvj2KC_AhFTAoj8nHaIxYjWzaI95XBAsC2sx0,17517
791
- claude_mpm-4.4.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
792
- claude_mpm-4.4.8.dist-info/entry_points.txt,sha256=FDPZgz8JOvD-6iuXY2l9Zbo9zYVRuE4uz4Qr0vLeGOk,471
793
- claude_mpm-4.4.8.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
794
- claude_mpm-4.4.8.dist-info/RECORD,,
776
+ claude_mpm-4.4.9.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
777
+ claude_mpm-4.4.9.dist-info/METADATA,sha256=RSN3ddmTCsiu9uTUoaSFYFeB7O56BR7awyIOpLkSVsA,17517
778
+ claude_mpm-4.4.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
779
+ claude_mpm-4.4.9.dist-info/entry_points.txt,sha256=FDPZgz8JOvD-6iuXY2l9Zbo9zYVRuE4uz4Qr0vLeGOk,471
780
+ claude_mpm-4.4.9.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
781
+ claude_mpm-4.4.9.dist-info/RECORD,,