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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +2002 -0
- claude_mpm/agents/PM_INSTRUCTIONS.md +1218 -905
- claude_mpm/agents/agent_loader.py +10 -17
- claude_mpm/agents/base_agent_loader.py +10 -35
- claude_mpm/agents/frontmatter_validator.py +68 -0
- claude_mpm/agents/templates/circuit-breakers.md +431 -45
- claude_mpm/cli/__init__.py +0 -1
- claude_mpm/cli/commands/__init__.py +2 -0
- claude_mpm/cli/commands/agent_state_manager.py +67 -23
- claude_mpm/cli/commands/agents.py +446 -25
- claude_mpm/cli/commands/auto_configure.py +535 -233
- claude_mpm/cli/commands/configure.py +1500 -147
- claude_mpm/cli/commands/configure_agent_display.py +13 -6
- claude_mpm/cli/commands/mpm_init/core.py +158 -1
- claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
- claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
- claude_mpm/cli/commands/postmortem.py +401 -0
- claude_mpm/cli/commands/run.py +1 -39
- claude_mpm/cli/commands/skills.py +322 -19
- claude_mpm/cli/commands/summarize.py +413 -0
- claude_mpm/cli/executor.py +8 -0
- claude_mpm/cli/interactive/agent_wizard.py +302 -195
- claude_mpm/cli/parsers/agents_parser.py +137 -0
- claude_mpm/cli/parsers/auto_configure_parser.py +13 -0
- claude_mpm/cli/parsers/base_parser.py +9 -0
- claude_mpm/cli/parsers/skills_parser.py +7 -0
- claude_mpm/cli/startup.py +133 -85
- claude_mpm/commands/mpm-agents-auto-configure.md +2 -2
- claude_mpm/commands/mpm-agents-list.md +2 -2
- claude_mpm/commands/mpm-config-view.md +2 -2
- claude_mpm/commands/mpm-help.md +3 -0
- claude_mpm/commands/{mpm-ticket-organize.md → mpm-organize.md} +4 -5
- claude_mpm/commands/mpm-postmortem.md +123 -0
- claude_mpm/commands/mpm-session-resume.md +2 -2
- claude_mpm/commands/mpm-ticket-view.md +2 -2
- claude_mpm/config/agent_presets.py +312 -82
- claude_mpm/config/agent_sources.py +27 -0
- claude_mpm/config/skill_presets.py +392 -0
- claude_mpm/constants.py +1 -0
- claude_mpm/core/claude_runner.py +2 -25
- claude_mpm/core/framework/loaders/agent_loader.py +8 -5
- claude_mpm/core/framework/loaders/file_loader.py +54 -101
- claude_mpm/core/interactive_session.py +19 -5
- claude_mpm/core/oneshot_session.py +16 -4
- claude_mpm/core/output_style_manager.py +173 -43
- claude_mpm/core/protocols/__init__.py +23 -0
- claude_mpm/core/protocols/runner_protocol.py +103 -0
- claude_mpm/core/protocols/session_protocol.py +131 -0
- claude_mpm/core/shared/singleton_manager.py +11 -4
- claude_mpm/core/socketio_pool.py +3 -3
- claude_mpm/core/system_context.py +38 -0
- claude_mpm/core/unified_agent_registry.py +134 -16
- claude_mpm/core/unified_config.py +22 -0
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +35 -2
- claude_mpm/hooks/claude_hooks/hook_handler.py +4 -0
- claude_mpm/hooks/claude_hooks/memory_integration.py +12 -1
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +4 -0
- claude_mpm/models/agent_definition.py +7 -0
- claude_mpm/scripts/launch_monitor.py +93 -13
- claude_mpm/services/agents/agent_recommendation_service.py +279 -0
- claude_mpm/services/agents/cache_git_manager.py +621 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +3 -2
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +110 -3
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +518 -55
- claude_mpm/services/agents/git_source_manager.py +20 -0
- claude_mpm/services/agents/sources/git_source_sync_service.py +45 -6
- claude_mpm/services/agents/toolchain_detector.py +6 -5
- claude_mpm/services/analysis/__init__.py +35 -0
- claude_mpm/services/analysis/clone_detector.py +1030 -0
- claude_mpm/services/analysis/postmortem_reporter.py +474 -0
- claude_mpm/services/analysis/postmortem_service.py +765 -0
- claude_mpm/services/command_deployment_service.py +106 -5
- claude_mpm/services/core/base.py +7 -2
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +7 -15
- claude_mpm/services/event_bus/config.py +3 -1
- claude_mpm/services/git/git_operations_service.py +8 -8
- claude_mpm/services/mcp_config_manager.py +75 -145
- claude_mpm/services/mcp_service_verifier.py +6 -3
- claude_mpm/services/monitor/daemon.py +37 -10
- claude_mpm/services/monitor/daemon_manager.py +134 -21
- claude_mpm/services/monitor/server.py +225 -19
- claude_mpm/services/project/project_organizer.py +4 -0
- claude_mpm/services/runner_configuration_service.py +16 -3
- claude_mpm/services/session_management_service.py +16 -4
- claude_mpm/services/socketio/event_normalizer.py +15 -1
- claude_mpm/services/socketio/server/core.py +160 -21
- claude_mpm/services/version_control/git_operations.py +103 -0
- claude_mpm/utils/agent_filters.py +261 -0
- claude_mpm/utils/gitignore.py +3 -0
- claude_mpm/utils/migration.py +372 -0
- claude_mpm/utils/progress.py +5 -1
- {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/METADATA +69 -84
- {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/RECORD +112 -153
- {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/entry_points.txt +0 -2
- claude_mpm/dashboard/analysis_runner.py +0 -455
- claude_mpm/dashboard/index.html +0 -13
- claude_mpm/dashboard/open_dashboard.py +0 -66
- claude_mpm/dashboard/static/css/activity.css +0 -1958
- claude_mpm/dashboard/static/css/connection-status.css +0 -370
- claude_mpm/dashboard/static/css/dashboard.css +0 -4701
- claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
- claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
- claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
- claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
- claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
- claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
- claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
- claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
- claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
- claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
- claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
- claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
- claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
- claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
- claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
- claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
- claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
- claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
- claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
- claude_mpm/dashboard/static/js/connection-manager.js +0 -536
- claude_mpm/dashboard/static/js/dashboard.js +0 -1914
- claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
- claude_mpm/dashboard/static/js/socket-client.js +0 -1474
- claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
- claude_mpm/dashboard/static/socket.io.min.js +0 -7
- claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
- claude_mpm/dashboard/templates/code_simple.html +0 -153
- claude_mpm/dashboard/templates/index.html +0 -606
- claude_mpm/dashboard/test_dashboard.html +0 -372
- claude_mpm/scripts/mcp_server.py +0 -75
- claude_mpm/scripts/mcp_wrapper.py +0 -39
- claude_mpm/services/mcp_gateway/__init__.py +0 -159
- claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
- claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
- claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
- claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
- claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
- claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
- claude_mpm/services/mcp_gateway/core/base.py +0 -312
- claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
- claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
- claude_mpm/services/mcp_gateway/core/process_pool.py +0 -971
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
- claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
- claude_mpm/services/mcp_gateway/main.py +0 -589
- claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
- claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
- claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
- claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
- claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
- claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
- claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
- claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
- claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
- /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
- {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/WHEEL +0 -0
- {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/licenses/LICENSE +0 -0
- {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
|
-
#
|
|
189
|
-
|
|
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}")
|
claude_mpm/services/core/base.py
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
#
|
|
130
|
-
|
|
131
|
-
|
|
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
|
|
139
|
-
status=
|
|
140
|
-
|
|
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", "
|
|
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
|
|
700
|
+
def check_mcp_services_available(self) -> Tuple[bool, str]:
|
|
702
701
|
"""
|
|
703
|
-
|
|
702
|
+
Check if required MCP services are available in ~/.claude.json (READ-ONLY).
|
|
704
703
|
|
|
705
|
-
This method
|
|
706
|
-
|
|
707
|
-
|
|
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 (
|
|
709
|
+
Tuple of (all_available: bool, message: str)
|
|
713
710
|
"""
|
|
714
|
-
|
|
715
|
-
|
|
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
|
-
|
|
729
|
-
|
|
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
|
-
#
|
|
734
|
-
|
|
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
|
-
|
|
737
|
-
|
|
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
|
-
#
|
|
727
|
+
# Check current project
|
|
740
728
|
current_project_key = str(self.project_root)
|
|
741
|
-
|
|
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
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
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
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
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
|
-
|
|
832
|
-
|
|
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
|
-
|
|
835
|
-
|
|
836
|
-
|
|
752
|
+
return (
|
|
753
|
+
True,
|
|
754
|
+
f"All required MCP services available ({len(expected_services)} services)",
|
|
755
|
+
)
|
|
837
756
|
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
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
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
#
|
|
868
|
-
|
|
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
|
-
#
|
|
623
|
+
# Check if services are available (read-only)
|
|
624
624
|
from .mcp_config_manager import MCPConfigManager
|
|
625
625
|
|
|
626
626
|
manager = MCPConfigManager()
|
|
627
|
-
|
|
628
|
-
|
|
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}")
|