claude-mpm 5.0.2__py3-none-any.whl → 5.4.3__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 (184) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +2002 -0
  3. claude_mpm/agents/PM_INSTRUCTIONS.md +1218 -905
  4. claude_mpm/agents/agent_loader.py +10 -17
  5. claude_mpm/agents/base_agent_loader.py +10 -35
  6. claude_mpm/agents/frontmatter_validator.py +68 -0
  7. claude_mpm/agents/templates/circuit-breakers.md +431 -45
  8. claude_mpm/cli/__init__.py +0 -1
  9. claude_mpm/cli/commands/__init__.py +2 -0
  10. claude_mpm/cli/commands/agent_state_manager.py +67 -23
  11. claude_mpm/cli/commands/agents.py +446 -25
  12. claude_mpm/cli/commands/auto_configure.py +535 -233
  13. claude_mpm/cli/commands/configure.py +1500 -147
  14. claude_mpm/cli/commands/configure_agent_display.py +13 -6
  15. claude_mpm/cli/commands/mpm_init/core.py +158 -1
  16. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  17. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  18. claude_mpm/cli/commands/postmortem.py +401 -0
  19. claude_mpm/cli/commands/run.py +1 -39
  20. claude_mpm/cli/commands/skills.py +322 -19
  21. claude_mpm/cli/commands/summarize.py +413 -0
  22. claude_mpm/cli/executor.py +8 -0
  23. claude_mpm/cli/interactive/agent_wizard.py +302 -195
  24. claude_mpm/cli/parsers/agents_parser.py +137 -0
  25. claude_mpm/cli/parsers/auto_configure_parser.py +13 -0
  26. claude_mpm/cli/parsers/base_parser.py +9 -0
  27. claude_mpm/cli/parsers/skills_parser.py +7 -0
  28. claude_mpm/cli/startup.py +133 -85
  29. claude_mpm/commands/mpm-agents-auto-configure.md +2 -2
  30. claude_mpm/commands/mpm-agents-list.md +2 -2
  31. claude_mpm/commands/mpm-config-view.md +2 -2
  32. claude_mpm/commands/mpm-help.md +3 -0
  33. claude_mpm/commands/{mpm-ticket-organize.md → mpm-organize.md} +4 -5
  34. claude_mpm/commands/mpm-postmortem.md +123 -0
  35. claude_mpm/commands/mpm-session-resume.md +2 -2
  36. claude_mpm/commands/mpm-ticket-view.md +2 -2
  37. claude_mpm/config/agent_presets.py +312 -82
  38. claude_mpm/config/agent_sources.py +27 -0
  39. claude_mpm/config/skill_presets.py +392 -0
  40. claude_mpm/constants.py +1 -0
  41. claude_mpm/core/claude_runner.py +2 -25
  42. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  43. claude_mpm/core/framework/loaders/file_loader.py +54 -101
  44. claude_mpm/core/interactive_session.py +19 -5
  45. claude_mpm/core/oneshot_session.py +16 -4
  46. claude_mpm/core/output_style_manager.py +173 -43
  47. claude_mpm/core/protocols/__init__.py +23 -0
  48. claude_mpm/core/protocols/runner_protocol.py +103 -0
  49. claude_mpm/core/protocols/session_protocol.py +131 -0
  50. claude_mpm/core/shared/singleton_manager.py +11 -4
  51. claude_mpm/core/socketio_pool.py +3 -3
  52. claude_mpm/core/system_context.py +38 -0
  53. claude_mpm/core/unified_agent_registry.py +134 -16
  54. claude_mpm/core/unified_config.py +22 -0
  55. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  56. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-313.pyc +0 -0
  57. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  58. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  59. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  60. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  61. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  62. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  63. claude_mpm/hooks/claude_hooks/event_handlers.py +35 -2
  64. claude_mpm/hooks/claude_hooks/hook_handler.py +4 -0
  65. claude_mpm/hooks/claude_hooks/memory_integration.py +12 -1
  66. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  67. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  68. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  69. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  70. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  71. claude_mpm/hooks/claude_hooks/services/connection_manager.py +4 -0
  72. claude_mpm/models/agent_definition.py +7 -0
  73. claude_mpm/scripts/launch_monitor.py +93 -13
  74. claude_mpm/services/agents/agent_recommendation_service.py +279 -0
  75. claude_mpm/services/agents/cache_git_manager.py +621 -0
  76. claude_mpm/services/agents/deployment/agent_template_builder.py +3 -2
  77. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +110 -3
  78. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +518 -55
  79. claude_mpm/services/agents/git_source_manager.py +20 -0
  80. claude_mpm/services/agents/sources/git_source_sync_service.py +45 -6
  81. claude_mpm/services/agents/toolchain_detector.py +6 -5
  82. claude_mpm/services/analysis/__init__.py +35 -0
  83. claude_mpm/services/analysis/clone_detector.py +1030 -0
  84. claude_mpm/services/analysis/postmortem_reporter.py +474 -0
  85. claude_mpm/services/analysis/postmortem_service.py +765 -0
  86. claude_mpm/services/command_deployment_service.py +106 -5
  87. claude_mpm/services/core/base.py +7 -2
  88. claude_mpm/services/diagnostics/checks/mcp_services_check.py +7 -15
  89. claude_mpm/services/event_bus/config.py +3 -1
  90. claude_mpm/services/git/git_operations_service.py +8 -8
  91. claude_mpm/services/mcp_config_manager.py +75 -145
  92. claude_mpm/services/mcp_service_verifier.py +6 -3
  93. claude_mpm/services/monitor/daemon.py +37 -10
  94. claude_mpm/services/monitor/daemon_manager.py +134 -21
  95. claude_mpm/services/monitor/server.py +225 -19
  96. claude_mpm/services/project/project_organizer.py +4 -0
  97. claude_mpm/services/runner_configuration_service.py +16 -3
  98. claude_mpm/services/session_management_service.py +16 -4
  99. claude_mpm/services/socketio/event_normalizer.py +15 -1
  100. claude_mpm/services/socketio/server/core.py +160 -21
  101. claude_mpm/services/version_control/git_operations.py +103 -0
  102. claude_mpm/utils/agent_filters.py +261 -0
  103. claude_mpm/utils/gitignore.py +3 -0
  104. claude_mpm/utils/migration.py +372 -0
  105. claude_mpm/utils/progress.py +5 -1
  106. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/METADATA +69 -84
  107. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/RECORD +112 -153
  108. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/entry_points.txt +0 -2
  109. claude_mpm/dashboard/analysis_runner.py +0 -455
  110. claude_mpm/dashboard/index.html +0 -13
  111. claude_mpm/dashboard/open_dashboard.py +0 -66
  112. claude_mpm/dashboard/static/css/activity.css +0 -1958
  113. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  114. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  115. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  116. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  117. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  118. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  119. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  120. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  121. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  122. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  123. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  124. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  125. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  126. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  127. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  128. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  129. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  130. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  131. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  132. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  133. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  134. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  135. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  136. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  137. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  138. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  139. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  140. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  141. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  142. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  143. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  144. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  145. claude_mpm/dashboard/templates/code_simple.html +0 -153
  146. claude_mpm/dashboard/templates/index.html +0 -606
  147. claude_mpm/dashboard/test_dashboard.html +0 -372
  148. claude_mpm/scripts/mcp_server.py +0 -75
  149. claude_mpm/scripts/mcp_wrapper.py +0 -39
  150. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  151. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  152. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  153. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  154. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  155. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  156. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  157. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  158. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  159. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  160. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -971
  161. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  162. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  163. claude_mpm/services/mcp_gateway/main.py +0 -589
  164. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  165. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  166. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  167. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  168. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  169. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  170. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  171. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  172. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  173. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  174. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  175. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  176. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  177. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  178. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  179. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  180. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  181. /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
  182. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/WHEEL +0 -0
  183. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/licenses/LICENSE +0 -0
  184. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/top_level.txt +0 -0
@@ -7,7 +7,6 @@ This service handles:
7
7
  4. Parsing and validating YAML frontmatter for namespace metadata (Phase 1 - 1M-400)
8
8
  """
9
9
 
10
- import shutil
11
10
  from pathlib import Path
12
11
  from typing import Any, Dict, List, Optional, Tuple
13
12
 
@@ -20,6 +19,15 @@ from claude_mpm.core.logger import get_logger
20
19
  class CommandDeploymentService(BaseService):
21
20
  """Service for deploying MPM slash commands."""
22
21
 
22
+ # Deprecated commands that have been replaced (cleanup on startup)
23
+ DEPRECATED_COMMANDS = [
24
+ "mpm-agents.md", # Replaced by mpm-agents-list.md
25
+ "mpm-auto-configure.md", # Replaced by mpm-agents-auto-configure.md
26
+ "mpm-config.md", # Replaced by mpm-config-view.md
27
+ "mpm-resume.md", # Replaced by mpm-session-resume.md
28
+ "mpm-ticket.md", # Replaced by mpm-ticket-view.md
29
+ ]
30
+
23
31
  def __init__(self):
24
32
  """Initialize the command deployment service."""
25
33
  super().__init__(name="command_deployment")
@@ -115,6 +123,33 @@ class CommandDeploymentService(BaseService):
115
123
 
116
124
  return errors
117
125
 
126
+ def _strip_deprecated_aliases(self, content: str) -> str:
127
+ """Strip deprecated_aliases from frontmatter to hide them from Claude Code UI.
128
+
129
+ This prevents deprecated aliases from appearing in the command list while
130
+ maintaining backward compatibility through command routing.
131
+
132
+ Args:
133
+ content: Command file content with frontmatter
134
+
135
+ Returns:
136
+ Content with deprecated_aliases removed from frontmatter
137
+ """
138
+ frontmatter, body = self._parse_frontmatter(content)
139
+
140
+ if not frontmatter or "deprecated_aliases" not in frontmatter:
141
+ return content
142
+
143
+ # Remove deprecated_aliases from frontmatter
144
+ frontmatter_copy = frontmatter.copy()
145
+ del frontmatter_copy["deprecated_aliases"]
146
+
147
+ # Reconstruct the file with modified frontmatter
148
+ frontmatter_yaml = yaml.dump(
149
+ frontmatter_copy, default_flow_style=False, sort_keys=False
150
+ )
151
+ return f"---\n{frontmatter_yaml}---\n{body}"
152
+
118
153
  def deploy_commands(self, force: bool = False) -> Dict[str, Any]:
119
154
  """Deploy MPM slash commands to user's Claude configuration.
120
155
 
@@ -185,8 +220,12 @@ class CommandDeploymentService(BaseService):
185
220
  )
186
221
  continue
187
222
 
188
- # Copy the file
189
- shutil.copy2(source_file, target_file)
223
+ # Strip deprecated_aliases from content before deployment
224
+ # This prevents them from appearing in Claude Code's command list UI
225
+ cleaned_content = self._strip_deprecated_aliases(content)
226
+
227
+ # Write the cleaned content to target
228
+ target_file.write_text(cleaned_content)
190
229
  self.logger.info(f"Deployed command: {source_file.name}")
191
230
  result["deployed"].append(source_file.name)
192
231
 
@@ -252,21 +291,83 @@ class CommandDeploymentService(BaseService):
252
291
 
253
292
  return removed
254
293
 
294
+ def remove_deprecated_commands(self) -> int:
295
+ """Remove deprecated MPM commands that have been replaced.
296
+
297
+ This method cleans up old command files that have been superseded by
298
+ new hierarchical command names. It's called automatically on startup
299
+ to ensure users don't have both old and new versions.
300
+
301
+ Returns:
302
+ Number of deprecated files removed
303
+ """
304
+ if not self.target_dir.exists():
305
+ self.logger.debug(
306
+ f"Target directory does not exist: {self.target_dir}, skipping deprecated command cleanup"
307
+ )
308
+ return 0
309
+
310
+ removed = 0
311
+ self.logger.info("Cleaning up deprecated commands...")
312
+
313
+ # Mapping of deprecated commands to their replacements for informative logging
314
+ replacement_map = {
315
+ "mpm-agents.md": "mpm-agents-list.md",
316
+ "mpm-auto-configure.md": "mpm-agents-auto-configure.md",
317
+ "mpm-config.md": "mpm-config-view.md",
318
+ "mpm-resume.md": "mpm-session-resume.md",
319
+ "mpm-ticket.md": "mpm-ticket-view.md",
320
+ }
321
+
322
+ for deprecated_cmd in self.DEPRECATED_COMMANDS:
323
+ deprecated_file = self.target_dir / deprecated_cmd
324
+ replacement = replacement_map.get(deprecated_cmd, "a newer command")
325
+
326
+ if deprecated_file.exists():
327
+ try:
328
+ deprecated_file.unlink()
329
+ self.logger.debug(
330
+ f"Removed deprecated command: {deprecated_cmd} (replaced by {replacement})"
331
+ )
332
+ removed += 1
333
+ except Exception as e:
334
+ # Log error but don't fail startup - this is non-critical
335
+ self.logger.warning(
336
+ f"Failed to remove deprecated command {deprecated_cmd}: {e}"
337
+ )
338
+
339
+ if removed > 0:
340
+ self.logger.info(f"Removed {removed} deprecated command(s)")
341
+ else:
342
+ self.logger.debug("No deprecated commands found to remove")
343
+
344
+ return removed
345
+
255
346
 
256
347
  def deploy_commands_on_startup(force: bool = False) -> None:
257
348
  """Convenience function to deploy commands during startup.
258
349
 
350
+ This function:
351
+ 1. Removes deprecated commands that have been replaced
352
+ 2. Deploys current command files
353
+
259
354
  Args:
260
355
  force: Force deployment even if files exist
261
356
  """
262
357
  service = CommandDeploymentService()
358
+ logger = get_logger("startup")
359
+
360
+ # Clean up deprecated commands BEFORE deploying new ones
361
+ removed_count = service.remove_deprecated_commands()
362
+ if removed_count > 0:
363
+ logger.info(f"Cleaned up {removed_count} deprecated command(s)")
364
+
365
+ # Deploy current commands
263
366
  result = service.deploy_commands(force=force)
264
367
 
265
368
  if result["deployed"]:
266
- logger = get_logger("startup")
267
369
  logger.info(f"MPM commands deployed: {', '.join(result['deployed'])}")
268
370
 
269
371
  if result["errors"]:
270
- logger = get_logger("startup")
271
372
  for error in result["errors"]:
272
373
  logger.warning(f"Command deployment issue: {error}")
@@ -224,10 +224,11 @@ class SingletonService(SyncBaseService):
224
224
 
225
225
  Ensures only one instance of the service exists with thread-safe initialization.
226
226
  Uses double-checked locking pattern to prevent race conditions.
227
+ Uses RLock (reentrant lock) to support recursive instantiation patterns.
227
228
  """
228
229
 
229
230
  _instances: Dict[type, "SingletonService"] = {}
230
- _lock = threading.Lock()
231
+ _lock = threading.RLock()
231
232
 
232
233
  def __new__(cls, *args, **kwargs):
233
234
  """Ensure only one instance exists with thread-safe initialization."""
@@ -247,7 +248,11 @@ class SingletonService(SyncBaseService):
247
248
  # Slow path - acquire lock and double-check
248
249
  with cls._lock:
249
250
  if cls not in cls._instances:
250
- cls._instances[cls] = cls()
251
+ # Use object.__new__ to bypass __new__ recursion
252
+ instance = object.__new__(cls)
253
+ cls._instances[cls] = instance
254
+ # Call __init__ explicitly after storing instance
255
+ instance.__init__()
251
256
  return cls._instances[cls]
252
257
 
253
258
  @classmethod
@@ -126,22 +126,14 @@ class MCPServicesCheck(BaseDiagnosticCheck):
126
126
  )
127
127
  sub_results.append(fix_result)
128
128
 
129
- # Also ensure configurations are updated for all projects
130
- config_success, config_message = (
131
- mcp_manager.ensure_mcp_services_configured()
132
- )
133
- if (
134
- config_message
135
- and config_message != "All MCP services already configured correctly"
136
- ):
129
+ # Check if MCP services are available (read-only check)
130
+ available, availability_message = mcp_manager.check_mcp_services_available()
131
+ if not available:
132
+ # Services not configured - provide installation instructions
137
133
  config_result = DiagnosticResult(
138
- category="MCP Configuration Update",
139
- status=(
140
- OperationResult.SUCCESS
141
- if config_success
142
- else ValidationSeverity.WARNING
143
- ),
144
- message=config_message,
134
+ category="MCP Service Availability",
135
+ status=ValidationSeverity.WARNING,
136
+ message=availability_message,
145
137
  details={"auto_config_applied": True},
146
138
  )
147
139
  sub_results.append(config_result)
@@ -52,9 +52,11 @@ class EventBusConfig:
52
52
  )
53
53
 
54
54
  # Relay configuration
55
+ # DirectSocketIORelay disabled by default - events already emit via direct sio.emit()
56
+ # Enable with CLAUDE_MPM_RELAY_ENABLED=true if needed for external consumers
55
57
  relay_enabled: bool = field(
56
58
  default_factory=lambda: os.environ.get(
57
- "CLAUDE_MPM_RELAY_ENABLED", "true"
59
+ "CLAUDE_MPM_RELAY_ENABLED", "false"
58
60
  ).lower()
59
61
  == "true"
60
62
  )
@@ -12,10 +12,10 @@ Design Decisions:
12
12
 
13
13
  Example:
14
14
  >>> service = GitOperationsService()
15
- >>> success = service.create_branch(Path("~/.claude-mpm/cache/agents"), "improve/research-memory")
15
+ >>> success = service.create_branch(Path("~/.claude-mpm/cache/remote-agents"), "improve/research-memory")
16
16
  >>> if success:
17
- ... service.stage_files(Path("~/.claude-mpm/cache/agents"), ["agents/research.md"])
18
- ... service.commit(Path("~/.claude-mpm/cache/agents"), "feat: improve research agent memory handling")
17
+ ... service.stage_files(Path("~/.claude-mpm/cache/remote-agents"), ["agents/research.md"])
18
+ ... service.commit(Path("~/.claude-mpm/cache/remote-agents"), "feat: improve research agent memory handling")
19
19
  """
20
20
 
21
21
  import subprocess
@@ -65,7 +65,7 @@ class GitOperationsService:
65
65
 
66
66
  Example:
67
67
  >>> service = GitOperationsService()
68
- >>> service.is_git_repo(Path("~/.claude-mpm/cache/agents"))
68
+ >>> service.is_git_repo(Path("~/.claude-mpm/cache/remote-agents"))
69
69
  True
70
70
  """
71
71
  try:
@@ -147,7 +147,7 @@ class GitOperationsService:
147
147
  Example:
148
148
  >>> service = GitOperationsService()
149
149
  >>> service.create_and_checkout_branch(
150
- ... Path("~/.claude-mpm/cache/agents"),
150
+ ... Path("~/.claude-mpm/cache/remote-agents"),
151
151
  ... "improve/research-memory",
152
152
  ... "main"
153
153
  ... )
@@ -242,7 +242,7 @@ class GitOperationsService:
242
242
  Example:
243
243
  >>> service = GitOperationsService()
244
244
  >>> service.commit(
245
- ... Path("~/.claude-mpm/cache/agents"),
245
+ ... Path("~/.claude-mpm/cache/remote-agents"),
246
246
  ... "feat(agent): improve research agent memory handling\\n\\n- Add hard limit of 5 files"
247
247
  ... )
248
248
  True
@@ -286,7 +286,7 @@ class GitOperationsService:
286
286
 
287
287
  Example:
288
288
  >>> service = GitOperationsService()
289
- >>> service.push(Path("~/.claude-mpm/cache/agents"), "improve/research-memory")
289
+ >>> service.push(Path("~/.claude-mpm/cache/remote-agents"), "improve/research-memory")
290
290
  True
291
291
  """
292
292
  self._validate_repo(repo_path)
@@ -404,7 +404,7 @@ class GitOperationsService:
404
404
 
405
405
  Example:
406
406
  >>> service = GitOperationsService()
407
- >>> valid, msg = service.validate_repo(Path("~/.claude-mpm/cache/agents"))
407
+ >>> valid, msg = service.validate_repo(Path("~/.claude-mpm/cache/remote-agents"))
408
408
  >>> if not valid:
409
409
  ... print(f"Repository invalid: {msg}")
410
410
  """
@@ -12,7 +12,6 @@ MCP service installations.
12
12
  import json
13
13
  import subprocess
14
14
  import sys
15
- from datetime import datetime, timezone
16
15
  from enum import Enum
17
16
  from pathlib import Path
18
17
  from typing import Dict, Optional, Tuple
@@ -698,175 +697,106 @@ class MCPConfigManager:
698
697
 
699
698
  return config
700
699
 
701
- def ensure_mcp_services_configured(self) -> Tuple[bool, str]:
700
+ def check_mcp_services_available(self) -> Tuple[bool, str]:
702
701
  """
703
- Ensure MCP services are configured correctly in ~/.claude.json on startup.
702
+ Check if required MCP services are available in ~/.claude.json (READ-ONLY).
704
703
 
705
- This method checks ALL projects in ~/.claude.json and ensures each has
706
- the correct, static MCP service configurations. It will:
707
- 1. Add missing services
708
- 2. Fix incorrect configurations
709
- 3. Update all projects, not just the current one
704
+ This method performs a READ-ONLY check of MCP service availability.
705
+ It does NOT modify ~/.claude.json. Users should install and configure
706
+ MCP services themselves via pip, npx, or Claude Desktop.
710
707
 
711
708
  Returns:
712
- Tuple of (success, message)
709
+ Tuple of (all_available: bool, message: str)
713
710
  """
714
- updated = False
715
- fixed_services = []
716
- added_services = []
717
-
718
- # Load existing Claude config or create minimal structure
719
- claude_config = {}
720
- if self.claude_config_path.exists():
721
- try:
722
- with self.claude_config_path.open() as f:
723
- claude_config = json.load(f)
724
- except Exception as e:
725
- self.logger.error(f"Error reading {self.claude_config_path}: {e}")
726
- return False, f"Failed to read Claude config: {e}"
711
+ # Get services Claude MPM expects to use (from ~/.claude-mpm/config/)
712
+ expected_services = self.get_filtered_services()
727
713
 
728
- # Ensure projects structure exists
729
- if "projects" not in claude_config:
730
- claude_config["projects"] = {}
731
- updated = True
714
+ if not expected_services:
715
+ return True, "No MCP services configured in Claude MPM"
732
716
 
733
- # Note: fix_mcp_service_issues() is already called during CLI initialization
734
- # Calling it here would duplicate the service health checks
717
+ # Load Claude config (read-only)
718
+ if not self.claude_config_path.exists():
719
+ return False, f"Claude config not found at {self.claude_config_path}"
735
720
 
736
- # Process ALL projects in the config, not just current one
737
- projects_to_update = list(claude_config.get("projects", {}).keys())
721
+ try:
722
+ with self.claude_config_path.open() as f:
723
+ claude_config = json.load(f)
724
+ except Exception as e:
725
+ return False, f"Failed to read Claude config: {e}"
738
726
 
739
- # Also add the current project if not in list
727
+ # Check current project
740
728
  current_project_key = str(self.project_root)
741
- if current_project_key not in projects_to_update:
742
- projects_to_update.append(current_project_key)
743
- # Initialize new project structure
744
- claude_config["projects"][current_project_key] = {
745
- "allowedTools": [],
746
- "history": [],
747
- "mcpContextUris": [],
748
- "mcpServers": {},
749
- "enabledMcpjsonServers": [],
750
- "disabledMcpjsonServers": [],
751
- "hasTrustDialogAccepted": False,
752
- "projectOnboardingSeenCount": 0,
753
- "hasClaudeMdExternalIncludesApproved": False,
754
- "hasClaudeMdExternalIncludesWarningShown": False,
755
- }
756
- updated = True
757
-
758
- # Update each project's MCP configurations
759
- for project_key in projects_to_update:
760
- project_config = claude_config["projects"][project_key]
761
-
762
- # Ensure mcpServers section exists
763
- if "mcpServers" not in project_config:
764
- project_config["mcpServers"] = {}
765
- updated = True
766
-
767
- # Check and fix each service configuration - now filtered by startup config
768
- services_to_configure = self.get_filtered_services()
769
-
770
- for service_name, correct_config in services_to_configure.items():
771
- # Check if service exists and has correct configuration
772
- existing_config = project_config["mcpServers"].get(service_name)
773
-
774
- # Determine if we need to update
775
- needs_update = False
776
- if not existing_config:
777
- # Service is missing
778
- needs_update = True
779
- added_services.append(f"{service_name} in {Path(project_key).name}")
780
- # Service exists, check if configuration is correct
781
- # Compare command and args (the most critical parts)
782
- elif existing_config.get("command") != correct_config.get(
783
- "command"
784
- ) or existing_config.get("args") != correct_config.get("args"):
785
- needs_update = True
786
- fixed_services.append(f"{service_name} in {Path(project_key).name}")
787
-
788
- # Update configuration if needed
789
- if needs_update:
790
- project_config["mcpServers"][service_name] = correct_config
791
- updated = True
792
- self.logger.debug(
793
- f"Updated MCP service config for {service_name} in project {Path(project_key).name}"
794
- )
729
+ project_config = claude_config.get("projects", {}).get(current_project_key)
795
730
 
796
- # Remove disabled services from configuration
797
- if self.config is not None:
798
- # Import Config here to avoid circular import
799
- from ..core.config import Config
731
+ if not project_config:
732
+ missing = list(expected_services.keys())
733
+ return (
734
+ False,
735
+ f"Current project not configured in Claude. Missing services: {', '.join(missing)}",
736
+ )
800
737
 
801
- if isinstance(self.config, Config):
802
- enabled_services = self.config.get(
803
- "startup.enabled_mcp_services", None
804
- )
805
- if enabled_services is not None:
806
- # Remove services that are not in the enabled list
807
- services_to_remove = []
808
- for service_name in project_config["mcpServers"]:
809
- if service_name not in enabled_services:
810
- services_to_remove.append(service_name)
811
-
812
- for service_name in services_to_remove:
813
- del project_config["mcpServers"][service_name]
814
- updated = True
815
- self.logger.debug(
816
- f"Removed disabled service {service_name} from project {Path(project_key).name}"
817
- )
818
-
819
- # Write updated config if changes were made
820
- if updated:
821
- try:
822
- # Create backup if file exists and is large (> 100KB)
823
- if self.claude_config_path.exists():
824
- file_size = self.claude_config_path.stat().st_size
825
- if file_size > 100000: # 100KB
826
- backup_path = self.claude_config_path.with_suffix(
827
- f".backup.{datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')}.json"
828
- )
829
- import shutil
738
+ # Check which services are missing
739
+ mcp_servers = project_config.get("mcpServers", {})
740
+ missing_services = [
741
+ name for name in expected_services if name not in mcp_servers
742
+ ]
830
743
 
831
- shutil.copy2(self.claude_config_path, backup_path)
832
- self.logger.debug(f"Created backup: {backup_path}")
744
+ if missing_services:
745
+ msg = (
746
+ f"Missing MCP services: {', '.join(missing_services)}. "
747
+ f"Install via: pip install {' '.join(missing_services)} "
748
+ f"or configure in Claude Desktop"
749
+ )
750
+ return False, msg
833
751
 
834
- # Write updated config
835
- with self.claude_config_path.open("w") as f:
836
- json.dump(claude_config, f, indent=2)
752
+ return (
753
+ True,
754
+ f"All required MCP services available ({len(expected_services)} services)",
755
+ )
837
756
 
838
- messages = []
839
- if added_services:
840
- messages.append(
841
- f"Added MCP services: {', '.join(added_services[:3])}"
842
- )
843
- if fixed_services:
844
- messages.append(
845
- f"Fixed MCP services: {', '.join(fixed_services[:3])}"
846
- )
757
+ def ensure_mcp_services_configured(self) -> Tuple[bool, str]:
758
+ """
759
+ DEPRECATED: Auto-configuring ~/.claude.json is no longer supported.
847
760
 
848
- if messages:
849
- return True, "; ".join(messages)
850
- return True, "All MCP services already configured correctly"
851
- except Exception as e:
852
- self.logger.error(f"Failed to write Claude config: {e}")
853
- return False, f"Failed to write configuration: {e}"
761
+ As of v4.15.0+, MCP services are user-controlled. Users should install
762
+ and configure MCP services themselves via:
763
+ - pip install <service-name>
764
+ - npx @modelcontextprotocol/...
765
+ - Claude Desktop UI
854
766
 
855
- return True, "All MCP services already configured correctly"
767
+ This method now only performs a read-only check and logs a deprecation warning.
768
+ Use check_mcp_services_available() for read-only checks.
769
+
770
+ Returns:
771
+ Tuple of (success, message)
772
+ """
773
+ import warnings
774
+
775
+ warnings.warn(
776
+ "ensure_mcp_services_configured() is deprecated and will be removed in v6.0.0. "
777
+ "MCP services are now user-controlled. Use check_mcp_services_available() instead.",
778
+ DeprecationWarning,
779
+ stacklevel=2,
780
+ )
781
+
782
+ # Delegate to read-only check
783
+ return self.check_mcp_services_available()
856
784
 
857
785
  def update_mcp_config(self, force_pipx: bool = True) -> Tuple[bool, str]:
858
786
  """
859
- Update the MCP configuration in ~/.claude.json.
787
+ DEPRECATED: Check MCP configuration in ~/.claude.json (READ-ONLY).
788
+
789
+ This method no longer modifies ~/.claude.json. Users should install
790
+ and configure MCP services themselves.
860
791
 
861
792
  Args:
862
- force_pipx: If True, only use pipx installations
793
+ force_pipx: Ignored (kept for backward compatibility)
863
794
 
864
795
  Returns:
865
- Tuple of (success, message)
796
+ Tuple of (success, message) from read-only check
866
797
  """
867
- # This method now delegates to ensure_mcp_services_configured
868
- # since we're updating the Claude config directly
869
- return self.ensure_mcp_services_configured()
798
+ # Delegate to read-only check
799
+ return self.check_mcp_services_available()
870
800
 
871
801
  def update_project_mcp_config(self, force_pipx: bool = True) -> Tuple[bool, str]:
872
802
  """
@@ -620,12 +620,15 @@ class MCPServiceVerifier:
620
620
  return True
621
621
 
622
622
  if "claude-mpm configure" in diagnostic.fix_command:
623
- # Trigger configuration update
623
+ # Check if services are available (read-only)
624
624
  from .mcp_config_manager import MCPConfigManager
625
625
 
626
626
  manager = MCPConfigManager()
627
- success, _ = manager.ensure_mcp_services_configured()
628
- return success
627
+ available, message = manager.check_mcp_services_available()
628
+ if not available:
629
+ # Cannot auto-fix - user must install services manually
630
+ self.logger.warning(f"Cannot auto-fix: {message}")
631
+ return available
629
632
 
630
633
  except Exception as e:
631
634
  self.logger.error(f"Auto-fix failed for {service_name}: {e}")