claude-mpm 4.0.22__py3-none-any.whl → 4.0.25__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/BUILD_NUMBER +1 -1
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +4 -1
- claude_mpm/agents/BASE_PM.md +3 -0
- claude_mpm/agents/templates/code_analyzer.json +3 -3
- claude_mpm/agents/templates/data_engineer.json +2 -2
- claude_mpm/agents/templates/documentation.json +36 -9
- claude_mpm/agents/templates/engineer.json +2 -2
- claude_mpm/agents/templates/ops.json +2 -2
- claude_mpm/agents/templates/qa.json +2 -2
- claude_mpm/agents/templates/refactoring_engineer.json +65 -43
- claude_mpm/agents/templates/security.json +2 -2
- claude_mpm/agents/templates/version_control.json +2 -2
- claude_mpm/agents/templates/web_ui.json +2 -2
- claude_mpm/cli/commands/agents.py +453 -113
- claude_mpm/cli/commands/aggregate.py +107 -15
- claude_mpm/cli/commands/cleanup.py +142 -10
- claude_mpm/cli/commands/config.py +358 -224
- claude_mpm/cli/commands/info.py +184 -75
- claude_mpm/cli/commands/mcp_command_router.py +5 -76
- claude_mpm/cli/commands/mcp_install_commands.py +68 -36
- claude_mpm/cli/commands/mcp_server_commands.py +30 -37
- claude_mpm/cli/commands/memory.py +331 -61
- claude_mpm/cli/commands/monitor.py +101 -7
- claude_mpm/cli/commands/run.py +368 -8
- claude_mpm/cli/commands/tickets.py +206 -24
- claude_mpm/cli/parsers/mcp_parser.py +3 -0
- claude_mpm/cli/shared/__init__.py +40 -0
- claude_mpm/cli/shared/argument_patterns.py +212 -0
- claude_mpm/cli/shared/command_base.py +234 -0
- claude_mpm/cli/shared/error_handling.py +238 -0
- claude_mpm/cli/shared/output_formatters.py +231 -0
- claude_mpm/config/agent_config.py +29 -8
- claude_mpm/core/container.py +6 -4
- claude_mpm/core/framework_loader.py +32 -9
- claude_mpm/core/service_registry.py +4 -2
- claude_mpm/core/shared/__init__.py +17 -0
- claude_mpm/core/shared/config_loader.py +320 -0
- claude_mpm/core/shared/path_resolver.py +277 -0
- claude_mpm/core/shared/singleton_manager.py +208 -0
- claude_mpm/hooks/claude_hooks/memory_integration.py +4 -2
- claude_mpm/hooks/claude_hooks/response_tracking.py +14 -3
- claude_mpm/hooks/memory_integration_hook.py +11 -2
- claude_mpm/services/agents/deployment/agent_deployment.py +43 -23
- claude_mpm/services/agents/deployment/deployment_wrapper.py +71 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +1 -0
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +43 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +4 -0
- claude_mpm/services/agents/deployment/processors/agent_processor.py +1 -1
- claude_mpm/services/agents/loading/base_agent_manager.py +11 -3
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +14 -5
- claude_mpm/services/event_aggregator.py +4 -2
- claude_mpm/services/mcp_gateway/config/config_loader.py +89 -28
- claude_mpm/services/mcp_gateway/config/configuration.py +29 -0
- claude_mpm/services/mcp_gateway/registry/service_registry.py +22 -5
- claude_mpm/services/memory/builder.py +6 -1
- claude_mpm/services/response_tracker.py +3 -1
- claude_mpm/services/runner_configuration_service.py +15 -6
- claude_mpm/services/shared/__init__.py +20 -0
- claude_mpm/services/shared/async_service_base.py +219 -0
- claude_mpm/services/shared/config_service_base.py +292 -0
- claude_mpm/services/shared/lifecycle_service_base.py +317 -0
- claude_mpm/services/shared/manager_base.py +303 -0
- claude_mpm/services/shared/service_factory.py +308 -0
- {claude_mpm-4.0.22.dist-info → claude_mpm-4.0.25.dist-info}/METADATA +19 -13
- {claude_mpm-4.0.22.dist-info → claude_mpm-4.0.25.dist-info}/RECORD +70 -54
- {claude_mpm-4.0.22.dist-info → claude_mpm-4.0.25.dist-info}/WHEEL +0 -0
- {claude_mpm-4.0.22.dist-info → claude_mpm-4.0.25.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.0.22.dist-info → claude_mpm-4.0.25.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.0.22.dist-info → claude_mpm-4.0.25.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
|
-
|
|
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
|
-
#
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
|
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,21 @@ 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
|
+
agents_needing_update.update(comparison_results.get("new_agents", []))
|
|
1127
|
+
|
|
1128
|
+
# Filter agents_to_deploy to only include those needing updates
|
|
1129
|
+
filtered_agents = {
|
|
1130
|
+
name: path for name, path in agents_to_deploy.items()
|
|
1131
|
+
if name in agents_needing_update
|
|
1132
|
+
}
|
|
1133
|
+
agents_to_deploy = filtered_agents
|
|
1134
|
+
|
|
1135
|
+
self.logger.info(f"Filtered to {len(agents_to_deploy)} agents needing deployment")
|
|
1116
1136
|
|
|
1117
1137
|
# Convert to list of Path objects
|
|
1118
1138
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
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()
|