claude-mpm 4.5.8__py3-none-any.whl → 4.5.12__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/__init__.py +20 -5
- claude_mpm/agents/agent_loader.py +19 -2
- claude_mpm/agents/base_agent_loader.py +5 -5
- claude_mpm/agents/frontmatter_validator.py +4 -4
- claude_mpm/agents/templates/agent-manager.json +3 -3
- claude_mpm/agents/templates/agentic-coder-optimizer.json +3 -3
- claude_mpm/agents/templates/api_qa.json +1 -1
- claude_mpm/agents/templates/clerk-ops.json +3 -3
- claude_mpm/agents/templates/code_analyzer.json +3 -3
- claude_mpm/agents/templates/dart_engineer.json +294 -0
- claude_mpm/agents/templates/data_engineer.json +3 -3
- claude_mpm/agents/templates/documentation.json +2 -2
- claude_mpm/agents/templates/engineer.json +2 -2
- claude_mpm/agents/templates/gcp_ops_agent.json +2 -2
- claude_mpm/agents/templates/imagemagick.json +1 -1
- claude_mpm/agents/templates/local_ops_agent.json +319 -41
- claude_mpm/agents/templates/memory_manager.json +2 -2
- claude_mpm/agents/templates/nextjs_engineer.json +2 -2
- claude_mpm/agents/templates/ops.json +2 -2
- claude_mpm/agents/templates/php-engineer.json +1 -1
- claude_mpm/agents/templates/project_organizer.json +1 -1
- claude_mpm/agents/templates/prompt-engineer.json +6 -4
- claude_mpm/agents/templates/python_engineer.json +2 -2
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/react_engineer.json +3 -3
- claude_mpm/agents/templates/refactoring_engineer.json +3 -3
- claude_mpm/agents/templates/research.json +2 -2
- claude_mpm/agents/templates/security.json +2 -2
- claude_mpm/agents/templates/ticketing.json +2 -2
- claude_mpm/agents/templates/typescript_engineer.json +2 -2
- claude_mpm/agents/templates/vercel_ops_agent.json +2 -2
- claude_mpm/agents/templates/version_control.json +2 -2
- claude_mpm/agents/templates/web_qa.json +6 -6
- claude_mpm/agents/templates/web_ui.json +3 -3
- claude_mpm/cli/__init__.py +49 -19
- claude_mpm/cli/commands/agent_manager.py +3 -3
- claude_mpm/cli/commands/agents.py +6 -6
- claude_mpm/cli/commands/aggregate.py +4 -4
- claude_mpm/cli/commands/analyze.py +2 -2
- claude_mpm/cli/commands/analyze_code.py +1 -1
- claude_mpm/cli/commands/cleanup.py +3 -3
- claude_mpm/cli/commands/config.py +2 -2
- claude_mpm/cli/commands/configure.py +605 -21
- claude_mpm/cli/commands/dashboard.py +1 -1
- claude_mpm/cli/commands/debug.py +3 -3
- claude_mpm/cli/commands/doctor.py +1 -1
- claude_mpm/cli/commands/mcp.py +7 -7
- claude_mpm/cli/commands/mcp_command_router.py +1 -1
- claude_mpm/cli/commands/mcp_config.py +2 -2
- claude_mpm/cli/commands/mcp_external_commands.py +2 -2
- claude_mpm/cli/commands/mcp_install_commands.py +3 -3
- claude_mpm/cli/commands/mcp_pipx_config.py +2 -2
- claude_mpm/cli/commands/mcp_setup_external.py +3 -3
- claude_mpm/cli/commands/monitor.py +1 -1
- claude_mpm/cli/commands/mpm_init_handler.py +1 -1
- claude_mpm/cli/interactive/agent_wizard.py +1 -1
- claude_mpm/cli/parsers/configure_parser.py +5 -0
- claude_mpm/cli/parsers/search_parser.py +1 -1
- claude_mpm/cli/shared/argument_patterns.py +2 -2
- claude_mpm/cli/shared/base_command.py +1 -1
- claude_mpm/cli/startup_logging.py +4 -4
- claude_mpm/config/experimental_features.py +4 -4
- claude_mpm/config/socketio_config.py +2 -2
- claude_mpm/core/__init__.py +53 -17
- claude_mpm/core/agent_session_manager.py +2 -2
- claude_mpm/core/api_validator.py +3 -3
- claude_mpm/core/base_service.py +10 -1
- claude_mpm/core/cache.py +2 -2
- claude_mpm/core/config.py +5 -5
- claude_mpm/core/config_aliases.py +4 -4
- claude_mpm/core/config_constants.py +1 -1
- claude_mpm/core/error_handler.py +1 -1
- claude_mpm/core/file_utils.py +5 -5
- claude_mpm/core/framework/formatters/capability_generator.py +5 -5
- claude_mpm/core/framework/loaders/agent_loader.py +1 -1
- claude_mpm/core/framework/processors/metadata_processor.py +1 -1
- claude_mpm/core/framework/processors/template_processor.py +3 -3
- claude_mpm/core/framework_loader.py +2 -2
- claude_mpm/core/log_manager.py +11 -4
- claude_mpm/core/logger.py +2 -2
- claude_mpm/core/optimized_startup.py +1 -1
- claude_mpm/core/output_style_manager.py +1 -1
- claude_mpm/core/service_registry.py +2 -2
- claude_mpm/core/session_manager.py +3 -3
- claude_mpm/core/shared/config_loader.py +1 -1
- claude_mpm/core/socketio_pool.py +2 -2
- claude_mpm/core/unified_agent_registry.py +2 -2
- claude_mpm/core/unified_config.py +6 -6
- claude_mpm/core/unified_paths.py +2 -2
- claude_mpm/dashboard/api/simple_directory.py +1 -1
- claude_mpm/generators/agent_profile_generator.py +1 -1
- claude_mpm/hooks/claude_hooks/event_handlers.py +2 -2
- claude_mpm/hooks/claude_hooks/installer.py +9 -9
- claude_mpm/hooks/claude_hooks/response_tracking.py +16 -11
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +16 -13
- claude_mpm/hooks/claude_hooks/tool_analysis.py +2 -2
- claude_mpm/hooks/memory_integration_hook.py +1 -1
- claude_mpm/hooks/validation_hooks.py +1 -1
- claude_mpm/init.py +4 -4
- claude_mpm/models/agent_session.py +1 -1
- claude_mpm/scripts/socketio_daemon.py +5 -5
- claude_mpm/services/__init__.py +145 -161
- claude_mpm/services/agent_capabilities_service.py +1 -1
- claude_mpm/services/agents/agent_builder.py +4 -4
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +1 -1
- claude_mpm/services/agents/deployment/agent_record_service.py +3 -3
- claude_mpm/services/agents/deployment/deployment_config_loader.py +21 -0
- claude_mpm/services/agents/deployment/deployment_wrapper.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +2 -2
- claude_mpm/services/agents/loading/agent_profile_loader.py +2 -2
- claude_mpm/services/agents/loading/base_agent_manager.py +12 -2
- claude_mpm/services/agents/local_template_manager.py +5 -5
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +1 -1
- claude_mpm/services/agents/registry/modification_tracker.py +19 -11
- claude_mpm/services/async_session_logger.py +3 -3
- claude_mpm/services/claude_session_logger.py +4 -4
- claude_mpm/services/cli/agent_listing_service.py +3 -3
- claude_mpm/services/cli/agent_validation_service.py +1 -1
- claude_mpm/services/cli/session_manager.py +2 -2
- claude_mpm/services/core/path_resolver.py +1 -1
- claude_mpm/services/diagnostics/checks/agent_check.py +1 -1
- claude_mpm/services/diagnostics/checks/claude_code_check.py +2 -2
- claude_mpm/services/diagnostics/checks/common_issues_check.py +3 -3
- claude_mpm/services/diagnostics/checks/configuration_check.py +2 -2
- claude_mpm/services/diagnostics/checks/installation_check.py +1 -1
- claude_mpm/services/diagnostics/checks/mcp_check.py +1 -1
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +9 -9
- claude_mpm/services/diagnostics/checks/monitor_check.py +1 -1
- claude_mpm/services/diagnostics/doctor_reporter.py +1 -1
- claude_mpm/services/event_aggregator.py +1 -1
- claude_mpm/services/event_bus/event_bus.py +7 -2
- claude_mpm/services/events/consumers/dead_letter.py +2 -2
- claude_mpm/services/framework_claude_md_generator/__init__.py +1 -1
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +3 -3
- claude_mpm/services/framework_claude_md_generator/version_manager.py +1 -1
- claude_mpm/services/hook_installer_service.py +7 -7
- claude_mpm/services/infrastructure/context_preservation.py +7 -7
- claude_mpm/services/infrastructure/daemon_manager.py +5 -5
- claude_mpm/services/mcp_config_manager.py +169 -48
- claude_mpm/services/mcp_gateway/__init__.py +98 -94
- claude_mpm/services/mcp_gateway/auto_configure.py +5 -5
- claude_mpm/services/mcp_gateway/config/config_loader.py +2 -2
- claude_mpm/services/mcp_gateway/config/configuration.py +3 -3
- claude_mpm/services/mcp_gateway/core/process_pool.py +3 -3
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +2 -2
- claude_mpm/services/mcp_gateway/core/startup_verification.py +1 -1
- claude_mpm/services/mcp_gateway/main.py +1 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +4 -2
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +2 -1
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +1 -1
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +1 -1
- claude_mpm/services/mcp_gateway/tools/hello_world.py +1 -1
- claude_mpm/services/mcp_gateway/utils/package_version_checker.py +5 -5
- claude_mpm/services/mcp_gateway/utils/update_preferences.py +2 -2
- claude_mpm/services/mcp_service_verifier.py +1 -1
- claude_mpm/services/memory/builder.py +1 -1
- claude_mpm/services/memory/cache/shared_prompt_cache.py +2 -1
- claude_mpm/services/memory/indexed_memory.py +3 -3
- claude_mpm/services/monitor/daemon.py +1 -1
- claude_mpm/services/monitor/daemon_manager.py +9 -9
- claude_mpm/services/monitor/event_emitter.py +1 -1
- claude_mpm/services/monitor/handlers/file.py +1 -1
- claude_mpm/services/monitor/handlers/hooks.py +3 -3
- claude_mpm/services/monitor/management/lifecycle.py +7 -7
- claude_mpm/services/monitor/server.py +2 -2
- claude_mpm/services/orphan_detection.py +788 -0
- claude_mpm/services/port_manager.py +2 -2
- claude_mpm/services/project/analyzer.py +3 -3
- claude_mpm/services/project/archive_manager.py +13 -13
- claude_mpm/services/project/dependency_analyzer.py +4 -4
- claude_mpm/services/project/documentation_manager.py +4 -4
- claude_mpm/services/project/enhanced_analyzer.py +8 -8
- claude_mpm/services/project/registry.py +4 -4
- claude_mpm/services/project_port_allocator.py +597 -0
- claude_mpm/services/response_tracker.py +1 -1
- claude_mpm/services/session_management_service.py +1 -1
- claude_mpm/services/session_manager.py +6 -4
- claude_mpm/services/socketio/event_normalizer.py +1 -1
- claude_mpm/services/socketio/handlers/code_analysis.py +14 -12
- claude_mpm/services/socketio/handlers/file.py +1 -1
- claude_mpm/services/socketio/migration_utils.py +1 -1
- claude_mpm/services/socketio/server/core.py +1 -1
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +1 -1
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +4 -4
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +1 -1
- claude_mpm/services/unified/config_strategies/config_schema.py +4 -4
- claude_mpm/services/unified/config_strategies/context_strategy.py +6 -6
- claude_mpm/services/unified/config_strategies/error_handling_strategy.py +10 -10
- claude_mpm/services/unified/config_strategies/file_loader_strategy.py +5 -5
- claude_mpm/services/unified/config_strategies/unified_config_service.py +8 -8
- claude_mpm/services/unified/config_strategies/validation_strategy.py +15 -15
- claude_mpm/services/unified/deployment_strategies/base.py +4 -4
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +15 -15
- claude_mpm/services/unified/deployment_strategies/local.py +9 -9
- claude_mpm/services/unified/deployment_strategies/utils.py +9 -9
- claude_mpm/services/unified/deployment_strategies/vercel.py +7 -7
- claude_mpm/services/unified/unified_config.py +5 -5
- claude_mpm/services/unified/unified_deployment.py +2 -2
- claude_mpm/services/utility_service.py +1 -1
- claude_mpm/services/version_control/conflict_resolution.py +2 -2
- claude_mpm/services/version_control/git_operations.py +3 -3
- claude_mpm/services/version_control/semantic_versioning.py +13 -13
- claude_mpm/services/version_control/version_parser.py +1 -1
- claude_mpm/storage/state_storage.py +12 -13
- claude_mpm/tools/code_tree_analyzer.py +5 -5
- claude_mpm/tools/code_tree_builder.py +4 -4
- claude_mpm/tools/socketio_debug.py +1 -1
- claude_mpm/utils/agent_dependency_loader.py +4 -4
- claude_mpm/utils/common.py +2 -2
- claude_mpm/utils/config_manager.py +3 -3
- claude_mpm/utils/dependency_cache.py +2 -2
- claude_mpm/utils/dependency_strategies.py +6 -6
- claude_mpm/utils/file_utils.py +11 -11
- claude_mpm/utils/log_cleanup.py +1 -1
- claude_mpm/utils/path_operations.py +1 -1
- claude_mpm/validation/agent_validator.py +2 -2
- claude_mpm/validation/frontmatter_validator.py +1 -1
- {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/METADATA +1 -1
- {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/RECORD +226 -223
- {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/WHEEL +0 -0
- {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/top_level.txt +0 -0
@@ -80,7 +80,7 @@ class SocketIODaemonManager:
|
|
80
80
|
return False
|
81
81
|
|
82
82
|
try:
|
83
|
-
with
|
83
|
+
with self.pid_file.open() as f:
|
84
84
|
pid = int(f.read().strip())
|
85
85
|
|
86
86
|
# Check if process exists and is running
|
@@ -135,7 +135,7 @@ class SocketIODaemonManager:
|
|
135
135
|
pid = os.fork()
|
136
136
|
if pid > 0:
|
137
137
|
# Parent process - save PID and exit
|
138
|
-
with
|
138
|
+
with self.pid_file.open("w") as f:
|
139
139
|
f.write(str(pid))
|
140
140
|
logger.info(f"Socket.IO server started as daemon (PID: {pid})")
|
141
141
|
return True
|
@@ -155,7 +155,7 @@ class SocketIODaemonManager:
|
|
155
155
|
os.umask(0)
|
156
156
|
|
157
157
|
# Redirect stdout/stderr to log file
|
158
|
-
with
|
158
|
+
with self.log_file.open("a") as log:
|
159
159
|
os.dup2(log.fileno(), sys.stdout.fileno())
|
160
160
|
os.dup2(log.fileno(), sys.stderr.fileno())
|
161
161
|
|
@@ -199,7 +199,7 @@ class SocketIODaemonManager:
|
|
199
199
|
return False
|
200
200
|
|
201
201
|
try:
|
202
|
-
with
|
202
|
+
with self.pid_file.open() as f:
|
203
203
|
pid = int(f.read().strip())
|
204
204
|
|
205
205
|
logger.info(f"Stopping Socket.IO server (PID: {pid})")
|
@@ -254,7 +254,7 @@ class SocketIODaemonManager:
|
|
254
254
|
}
|
255
255
|
|
256
256
|
if status_info["running"]:
|
257
|
-
with
|
257
|
+
with self.pid_file.open() as f:
|
258
258
|
status_info["pid"] = int(f.read().strip())
|
259
259
|
|
260
260
|
# Check port accessibility
|
@@ -44,7 +44,9 @@ class MCPConfigManager:
|
|
44
44
|
# Known missing dependencies for MCP services that pipx doesn't handle automatically
|
45
45
|
# Maps service names to list of missing dependencies that need injection
|
46
46
|
SERVICE_MISSING_DEPENDENCIES = {
|
47
|
-
"mcp-ticketer": [
|
47
|
+
"mcp-ticketer": [
|
48
|
+
"gql"
|
49
|
+
], # mcp-ticketer v0.1.8+ needs gql but doesn't declare it
|
48
50
|
# Add more services here as needed, e.g.:
|
49
51
|
# "another-service": ["dep1", "dep2"],
|
50
52
|
}
|
@@ -80,15 +82,75 @@ class MCPConfigManager:
|
|
80
82
|
},
|
81
83
|
}
|
82
84
|
|
83
|
-
def __init__(self):
|
84
|
-
"""Initialize the MCP configuration manager.
|
85
|
+
def __init__(self, config=None):
|
86
|
+
"""Initialize the MCP configuration manager.
|
87
|
+
|
88
|
+
Args:
|
89
|
+
config: Optional Config object for filtering services
|
90
|
+
"""
|
85
91
|
self.logger = get_logger(__name__)
|
86
92
|
self.pipx_base = Path.home() / ".local" / "pipx" / "venvs"
|
87
93
|
self.project_root = Path.cwd()
|
88
94
|
|
95
|
+
# Validate config type if provided
|
96
|
+
if config is not None:
|
97
|
+
from ..core.config import Config
|
98
|
+
|
99
|
+
if not isinstance(config, Config):
|
100
|
+
self.logger.warning(
|
101
|
+
f"Invalid config type provided to MCPConfigManager: "
|
102
|
+
f"{type(config).__name__}. Expected Config. "
|
103
|
+
f"Proceeding with config=None (all services enabled)."
|
104
|
+
)
|
105
|
+
config = None
|
106
|
+
|
107
|
+
self.config = config
|
108
|
+
|
89
109
|
# Use the proper Claude config file location
|
90
110
|
self.claude_config_path = ConfigLocation.CLAUDE_JSON.value
|
91
111
|
|
112
|
+
def should_enable_service(self, service_name: str) -> bool:
|
113
|
+
"""
|
114
|
+
Check if an MCP service should be enabled based on startup configuration.
|
115
|
+
|
116
|
+
Args:
|
117
|
+
service_name: Name of the MCP service
|
118
|
+
|
119
|
+
Returns:
|
120
|
+
True if the service should be enabled, False otherwise
|
121
|
+
"""
|
122
|
+
# If no config provided, enable all services by default
|
123
|
+
if self.config is None:
|
124
|
+
return True
|
125
|
+
|
126
|
+
# Import Config here to avoid circular import at module level
|
127
|
+
from ..core.config import Config
|
128
|
+
|
129
|
+
# Validate config type
|
130
|
+
if not isinstance(self.config, Config):
|
131
|
+
self.logger.warning(
|
132
|
+
f"Invalid config type: {type(self.config).__name__}, "
|
133
|
+
f"expected Config. Enabling all services by default."
|
134
|
+
)
|
135
|
+
return True
|
136
|
+
|
137
|
+
# Get startup configuration
|
138
|
+
enabled_services = self.config.get("startup.enabled_mcp_services", None)
|
139
|
+
|
140
|
+
# If no startup preferences configured, enable all services
|
141
|
+
if enabled_services is None:
|
142
|
+
return True
|
143
|
+
|
144
|
+
# Check if this service is in the enabled list
|
145
|
+
is_enabled = service_name in enabled_services
|
146
|
+
|
147
|
+
if not is_enabled:
|
148
|
+
self.logger.debug(
|
149
|
+
f"MCP service '{service_name}' disabled by startup configuration"
|
150
|
+
)
|
151
|
+
|
152
|
+
return is_enabled
|
153
|
+
|
92
154
|
def detect_service_path(self, service_name: str) -> Optional[str]:
|
93
155
|
"""
|
94
156
|
Detect the best path for an MCP service.
|
@@ -137,7 +199,7 @@ class MCPConfigManager:
|
|
137
199
|
f"Found kuzu-memory with MCP support at {path}"
|
138
200
|
)
|
139
201
|
return path
|
140
|
-
except:
|
202
|
+
except (subprocess.SubprocessError, subprocess.TimeoutExpired, OSError):
|
141
203
|
pass
|
142
204
|
|
143
205
|
# If no MCP-capable version found, log warning but return None
|
@@ -509,7 +571,7 @@ class MCPConfigManager:
|
|
509
571
|
if result.returncode == 0 or "version" in result.stdout.lower():
|
510
572
|
use_pipx_run = True
|
511
573
|
self.logger.debug(f"Will use 'pipx run' for {service_name}")
|
512
|
-
except:
|
574
|
+
except (subprocess.SubprocessError, subprocess.TimeoutExpired, OSError):
|
513
575
|
pass
|
514
576
|
|
515
577
|
# Try uvx if pipx run not available
|
@@ -525,7 +587,7 @@ class MCPConfigManager:
|
|
525
587
|
if result.returncode == 0 or "version" in result.stdout.lower():
|
526
588
|
use_uvx = True
|
527
589
|
self.logger.debug(f"Will use 'uvx' for {service_name}")
|
528
|
-
except:
|
590
|
+
except (subprocess.SubprocessError, subprocess.TimeoutExpired, OSError):
|
529
591
|
pass
|
530
592
|
|
531
593
|
# If neither work, try to find direct path
|
@@ -657,7 +719,7 @@ class MCPConfigManager:
|
|
657
719
|
claude_config = {}
|
658
720
|
if self.claude_config_path.exists():
|
659
721
|
try:
|
660
|
-
with
|
722
|
+
with self.claude_config_path.open() as f:
|
661
723
|
claude_config = json.load(f)
|
662
724
|
except Exception as e:
|
663
725
|
self.logger.error(f"Error reading {self.claude_config_path}: {e}")
|
@@ -668,10 +730,8 @@ class MCPConfigManager:
|
|
668
730
|
claude_config["projects"] = {}
|
669
731
|
updated = True
|
670
732
|
|
671
|
-
#
|
672
|
-
|
673
|
-
if not fix_success:
|
674
|
-
self.logger.warning(f"Some MCP services could not be fixed: {fix_message}")
|
733
|
+
# Note: fix_mcp_service_issues() is already called during CLI initialization
|
734
|
+
# Calling it here would duplicate the service health checks
|
675
735
|
|
676
736
|
# Process ALL projects in the config, not just current one
|
677
737
|
projects_to_update = list(claude_config.get("projects", {}).keys())
|
@@ -704,19 +764,10 @@ class MCPConfigManager:
|
|
704
764
|
project_config["mcpServers"] = {}
|
705
765
|
updated = True
|
706
766
|
|
707
|
-
# Check and fix each service configuration
|
708
|
-
|
709
|
-
# Get the correct static configuration with project-specific paths
|
710
|
-
correct_config = self.get_static_service_config(
|
711
|
-
service_name, project_key
|
712
|
-
)
|
713
|
-
|
714
|
-
if not correct_config:
|
715
|
-
self.logger.warning(
|
716
|
-
f"No static config available for {service_name}"
|
717
|
-
)
|
718
|
-
continue
|
767
|
+
# Check and fix each service configuration - now filtered by startup config
|
768
|
+
services_to_configure = self.get_filtered_services()
|
719
769
|
|
770
|
+
for service_name, correct_config in services_to_configure.items():
|
720
771
|
# Check if service exists and has correct configuration
|
721
772
|
existing_config = project_config["mcpServers"].get(service_name)
|
722
773
|
|
@@ -742,6 +793,29 @@ class MCPConfigManager:
|
|
742
793
|
f"Updated MCP service config for {service_name} in project {Path(project_key).name}"
|
743
794
|
)
|
744
795
|
|
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
|
800
|
+
|
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
|
+
|
745
819
|
# Write updated config if changes were made
|
746
820
|
if updated:
|
747
821
|
try:
|
@@ -758,7 +832,7 @@ class MCPConfigManager:
|
|
758
832
|
self.logger.debug(f"Created backup: {backup_path}")
|
759
833
|
|
760
834
|
# Write updated config
|
761
|
-
with
|
835
|
+
with self.claude_config_path.open("w") as f:
|
762
836
|
json.dump(claude_config, f, indent=2)
|
763
837
|
|
764
838
|
messages = []
|
@@ -810,7 +884,7 @@ class MCPConfigManager:
|
|
810
884
|
existing_config = {}
|
811
885
|
if mcp_config_path.exists():
|
812
886
|
try:
|
813
|
-
with open(
|
887
|
+
with mcp_config_path.open() as f:
|
814
888
|
existing_config = json.load(f)
|
815
889
|
except Exception as e:
|
816
890
|
self.logger.error(f"Error reading existing config: {e}")
|
@@ -838,7 +912,7 @@ class MCPConfigManager:
|
|
838
912
|
|
839
913
|
# Write the updated configuration
|
840
914
|
try:
|
841
|
-
with open(
|
915
|
+
with mcp_config_path.open("w") as f:
|
842
916
|
json.dump(new_config, f, indent=2)
|
843
917
|
|
844
918
|
if missing_services:
|
@@ -863,7 +937,7 @@ class MCPConfigManager:
|
|
863
937
|
mcp_config_path = self.project_root / ConfigLocation.PROJECT_MCP.value
|
864
938
|
if mcp_config_path.exists():
|
865
939
|
try:
|
866
|
-
with open(
|
940
|
+
with mcp_config_path.open() as f:
|
867
941
|
config = json.load(f)
|
868
942
|
results = {}
|
869
943
|
for service_name, service_config in config.get(
|
@@ -877,7 +951,7 @@ class MCPConfigManager:
|
|
877
951
|
return {}
|
878
952
|
|
879
953
|
try:
|
880
|
-
with
|
954
|
+
with self.claude_config_path.open() as f:
|
881
955
|
claude_config = json.load(f)
|
882
956
|
|
883
957
|
# Get project's MCP servers
|
@@ -953,7 +1027,9 @@ class MCPConfigManager:
|
|
953
1027
|
if result.returncode == 0:
|
954
1028
|
# Inject any missing dependencies if needed
|
955
1029
|
if service_name in self.SERVICE_MISSING_DEPENDENCIES:
|
956
|
-
self.logger.debug(
|
1030
|
+
self.logger.debug(
|
1031
|
+
f"Injecting missing dependencies for newly installed {service_name}..."
|
1032
|
+
)
|
957
1033
|
self._inject_missing_dependencies(service_name)
|
958
1034
|
|
959
1035
|
# Verify installation worked
|
@@ -1063,18 +1139,19 @@ class MCPConfigManager:
|
|
1063
1139
|
Returns:
|
1064
1140
|
Tuple of (success, message)
|
1065
1141
|
"""
|
1066
|
-
self.logger.info("🔍 Checking MCP services for issues...")
|
1067
|
-
|
1068
1142
|
services_to_fix = []
|
1069
1143
|
fixed_services = []
|
1070
1144
|
failed_services = []
|
1071
1145
|
|
1072
1146
|
# Check each service for issues
|
1073
1147
|
for service_name in self.PIPX_SERVICES:
|
1148
|
+
self.logger.info(f"🔍 Checking {service_name} for issues...")
|
1074
1149
|
issue_type = self._detect_service_issue(service_name)
|
1075
1150
|
if issue_type:
|
1076
1151
|
services_to_fix.append((service_name, issue_type))
|
1077
|
-
self.logger.
|
1152
|
+
self.logger.info(f" ⚠️ Found issue with {service_name}: {issue_type}")
|
1153
|
+
else:
|
1154
|
+
self.logger.debug(f" ✅ {service_name} is functioning correctly")
|
1078
1155
|
|
1079
1156
|
if not services_to_fix:
|
1080
1157
|
return True, "All MCP services are functioning correctly"
|
@@ -1118,14 +1195,20 @@ class MCPConfigManager:
|
|
1118
1195
|
issue_after_injection = self._detect_service_issue(service_name)
|
1119
1196
|
if issue_after_injection is None:
|
1120
1197
|
fixed_services.append(f"{service_name} (dependencies injected)")
|
1121
|
-
self.logger.info(
|
1198
|
+
self.logger.info(
|
1199
|
+
f" ✅ Fixed {service_name} with dependency injection"
|
1200
|
+
)
|
1122
1201
|
continue # Move to next service
|
1123
1202
|
|
1124
1203
|
# If injection alone didn't work, try full reinstall
|
1125
|
-
self.logger.info(
|
1204
|
+
self.logger.info(
|
1205
|
+
" Dependency injection insufficient, trying full reinstall..."
|
1206
|
+
)
|
1126
1207
|
success = self._auto_reinstall_mcp_service(service_name)
|
1127
1208
|
if success:
|
1128
|
-
fixed_services.append(
|
1209
|
+
fixed_services.append(
|
1210
|
+
f"{service_name} (auto-reinstalled with dependencies)"
|
1211
|
+
)
|
1129
1212
|
else:
|
1130
1213
|
# Provide specific manual fix for known services
|
1131
1214
|
if service_name == "mcp-ticketer":
|
@@ -1164,7 +1247,12 @@ class MCPConfigManager:
|
|
1164
1247
|
for failed in failed_services:
|
1165
1248
|
service = failed.split(" ")[0]
|
1166
1249
|
if service in self.SERVICE_MISSING_DEPENDENCIES:
|
1167
|
-
deps = " ".join(
|
1250
|
+
deps = " ".join(
|
1251
|
+
[
|
1252
|
+
f"&& pipx inject {service} {dep}"
|
1253
|
+
for dep in self.SERVICE_MISSING_DEPENDENCIES[service]
|
1254
|
+
]
|
1255
|
+
)
|
1168
1256
|
message += f"\n • {service}: pipx uninstall {service} && pipx install {service} {deps}"
|
1169
1257
|
else:
|
1170
1258
|
message += f"\n • {service}: pipx uninstall {service} && pipx install {service}"
|
@@ -1191,7 +1279,9 @@ class MCPConfigManager:
|
|
1191
1279
|
if pipx_venv_bin.exists():
|
1192
1280
|
# Test the installed version directly (has injected dependencies)
|
1193
1281
|
# This avoids using pipx run which downloads a fresh cache copy without dependencies
|
1194
|
-
self.logger.debug(
|
1282
|
+
self.logger.debug(
|
1283
|
+
f" Testing {service_name} from installed pipx venv: {pipx_venv_bin}"
|
1284
|
+
)
|
1195
1285
|
result = subprocess.run(
|
1196
1286
|
[str(pipx_venv_bin), "--help"],
|
1197
1287
|
capture_output=True,
|
@@ -1225,19 +1315,25 @@ class MCPConfigManager:
|
|
1225
1315
|
or "help" in combined_output
|
1226
1316
|
or result.returncode in [0, 1]
|
1227
1317
|
):
|
1228
|
-
self.logger.debug(
|
1318
|
+
self.logger.debug(
|
1319
|
+
f" {service_name} is working correctly (installed in venv)"
|
1320
|
+
)
|
1229
1321
|
return None # Service is working
|
1230
1322
|
|
1231
1323
|
# Unknown issue
|
1232
1324
|
if result.returncode not in [0, 1]:
|
1233
|
-
self.logger.debug(
|
1325
|
+
self.logger.debug(
|
1326
|
+
f"{service_name} returned unexpected exit code: {result.returncode}"
|
1327
|
+
)
|
1234
1328
|
return "unknown_error"
|
1235
1329
|
|
1236
1330
|
return None # Default to working if no issues detected
|
1237
1331
|
|
1238
1332
|
# Service not installed in pipx venv - use pipx run for detection
|
1239
1333
|
# Note: pipx run uses cache which may not have injected dependencies
|
1240
|
-
self.logger.debug(
|
1334
|
+
self.logger.debug(
|
1335
|
+
f" Testing {service_name} via pipx run (not installed in venv)"
|
1336
|
+
)
|
1241
1337
|
result = subprocess.run(
|
1242
1338
|
["pipx", "run", service_name, "--help"],
|
1243
1339
|
capture_output=True,
|
@@ -1265,7 +1361,9 @@ class MCPConfigManager:
|
|
1265
1361
|
):
|
1266
1362
|
# Don't report missing_dependency for cache version - it may be missing injected deps
|
1267
1363
|
# Just report that service needs to be installed properly
|
1268
|
-
self.logger.debug(
|
1364
|
+
self.logger.debug(
|
1365
|
+
f"{service_name} has import errors in pipx run cache - needs proper installation"
|
1366
|
+
)
|
1269
1367
|
return "not_installed"
|
1270
1368
|
|
1271
1369
|
# Path issues
|
@@ -1331,7 +1429,9 @@ class MCPConfigManager:
|
|
1331
1429
|
if install_result.returncode == 0:
|
1332
1430
|
# Inject any missing dependencies if needed
|
1333
1431
|
if service_name in self.SERVICE_MISSING_DEPENDENCIES:
|
1334
|
-
self.logger.debug(
|
1432
|
+
self.logger.debug(
|
1433
|
+
f"Injecting missing dependencies for {service_name}..."
|
1434
|
+
)
|
1335
1435
|
self._inject_missing_dependencies(service_name)
|
1336
1436
|
|
1337
1437
|
# Verify the reinstall worked
|
@@ -1394,12 +1494,13 @@ class MCPConfigManager:
|
|
1394
1494
|
if result.returncode == 0:
|
1395
1495
|
self.logger.info(f" ✅ Successfully injected {dep}")
|
1396
1496
|
# Check if already injected (pipx will complain if package already exists)
|
1397
|
-
elif
|
1497
|
+
elif (
|
1498
|
+
"already satisfied" in result.stderr.lower()
|
1499
|
+
or "already installed" in result.stderr.lower()
|
1500
|
+
):
|
1398
1501
|
self.logger.debug(f" {dep} already present in {service_name}")
|
1399
1502
|
else:
|
1400
|
-
self.logger.error(
|
1401
|
-
f" Failed to inject {dep}: {result.stderr}"
|
1402
|
-
)
|
1503
|
+
self.logger.error(f" Failed to inject {dep}: {result.stderr}")
|
1403
1504
|
all_successful = False
|
1404
1505
|
|
1405
1506
|
except subprocess.TimeoutExpired:
|
@@ -1467,7 +1568,9 @@ class MCPConfigManager:
|
|
1467
1568
|
|
1468
1569
|
# Inject any missing dependencies that pipx doesn't handle automatically
|
1469
1570
|
if service_name in self.SERVICE_MISSING_DEPENDENCIES:
|
1470
|
-
self.logger.info(
|
1571
|
+
self.logger.info(
|
1572
|
+
f" → Fixing missing dependencies for {service_name}..."
|
1573
|
+
)
|
1471
1574
|
if not self._inject_missing_dependencies(service_name):
|
1472
1575
|
self.logger.warning(
|
1473
1576
|
f"Failed to inject all dependencies for {service_name}, but continuing..."
|
@@ -1535,7 +1638,7 @@ class MCPConfigManager:
|
|
1535
1638
|
if result.returncode == 0 or "version" in result.stdout.lower():
|
1536
1639
|
self.logger.debug(f"{service_name} accessible via 'pipx run'")
|
1537
1640
|
return True
|
1538
|
-
except:
|
1641
|
+
except (subprocess.SubprocessError, subprocess.TimeoutExpired, OSError):
|
1539
1642
|
pass
|
1540
1643
|
return False
|
1541
1644
|
|
@@ -1608,3 +1711,21 @@ class MCPConfigManager:
|
|
1608
1711
|
|
1609
1712
|
# For other services, try pipx run
|
1610
1713
|
return None
|
1714
|
+
|
1715
|
+
def get_filtered_services(self) -> Dict[str, Dict]:
|
1716
|
+
"""Get all MCP service configurations filtered by startup configuration.
|
1717
|
+
|
1718
|
+
Returns:
|
1719
|
+
Dictionary of service configurations, filtered based on startup settings
|
1720
|
+
"""
|
1721
|
+
filtered_services = {}
|
1722
|
+
|
1723
|
+
for service_name in self.STATIC_MCP_CONFIGS:
|
1724
|
+
if self.should_enable_service(service_name):
|
1725
|
+
# Get the actual service configuration with proper paths
|
1726
|
+
service_config = self.get_static_service_config(service_name)
|
1727
|
+
if service_config:
|
1728
|
+
filtered_services[service_name] = service_config
|
1729
|
+
# Removed noisy debug logging that was called multiple times per startup
|
1730
|
+
|
1731
|
+
return filtered_services
|
@@ -26,100 +26,104 @@ __version__ = "0.1.0"
|
|
26
26
|
|
27
27
|
|
28
28
|
# Lazy imports to prevent circular dependencies and improve startup performance
|
29
|
-
def __getattr__(name):
|
30
|
-
"""Lazy import mechanism for MCP Gateway components."""
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
29
|
+
def __getattr__(name):
|
30
|
+
"""Lazy import mechanism for MCP Gateway components using dictionary-based mapping."""
|
31
|
+
from importlib import import_module
|
32
|
+
|
33
|
+
# Dictionary mapping: name -> (module_path, attribute_name)
|
34
|
+
_LAZY_IMPORTS = {
|
35
|
+
# Core interfaces and base classes
|
36
|
+
"IMCPGateway": (
|
37
|
+
"claude_mpm.services.mcp_gateway.core.interfaces",
|
38
|
+
"IMCPGateway",
|
39
|
+
),
|
40
|
+
"IMCPToolRegistry": (
|
41
|
+
"claude_mpm.services.mcp_gateway.core.interfaces",
|
42
|
+
"IMCPToolRegistry",
|
43
|
+
),
|
44
|
+
"IMCPConfiguration": (
|
45
|
+
"claude_mpm.services.mcp_gateway.core.interfaces",
|
46
|
+
"IMCPConfiguration",
|
47
|
+
),
|
48
|
+
"IMCPToolAdapter": (
|
49
|
+
"claude_mpm.services.mcp_gateway.core.interfaces",
|
50
|
+
"IMCPToolAdapter",
|
51
|
+
),
|
52
|
+
"BaseMCPService": (
|
53
|
+
"claude_mpm.services.mcp_gateway.core.base",
|
54
|
+
"BaseMCPService",
|
55
|
+
),
|
56
|
+
# Gateway implementations
|
57
|
+
"MCPGateway": (
|
58
|
+
"claude_mpm.services.mcp_gateway.server.mcp_gateway",
|
59
|
+
"MCPGateway",
|
60
|
+
),
|
61
|
+
"StdioHandler": (
|
62
|
+
"claude_mpm.services.mcp_gateway.server.stdio_handler",
|
63
|
+
"StdioHandler",
|
64
|
+
),
|
65
|
+
"AlternativeStdioHandler": (
|
66
|
+
"claude_mpm.services.mcp_gateway.server.stdio_handler",
|
67
|
+
"AlternativeStdioHandler",
|
68
|
+
),
|
69
|
+
# Tool registry and adapters
|
70
|
+
"ToolRegistry": (
|
71
|
+
"claude_mpm.services.mcp_gateway.registry.tool_registry",
|
72
|
+
"ToolRegistry",
|
73
|
+
),
|
74
|
+
"BaseToolAdapter": (
|
75
|
+
"claude_mpm.services.mcp_gateway.tools.base_adapter",
|
76
|
+
"BaseToolAdapter",
|
77
|
+
),
|
78
|
+
"EchoToolAdapter": (
|
79
|
+
"claude_mpm.services.mcp_gateway.tools.base_adapter",
|
80
|
+
"EchoToolAdapter",
|
81
|
+
),
|
82
|
+
"CalculatorToolAdapter": (
|
83
|
+
"claude_mpm.services.mcp_gateway.tools.base_adapter",
|
84
|
+
"CalculatorToolAdapter",
|
85
|
+
),
|
86
|
+
"SystemInfoToolAdapter": (
|
87
|
+
"claude_mpm.services.mcp_gateway.tools.base_adapter",
|
88
|
+
"SystemInfoToolAdapter",
|
89
|
+
),
|
90
|
+
# Configuration management
|
91
|
+
"MCPConfiguration": (
|
92
|
+
"claude_mpm.services.mcp_gateway.config.configuration",
|
93
|
+
"MCPConfiguration",
|
94
|
+
),
|
95
|
+
"MCPConfigLoader": (
|
96
|
+
"claude_mpm.services.mcp_gateway.config.config_loader",
|
97
|
+
"MCPConfigLoader",
|
98
|
+
),
|
99
|
+
# Service registry
|
100
|
+
"MCPServiceRegistry": (
|
101
|
+
"claude_mpm.services.mcp_gateway.registry.service_registry",
|
102
|
+
"MCPServiceRegistry",
|
103
|
+
),
|
104
|
+
# Exceptions
|
105
|
+
"MCPException": (
|
106
|
+
"claude_mpm.services.mcp_gateway.core.exceptions",
|
107
|
+
"MCPException",
|
108
|
+
),
|
109
|
+
"MCPConfigurationError": (
|
110
|
+
"claude_mpm.services.mcp_gateway.core.exceptions",
|
111
|
+
"MCPConfigurationError",
|
112
|
+
),
|
113
|
+
"MCPToolNotFoundError": (
|
114
|
+
"claude_mpm.services.mcp_gateway.core.exceptions",
|
115
|
+
"MCPToolNotFoundError",
|
116
|
+
),
|
117
|
+
"MCPServerError": (
|
118
|
+
"claude_mpm.services.mcp_gateway.core.exceptions",
|
119
|
+
"MCPServerError",
|
120
|
+
),
|
121
|
+
}
|
122
|
+
|
123
|
+
if name in _LAZY_IMPORTS:
|
124
|
+
module_path, attr_name = _LAZY_IMPORTS[name]
|
125
|
+
module = import_module(module_path)
|
126
|
+
return getattr(module, attr_name)
|
123
127
|
|
124
128
|
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
125
129
|
|