claude-mpm 4.0.32__py3-none-any.whl → 4.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/INSTRUCTIONS.md +70 -2
- claude_mpm/agents/OUTPUT_STYLE.md +0 -11
- claude_mpm/agents/WORKFLOW.md +14 -2
- claude_mpm/agents/templates/documentation.json +51 -34
- claude_mpm/agents/templates/research.json +0 -11
- claude_mpm/cli/__init__.py +111 -33
- claude_mpm/cli/commands/agent_manager.py +10 -8
- claude_mpm/cli/commands/agents.py +82 -0
- claude_mpm/cli/commands/cleanup_orphaned_agents.py +150 -0
- claude_mpm/cli/commands/mcp_pipx_config.py +199 -0
- claude_mpm/cli/parsers/agents_parser.py +27 -0
- claude_mpm/cli/parsers/base_parser.py +6 -0
- claude_mpm/cli/startup_logging.py +75 -0
- claude_mpm/core/framework_loader.py +173 -84
- claude_mpm/dashboard/static/css/dashboard.css +449 -0
- claude_mpm/dashboard/static/dist/components/agent-inference.js +1 -1
- claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +1 -1
- claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/components/session-manager.js +1 -1
- claude_mpm/dashboard/static/dist/dashboard.js +1 -1
- claude_mpm/dashboard/static/dist/socket-client.js +1 -1
- claude_mpm/dashboard/static/js/components/agent-hierarchy.js +774 -0
- claude_mpm/dashboard/static/js/components/agent-inference.js +257 -3
- claude_mpm/dashboard/static/js/components/build-tracker.js +323 -0
- claude_mpm/dashboard/static/js/components/event-viewer.js +168 -39
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +17 -0
- claude_mpm/dashboard/static/js/components/session-manager.js +23 -3
- claude_mpm/dashboard/static/js/components/socket-manager.js +2 -0
- claude_mpm/dashboard/static/js/dashboard.js +207 -31
- claude_mpm/dashboard/static/js/socket-client.js +92 -11
- claude_mpm/dashboard/templates/index.html +1 -0
- claude_mpm/hooks/claude_hooks/connection_pool.py +25 -4
- claude_mpm/hooks/claude_hooks/event_handlers.py +81 -19
- claude_mpm/hooks/claude_hooks/hook_handler.py +125 -163
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +398 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +10 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +34 -48
- claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -1
- claude_mpm/services/agents/deployment/agent_template_builder.py +20 -11
- claude_mpm/services/agents/deployment/agent_version_manager.py +4 -1
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +10 -25
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +396 -13
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +3 -2
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +10 -3
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +10 -14
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +8 -85
- claude_mpm/services/agents/memory/content_manager.py +98 -105
- claude_mpm/services/event_bus/__init__.py +18 -0
- claude_mpm/services/event_bus/config.py +165 -0
- claude_mpm/services/event_bus/event_bus.py +349 -0
- claude_mpm/services/event_bus/relay.py +297 -0
- claude_mpm/services/events/__init__.py +44 -0
- claude_mpm/services/events/consumers/__init__.py +18 -0
- claude_mpm/services/events/consumers/dead_letter.py +296 -0
- claude_mpm/services/events/consumers/logging.py +183 -0
- claude_mpm/services/events/consumers/metrics.py +242 -0
- claude_mpm/services/events/consumers/socketio.py +376 -0
- claude_mpm/services/events/core.py +470 -0
- claude_mpm/services/events/interfaces.py +230 -0
- claude_mpm/services/events/producers/__init__.py +14 -0
- claude_mpm/services/events/producers/hook.py +269 -0
- claude_mpm/services/events/producers/system.py +327 -0
- claude_mpm/services/mcp_gateway/auto_configure.py +372 -0
- claude_mpm/services/mcp_gateway/core/process_pool.py +411 -0
- claude_mpm/services/mcp_gateway/server/stdio_server.py +13 -0
- claude_mpm/services/monitor_build_service.py +345 -0
- claude_mpm/services/socketio/event_normalizer.py +667 -0
- claude_mpm/services/socketio/handlers/connection.py +81 -23
- claude_mpm/services/socketio/handlers/hook.py +14 -5
- claude_mpm/services/socketio/migration_utils.py +329 -0
- claude_mpm/services/socketio/server/broadcaster.py +26 -33
- claude_mpm/services/socketio/server/core.py +29 -5
- claude_mpm/services/socketio/server/eventbus_integration.py +189 -0
- claude_mpm/services/socketio/server/main.py +25 -0
- {claude_mpm-4.0.32.dist-info → claude_mpm-4.1.0.dist-info}/METADATA +28 -9
- {claude_mpm-4.0.32.dist-info → claude_mpm-4.1.0.dist-info}/RECORD +82 -56
- {claude_mpm-4.0.32.dist-info → claude_mpm-4.1.0.dist-info}/WHEEL +0 -0
- {claude_mpm-4.0.32.dist-info → claude_mpm-4.1.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.0.32.dist-info → claude_mpm-4.1.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.0.32.dist-info → claude_mpm-4.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""CLI command to clean up orphaned agents without templates.
|
|
2
|
+
|
|
3
|
+
This command helps manage deployed agents that no longer have corresponding
|
|
4
|
+
templates, which can happen when agents are removed from the system or when
|
|
5
|
+
switching between different agent sources.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
from claude_mpm.core.logging_config import get_logger
|
|
13
|
+
from claude_mpm.services.agents.deployment.multi_source_deployment_service import (
|
|
14
|
+
MultiSourceAgentDeploymentService
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def add_parser(subparsers: argparse._SubParsersAction) -> None:
|
|
19
|
+
"""Add the cleanup-orphaned-agents command parser.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
subparsers: The subparsers object from argparse
|
|
23
|
+
"""
|
|
24
|
+
parser = subparsers.add_parser(
|
|
25
|
+
"cleanup-orphaned-agents",
|
|
26
|
+
help="Clean up orphaned agents that don't have templates",
|
|
27
|
+
description=(
|
|
28
|
+
"Detect and optionally remove deployed agents that no longer have "
|
|
29
|
+
"corresponding templates. This can happen when agents are removed "
|
|
30
|
+
"from the system or when switching between agent sources."
|
|
31
|
+
),
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
"--agents-dir",
|
|
36
|
+
type=Path,
|
|
37
|
+
help="Directory containing deployed agents (default: .claude/agents/)",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
"--dry-run",
|
|
42
|
+
action="store_true",
|
|
43
|
+
default=True,
|
|
44
|
+
help="Only show what would be removed without actually removing (default)",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"--force",
|
|
49
|
+
action="store_true",
|
|
50
|
+
help="Actually remove orphaned agents (disables dry-run)",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
parser.add_argument(
|
|
54
|
+
"--quiet",
|
|
55
|
+
action="store_true",
|
|
56
|
+
help="Only show summary, not individual agents",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
parser.set_defaults(func=cleanup_orphaned_agents)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def cleanup_orphaned_agents(args: argparse.Namespace) -> int:
|
|
63
|
+
"""Clean up orphaned agents.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
args: Command line arguments
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
Exit code (0 for success, non-zero for errors)
|
|
70
|
+
"""
|
|
71
|
+
logger = get_logger(__name__)
|
|
72
|
+
|
|
73
|
+
# Determine agents directory
|
|
74
|
+
if args.agents_dir:
|
|
75
|
+
agents_dir = args.agents_dir
|
|
76
|
+
else:
|
|
77
|
+
# Check for project-level .claude/agents first
|
|
78
|
+
project_agents_dir = Path.cwd() / ".claude" / "agents"
|
|
79
|
+
if project_agents_dir.exists():
|
|
80
|
+
agents_dir = project_agents_dir
|
|
81
|
+
else:
|
|
82
|
+
# Fall back to user home directory
|
|
83
|
+
agents_dir = Path.home() / ".claude" / "agents"
|
|
84
|
+
|
|
85
|
+
if not agents_dir.exists():
|
|
86
|
+
logger.info(f"Agents directory not found: {agents_dir}")
|
|
87
|
+
return 0
|
|
88
|
+
|
|
89
|
+
logger.info(f"Checking for orphaned agents in: {agents_dir}")
|
|
90
|
+
|
|
91
|
+
# Initialize service
|
|
92
|
+
service = MultiSourceAgentDeploymentService()
|
|
93
|
+
|
|
94
|
+
# Determine if we're doing a dry run
|
|
95
|
+
dry_run = args.dry_run and not args.force
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
# Perform cleanup
|
|
99
|
+
results = service.cleanup_orphaned_agents(agents_dir, dry_run=dry_run)
|
|
100
|
+
|
|
101
|
+
# Handle results
|
|
102
|
+
if not results["orphaned"]:
|
|
103
|
+
logger.info("✅ No orphaned agents found")
|
|
104
|
+
return 0
|
|
105
|
+
|
|
106
|
+
if not args.quiet:
|
|
107
|
+
logger.info(f"\nFound {len(results['orphaned'])} orphaned agent(s):")
|
|
108
|
+
for orphan in results["orphaned"]:
|
|
109
|
+
logger.info(f" - {orphan['name']} v{orphan['version']}")
|
|
110
|
+
|
|
111
|
+
if dry_run:
|
|
112
|
+
logger.info(
|
|
113
|
+
f"\n📝 This was a dry run. Use --force to actually remove "
|
|
114
|
+
f"{len(results['orphaned'])} orphaned agent(s)"
|
|
115
|
+
)
|
|
116
|
+
else:
|
|
117
|
+
if results["removed"]:
|
|
118
|
+
logger.info(
|
|
119
|
+
f"\n✅ Successfully removed {len(results['removed'])} orphaned agent(s)"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
if results["errors"]:
|
|
123
|
+
logger.error(f"\n❌ Encountered {len(results['errors'])} error(s):")
|
|
124
|
+
for error in results["errors"]:
|
|
125
|
+
logger.error(f" - {error}")
|
|
126
|
+
return 1
|
|
127
|
+
|
|
128
|
+
return 0
|
|
129
|
+
|
|
130
|
+
except Exception as e:
|
|
131
|
+
logger.error(f"Error during cleanup: {e}")
|
|
132
|
+
return 1
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# For backward compatibility
|
|
136
|
+
def main(args: Optional[argparse.Namespace] = None) -> int:
|
|
137
|
+
"""Main entry point for the command.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
args: Command line arguments
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Exit code
|
|
144
|
+
"""
|
|
145
|
+
if args is None:
|
|
146
|
+
parser = argparse.ArgumentParser()
|
|
147
|
+
add_parser(parser.add_subparsers())
|
|
148
|
+
args = parser.parse_args()
|
|
149
|
+
|
|
150
|
+
return cleanup_orphaned_agents(args)
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
MCP configuration command for pipx installations.
|
|
4
|
+
|
|
5
|
+
This module provides a CLI command to configure MCP for users who installed
|
|
6
|
+
claude-mpm via pipx.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import platform
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Optional, Dict, Any
|
|
16
|
+
|
|
17
|
+
from claude_mpm.core.logger import get_logger
|
|
18
|
+
|
|
19
|
+
logger = get_logger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def find_claude_config_path() -> Path:
|
|
23
|
+
"""Find the Claude Code configuration file path."""
|
|
24
|
+
system = platform.system()
|
|
25
|
+
|
|
26
|
+
if system == "Darwin": # macOS
|
|
27
|
+
config_path = Path.home() / "Library" / "Application Support" / "Claude" / "claude_desktop_config.json"
|
|
28
|
+
elif system == "Windows":
|
|
29
|
+
appdata = os.environ.get("APPDATA")
|
|
30
|
+
if appdata:
|
|
31
|
+
config_path = Path(appdata) / "Claude" / "claude_desktop_config.json"
|
|
32
|
+
else:
|
|
33
|
+
config_path = Path.home() / "AppData" / "Roaming" / "Claude" / "claude_desktop_config.json"
|
|
34
|
+
else: # Linux and others
|
|
35
|
+
config_path = Path.home() / ".config" / "Claude" / "claude_desktop_config.json"
|
|
36
|
+
|
|
37
|
+
return config_path
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def check_pipx_installation() -> bool:
|
|
41
|
+
"""Check if claude-mpm is installed via pipx."""
|
|
42
|
+
try:
|
|
43
|
+
# Check if running from pipx
|
|
44
|
+
if "pipx" in sys.executable.lower():
|
|
45
|
+
return True
|
|
46
|
+
|
|
47
|
+
# Check pipx list
|
|
48
|
+
result = subprocess.run(
|
|
49
|
+
["pipx", "list", "--json"],
|
|
50
|
+
capture_output=True,
|
|
51
|
+
text=True,
|
|
52
|
+
timeout=5
|
|
53
|
+
)
|
|
54
|
+
if result.returncode == 0:
|
|
55
|
+
pipx_data = json.loads(result.stdout)
|
|
56
|
+
return "claude-mpm" in pipx_data.get("venvs", {})
|
|
57
|
+
except Exception:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
return False
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def create_mcp_config() -> Dict[str, Any]:
|
|
64
|
+
"""Create MCP configuration for pipx installation."""
|
|
65
|
+
return {
|
|
66
|
+
"mcpServers": {
|
|
67
|
+
"claude-mpm-gateway": {
|
|
68
|
+
"command": "claude-mpm-mcp"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def configure_mcp_for_pipx(args) -> int:
|
|
75
|
+
"""
|
|
76
|
+
Configure MCP for pipx installation.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
args: Command line arguments
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Exit code (0 for success, 1 for failure)
|
|
83
|
+
"""
|
|
84
|
+
print("Claude MPM - MCP Configuration for pipx")
|
|
85
|
+
print("=" * 40)
|
|
86
|
+
|
|
87
|
+
# Check if this is a pipx installation
|
|
88
|
+
if not check_pipx_installation():
|
|
89
|
+
print("\n⚠️ This doesn't appear to be a pipx installation")
|
|
90
|
+
print("This command is specifically for pipx users.")
|
|
91
|
+
print("\nFor other installation methods, see:")
|
|
92
|
+
print(" docs/MCP_SETUP.md")
|
|
93
|
+
|
|
94
|
+
if not args.force:
|
|
95
|
+
return 1
|
|
96
|
+
print("\n--force flag detected, continuing anyway...")
|
|
97
|
+
|
|
98
|
+
# Find Claude config
|
|
99
|
+
config_path = find_claude_config_path()
|
|
100
|
+
print(f"\n📁 Claude config path: {config_path}")
|
|
101
|
+
|
|
102
|
+
# Load existing config
|
|
103
|
+
existing_config = {}
|
|
104
|
+
if config_path.exists():
|
|
105
|
+
try:
|
|
106
|
+
with open(config_path, 'r') as f:
|
|
107
|
+
existing_config = json.load(f)
|
|
108
|
+
print("✅ Existing config loaded")
|
|
109
|
+
except json.JSONDecodeError:
|
|
110
|
+
print("⚠️ Config exists but is invalid JSON")
|
|
111
|
+
if not args.force:
|
|
112
|
+
print("Use --force to overwrite")
|
|
113
|
+
return 1
|
|
114
|
+
else:
|
|
115
|
+
print("📝 Config will be created")
|
|
116
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
117
|
+
|
|
118
|
+
# Check for existing MCP config
|
|
119
|
+
if "mcpServers" in existing_config and "claude-mpm-gateway" in existing_config["mcpServers"]:
|
|
120
|
+
print("\n⚠️ claude-mpm-gateway is already configured")
|
|
121
|
+
if not args.force:
|
|
122
|
+
print("Use --force to overwrite")
|
|
123
|
+
return 0
|
|
124
|
+
print("Overwriting existing configuration...")
|
|
125
|
+
|
|
126
|
+
# Create and merge config
|
|
127
|
+
mcp_config = create_mcp_config()
|
|
128
|
+
existing_config.update(mcp_config)
|
|
129
|
+
|
|
130
|
+
# Show what will be written
|
|
131
|
+
if not args.quiet:
|
|
132
|
+
print("\n📝 Configuration to write:")
|
|
133
|
+
print(json.dumps(mcp_config, indent=2))
|
|
134
|
+
|
|
135
|
+
# Write config
|
|
136
|
+
if not args.dry_run:
|
|
137
|
+
try:
|
|
138
|
+
with open(config_path, 'w') as f:
|
|
139
|
+
json.dump(existing_config, f, indent=2)
|
|
140
|
+
print(f"\n✅ Configuration written to: {config_path}")
|
|
141
|
+
except Exception as e:
|
|
142
|
+
print(f"\n❌ Failed to write config: {e}")
|
|
143
|
+
return 1
|
|
144
|
+
else:
|
|
145
|
+
print("\n--dry-run: Configuration not written")
|
|
146
|
+
|
|
147
|
+
# Test the command
|
|
148
|
+
print("\n🧪 Testing claude-mpm-mcp command...")
|
|
149
|
+
try:
|
|
150
|
+
result = subprocess.run(
|
|
151
|
+
["which", "claude-mpm-mcp"],
|
|
152
|
+
capture_output=True,
|
|
153
|
+
text=True,
|
|
154
|
+
timeout=2
|
|
155
|
+
)
|
|
156
|
+
if result.returncode == 0:
|
|
157
|
+
print(f"✅ Command found: {result.stdout.strip()}")
|
|
158
|
+
else:
|
|
159
|
+
print("⚠️ Command not found in PATH")
|
|
160
|
+
print(" Ensure pipx bin directory is in your PATH")
|
|
161
|
+
except Exception as e:
|
|
162
|
+
print(f"⚠️ Could not test command: {e}")
|
|
163
|
+
|
|
164
|
+
print("\n✨ Next steps:")
|
|
165
|
+
print("1. Restart Claude Code")
|
|
166
|
+
print("2. Look for the MCP icon in the interface")
|
|
167
|
+
print("3. Try using @claude-mpm-gateway in a conversation")
|
|
168
|
+
print("\nFor more help, see: docs/MCP_PIPX_SETUP.md")
|
|
169
|
+
|
|
170
|
+
return 0
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def add_parser(subparsers):
|
|
174
|
+
"""Add the mcp-pipx-config command parser."""
|
|
175
|
+
parser = subparsers.add_parser(
|
|
176
|
+
'mcp-pipx-config',
|
|
177
|
+
help='Configure MCP for pipx installation',
|
|
178
|
+
description='Configure MCP Gateway for Claude Code when installed via pipx'
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
parser.add_argument(
|
|
182
|
+
'--force',
|
|
183
|
+
action='store_true',
|
|
184
|
+
help='Force configuration even if not pipx or already configured'
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
parser.add_argument(
|
|
188
|
+
'--dry-run',
|
|
189
|
+
action='store_true',
|
|
190
|
+
help='Show what would be done without making changes'
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
parser.add_argument(
|
|
194
|
+
'--quiet',
|
|
195
|
+
action='store_true',
|
|
196
|
+
help='Suppress non-essential output'
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
parser.set_defaults(func=configure_mcp_for_pipx)
|
|
@@ -132,5 +132,32 @@ def add_agents_subparser(subparsers) -> argparse.ArgumentParser:
|
|
|
132
132
|
default=3,
|
|
133
133
|
help="Maximum retry attempts per package (default: 3)",
|
|
134
134
|
)
|
|
135
|
+
|
|
136
|
+
# Cleanup orphaned agents
|
|
137
|
+
cleanup_orphaned_parser = agents_subparsers.add_parser(
|
|
138
|
+
"cleanup-orphaned",
|
|
139
|
+
help="Clean up orphaned agents that don't have templates"
|
|
140
|
+
)
|
|
141
|
+
cleanup_orphaned_parser.add_argument(
|
|
142
|
+
"--agents-dir",
|
|
143
|
+
type=Path,
|
|
144
|
+
help="Directory containing deployed agents (default: .claude/agents/)",
|
|
145
|
+
)
|
|
146
|
+
cleanup_orphaned_parser.add_argument(
|
|
147
|
+
"--dry-run",
|
|
148
|
+
action="store_true",
|
|
149
|
+
default=True,
|
|
150
|
+
help="Only show what would be removed without actually removing (default)",
|
|
151
|
+
)
|
|
152
|
+
cleanup_orphaned_parser.add_argument(
|
|
153
|
+
"--force",
|
|
154
|
+
action="store_true",
|
|
155
|
+
help="Actually remove orphaned agents (disables dry-run)",
|
|
156
|
+
)
|
|
157
|
+
cleanup_orphaned_parser.add_argument(
|
|
158
|
+
"--quiet",
|
|
159
|
+
action="store_true",
|
|
160
|
+
help="Only show summary, not individual agents",
|
|
161
|
+
)
|
|
135
162
|
|
|
136
163
|
return agents_parser
|
|
@@ -338,6 +338,12 @@ def create_parser(
|
|
|
338
338
|
from ..commands.cleanup import add_cleanup_parser
|
|
339
339
|
|
|
340
340
|
add_cleanup_parser(subparsers)
|
|
341
|
+
|
|
342
|
+
# MCP pipx configuration command
|
|
343
|
+
if hasattr(CLICommands, "MCP_PIPX_CONFIG") or True: # Always add for now
|
|
344
|
+
from ..commands.mcp_pipx_config import add_parser as add_mcp_pipx_parser
|
|
345
|
+
|
|
346
|
+
add_mcp_pipx_parser(subparsers)
|
|
341
347
|
|
|
342
348
|
from ..commands.doctor import add_doctor_parser
|
|
343
349
|
|
|
@@ -66,8 +66,10 @@ class StartupStatusLogger:
|
|
|
66
66
|
self.logger.info(f"MCP Server: {config_status['servers_count']} server(s) configured")
|
|
67
67
|
else:
|
|
68
68
|
self.logger.info("MCP Server: No servers configured")
|
|
69
|
+
self._log_mcp_setup_hint()
|
|
69
70
|
else:
|
|
70
71
|
self.logger.info("MCP Server: No configuration found in ~/.claude.json")
|
|
72
|
+
self._log_mcp_setup_hint()
|
|
71
73
|
|
|
72
74
|
# Check for claude-mpm MCP gateway status
|
|
73
75
|
gateway_status = self._check_mcp_gateway_status()
|
|
@@ -75,6 +77,9 @@ class StartupStatusLogger:
|
|
|
75
77
|
self.logger.info("MCP Gateway: Claude MPM gateway configured")
|
|
76
78
|
else:
|
|
77
79
|
self.logger.info("MCP Gateway: Claude MPM gateway not configured")
|
|
80
|
+
# Check if this is a pipx installation that could benefit from auto-config
|
|
81
|
+
if self._is_pipx_installation() and not self._has_auto_config_preference():
|
|
82
|
+
self.logger.info("MCP Gateway: Auto-configuration available for pipx users")
|
|
78
83
|
|
|
79
84
|
except Exception as e:
|
|
80
85
|
self.logger.warning(f"MCP Server: Status check failed - {e}")
|
|
@@ -293,6 +298,76 @@ class StartupStatusLogger:
|
|
|
293
298
|
result["error"] = str(e)
|
|
294
299
|
|
|
295
300
|
return result
|
|
301
|
+
|
|
302
|
+
def _is_pipx_installation(self) -> bool:
|
|
303
|
+
"""Check if this is a pipx installation."""
|
|
304
|
+
try:
|
|
305
|
+
# Check if running from pipx
|
|
306
|
+
if "pipx" in sys.executable.lower():
|
|
307
|
+
return True
|
|
308
|
+
|
|
309
|
+
# Check module path
|
|
310
|
+
import claude_mpm
|
|
311
|
+
module_path = Path(claude_mpm.__file__).parent
|
|
312
|
+
if "pipx" in str(module_path):
|
|
313
|
+
return True
|
|
314
|
+
except Exception:
|
|
315
|
+
pass
|
|
316
|
+
|
|
317
|
+
return False
|
|
318
|
+
|
|
319
|
+
def _has_auto_config_preference(self) -> bool:
|
|
320
|
+
"""Check if user has already been asked about auto-configuration."""
|
|
321
|
+
try:
|
|
322
|
+
from ..config.paths import paths
|
|
323
|
+
preference_file = paths.claude_mpm_dir_hidden / "mcp_auto_config_preference.json"
|
|
324
|
+
return preference_file.exists()
|
|
325
|
+
except Exception:
|
|
326
|
+
return False
|
|
327
|
+
|
|
328
|
+
def _log_mcp_setup_hint(self) -> None:
|
|
329
|
+
"""Log helpful hints for MCP setup."""
|
|
330
|
+
# Check if installed via pipx
|
|
331
|
+
is_pipx = self._check_pipx_installation()
|
|
332
|
+
|
|
333
|
+
if is_pipx:
|
|
334
|
+
self.logger.info("💡 TIP: It looks like you installed claude-mpm via pipx")
|
|
335
|
+
self.logger.info(" To configure MCP for Claude Code with pipx:")
|
|
336
|
+
self.logger.info(" 1. Run: python3 scripts/configure_mcp_pipx.py")
|
|
337
|
+
self.logger.info(" 2. Or see: docs/MCP_PIPX_SETUP.md for manual setup")
|
|
338
|
+
self.logger.info(" 3. Restart Claude Code after configuration")
|
|
339
|
+
else:
|
|
340
|
+
self.logger.info("💡 TIP: To enable MCP integration with Claude Code:")
|
|
341
|
+
self.logger.info(" 1. See docs/MCP_SETUP.md for setup instructions")
|
|
342
|
+
self.logger.info(" 2. Run: claude-mpm doctor --check mcp to verify")
|
|
343
|
+
self.logger.info(" 3. Restart Claude Code after configuration")
|
|
344
|
+
|
|
345
|
+
def _check_pipx_installation(self) -> bool:
|
|
346
|
+
"""Check if claude-mpm was installed via pipx."""
|
|
347
|
+
try:
|
|
348
|
+
# Check if running from a pipx venv
|
|
349
|
+
if "pipx" in sys.executable.lower():
|
|
350
|
+
return True
|
|
351
|
+
|
|
352
|
+
# Check if claude-mpm-mcp command exists and is from pipx
|
|
353
|
+
mcp_cmd = shutil.which("claude-mpm-mcp")
|
|
354
|
+
if mcp_cmd and "pipx" in mcp_cmd.lower():
|
|
355
|
+
return True
|
|
356
|
+
|
|
357
|
+
# Try to check pipx list
|
|
358
|
+
result = subprocess.run(
|
|
359
|
+
["pipx", "list"],
|
|
360
|
+
capture_output=True,
|
|
361
|
+
text=True,
|
|
362
|
+
timeout=2
|
|
363
|
+
)
|
|
364
|
+
if result.returncode == 0 and "claude-mpm" in result.stdout:
|
|
365
|
+
return True
|
|
366
|
+
|
|
367
|
+
except Exception:
|
|
368
|
+
pass
|
|
369
|
+
|
|
370
|
+
return False
|
|
296
371
|
|
|
297
372
|
|
|
298
373
|
def setup_startup_logging(project_root: Optional[Path] = None) -> Path:
|