claude-mpm 4.0.23__py3-none-any.whl → 4.0.28__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. claude_mpm/BUILD_NUMBER +1 -1
  2. claude_mpm/VERSION +1 -1
  3. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +4 -1
  4. claude_mpm/agents/BASE_PM.md +3 -0
  5. claude_mpm/agents/templates/code_analyzer.json +2 -2
  6. claude_mpm/cli/commands/agents.py +453 -113
  7. claude_mpm/cli/commands/aggregate.py +107 -15
  8. claude_mpm/cli/commands/cleanup.py +142 -10
  9. claude_mpm/cli/commands/config.py +358 -224
  10. claude_mpm/cli/commands/info.py +184 -75
  11. claude_mpm/cli/commands/mcp_command_router.py +5 -76
  12. claude_mpm/cli/commands/mcp_install_commands.py +68 -36
  13. claude_mpm/cli/commands/mcp_server_commands.py +30 -37
  14. claude_mpm/cli/commands/memory.py +331 -61
  15. claude_mpm/cli/commands/monitor.py +101 -7
  16. claude_mpm/cli/commands/run.py +368 -8
  17. claude_mpm/cli/commands/tickets.py +206 -24
  18. claude_mpm/cli/parsers/mcp_parser.py +3 -0
  19. claude_mpm/cli/shared/__init__.py +40 -0
  20. claude_mpm/cli/shared/argument_patterns.py +212 -0
  21. claude_mpm/cli/shared/command_base.py +234 -0
  22. claude_mpm/cli/shared/error_handling.py +238 -0
  23. claude_mpm/cli/shared/output_formatters.py +231 -0
  24. claude_mpm/config/agent_config.py +29 -8
  25. claude_mpm/core/container.py +6 -4
  26. claude_mpm/core/service_registry.py +4 -2
  27. claude_mpm/core/shared/__init__.py +17 -0
  28. claude_mpm/core/shared/config_loader.py +320 -0
  29. claude_mpm/core/shared/path_resolver.py +277 -0
  30. claude_mpm/core/shared/singleton_manager.py +208 -0
  31. claude_mpm/hooks/claude_hooks/memory_integration.py +4 -2
  32. claude_mpm/hooks/claude_hooks/response_tracking.py +14 -3
  33. claude_mpm/hooks/memory_integration_hook.py +11 -2
  34. claude_mpm/services/agents/deployment/agent_deployment.py +49 -23
  35. claude_mpm/services/agents/deployment/deployment_wrapper.py +71 -0
  36. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +1 -0
  37. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +43 -0
  38. claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +4 -0
  39. claude_mpm/services/agents/deployment/processors/agent_processor.py +1 -1
  40. claude_mpm/services/agents/loading/base_agent_manager.py +11 -3
  41. claude_mpm/services/agents/registry/deployed_agent_discovery.py +14 -5
  42. claude_mpm/services/event_aggregator.py +4 -2
  43. claude_mpm/services/mcp_gateway/config/config_loader.py +89 -28
  44. claude_mpm/services/mcp_gateway/config/configuration.py +29 -0
  45. claude_mpm/services/mcp_gateway/registry/service_registry.py +22 -5
  46. claude_mpm/services/memory/builder.py +6 -1
  47. claude_mpm/services/response_tracker.py +3 -1
  48. claude_mpm/services/runner_configuration_service.py +15 -6
  49. claude_mpm/services/shared/__init__.py +20 -0
  50. claude_mpm/services/shared/async_service_base.py +219 -0
  51. claude_mpm/services/shared/config_service_base.py +292 -0
  52. claude_mpm/services/shared/lifecycle_service_base.py +317 -0
  53. claude_mpm/services/shared/manager_base.py +303 -0
  54. claude_mpm/services/shared/service_factory.py +308 -0
  55. {claude_mpm-4.0.23.dist-info → claude_mpm-4.0.28.dist-info}/METADATA +19 -13
  56. {claude_mpm-4.0.23.dist-info → claude_mpm-4.0.28.dist-info}/RECORD +60 -44
  57. {claude_mpm-4.0.23.dist-info → claude_mpm-4.0.28.dist-info}/WHEEL +0 -0
  58. {claude_mpm-4.0.23.dist-info → claude_mpm-4.0.28.dist-info}/entry_points.txt +0 -0
  59. {claude_mpm-4.0.23.dist-info → claude_mpm-4.0.28.dist-info}/licenses/LICENSE +0 -0
  60. {claude_mpm-4.0.23.dist-info → claude_mpm-4.0.28.dist-info}/top_level.txt +0 -0
@@ -45,6 +45,7 @@ from claude_mpm.core.logging_config import (
45
45
  log_operation,
46
46
  log_performance_context,
47
47
  )
48
+ from claude_mpm.services.shared import ConfigServiceBase
48
49
 
49
50
  from .agent_configuration_manager import AgentConfigurationManager
50
51
  from .agent_discovery_service import AgentDiscoveryService
@@ -58,7 +59,7 @@ from .agent_version_manager import AgentVersionManager
58
59
  from .multi_source_deployment_service import MultiSourceAgentDeploymentService
59
60
 
60
61
 
61
- class AgentDeploymentService(AgentDeploymentInterface):
62
+ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
62
63
  """Service for deploying Claude Code native agents.
63
64
 
64
65
  METRICS COLLECTION OPPORTUNITIES:
@@ -94,6 +95,7 @@ class AgentDeploymentService(AgentDeploymentInterface):
94
95
  templates_dir: Optional[Path] = None,
95
96
  base_agent_path: Optional[Path] = None,
96
97
  working_directory: Optional[Path] = None,
98
+ config: Optional[Config] = None,
97
99
  ):
98
100
  """
99
101
  Initialize agent deployment service.
@@ -102,13 +104,15 @@ class AgentDeploymentService(AgentDeploymentInterface):
102
104
  templates_dir: Directory containing agent JSON files
103
105
  base_agent_path: Path to base_agent.md file
104
106
  working_directory: User's working directory (for project agents)
107
+ config: Configuration instance
105
108
 
106
109
  METRICS OPPORTUNITY: Track initialization performance:
107
110
  - Template directory scan time
108
111
  - Base agent loading time
109
112
  - Initial validation overhead
110
113
  """
111
- self.logger = get_logger(__name__)
114
+ # Initialize base class with configuration
115
+ super().__init__("agent_deployment", config=config)
112
116
 
113
117
  # Initialize template builder service
114
118
  self.template_builder = AgentTemplateBuilder()
@@ -139,26 +143,22 @@ class AgentDeploymentService(AgentDeploymentInterface):
139
143
  "deployment_errors": {},
140
144
  }
141
145
 
142
- # Determine the actual working directory
143
- # For deployment, we need to track the working directory but NOT use it
144
- # for determining where system agents go (they always go to ~/.claude/agents/)
145
- # Priority: working_directory param > current directory
146
- if working_directory:
147
- self.working_directory = Path(working_directory)
148
- else:
149
- # Use current directory but don't let CLAUDE_MPM_USER_PWD affect system agent deployment
150
- self.working_directory = Path.cwd()
151
-
146
+ # Determine the actual working directory using configuration
147
+ # Priority: param > config > environment > current directory
148
+ self.working_directory = self.get_config_value(
149
+ "working_directory",
150
+ default=working_directory or Path.cwd(),
151
+ config_type=Path
152
+ )
152
153
  self.logger.info(f"Working directory for deployment: {self.working_directory}")
153
154
 
154
- # Find templates directory using centralized path management
155
- if templates_dir:
156
- self.templates_dir = Path(templates_dir)
157
- else:
158
- # Use centralized paths instead of fragile parent calculations
159
- # For system agents, still use templates subdirectory
160
- # For project/user agents, this should be overridden with actual agents dir
161
- self.templates_dir = paths.agents_dir / "templates"
155
+ # Find templates directory using configuration
156
+ # Priority: param > config > default paths
157
+ self.templates_dir = self.get_config_value(
158
+ "templates_dir",
159
+ default=templates_dir or paths.agents_dir / "templates",
160
+ config_type=Path
161
+ )
162
162
 
163
163
  # Initialize discovery service (after templates_dir is set)
164
164
  self.discovery_service = AgentDiscoveryService(self.templates_dir)
@@ -166,9 +166,13 @@ class AgentDeploymentService(AgentDeploymentInterface):
166
166
  # Initialize multi-source deployment service for version comparison
167
167
  self.multi_source_service = MultiSourceAgentDeploymentService()
168
168
 
169
- # Find base agent file with priority-based search
169
+ # Find base agent file using configuration
170
+ # Priority: param > config > search
171
+ configured_base_agent = self.get_config_value("base_agent_path", config_type=Path)
170
172
  if base_agent_path:
171
173
  self.base_agent_path = Path(base_agent_path)
174
+ elif configured_base_agent:
175
+ self.base_agent_path = configured_base_agent
172
176
  else:
173
177
  # Priority-based search for base_agent.json
174
178
  self.base_agent_path = self._find_base_agent_file()
@@ -382,7 +386,7 @@ class AgentDeploymentService(AgentDeploymentInterface):
382
386
  if use_multi_source:
383
387
  # Use multi-source deployment to get highest version agents
384
388
  template_files, agent_sources = self._get_multi_source_templates(
385
- excluded_agents, config, agents_dir
389
+ excluded_agents, config, agents_dir, force_rebuild
386
390
  )
387
391
  results["total"] = len(template_files)
388
392
  results["multi_source"] = True
@@ -1056,7 +1060,8 @@ class AgentDeploymentService(AgentDeploymentInterface):
1056
1060
  return False
1057
1061
 
1058
1062
  def _get_multi_source_templates(
1059
- self, excluded_agents: List[str], config: Config, agents_dir: Path
1063
+ self, excluded_agents: List[str], config: Config, agents_dir: Path,
1064
+ force_rebuild: bool = False
1060
1065
  ) -> Tuple[List[Path], Dict[str, str]]:
1061
1066
  """Get agent templates from multiple sources with version comparison.
1062
1067
 
@@ -1113,6 +1118,27 @@ class AgentDeploymentService(AgentDeploymentInterface):
1113
1118
  self.logger.info(f"Version upgrades available for {len(comparison_results['version_upgrades'])} agents")
1114
1119
  if comparison_results.get("source_changes"):
1115
1120
  self.logger.info(f"Source changes for {len(comparison_results['source_changes'])} agents")
1121
+
1122
+ # Filter agents based on comparison results (unless force_rebuild is set)
1123
+ if not force_rebuild:
1124
+ # Only deploy agents that need updates or are new
1125
+ agents_needing_update = set(comparison_results.get("needs_update", []))
1126
+
1127
+ # Extract agent names from new_agents list (which contains dicts)
1128
+ new_agent_names = [
1129
+ agent["name"] if isinstance(agent, dict) else agent
1130
+ for agent in comparison_results.get("new_agents", [])
1131
+ ]
1132
+ agents_needing_update.update(new_agent_names)
1133
+
1134
+ # Filter agents_to_deploy to only include those needing updates
1135
+ filtered_agents = {
1136
+ name: path for name, path in agents_to_deploy.items()
1137
+ if name in agents_needing_update
1138
+ }
1139
+ agents_to_deploy = filtered_agents
1140
+
1141
+ self.logger.info(f"Filtered to {len(agents_to_deploy)} agents needing deployment")
1116
1142
 
1117
1143
  # Convert to list of Path objects
1118
1144
  template_files = list(agents_to_deploy.values())
@@ -0,0 +1,71 @@
1
+ """Temporary wrapper to provide backward compatibility for CLI commands."""
2
+
3
+ from pathlib import Path
4
+ from typing import Dict, Any
5
+
6
+ from claude_mpm.services.agents.deployment import AgentDeploymentService
7
+
8
+
9
+ class DeploymentServiceWrapper:
10
+ """Wrapper to provide backward-compatible methods for the CLI."""
11
+
12
+ def __init__(self, deployment_service: AgentDeploymentService):
13
+ """Initialize wrapper with actual deployment service."""
14
+ self.service = deployment_service
15
+ # Pass through all attributes
16
+ for attr in dir(deployment_service):
17
+ if not attr.startswith('_') and not hasattr(self, attr):
18
+ setattr(self, attr, getattr(deployment_service, attr))
19
+
20
+ def deploy_system_agents(self, force: bool = False) -> Dict[str, Any]:
21
+ """Deploy system agents only.
22
+
23
+ Args:
24
+ force: Force rebuild even if agents are up to date
25
+
26
+ Returns:
27
+ Deployment results
28
+ """
29
+ # Deploy agents with default target (system agents location)
30
+ result = self.service.deploy_agents(
31
+ force_rebuild=force,
32
+ deployment_mode="update"
33
+ )
34
+
35
+ # Transform result to expected format
36
+ return {
37
+ "deployed_count": len(result.get("deployed", [])) + len(result.get("updated", [])),
38
+ "deployed": result.get("deployed", []),
39
+ "updated": result.get("updated", []),
40
+ "errors": result.get("errors", []),
41
+ "target_dir": result.get("target_dir", "")
42
+ }
43
+
44
+ def deploy_project_agents(self, force: bool = False) -> Dict[str, Any]:
45
+ """Deploy project agents only.
46
+
47
+ Args:
48
+ force: Force rebuild even if agents are up to date
49
+
50
+ Returns:
51
+ Deployment results
52
+ """
53
+ # Check if project agents directory exists
54
+ project_dir = Path.cwd() / ".claude-mpm" / "agents"
55
+ if not project_dir.exists():
56
+ return {
57
+ "deployed_count": 0,
58
+ "deployed": [],
59
+ "updated": [],
60
+ "errors": [],
61
+ "target_dir": ""
62
+ }
63
+
64
+ # For now, return empty result as project agents are handled differently
65
+ return {
66
+ "deployed_count": 0,
67
+ "deployed": [],
68
+ "updated": [],
69
+ "errors": [],
70
+ "target_dir": str(project_dir)
71
+ }
@@ -42,6 +42,7 @@ class PipelineContext:
42
42
  template_files: List[Path] = field(default_factory=list)
43
43
  base_agent_data: Optional[Dict[str, Any]] = None
44
44
  base_agent_version: Optional[tuple] = None
45
+ agent_sources: Dict[str, str] = field(default_factory=dict) # Maps agent names to sources
45
46
 
46
47
  # Deployment results
47
48
  results: Dict[str, Any] = field(default_factory=dict)
@@ -2,6 +2,8 @@
2
2
 
3
3
  import time
4
4
 
5
+ from pathlib import Path
6
+
5
7
  from claude_mpm.services.agents.deployment.processors import (
6
8
  AgentDeploymentContext,
7
9
  AgentDeploymentResult,
@@ -48,6 +50,14 @@ class AgentProcessingStep(BaseDeploymentStep):
48
50
  # Process each template file
49
51
  for template_file in context.template_files:
50
52
  try:
53
+ # Determine source for this agent
54
+ agent_name = template_file.stem
55
+ # Use source from context if available (multi-source deployment), otherwise determine it
56
+ if context.agent_sources and agent_name in context.agent_sources:
57
+ source_info = context.agent_sources[agent_name]
58
+ else:
59
+ source_info = self._determine_agent_source(template_file)
60
+
51
61
  # Create agent deployment context
52
62
  agent_context = AgentDeploymentContext.from_template_file(
53
63
  template_file=template_file,
@@ -56,6 +66,7 @@ class AgentProcessingStep(BaseDeploymentStep):
56
66
  base_agent_version=context.base_agent_version or (1, 0, 0),
57
67
  force_rebuild=context.force_rebuild,
58
68
  deployment_mode=context.deployment_mode,
69
+ source_info=source_info,
59
70
  )
60
71
 
61
72
  # Validate agent if requested
@@ -193,3 +204,35 @@ class AgentProcessingStep(BaseDeploymentStep):
193
204
  from .target_directory_step import TargetDirectorySetupStep
194
205
 
195
206
  return [TargetDirectorySetupStep]
207
+
208
+ def _determine_agent_source(self, template_path: Path) -> str:
209
+ """Determine the source of an agent from its template path.
210
+
211
+ Args:
212
+ template_path: Path to the agent template
213
+
214
+ Returns:
215
+ Source string (system/project/user/unknown)
216
+ """
217
+ template_str = str(template_path.resolve())
218
+
219
+ # Check if it's a system template
220
+ if "/claude_mpm/agents/templates/" in template_str or "/src/claude_mpm/agents/templates/" in template_str:
221
+ return "system"
222
+
223
+ # Check if it's a project agent
224
+ if "/.claude-mpm/agents/" in template_str:
225
+ # Check if it's in the current working directory
226
+ try:
227
+ from pathlib import Path
228
+ cwd = Path.cwd()
229
+ if str(cwd) in template_str:
230
+ return "project"
231
+ except:
232
+ pass
233
+
234
+ # Check if it's a user agent
235
+ if "/.claude/agents/" in template_str:
236
+ return "user"
237
+
238
+ return "unknown"
@@ -21,6 +21,7 @@ class AgentDeploymentContext:
21
21
  # Deployment configuration
22
22
  force_rebuild: bool = False
23
23
  deployment_mode: str = "update"
24
+ source_info: str = "unknown" # Source of the agent (system/project/user)
24
25
 
25
26
  # Base agent data
26
27
  base_agent_data: Optional[Dict[str, Any]] = None
@@ -53,6 +54,7 @@ class AgentDeploymentContext:
53
54
  base_agent_version: tuple,
54
55
  force_rebuild: bool = False,
55
56
  deployment_mode: str = "update",
57
+ source_info: str = "unknown",
56
58
  ) -> "AgentDeploymentContext":
57
59
  """Create context from template file.
58
60
 
@@ -63,6 +65,7 @@ class AgentDeploymentContext:
63
65
  base_agent_version: Base agent version
64
66
  force_rebuild: Whether to force rebuild
65
67
  deployment_mode: Deployment mode
68
+ source_info: Source of the agent (system/project/user)
66
69
 
67
70
  Returns:
68
71
  AgentDeploymentContext instance
@@ -79,6 +82,7 @@ class AgentDeploymentContext:
79
82
  base_agent_data=base_agent_data,
80
83
  base_agent_version=base_agent_version,
81
84
  agents_dir=agents_dir,
85
+ source_info=source_info,
82
86
  )
83
87
 
84
88
  def is_update(self) -> bool:
@@ -185,7 +185,7 @@ class AgentProcessor:
185
185
  """
186
186
  try:
187
187
  return self.template_builder.build_agent_markdown(
188
- context.agent_name, context.template_file, context.base_agent_data
188
+ context.agent_name, context.template_file, context.base_agent_data, context.source_info
189
189
  )
190
190
  except Exception as e:
191
191
  raise AgentDeploymentError(
@@ -16,6 +16,7 @@ from typing import Any, Dict, List, Optional
16
16
 
17
17
  from claude_mpm.agents.base_agent_loader import clear_base_agent_cache
18
18
  from claude_mpm.services.memory.cache.shared_prompt_cache import SharedPromptCache
19
+ from claude_mpm.services.shared import ConfigServiceBase
19
20
 
20
21
  logger = logging.getLogger(__name__)
21
22
 
@@ -63,12 +64,19 @@ class BaseAgentStructure:
63
64
  raw_sections: Dict[str, str] = field(default_factory=dict)
64
65
 
65
66
 
66
- class BaseAgentManager:
67
+ class BaseAgentManager(ConfigServiceBase):
67
68
  """Manages base_agent.md with structured updates and validation."""
68
69
 
69
- def __init__(self, agents_dir: Optional[Path] = None):
70
+ def __init__(self, agents_dir: Optional[Path] = None, config: Optional[Dict[str, Any]] = None):
70
71
  """Initialize BaseAgentManager."""
71
- self.agents_dir = agents_dir or Path(__file__).parent.parent / "agents"
72
+ super().__init__("base_agent_manager", config=config)
73
+
74
+ # Initialize paths using configuration
75
+ self.agents_dir = self.get_config_value(
76
+ "agents_dir",
77
+ default=agents_dir or Path(__file__).parent.parent / "agents",
78
+ config_type=Path
79
+ )
72
80
  self.base_agent_path = self.agents_dir / "BASE_AGENT_TEMPLATE.md"
73
81
  self.cache = SharedPromptCache.get_instance()
74
82
 
@@ -7,26 +7,35 @@ handling both new standardized schema and legacy agent formats.
7
7
  import json
8
8
  import logging
9
9
  from pathlib import Path
10
- from typing import Any, Dict, List
10
+ from typing import Any, Dict, List, Optional
11
11
 
12
12
  from claude_mpm.core.agent_registry import AgentRegistryAdapter
13
13
  from claude_mpm.core.unified_paths import get_path_manager
14
+ from claude_mpm.services.shared import ConfigServiceBase
14
15
 
15
16
  logger = logging.getLogger(__name__)
16
17
 
17
18
 
18
- class DeployedAgentDiscovery:
19
+ class DeployedAgentDiscovery(ConfigServiceBase):
19
20
  """Discovers and analyzes deployed agents in the project."""
20
21
 
21
- def __init__(self, project_root: Path = None):
22
+ def __init__(self, project_root: Path = None, config: Optional[Dict[str, Any]] = None):
22
23
  """Initialize the discovery service.
23
24
 
24
25
  Args:
25
26
  project_root: Project root path. Defaults to auto-detected root.
27
+ config: Configuration dictionary
26
28
  """
27
- self.project_root = project_root or get_path_manager().get_project_root()
29
+ super().__init__("deployed_agent_discovery", config=config)
30
+
31
+ # Initialize project root using configuration
32
+ self.project_root = self.get_config_value(
33
+ "project_root",
34
+ default=project_root or get_path_manager().project_root,
35
+ config_type=Path
36
+ )
28
37
  self.agent_registry = AgentRegistryAdapter()
29
- logger.debug(
38
+ self.logger.debug(
30
39
  f"Initialized DeployedAgentDiscovery with root: {self.project_root}"
31
40
  )
32
41
 
@@ -60,10 +60,12 @@ class EventAggregator:
60
60
  self.port = port
61
61
  self.logger = get_logger("event_aggregator")
62
62
 
63
- # Load configuration
63
+ # Load configuration using ConfigLoader
64
64
  from claude_mpm.core.config import Config
65
+ from claude_mpm.core.shared.config_loader import ConfigLoader
65
66
 
66
- self.config = Config()
67
+ config_loader = ConfigLoader()
68
+ self.config = config_loader.load_main_config()
67
69
 
68
70
  # Session storage
69
71
  self.active_sessions: Dict[str, AgentSession] = {}
@@ -7,6 +7,8 @@ MCP Gateway Configuration Loader
7
7
  Handles loading and discovery of MCP configuration files.
8
8
 
9
9
  Part of ISS-0034: Infrastructure Setup - MCP Gateway Project Foundation
10
+
11
+ UPDATED: Migrated to use shared ConfigLoader pattern (TSK-0141)
10
12
  """
11
13
 
12
14
  import os
@@ -15,6 +17,7 @@ from typing import List, Optional
15
17
  import yaml
16
18
 
17
19
  from claude_mpm.core.logger import get_logger
20
+ from claude_mpm.core.shared.config_loader import ConfigLoader, ConfigPattern
18
21
 
19
22
 
20
23
  class MCPConfigLoader:
@@ -27,25 +30,41 @@ class MCPConfigLoader:
27
30
  WHY: We separate configuration loading from the main configuration
28
31
  service to support multiple configuration sources and provide a clean
29
32
  abstraction for configuration discovery.
33
+
34
+ UPDATED: Now uses shared ConfigLoader pattern for consistency (TSK-0141)
30
35
  """
31
36
 
32
- # Standard configuration file search paths
33
- CONFIG_SEARCH_PATHS = [
34
- # User-specific configurations
35
- Path("~/.claude/mcp/config.yaml"),
36
- Path("~/.claude/mcp_gateway.yaml"),
37
- Path("~/.config/claude-mpm/mcp_gateway.yaml"),
38
- # Project-specific configurations
39
- Path("./mcp_gateway.yaml"),
40
- Path("./config/mcp_gateway.yaml"),
41
- Path("./.claude/mcp_gateway.yaml"),
42
- # System-wide configurations
43
- Path("/etc/claude-mpm/mcp_gateway.yaml"),
44
- ]
37
+ # MCP Gateway configuration pattern
38
+ MCP_CONFIG_PATTERN = ConfigPattern(
39
+ filenames=[
40
+ "mcp_gateway.yaml",
41
+ "mcp_gateway.yml",
42
+ ".mcp_gateway.yaml",
43
+ ".mcp_gateway.yml",
44
+ "config.yaml",
45
+ "config.yml"
46
+ ],
47
+ search_paths=[
48
+ "~/.claude/mcp",
49
+ "~/.config/claude-mpm",
50
+ ".",
51
+ "./config",
52
+ "./.claude",
53
+ "/etc/claude-mpm"
54
+ ],
55
+ env_prefix="CLAUDE_MPM_MCP_",
56
+ defaults={
57
+ "host": "localhost",
58
+ "port": 3000,
59
+ "debug": False,
60
+ "timeout": 30
61
+ }
62
+ )
45
63
 
46
64
  def __init__(self):
47
65
  """Initialize configuration loader."""
48
66
  self.logger = get_logger("MCPConfigLoader")
67
+ self._shared_loader = ConfigLoader()
49
68
 
50
69
  def find_config_file(self) -> Optional[Path]:
51
70
  """
@@ -154,22 +173,64 @@ class MCPConfigLoader:
154
173
  """
155
174
  from .configuration import MCPConfiguration
156
175
 
157
- # Start with defaults
158
- config = MCPConfiguration.DEFAULT_CONFIG.copy()
159
-
160
- # Load from file
161
- file_path = config_path or self.find_config_file()
162
- if file_path:
163
- file_config = self.load_from_file(file_path)
164
- if file_config:
165
- config = self._merge_configs(config, file_config)
166
-
167
- # Apply environment overrides
168
- env_config = self.load_from_env()
169
- if env_config:
170
- config = self._merge_configs(config, env_config)
176
+ if config_path:
177
+ # Use specific config file with shared loader
178
+ pattern = ConfigPattern(
179
+ filenames=[config_path.name],
180
+ search_paths=[str(config_path.parent)],
181
+ env_prefix=self.MCP_CONFIG_PATTERN.env_prefix,
182
+ defaults=MCPConfiguration.DEFAULT_CONFIG.copy()
183
+ )
184
+ config_obj = self._shared_loader.load_config(pattern, cache_key=f"mcp_{config_path}")
185
+ return config_obj.to_dict()
186
+ else:
187
+ # Use standard MCP pattern with defaults
188
+ pattern = ConfigPattern(
189
+ filenames=self.MCP_CONFIG_PATTERN.filenames,
190
+ search_paths=self.MCP_CONFIG_PATTERN.search_paths,
191
+ env_prefix=self.MCP_CONFIG_PATTERN.env_prefix,
192
+ defaults=MCPConfiguration.DEFAULT_CONFIG.copy()
193
+ )
194
+ config_obj = self._shared_loader.load_config(pattern, cache_key="mcp_gateway")
195
+ return config_obj.to_dict()
196
+
197
+ # Backward compatibility methods (deprecated)
198
+ def find_config_file(self) -> Optional[Path]:
199
+ """Find configuration file using legacy method (deprecated)."""
200
+ import warnings
201
+ warnings.warn(
202
+ "find_config_file is deprecated. Use load() method instead.",
203
+ DeprecationWarning,
204
+ stacklevel=2
205
+ )
206
+
207
+ # Use shared loader to find config file
208
+ config_file = self._shared_loader._find_config_file(self.MCP_CONFIG_PATTERN)
209
+ return config_file
171
210
 
172
- return config
211
+ def load_from_file(self, config_path: Path) -> Optional[dict]:
212
+ """Load from file using legacy method (deprecated)."""
213
+ import warnings
214
+ warnings.warn(
215
+ "load_from_file is deprecated. Use load() method instead.",
216
+ DeprecationWarning,
217
+ stacklevel=2
218
+ )
219
+
220
+ # Use shared loader
221
+ return self._shared_loader._load_config_file(config_path)
222
+
223
+ def load_from_env(self, prefix: str = "CLAUDE_MPM_MCP_") -> dict:
224
+ """Load from environment using legacy method (deprecated)."""
225
+ import warnings
226
+ warnings.warn(
227
+ "load_from_env is deprecated. Use load() method instead.",
228
+ DeprecationWarning,
229
+ stacklevel=2
230
+ )
231
+
232
+ # Use shared loader
233
+ return self._shared_loader._load_env_config(prefix)
173
234
 
174
235
  def _merge_configs(self, base: dict, overlay: dict) -> dict:
175
236
  """
@@ -17,6 +17,7 @@ import yaml
17
17
  from claude_mpm.services.mcp_gateway.core.base import BaseMCPService
18
18
  from claude_mpm.services.mcp_gateway.core.exceptions import MCPConfigurationError
19
19
  from claude_mpm.services.mcp_gateway.core.interfaces import IMCPConfiguration
20
+ from claude_mpm.services.shared import ConfigServiceBase
20
21
 
21
22
 
22
23
  class MCPConfiguration(BaseMCPService, IMCPConfiguration):
@@ -88,6 +89,12 @@ class MCPConfiguration(BaseMCPService, IMCPConfiguration):
88
89
  self._config_data: Dict[str, Any] = {}
89
90
  self._is_loaded = False
90
91
 
92
+ # Initialize shared configuration utilities
93
+ self._config_helper = ConfigServiceBase("mcp_configuration")
94
+
95
+ # Merge environment configuration
96
+ self._config_helper.merge_env_config("CLAUDE_MPM_MCP_")
97
+
91
98
  async def _do_initialize(self) -> bool:
92
99
  """
93
100
  Initialize the configuration service.
@@ -376,3 +383,25 @@ class MCPConfiguration(BaseMCPService, IMCPConfiguration):
376
383
 
377
384
  # Revalidate
378
385
  return self.validate()
386
+
387
+ def get_config_with_validation(self, key: str, default: Any = None, config_type: type = None) -> Any:
388
+ """
389
+ Get configuration value with validation using shared utilities.
390
+
391
+ Args:
392
+ key: Configuration key (supports dot notation)
393
+ default: Default value if not found
394
+ config_type: Expected type for validation
395
+
396
+ Returns:
397
+ Configuration value
398
+ """
399
+ try:
400
+ return self._config_helper.get_config_value(key, default, config_type=config_type)
401
+ except ValueError:
402
+ # Fall back to standard get method
403
+ return self.get(key, default)
404
+
405
+ def get_config_summary(self) -> Dict[str, Any]:
406
+ """Get configuration summary using shared utilities."""
407
+ return self._config_helper.get_config_summary()