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,372 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Gateway Auto-Configuration Service
|
|
3
|
+
======================================
|
|
4
|
+
|
|
5
|
+
Provides automatic MCP configuration for pipx installations with user consent.
|
|
6
|
+
Detects unconfigured MCP setups and offers one-time configuration prompts.
|
|
7
|
+
|
|
8
|
+
WHY: Users installing via pipx should have MCP work out-of-the-box with minimal
|
|
9
|
+
friction. This service detects unconfigured installations and offers automatic
|
|
10
|
+
setup with user consent.
|
|
11
|
+
|
|
12
|
+
DESIGN DECISIONS:
|
|
13
|
+
- Only prompts once (saves preference to avoid repeated prompts)
|
|
14
|
+
- Quick timeout with safe default (no configuration)
|
|
15
|
+
- Non-intrusive with environment variable override
|
|
16
|
+
- Creates backups before modifying any configuration
|
|
17
|
+
- Validates JSON before and after modifications
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
import os
|
|
22
|
+
import sys
|
|
23
|
+
import time
|
|
24
|
+
from datetime import datetime
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from typing import Optional, Dict, Any, Tuple
|
|
27
|
+
|
|
28
|
+
from claude_mpm.core.logger import get_logger
|
|
29
|
+
from claude_mpm.config.paths import paths
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class MCPAutoConfigurator:
|
|
33
|
+
"""
|
|
34
|
+
Handles automatic MCP configuration for pipx installations.
|
|
35
|
+
|
|
36
|
+
Provides a one-time prompt to configure MCP Gateway with user consent,
|
|
37
|
+
making the experience seamless for pipx users while respecting choice.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self):
|
|
41
|
+
"""Initialize the auto-configurator."""
|
|
42
|
+
self.logger = get_logger("MCPAutoConfig")
|
|
43
|
+
self.config_dir = paths.claude_mpm_dir_hidden
|
|
44
|
+
self.preference_file = self.config_dir / "mcp_auto_config_preference.json"
|
|
45
|
+
self.claude_config_path = Path.home() / ".claude.json"
|
|
46
|
+
|
|
47
|
+
def should_auto_configure(self) -> bool:
|
|
48
|
+
"""
|
|
49
|
+
Check if auto-configuration should be attempted.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
True if auto-configuration should be offered, False otherwise
|
|
53
|
+
"""
|
|
54
|
+
# Check environment variable override
|
|
55
|
+
if os.environ.get("CLAUDE_MPM_NO_AUTO_CONFIG"):
|
|
56
|
+
self.logger.debug("Auto-configuration disabled via environment variable")
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
# Check if already configured
|
|
60
|
+
if self._is_mcp_configured():
|
|
61
|
+
self.logger.debug("MCP already configured")
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
# Check if this is a pipx installation
|
|
65
|
+
if not self._is_pipx_installation():
|
|
66
|
+
self.logger.debug("Not a pipx installation")
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
# Check if we've already asked
|
|
70
|
+
if self._has_user_preference():
|
|
71
|
+
self.logger.debug("User preference already saved")
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
return True
|
|
75
|
+
|
|
76
|
+
def _is_mcp_configured(self) -> bool:
|
|
77
|
+
"""Check if MCP is already configured in Claude Code."""
|
|
78
|
+
if not self.claude_config_path.exists():
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
with open(self.claude_config_path, 'r') as f:
|
|
83
|
+
config = json.load(f)
|
|
84
|
+
|
|
85
|
+
# Check if claude-mpm-gateway is configured
|
|
86
|
+
mcp_servers = config.get("mcpServers", {})
|
|
87
|
+
return "claude-mpm-gateway" in mcp_servers
|
|
88
|
+
|
|
89
|
+
except (json.JSONDecodeError, IOError):
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
def _is_pipx_installation(self) -> bool:
|
|
93
|
+
"""Check if claude-mpm is installed via pipx."""
|
|
94
|
+
# Check if running from pipx virtual environment
|
|
95
|
+
if "pipx" in sys.executable.lower():
|
|
96
|
+
return True
|
|
97
|
+
|
|
98
|
+
# Check module path
|
|
99
|
+
try:
|
|
100
|
+
import claude_mpm
|
|
101
|
+
module_path = Path(claude_mpm.__file__).parent
|
|
102
|
+
if "pipx" in str(module_path):
|
|
103
|
+
return True
|
|
104
|
+
except Exception:
|
|
105
|
+
pass
|
|
106
|
+
|
|
107
|
+
# Check for pipx in PATH for claude-mpm command
|
|
108
|
+
try:
|
|
109
|
+
import subprocess
|
|
110
|
+
import platform
|
|
111
|
+
|
|
112
|
+
# Use appropriate command for OS
|
|
113
|
+
if platform.system() == "Windows":
|
|
114
|
+
cmd = ["where", "claude-mpm"]
|
|
115
|
+
else:
|
|
116
|
+
cmd = ["which", "claude-mpm"]
|
|
117
|
+
|
|
118
|
+
result = subprocess.run(
|
|
119
|
+
cmd,
|
|
120
|
+
capture_output=True,
|
|
121
|
+
text=True,
|
|
122
|
+
timeout=2
|
|
123
|
+
)
|
|
124
|
+
if result.returncode == 0 and "pipx" in result.stdout:
|
|
125
|
+
return True
|
|
126
|
+
except Exception:
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
def _has_user_preference(self) -> bool:
|
|
132
|
+
"""Check if user has already been asked about auto-configuration."""
|
|
133
|
+
if not self.preference_file.exists():
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
with open(self.preference_file, 'r') as f:
|
|
138
|
+
prefs = json.load(f)
|
|
139
|
+
return prefs.get("asked", False)
|
|
140
|
+
except (json.JSONDecodeError, IOError):
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
def _save_user_preference(self, choice: str):
|
|
144
|
+
"""Save user's preference to avoid asking again."""
|
|
145
|
+
self.config_dir.mkdir(parents=True, exist_ok=True)
|
|
146
|
+
|
|
147
|
+
prefs = {
|
|
148
|
+
"asked": True,
|
|
149
|
+
"choice": choice,
|
|
150
|
+
"timestamp": datetime.now().isoformat()
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
with open(self.preference_file, 'w') as f:
|
|
155
|
+
json.dump(prefs, f, indent=2)
|
|
156
|
+
except Exception as e:
|
|
157
|
+
self.logger.debug(f"Could not save preference: {e}")
|
|
158
|
+
|
|
159
|
+
def prompt_user(self, timeout: int = 10) -> Optional[bool]:
|
|
160
|
+
"""
|
|
161
|
+
Prompt user for auto-configuration with timeout.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
timeout: Seconds to wait for response (default 10)
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
True if user agrees, False if declines, None if timeout
|
|
168
|
+
"""
|
|
169
|
+
print("\n" + "="*60)
|
|
170
|
+
print("🔧 MCP Gateway Configuration")
|
|
171
|
+
print("="*60)
|
|
172
|
+
print("\nClaude MPM can automatically configure MCP Gateway for")
|
|
173
|
+
print("Claude Code integration. This enables advanced features:")
|
|
174
|
+
print(" • File analysis and summarization")
|
|
175
|
+
print(" • System diagnostics")
|
|
176
|
+
print(" • Ticket management")
|
|
177
|
+
print(" • And more...")
|
|
178
|
+
print("\nWould you like to configure it now? (y/n)")
|
|
179
|
+
print(f"(Auto-declining in {timeout} seconds)")
|
|
180
|
+
|
|
181
|
+
# Use threading for cross-platform timeout support
|
|
182
|
+
import threading
|
|
183
|
+
try:
|
|
184
|
+
# Python 3.7+ has queue built-in
|
|
185
|
+
import queue
|
|
186
|
+
except ImportError:
|
|
187
|
+
# Python 2.x fallback
|
|
188
|
+
import Queue as queue
|
|
189
|
+
|
|
190
|
+
user_input = None
|
|
191
|
+
|
|
192
|
+
def get_input():
|
|
193
|
+
nonlocal user_input
|
|
194
|
+
try:
|
|
195
|
+
user_input = input("> ").strip().lower()
|
|
196
|
+
except (EOFError, KeyboardInterrupt):
|
|
197
|
+
user_input = 'n'
|
|
198
|
+
|
|
199
|
+
# Start input thread
|
|
200
|
+
input_thread = threading.Thread(target=get_input)
|
|
201
|
+
input_thread.daemon = True
|
|
202
|
+
input_thread.start()
|
|
203
|
+
|
|
204
|
+
# Wait for input or timeout
|
|
205
|
+
input_thread.join(timeout)
|
|
206
|
+
|
|
207
|
+
if input_thread.is_alive():
|
|
208
|
+
# Timed out
|
|
209
|
+
print("\n(Timed out - declining)")
|
|
210
|
+
return None
|
|
211
|
+
else:
|
|
212
|
+
# Got input
|
|
213
|
+
if user_input in ['y', 'yes']:
|
|
214
|
+
return True
|
|
215
|
+
else:
|
|
216
|
+
return False
|
|
217
|
+
|
|
218
|
+
def auto_configure(self) -> bool:
|
|
219
|
+
"""
|
|
220
|
+
Perform automatic MCP configuration.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
True if configuration successful, False otherwise
|
|
224
|
+
"""
|
|
225
|
+
try:
|
|
226
|
+
# Create backup if config exists
|
|
227
|
+
if self.claude_config_path.exists():
|
|
228
|
+
backup_path = self._create_backup()
|
|
229
|
+
if backup_path:
|
|
230
|
+
print(f"✅ Backup created: {backup_path}")
|
|
231
|
+
|
|
232
|
+
# Load or create configuration
|
|
233
|
+
config = self._load_or_create_config()
|
|
234
|
+
|
|
235
|
+
# Add MCP Gateway configuration
|
|
236
|
+
if "mcpServers" not in config:
|
|
237
|
+
config["mcpServers"] = {}
|
|
238
|
+
|
|
239
|
+
# Find claude-mpm executable
|
|
240
|
+
executable = self._find_claude_mpm_executable()
|
|
241
|
+
if not executable:
|
|
242
|
+
print("❌ Could not find claude-mpm executable")
|
|
243
|
+
return False
|
|
244
|
+
|
|
245
|
+
# Configure MCP server
|
|
246
|
+
config["mcpServers"]["claude-mpm-gateway"] = {
|
|
247
|
+
"command": str(executable),
|
|
248
|
+
"args": ["mcp", "server"],
|
|
249
|
+
"env": {
|
|
250
|
+
"MCP_MODE": "production"
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
# Save configuration
|
|
255
|
+
with open(self.claude_config_path, 'w') as f:
|
|
256
|
+
json.dump(config, f, indent=2)
|
|
257
|
+
|
|
258
|
+
print(f"✅ Configuration saved to: {self.claude_config_path}")
|
|
259
|
+
print("\n🎉 MCP Gateway configured successfully!")
|
|
260
|
+
print("\nNext steps:")
|
|
261
|
+
print("1. Restart Claude Code (if running)")
|
|
262
|
+
print("2. Look for the MCP icon in the interface")
|
|
263
|
+
print("3. Try @claude-mpm-gateway in a conversation")
|
|
264
|
+
|
|
265
|
+
return True
|
|
266
|
+
|
|
267
|
+
except Exception as e:
|
|
268
|
+
self.logger.error(f"Auto-configuration failed: {e}")
|
|
269
|
+
print(f"❌ Configuration failed: {e}")
|
|
270
|
+
print("\nYou can configure manually with:")
|
|
271
|
+
print(" claude-mpm mcp install")
|
|
272
|
+
return False
|
|
273
|
+
|
|
274
|
+
def _create_backup(self) -> Optional[Path]:
|
|
275
|
+
"""Create backup of existing configuration."""
|
|
276
|
+
try:
|
|
277
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
278
|
+
backup_path = self.claude_config_path.with_suffix(f'.backup.{timestamp}.json')
|
|
279
|
+
|
|
280
|
+
import shutil
|
|
281
|
+
shutil.copy2(self.claude_config_path, backup_path)
|
|
282
|
+
return backup_path
|
|
283
|
+
|
|
284
|
+
except Exception as e:
|
|
285
|
+
self.logger.debug(f"Could not create backup: {e}")
|
|
286
|
+
return None
|
|
287
|
+
|
|
288
|
+
def _load_or_create_config(self) -> Dict[str, Any]:
|
|
289
|
+
"""Load existing config or create new one."""
|
|
290
|
+
if self.claude_config_path.exists():
|
|
291
|
+
try:
|
|
292
|
+
with open(self.claude_config_path, 'r') as f:
|
|
293
|
+
return json.load(f)
|
|
294
|
+
except json.JSONDecodeError:
|
|
295
|
+
self.logger.warning("Existing config is invalid JSON, creating new")
|
|
296
|
+
|
|
297
|
+
return {}
|
|
298
|
+
|
|
299
|
+
def _find_claude_mpm_executable(self) -> Optional[str]:
|
|
300
|
+
"""Find the claude-mpm executable path."""
|
|
301
|
+
# Try direct command first
|
|
302
|
+
import subprocess
|
|
303
|
+
import platform
|
|
304
|
+
|
|
305
|
+
try:
|
|
306
|
+
# Use appropriate command for OS
|
|
307
|
+
if platform.system() == "Windows":
|
|
308
|
+
cmd = ["where", "claude-mpm"]
|
|
309
|
+
else:
|
|
310
|
+
cmd = ["which", "claude-mpm"]
|
|
311
|
+
|
|
312
|
+
result = subprocess.run(
|
|
313
|
+
cmd,
|
|
314
|
+
capture_output=True,
|
|
315
|
+
text=True,
|
|
316
|
+
timeout=2
|
|
317
|
+
)
|
|
318
|
+
if result.returncode == 0:
|
|
319
|
+
executable_path = result.stdout.strip()
|
|
320
|
+
# On Windows, 'where' might return multiple paths
|
|
321
|
+
if platform.system() == "Windows" and '\n' in executable_path:
|
|
322
|
+
executable_path = executable_path.split('\n')[0]
|
|
323
|
+
return executable_path
|
|
324
|
+
except Exception:
|
|
325
|
+
pass
|
|
326
|
+
|
|
327
|
+
# Try to find via shutil.which (more portable)
|
|
328
|
+
import shutil
|
|
329
|
+
claude_mpm_path = shutil.which("claude-mpm")
|
|
330
|
+
if claude_mpm_path:
|
|
331
|
+
return claude_mpm_path
|
|
332
|
+
|
|
333
|
+
# Fallback to Python module invocation
|
|
334
|
+
return sys.executable
|
|
335
|
+
|
|
336
|
+
def run(self) -> bool:
|
|
337
|
+
"""
|
|
338
|
+
Main entry point for auto-configuration.
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
True if configured (or already configured), False otherwise
|
|
342
|
+
"""
|
|
343
|
+
if not self.should_auto_configure():
|
|
344
|
+
return True # Already configured or not applicable
|
|
345
|
+
|
|
346
|
+
# Prompt user
|
|
347
|
+
user_choice = self.prompt_user()
|
|
348
|
+
|
|
349
|
+
# Save preference to not ask again
|
|
350
|
+
self._save_user_preference("yes" if user_choice else "no")
|
|
351
|
+
|
|
352
|
+
if user_choice:
|
|
353
|
+
return self.auto_configure()
|
|
354
|
+
else:
|
|
355
|
+
if user_choice is False: # User explicitly said no
|
|
356
|
+
print("\n📝 You can configure MCP later with:")
|
|
357
|
+
print(" claude-mpm mcp install")
|
|
358
|
+
# If timeout (None), don't show additional message
|
|
359
|
+
return False
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def check_and_configure_mcp() -> bool:
|
|
363
|
+
"""
|
|
364
|
+
Check and potentially configure MCP for pipx installations.
|
|
365
|
+
|
|
366
|
+
This is the main entry point called during CLI initialization.
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
True if MCP is configured (or configuration was successful), False otherwise
|
|
370
|
+
"""
|
|
371
|
+
configurator = MCPAutoConfigurator()
|
|
372
|
+
return configurator.run()
|