claude-mpm 4.5.11__py3-none-any.whl → 4.5.13__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 (190) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_ENGINEER.md +47 -0
  3. claude_mpm/agents/BASE_QA.md +60 -0
  4. claude_mpm/agents/frontmatter_validator.py +4 -4
  5. claude_mpm/agents/templates/nextjs_engineer.json +2 -2
  6. claude_mpm/agents/templates/qa.json +13 -3
  7. claude_mpm/agents/templates/react_engineer.json +2 -2
  8. claude_mpm/agents/templates/typescript_engineer.json +2 -2
  9. claude_mpm/agents/templates/web_qa.json +14 -3
  10. claude_mpm/cli/commands/agent_manager.py +3 -3
  11. claude_mpm/cli/commands/agents.py +6 -6
  12. claude_mpm/cli/commands/aggregate.py +4 -4
  13. claude_mpm/cli/commands/analyze.py +2 -2
  14. claude_mpm/cli/commands/analyze_code.py +1 -1
  15. claude_mpm/cli/commands/cleanup.py +3 -3
  16. claude_mpm/cli/commands/config.py +2 -2
  17. claude_mpm/cli/commands/configure.py +14 -14
  18. claude_mpm/cli/commands/dashboard.py +1 -1
  19. claude_mpm/cli/commands/debug.py +3 -3
  20. claude_mpm/cli/commands/doctor.py +1 -1
  21. claude_mpm/cli/commands/mcp.py +7 -7
  22. claude_mpm/cli/commands/mcp_command_router.py +1 -1
  23. claude_mpm/cli/commands/mcp_config.py +2 -2
  24. claude_mpm/cli/commands/mcp_external_commands.py +2 -2
  25. claude_mpm/cli/commands/mcp_install_commands.py +3 -3
  26. claude_mpm/cli/commands/mcp_pipx_config.py +2 -2
  27. claude_mpm/cli/commands/mcp_setup_external.py +3 -3
  28. claude_mpm/cli/commands/monitor.py +1 -1
  29. claude_mpm/cli/commands/mpm_init_handler.py +1 -1
  30. claude_mpm/cli/interactive/agent_wizard.py +1 -1
  31. claude_mpm/cli/parsers/search_parser.py +1 -1
  32. claude_mpm/cli/shared/argument_patterns.py +2 -2
  33. claude_mpm/cli/shared/base_command.py +1 -1
  34. claude_mpm/cli/startup_logging.py +6 -4
  35. claude_mpm/config/experimental_features.py +4 -4
  36. claude_mpm/config/socketio_config.py +2 -2
  37. claude_mpm/core/agent_session_manager.py +2 -2
  38. claude_mpm/core/api_validator.py +3 -3
  39. claude_mpm/core/base_service.py +10 -1
  40. claude_mpm/core/cache.py +2 -2
  41. claude_mpm/core/config.py +4 -4
  42. claude_mpm/core/config_aliases.py +4 -4
  43. claude_mpm/core/config_constants.py +1 -1
  44. claude_mpm/core/error_handler.py +1 -1
  45. claude_mpm/core/file_utils.py +5 -5
  46. claude_mpm/core/framework/formatters/capability_generator.py +5 -5
  47. claude_mpm/core/framework/loaders/agent_loader.py +1 -1
  48. claude_mpm/core/framework/processors/metadata_processor.py +1 -1
  49. claude_mpm/core/framework/processors/template_processor.py +3 -3
  50. claude_mpm/core/framework_loader.py +2 -2
  51. claude_mpm/core/log_manager.py +4 -4
  52. claude_mpm/core/logger.py +2 -2
  53. claude_mpm/core/optimized_startup.py +1 -1
  54. claude_mpm/core/output_style_manager.py +1 -1
  55. claude_mpm/core/service_registry.py +2 -2
  56. claude_mpm/core/session_manager.py +3 -3
  57. claude_mpm/core/shared/config_loader.py +1 -1
  58. claude_mpm/core/socketio_pool.py +2 -2
  59. claude_mpm/core/unified_agent_registry.py +2 -2
  60. claude_mpm/core/unified_config.py +6 -6
  61. claude_mpm/core/unified_paths.py +2 -2
  62. claude_mpm/dashboard/api/simple_directory.py +1 -1
  63. claude_mpm/generators/agent_profile_generator.py +1 -1
  64. claude_mpm/hooks/claude_hooks/event_handlers.py +2 -2
  65. claude_mpm/hooks/claude_hooks/installer.py +9 -9
  66. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +7 -2
  67. claude_mpm/hooks/claude_hooks/tool_analysis.py +2 -2
  68. claude_mpm/hooks/memory_integration_hook.py +1 -1
  69. claude_mpm/hooks/validation_hooks.py +1 -1
  70. claude_mpm/init.py +4 -4
  71. claude_mpm/models/agent_session.py +1 -1
  72. claude_mpm/scripts/socketio_daemon.py +5 -5
  73. claude_mpm/services/__init__.py +2 -2
  74. claude_mpm/services/agent_capabilities_service.py +1 -1
  75. claude_mpm/services/agents/agent_builder.py +6 -4
  76. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +1 -1
  77. claude_mpm/services/agents/deployment/agent_metrics_collector.py +1 -1
  78. claude_mpm/services/agents/deployment/agent_record_service.py +3 -3
  79. claude_mpm/services/agents/deployment/deployment_wrapper.py +1 -1
  80. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +2 -2
  81. claude_mpm/services/agents/loading/agent_profile_loader.py +2 -2
  82. claude_mpm/services/agents/local_template_manager.py +5 -5
  83. claude_mpm/services/agents/registry/deployed_agent_discovery.py +1 -1
  84. claude_mpm/services/agents/registry/modification_tracker.py +19 -11
  85. claude_mpm/services/async_session_logger.py +1 -1
  86. claude_mpm/services/claude_session_logger.py +1 -1
  87. claude_mpm/services/cli/agent_listing_service.py +3 -3
  88. claude_mpm/services/cli/agent_validation_service.py +1 -1
  89. claude_mpm/services/cli/session_manager.py +2 -2
  90. claude_mpm/services/core/path_resolver.py +1 -1
  91. claude_mpm/services/diagnostics/checks/agent_check.py +1 -1
  92. claude_mpm/services/diagnostics/checks/claude_code_check.py +2 -2
  93. claude_mpm/services/diagnostics/checks/common_issues_check.py +3 -3
  94. claude_mpm/services/diagnostics/checks/configuration_check.py +2 -2
  95. claude_mpm/services/diagnostics/checks/installation_check.py +1 -1
  96. claude_mpm/services/diagnostics/checks/mcp_check.py +1 -1
  97. claude_mpm/services/diagnostics/checks/mcp_services_check.py +9 -9
  98. claude_mpm/services/diagnostics/checks/monitor_check.py +1 -1
  99. claude_mpm/services/diagnostics/doctor_reporter.py +1 -1
  100. claude_mpm/services/event_aggregator.py +1 -1
  101. claude_mpm/services/event_bus/event_bus.py +9 -2
  102. claude_mpm/services/events/consumers/dead_letter.py +2 -2
  103. claude_mpm/services/framework_claude_md_generator/__init__.py +1 -1
  104. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +3 -3
  105. claude_mpm/services/framework_claude_md_generator/version_manager.py +1 -1
  106. claude_mpm/services/hook_installer_service.py +7 -7
  107. claude_mpm/services/infrastructure/context_preservation.py +7 -7
  108. claude_mpm/services/infrastructure/daemon_manager.py +5 -5
  109. claude_mpm/services/mcp_config_manager.py +10 -10
  110. claude_mpm/services/mcp_gateway/auto_configure.py +5 -5
  111. claude_mpm/services/mcp_gateway/config/config_loader.py +2 -2
  112. claude_mpm/services/mcp_gateway/config/configuration.py +5 -3
  113. claude_mpm/services/mcp_gateway/core/process_pool.py +3 -3
  114. claude_mpm/services/mcp_gateway/core/singleton_manager.py +2 -2
  115. claude_mpm/services/mcp_gateway/core/startup_verification.py +1 -1
  116. claude_mpm/services/mcp_gateway/main.py +1 -1
  117. claude_mpm/services/mcp_gateway/registry/service_registry.py +4 -2
  118. claude_mpm/services/mcp_gateway/registry/tool_registry.py +2 -1
  119. claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
  120. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +1 -1
  121. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +1 -1
  122. claude_mpm/services/mcp_gateway/tools/hello_world.py +1 -1
  123. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +5 -5
  124. claude_mpm/services/mcp_gateway/utils/update_preferences.py +2 -2
  125. claude_mpm/services/mcp_service_verifier.py +1 -1
  126. claude_mpm/services/memory/builder.py +1 -1
  127. claude_mpm/services/memory/cache/shared_prompt_cache.py +2 -1
  128. claude_mpm/services/memory/indexed_memory.py +3 -3
  129. claude_mpm/services/monitor/daemon.py +1 -1
  130. claude_mpm/services/monitor/daemon_manager.py +9 -9
  131. claude_mpm/services/monitor/handlers/file.py +1 -1
  132. claude_mpm/services/monitor/handlers/hooks.py +3 -3
  133. claude_mpm/services/monitor/management/lifecycle.py +7 -7
  134. claude_mpm/services/monitor/server.py +2 -2
  135. claude_mpm/services/orphan_detection.py +11 -16
  136. claude_mpm/services/port_manager.py +2 -2
  137. claude_mpm/services/project/analyzer.py +3 -3
  138. claude_mpm/services/project/archive_manager.py +17 -13
  139. claude_mpm/services/project/dependency_analyzer.py +4 -4
  140. claude_mpm/services/project/documentation_manager.py +4 -4
  141. claude_mpm/services/project/enhanced_analyzer.py +19 -8
  142. claude_mpm/services/project/registry.py +4 -4
  143. claude_mpm/services/project_port_allocator.py +7 -12
  144. claude_mpm/services/session_management_service.py +1 -1
  145. claude_mpm/services/socketio/event_normalizer.py +1 -1
  146. claude_mpm/services/socketio/handlers/code_analysis.py +14 -12
  147. claude_mpm/services/socketio/handlers/file.py +1 -1
  148. claude_mpm/services/socketio/migration_utils.py +1 -1
  149. claude_mpm/services/socketio/server/core.py +1 -1
  150. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +1 -1
  151. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +4 -4
  152. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +1 -1
  153. claude_mpm/services/unified/config_strategies/config_schema.py +4 -4
  154. claude_mpm/services/unified/config_strategies/context_strategy.py +8 -6
  155. claude_mpm/services/unified/config_strategies/error_handling_strategy.py +10 -10
  156. claude_mpm/services/unified/config_strategies/file_loader_strategy.py +5 -5
  157. claude_mpm/services/unified/config_strategies/unified_config_service.py +8 -8
  158. claude_mpm/services/unified/config_strategies/validation_strategy.py +15 -15
  159. claude_mpm/services/unified/deployment_strategies/base.py +4 -4
  160. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +15 -15
  161. claude_mpm/services/unified/deployment_strategies/local.py +11 -11
  162. claude_mpm/services/unified/deployment_strategies/utils.py +11 -9
  163. claude_mpm/services/unified/deployment_strategies/vercel.py +7 -9
  164. claude_mpm/services/unified/unified_config.py +5 -5
  165. claude_mpm/services/unified/unified_deployment.py +2 -2
  166. claude_mpm/services/utility_service.py +1 -1
  167. claude_mpm/services/version_control/conflict_resolution.py +2 -2
  168. claude_mpm/services/version_control/git_operations.py +3 -3
  169. claude_mpm/services/version_control/semantic_versioning.py +13 -13
  170. claude_mpm/services/version_control/version_parser.py +1 -1
  171. claude_mpm/storage/state_storage.py +12 -13
  172. claude_mpm/tools/code_tree_analyzer.py +5 -5
  173. claude_mpm/tools/code_tree_builder.py +4 -4
  174. claude_mpm/tools/socketio_debug.py +1 -1
  175. claude_mpm/utils/agent_dependency_loader.py +4 -4
  176. claude_mpm/utils/common.py +2 -2
  177. claude_mpm/utils/config_manager.py +3 -3
  178. claude_mpm/utils/dependency_cache.py +2 -2
  179. claude_mpm/utils/dependency_strategies.py +6 -6
  180. claude_mpm/utils/file_utils.py +11 -11
  181. claude_mpm/utils/log_cleanup.py +1 -1
  182. claude_mpm/utils/path_operations.py +1 -1
  183. claude_mpm/validation/agent_validator.py +2 -2
  184. claude_mpm/validation/frontmatter_validator.py +1 -1
  185. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/METADATA +1 -1
  186. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/RECORD +190 -190
  187. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/WHEEL +0 -0
  188. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/entry_points.txt +0 -0
  189. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/licenses/LICENSE +0 -0
  190. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/top_level.txt +0 -0
@@ -95,7 +95,7 @@ class DeploymentManager:
95
95
 
96
96
  # Check if file exists and compare versions
97
97
  if target_file.exists() and not force:
98
- with open(target_file) as f:
98
+ with target_file.open() as f:
99
99
  existing_content = f.read()
100
100
  existing_fw_ver = self.version_manager.parse_current_version(
101
101
  existing_content
@@ -110,7 +110,7 @@ class DeploymentManager:
110
110
  parent_path.mkdir(parents=True, exist_ok=True)
111
111
 
112
112
  # Write content
113
- with open(target_file, "w") as f:
113
+ with target_file.open("w") as f:
114
114
  f.write(content)
115
115
 
116
116
  # Get version info for success message
@@ -137,7 +137,7 @@ class DeploymentManager:
137
137
  return True, f"{self.target_filename} does not exist"
138
138
 
139
139
  try:
140
- with open(target_file) as f:
140
+ with target_file.open() as f:
141
141
  existing_content = f.read()
142
142
  existing_fw_ver = self.version_manager.parse_current_version(
143
143
  existing_content
@@ -38,7 +38,7 @@ class VersionManager:
38
38
  version_path = package_path.parent / "framework" / "VERSION"
39
39
 
40
40
  if version_path.exists():
41
- with open(version_path) as f:
41
+ with version_path.open() as f:
42
42
  version_content = f.read().strip()
43
43
  # Framework VERSION file contains just the framework version number
44
44
  try:
@@ -24,7 +24,7 @@ class HookInstallerService:
24
24
  self.claude_dir = Path.home() / ".claude"
25
25
  self.settings_file = self.claude_dir / "settings.json"
26
26
 
27
- def is_hooks_configured(self) -> bool: # noqa: PLR0911
27
+ def is_hooks_configured(self) -> bool:
28
28
  """Check if hooks are configured in Claude settings.
29
29
 
30
30
  Returns:
@@ -35,7 +35,7 @@ class HookInstallerService:
35
35
  self.logger.debug("Claude settings file does not exist")
36
36
  return False
37
37
 
38
- with open(self.settings_file) as f:
38
+ with self.settings_file.open() as f:
39
39
  settings = json.load(f)
40
40
 
41
41
  # Check if hooks section exists
@@ -292,7 +292,7 @@ class HookInstallerService:
292
292
 
293
293
  # Load existing settings or create new
294
294
  if self.settings_file.exists():
295
- with open(self.settings_file) as f:
295
+ with self.settings_file.open() as f:
296
296
  settings = json.load(f)
297
297
  self.logger.debug("Found existing Claude settings")
298
298
  else:
@@ -320,7 +320,7 @@ class HookInstallerService:
320
320
  settings["hooks"][event_type] = [hook_config]
321
321
 
322
322
  # Write settings
323
- with open(self.settings_file, "w") as f:
323
+ with self.settings_file.open("w") as f:
324
324
  json.dump(settings, f, indent=2)
325
325
 
326
326
  self.logger.info(f"Updated Claude settings at: {self.settings_file}")
@@ -380,7 +380,7 @@ class HookInstallerService:
380
380
 
381
381
  self.logger.info("Removing Claude Code hooks...")
382
382
 
383
- with open(self.settings_file) as f:
383
+ with self.settings_file.open() as f:
384
384
  settings = json.load(f)
385
385
 
386
386
  hooks_removed = 0
@@ -422,7 +422,7 @@ class HookInstallerService:
422
422
  del settings["hooks"]
423
423
 
424
424
  # Write updated settings
425
- with open(self.settings_file, "w") as f:
425
+ with self.settings_file.open("w") as f:
426
426
  json.dump(settings, f, indent=2)
427
427
 
428
428
  if hooks_removed > 0:
@@ -469,7 +469,7 @@ class HookInstallerService:
469
469
 
470
470
  try:
471
471
  if self.settings_file.exists():
472
- with open(self.settings_file) as f:
472
+ with self.settings_file.open() as f:
473
473
  settings = json.load(f)
474
474
 
475
475
  if "hooks" in settings:
@@ -151,7 +151,7 @@ class ContextPreservationService(BaseService):
151
151
  return None
152
152
 
153
153
  # Use streaming to find active conversation
154
- with open(self.claude_json_path, "rb") as f:
154
+ with self.claude_json_path.open("rb") as f:
155
155
  parser = ijson.parse(f)
156
156
 
157
157
  active_conv_id = None
@@ -207,7 +207,7 @@ class ContextPreservationService(BaseService):
207
207
  keep_recent_days * 86400
208
208
  )
209
209
 
210
- with open(self.claude_json_path) as f:
210
+ with self.claude_json_path.open() as f:
211
211
  data = json.load(f)
212
212
 
213
213
  original_count = len(data.get("conversations", []))
@@ -239,7 +239,7 @@ class ContextPreservationService(BaseService):
239
239
 
240
240
  # Write compressed version
241
241
  temp_path = self.claude_json_path.with_suffix(".tmp")
242
- with open(temp_path, "w") as f:
242
+ with temp_path.open("w") as f:
243
243
  json.dump(data, f, separators=(",", ":")) # Compact format
244
244
 
245
245
  # Replace original
@@ -316,7 +316,7 @@ class ContextPreservationService(BaseService):
316
316
  # Use streaming to extract preferences
317
317
  preferences = {}
318
318
 
319
- with open(self.claude_json_path, "rb") as f:
319
+ with self.claude_json_path.open("rb") as f:
320
320
  parser = ijson.parse(f)
321
321
 
322
322
  for prefix, event, value in parser:
@@ -339,7 +339,7 @@ class ContextPreservationService(BaseService):
339
339
  ) -> ConversationState:
340
340
  """Parse Claude JSON using standard JSON parser."""
341
341
  try:
342
- with open(self.claude_json_path) as f:
342
+ with self.claude_json_path.open() as f:
343
343
  data = json.load(f)
344
344
 
345
345
  return await self._extract_conversation_state(data, extract_full)
@@ -357,7 +357,7 @@ class ContextPreservationService(BaseService):
357
357
  preferences = {}
358
358
  open_files = []
359
359
 
360
- with open(self.claude_json_path, "rb") as f:
360
+ with self.claude_json_path.open("rb") as f:
361
361
  parser = ijson.parse(f)
362
362
 
363
363
  conversation_count = 0
@@ -527,7 +527,7 @@ class ContextPreservationService(BaseService):
527
527
  backup_name += ".gz"
528
528
  backup_path = self.claude_backup_dir / backup_name
529
529
 
530
- with open(self.claude_json_path, "rb") as f_in:
530
+ with self.claude_json_path.open("rb") as f_in:
531
531
  with gzip.open(backup_path, "wb") as f_out:
532
532
  shutil.copyfileobj(f_in, f_out)
533
533
  else:
@@ -80,7 +80,7 @@ class SocketIODaemonManager:
80
80
  return False
81
81
 
82
82
  try:
83
- with open(self.pid_file) as f:
83
+ with self.pid_file.open() as f:
84
84
  pid = int(f.read().strip())
85
85
 
86
86
  # Check if process exists and is running
@@ -135,7 +135,7 @@ class SocketIODaemonManager:
135
135
  pid = os.fork()
136
136
  if pid > 0:
137
137
  # Parent process - save PID and exit
138
- with open(self.pid_file, "w") as f:
138
+ with self.pid_file.open("w") as f:
139
139
  f.write(str(pid))
140
140
  logger.info(f"Socket.IO server started as daemon (PID: {pid})")
141
141
  return True
@@ -155,7 +155,7 @@ class SocketIODaemonManager:
155
155
  os.umask(0)
156
156
 
157
157
  # Redirect stdout/stderr to log file
158
- with open(self.log_file, "a") as log:
158
+ with self.log_file.open("a") as log:
159
159
  os.dup2(log.fileno(), sys.stdout.fileno())
160
160
  os.dup2(log.fileno(), sys.stderr.fileno())
161
161
 
@@ -199,7 +199,7 @@ class SocketIODaemonManager:
199
199
  return False
200
200
 
201
201
  try:
202
- with open(self.pid_file) as f:
202
+ with self.pid_file.open() as f:
203
203
  pid = int(f.read().strip())
204
204
 
205
205
  logger.info(f"Stopping Socket.IO server (PID: {pid})")
@@ -254,7 +254,7 @@ class SocketIODaemonManager:
254
254
  }
255
255
 
256
256
  if status_info["running"]:
257
- with open(self.pid_file) as f:
257
+ with self.pid_file.open() as f:
258
258
  status_info["pid"] = int(f.read().strip())
259
259
 
260
260
  # Check port accessibility
@@ -199,7 +199,7 @@ class MCPConfigManager:
199
199
  f"Found kuzu-memory with MCP support at {path}"
200
200
  )
201
201
  return path
202
- except:
202
+ except (subprocess.SubprocessError, subprocess.TimeoutExpired, OSError):
203
203
  pass
204
204
 
205
205
  # If no MCP-capable version found, log warning but return None
@@ -571,7 +571,7 @@ class MCPConfigManager:
571
571
  if result.returncode == 0 or "version" in result.stdout.lower():
572
572
  use_pipx_run = True
573
573
  self.logger.debug(f"Will use 'pipx run' for {service_name}")
574
- except:
574
+ except (subprocess.SubprocessError, subprocess.TimeoutExpired, OSError):
575
575
  pass
576
576
 
577
577
  # Try uvx if pipx run not available
@@ -587,7 +587,7 @@ class MCPConfigManager:
587
587
  if result.returncode == 0 or "version" in result.stdout.lower():
588
588
  use_uvx = True
589
589
  self.logger.debug(f"Will use 'uvx' for {service_name}")
590
- except:
590
+ except (subprocess.SubprocessError, subprocess.TimeoutExpired, OSError):
591
591
  pass
592
592
 
593
593
  # If neither work, try to find direct path
@@ -719,7 +719,7 @@ class MCPConfigManager:
719
719
  claude_config = {}
720
720
  if self.claude_config_path.exists():
721
721
  try:
722
- with open(self.claude_config_path) as f:
722
+ with self.claude_config_path.open() as f:
723
723
  claude_config = json.load(f)
724
724
  except Exception as e:
725
725
  self.logger.error(f"Error reading {self.claude_config_path}: {e}")
@@ -832,7 +832,7 @@ class MCPConfigManager:
832
832
  self.logger.debug(f"Created backup: {backup_path}")
833
833
 
834
834
  # Write updated config
835
- with open(self.claude_config_path, "w") as f:
835
+ with self.claude_config_path.open("w") as f:
836
836
  json.dump(claude_config, f, indent=2)
837
837
 
838
838
  messages = []
@@ -884,7 +884,7 @@ class MCPConfigManager:
884
884
  existing_config = {}
885
885
  if mcp_config_path.exists():
886
886
  try:
887
- with open(mcp_config_path) as f:
887
+ with mcp_config_path.open() as f:
888
888
  existing_config = json.load(f)
889
889
  except Exception as e:
890
890
  self.logger.error(f"Error reading existing config: {e}")
@@ -912,7 +912,7 @@ class MCPConfigManager:
912
912
 
913
913
  # Write the updated configuration
914
914
  try:
915
- with open(mcp_config_path, "w") as f:
915
+ with mcp_config_path.open("w") as f:
916
916
  json.dump(new_config, f, indent=2)
917
917
 
918
918
  if missing_services:
@@ -937,7 +937,7 @@ class MCPConfigManager:
937
937
  mcp_config_path = self.project_root / ConfigLocation.PROJECT_MCP.value
938
938
  if mcp_config_path.exists():
939
939
  try:
940
- with open(mcp_config_path) as f:
940
+ with mcp_config_path.open() as f:
941
941
  config = json.load(f)
942
942
  results = {}
943
943
  for service_name, service_config in config.get(
@@ -951,7 +951,7 @@ class MCPConfigManager:
951
951
  return {}
952
952
 
953
953
  try:
954
- with open(self.claude_config_path) as f:
954
+ with self.claude_config_path.open() as f:
955
955
  claude_config = json.load(f)
956
956
 
957
957
  # Get project's MCP servers
@@ -1638,7 +1638,7 @@ class MCPConfigManager:
1638
1638
  if result.returncode == 0 or "version" in result.stdout.lower():
1639
1639
  self.logger.debug(f"{service_name} accessible via 'pipx run'")
1640
1640
  return True
1641
- except:
1641
+ except (subprocess.SubprocessError, subprocess.TimeoutExpired, OSError):
1642
1642
  pass
1643
1643
  return False
1644
1644
 
@@ -78,7 +78,7 @@ class MCPAutoConfigurator:
78
78
  return False
79
79
 
80
80
  try:
81
- with open(self.claude_config_path) as f:
81
+ with self.claude_config_path.open() as f:
82
82
  config = json.load(f)
83
83
 
84
84
  # Check if claude-mpm-gateway is configured
@@ -131,7 +131,7 @@ class MCPAutoConfigurator:
131
131
  return False
132
132
 
133
133
  try:
134
- with open(self.preference_file) as f:
134
+ with self.preference_file.open() as f:
135
135
  prefs = json.load(f)
136
136
  return prefs.get("asked", False)
137
137
  except (OSError, json.JSONDecodeError):
@@ -148,7 +148,7 @@ class MCPAutoConfigurator:
148
148
  }
149
149
 
150
150
  try:
151
- with open(self.preference_file, "w") as f:
151
+ with self.preference_file.open("w") as f:
152
152
  json.dump(prefs, f, indent=2)
153
153
  except Exception as e:
154
154
  self.logger.debug(f"Could not save preference: {e}")
@@ -244,7 +244,7 @@ class MCPAutoConfigurator:
244
244
  }
245
245
 
246
246
  # Save configuration
247
- with open(self.claude_config_path, "w") as f:
247
+ with self.claude_config_path.open("w") as f:
248
248
  json.dump(config, f, indent=2)
249
249
 
250
250
  print(f"✅ Configuration saved to: {self.claude_config_path}")
@@ -284,7 +284,7 @@ class MCPAutoConfigurator:
284
284
  """Load existing config or create new one."""
285
285
  if self.claude_config_path.exists():
286
286
  try:
287
- with open(self.claude_config_path) as f:
287
+ with self.claude_config_path.open() as f:
288
288
  return json.load(f)
289
289
  except json.JSONDecodeError:
290
290
  self.logger.warning("Existing config is invalid JSON, creating new")
@@ -97,7 +97,7 @@ class MCPConfigLoader:
97
97
  self.logger.error(f"Configuration file not found: {expanded_path}")
98
98
  return None
99
99
 
100
- with open(expanded_path) as f:
100
+ with expanded_path.open() as f:
101
101
  config = yaml.safe_load(f)
102
102
 
103
103
  self.logger.info(f"Configuration loaded from {expanded_path}")
@@ -271,7 +271,7 @@ class MCPConfigLoader:
271
271
  expanded_path = path.expanduser()
272
272
  expanded_path.parent.mkdir(parents=True, exist_ok=True)
273
273
 
274
- with open(expanded_path, "w") as f:
274
+ with expanded_path.open("w") as f:
275
275
  yaml.dump(
276
276
  MCPConfiguration.DEFAULT_CONFIG,
277
277
  f,
@@ -155,7 +155,7 @@ class MCPConfiguration(BaseMCPService, IMCPConfiguration):
155
155
  self.log_warning(f"Configuration file not found: {config_path}")
156
156
  return True # Not an error, use defaults
157
157
 
158
- with open(config_path) as f:
158
+ with config_path.open() as f:
159
159
  if config_path.suffix in [".yaml", ".yml"]:
160
160
  loaded_config = yaml.safe_load(f) or {}
161
161
  else:
@@ -171,7 +171,9 @@ class MCPConfiguration(BaseMCPService, IMCPConfiguration):
171
171
  return True
172
172
 
173
173
  except yaml.YAMLError as e:
174
- raise MCPConfigurationError(f"Failed to parse YAML configuration: {e}")
174
+ raise MCPConfigurationError(
175
+ f"Failed to parse YAML configuration: {e}"
176
+ ) from e
175
177
  except Exception as e:
176
178
  self.log_error(f"Failed to load configuration: {e}")
177
179
  return False
@@ -364,7 +366,7 @@ class MCPConfiguration(BaseMCPService, IMCPConfiguration):
364
366
  save_path = Path(save_path).expanduser()
365
367
  save_path.parent.mkdir(parents=True, exist_ok=True)
366
368
 
367
- with open(save_path, "w") as f:
369
+ with save_path.open("w") as f:
368
370
  yaml.dump(
369
371
  self._config_data, f, default_flow_style=False, sort_keys=True
370
372
  )
@@ -190,7 +190,7 @@ class MCPProcessPool:
190
190
 
191
191
  # Write process info to file for debugging
192
192
  info_file = self.pool_dir / f"{server_name}_{process.pid}.json"
193
- with open(info_file, "w") as f:
193
+ with info_file.open("w") as f:
194
194
  json.dump(self._process_info[server_name], f, indent=2)
195
195
 
196
196
  return process
@@ -934,7 +934,7 @@ async def pre_warm_mcp_servers():
934
934
  #
935
935
  # if claude_config_path.exists():
936
936
  # try:
937
- # with open(claude_config_path) as f:
937
+ # with claude_config_path.open() as f:
938
938
  # config_data = json.load(f)
939
939
  # mcp_servers = config_data.get("mcpServers", {})
940
940
  # configs.update(mcp_servers)
@@ -945,7 +945,7 @@ async def pre_warm_mcp_servers():
945
945
  # mcp_config_path = Path.cwd() / ".mcp.json"
946
946
  # if mcp_config_path.exists():
947
947
  # try:
948
- # with open(mcp_config_path) as f:
948
+ # with mcp_config_path.open() as f:
949
949
  # config_data = json.load(f)
950
950
  # mcp_servers = config_data.get("mcpServers", {})
951
951
  # configs.update(mcp_servers)
@@ -149,7 +149,7 @@ class MCPGatewayManager:
149
149
  return None
150
150
 
151
151
  try:
152
- with open(self.instance_file) as f:
152
+ with self.instance_file.open() as f:
153
153
  instance_info = json.load(f)
154
154
 
155
155
  # Validate PID is still running
@@ -195,7 +195,7 @@ class MCPGatewayManager:
195
195
  "lock_file": str(self.lock_file),
196
196
  }
197
197
 
198
- with open(self.instance_file, "w") as f:
198
+ with self.instance_file.open("w") as f:
199
199
  json.dump(instance_info, f, indent=2)
200
200
 
201
201
  self._current_instance = instance_info
@@ -125,7 +125,7 @@ class MCPGatewayStartupVerifier:
125
125
  try:
126
126
  import json
127
127
 
128
- with open(self.config_file, "w") as f:
128
+ with self.config_file.open("w") as f:
129
129
  json.dump(default_config, f, indent=2)
130
130
 
131
131
  self.logger.info(
@@ -63,7 +63,7 @@ except ImportError:
63
63
  try:
64
64
  from claude_mpm.services.mcp_gateway.server.mcp_gateway import MCPGateway
65
65
  except ImportError as e:
66
- raise ImportError(f"Critical: Cannot import MCPGateway server: {e}")
66
+ raise ImportError(f"Critical: Cannot import MCPGateway server: {e}") from e
67
67
 
68
68
  try:
69
69
  from claude_mpm.services.mcp_gateway.server.stdio_handler import StdioHandler
@@ -224,7 +224,8 @@ class MCPServiceRegistry(ManagerBase):
224
224
  try:
225
225
  import asyncio
226
226
 
227
- asyncio.create_task(instance.stop())
227
+ _task = asyncio.create_task(instance.stop()) # noqa: RUF006
228
+ # Fire-and-forget shutdown during unregister
228
229
  except Exception as e:
229
230
  self.logger.warning(
230
231
  f"Error stopping service {interface.__name__}: {e}"
@@ -335,7 +336,8 @@ class MCPServiceRegistry(ManagerBase):
335
336
  try:
336
337
  import asyncio
337
338
 
338
- asyncio.create_task(instance.stop())
339
+ _task = asyncio.create_task(instance.stop()) # noqa: RUF006
340
+ # Fire-and-forget shutdown during clear
339
341
  except Exception as e:
340
342
  self.logger.warning(f"Error stopping service: {e}")
341
343
 
@@ -216,7 +216,8 @@ class ToolRegistry(BaseMCPService, IMCPToolRegistry):
216
216
 
217
217
  # Shutdown adapter (outside lock to avoid deadlock)
218
218
  try:
219
- asyncio.create_task(adapter.shutdown())
219
+ _task = asyncio.create_task(adapter.shutdown()) # noqa: RUF006
220
+ # Fire-and-forget shutdown during tool unregister
220
221
  except Exception as e:
221
222
  self.log_warning(f"Error shutting down tool adapter {tool_name}: {e}")
222
223
 
@@ -139,7 +139,7 @@ class StdioHandler(BaseMCPService, IMCPCommunication):
139
139
  self._metrics["errors"] += 1
140
140
  raise
141
141
 
142
- async def receive_message(self) -> Optional[Dict[str, Any]]: # noqa: PLR0911
142
+ async def receive_message(self) -> Optional[Dict[str, Any]]:
143
143
  """
144
144
  Receive a message from the MCP client via stdin.
145
145
 
@@ -306,7 +306,7 @@ class DocumentSummarizerTool(BaseToolAdapter):
306
306
  continue
307
307
 
308
308
  # If all fail, read as binary and decode with errors='ignore'
309
- with open(file_path, "rb") as f:
309
+ with file_path.open("rb") as f:
310
310
  content = f.read()
311
311
  return content.decode("utf-8", errors="ignore")
312
312
 
@@ -383,7 +383,7 @@ class HealthCheckTool(BaseToolAdapter):
383
383
  # Try to load configuration
384
384
  import json
385
385
 
386
- with open(config_file) as f:
386
+ with config_file.open() as f:
387
387
  config_data = json.load(f)
388
388
 
389
389
  check_result["checks"]["config_valid"] = True
@@ -204,7 +204,7 @@ class HelloWorldTool(BaseToolAdapter):
204
204
  self.greeting_history: List[Dict[str, Any]] = []
205
205
  self.max_history_size = 100
206
206
 
207
- def validate_parameters(self, parameters: Dict[str, Any]) -> bool: # noqa: PLR0911
207
+ def validate_parameters(self, parameters: Dict[str, Any]) -> bool:
208
208
  """
209
209
  Enhanced parameter validation with detailed error messages.
210
210
 
@@ -8,7 +8,7 @@ Provides non-blocking version checking for MCP tools like kuzu-memory.
8
8
 
9
9
  import asyncio
10
10
  import json
11
- from datetime import datetime, timedelta
11
+ from datetime import datetime, timedelta, timezone
12
12
  from pathlib import Path
13
13
  from typing import Any, Dict, Optional
14
14
 
@@ -83,7 +83,7 @@ class PackageVersionChecker:
83
83
  "latest": latest,
84
84
  "update_available": version.parse(latest)
85
85
  > version.parse(current_version),
86
- "checked_at": datetime.now().isoformat(),
86
+ "checked_at": datetime.now(timezone.utc).isoformat(),
87
87
  }
88
88
  self._write_cache(cache_file, result)
89
89
  return result
@@ -133,12 +133,12 @@ class PackageVersionChecker:
133
133
  return None
134
134
 
135
135
  try:
136
- with open(cache_file) as f:
136
+ with cache_file.open() as f:
137
137
  data = json.load(f)
138
138
 
139
139
  # Check TTL
140
140
  checked_at = datetime.fromisoformat(data["checked_at"])
141
- if datetime.now() - checked_at < timedelta(seconds=ttl):
141
+ if datetime.now(timezone.utc) - checked_at < timedelta(seconds=ttl):
142
142
  return data
143
143
  except Exception as e:
144
144
  self.logger.debug(f"Cache read error: {e}")
@@ -154,7 +154,7 @@ class PackageVersionChecker:
154
154
  data: Data to cache
155
155
  """
156
156
  try:
157
- with open(cache_file, "w") as f:
157
+ with cache_file.open("w") as f:
158
158
  json.dump(data, f, indent=2)
159
159
  except Exception as e:
160
160
  self.logger.debug(f"Cache write failed: {e}")
@@ -36,7 +36,7 @@ class UpdatePreferences:
36
36
  """
37
37
  if cls.PREFS_FILE.exists():
38
38
  try:
39
- with open(cls.PREFS_FILE) as f:
39
+ with cls.PREFS_FILE.open() as f:
40
40
  return json.load(f)
41
41
  except (OSError, json.JSONDecodeError):
42
42
  # Return empty dict if file is corrupted or unreadable
@@ -53,7 +53,7 @@ class UpdatePreferences:
53
53
  """
54
54
  cls.PREFS_FILE.parent.mkdir(parents=True, exist_ok=True)
55
55
  try:
56
- with open(cls.PREFS_FILE, "w") as f:
56
+ with cls.PREFS_FILE.open("w") as f:
57
57
  json.dump(prefs, f, indent=2)
58
58
  except OSError:
59
59
  # Silently fail if we can't write preferences
@@ -429,7 +429,7 @@ class MCPServiceVerifier:
429
429
  return {"configured": False, "correct": False}
430
430
 
431
431
  try:
432
- with open(self.claude_config_path) as f:
432
+ with self.claude_config_path.open() as f:
433
433
  config = json.load(f)
434
434
 
435
435
  # Check if project is configured
@@ -458,7 +458,7 @@ class MemoryBuilder(LoggerMixin):
458
458
  import tomllib
459
459
  except ImportError:
460
460
  import tomli as tomllib
461
- with open(file_path, "rb") as f:
461
+ with file_path.open("rb") as f:
462
462
  config_data = tomllib.load(f)
463
463
  items = self._extract_from_toml_config(config_data, source)
464
464
  extracted_items.extend(items)
@@ -194,7 +194,8 @@ class SharedPromptCache(BaseService):
194
194
  with cls._lock:
195
195
  if cls._instance is not None:
196
196
  if cls._instance.running:
197
- asyncio.create_task(cls._instance.stop())
197
+ _task = asyncio.create_task(cls._instance.stop()) # noqa: RUF006
198
+ # Fire-and-forget cleanup task during test reset
198
199
  cls._instance = None
199
200
 
200
201
  async def _initialize(self) -> None:
@@ -213,7 +213,7 @@ class InvertedIndex:
213
213
  "doc_freqs": dict(self.doc_freqs),
214
214
  "doc_count": self.doc_count,
215
215
  }
216
- with open(path, "wb") as f:
216
+ with path.open("wb") as f:
217
217
  pickle.dump(data, f)
218
218
 
219
219
  def load(self, path: Path):
@@ -221,7 +221,7 @@ class InvertedIndex:
221
221
  if not path.exists():
222
222
  return
223
223
 
224
- with open(path, "rb") as f:
224
+ with path.open("rb") as f:
225
225
  data = pickle.load(f)
226
226
 
227
227
  self.index = defaultdict(set, {k: set(v) for k, v in data["index"].items()})
@@ -597,7 +597,7 @@ class IndexedMemoryService:
597
597
  # Load other indexes
598
598
  indexes_path = self.data_dir / "indexes.pkl"
599
599
  if indexes_path.exists():
600
- with open(indexes_path, "rb") as f:
600
+ with indexes_path.open("rb") as f:
601
601
  data = pickle.load(f)
602
602
 
603
603
  self.memories = data.get("memories", {})
@@ -121,7 +121,7 @@ class UnifiedMonitorDaemon:
121
121
  """
122
122
  return self.daemon_manager.cleanup_port_conflicts()
123
123
 
124
- def _start_daemon(self, force_restart: bool = False) -> bool: # noqa: PLR0911
124
+ def _start_daemon(self, force_restart: bool = False) -> bool:
125
125
  """Start as background daemon process.
126
126
 
127
127
  Args: