claude-mpm 5.1.9__py3-none-any.whl → 5.4.22__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.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (176) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +4 -0
  3. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +1 -1
  4. claude_mpm/agents/PM_INSTRUCTIONS.md +290 -34
  5. claude_mpm/agents/agent_loader.py +13 -44
  6. claude_mpm/agents/templates/circuit-breakers.md +138 -1
  7. claude_mpm/cli/__main__.py +4 -0
  8. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  9. claude_mpm/cli/commands/agent_state_manager.py +8 -17
  10. claude_mpm/cli/commands/agents.py +0 -31
  11. claude_mpm/cli/commands/auto_configure.py +210 -25
  12. claude_mpm/cli/commands/config.py +88 -2
  13. claude_mpm/cli/commands/configure.py +1097 -158
  14. claude_mpm/cli/commands/configure_agent_display.py +15 -6
  15. claude_mpm/cli/commands/mpm_init/core.py +160 -46
  16. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  17. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  18. claude_mpm/cli/commands/skills.py +214 -189
  19. claude_mpm/cli/commands/summarize.py +413 -0
  20. claude_mpm/cli/executor.py +11 -3
  21. claude_mpm/cli/parsers/agents_parser.py +0 -9
  22. claude_mpm/cli/parsers/auto_configure_parser.py +0 -138
  23. claude_mpm/cli/parsers/base_parser.py +5 -0
  24. claude_mpm/cli/parsers/config_parser.py +153 -83
  25. claude_mpm/cli/parsers/skills_parser.py +3 -2
  26. claude_mpm/cli/startup.py +550 -94
  27. claude_mpm/commands/mpm-config.md +265 -0
  28. claude_mpm/commands/mpm-help.md +14 -95
  29. claude_mpm/commands/mpm-organize.md +500 -0
  30. claude_mpm/config/agent_sources.py +27 -0
  31. claude_mpm/core/framework/formatters/content_formatter.py +3 -13
  32. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  33. claude_mpm/core/framework_loader.py +4 -2
  34. claude_mpm/core/logger.py +13 -0
  35. claude_mpm/core/socketio_pool.py +3 -3
  36. claude_mpm/core/unified_agent_registry.py +5 -15
  37. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  38. claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
  39. claude_mpm/hooks/claude_hooks/hook_handler.py +6 -0
  40. claude_mpm/hooks/claude_hooks/installer.py +33 -10
  41. claude_mpm/hooks/claude_hooks/memory_integration.py +26 -9
  42. claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
  43. claude_mpm/hooks/claude_hooks/services/connection_manager.py +4 -0
  44. claude_mpm/hooks/memory_integration_hook.py +46 -1
  45. claude_mpm/init.py +0 -19
  46. claude_mpm/scripts/claude-hook-handler.sh +58 -18
  47. claude_mpm/scripts/launch_monitor.py +93 -13
  48. claude_mpm/scripts/start_activity_logging.py +0 -0
  49. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  50. claude_mpm/services/agents/agent_review_service.py +280 -0
  51. claude_mpm/services/agents/deployment/agent_discovery_service.py +2 -3
  52. claude_mpm/services/agents/deployment/agent_template_builder.py +4 -2
  53. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +78 -9
  54. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +335 -53
  55. claude_mpm/services/agents/git_source_manager.py +34 -0
  56. claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
  57. claude_mpm/services/agents/sources/git_source_sync_service.py +8 -1
  58. claude_mpm/services/agents/toolchain_detector.py +10 -6
  59. claude_mpm/services/analysis/__init__.py +11 -1
  60. claude_mpm/services/analysis/clone_detector.py +1030 -0
  61. claude_mpm/services/command_deployment_service.py +81 -10
  62. claude_mpm/services/event_bus/config.py +3 -1
  63. claude_mpm/services/git/git_operations_service.py +93 -8
  64. claude_mpm/services/monitor/daemon.py +9 -2
  65. claude_mpm/services/monitor/daemon_manager.py +39 -3
  66. claude_mpm/services/monitor/server.py +225 -19
  67. claude_mpm/services/self_upgrade_service.py +120 -12
  68. claude_mpm/services/skills/__init__.py +3 -0
  69. claude_mpm/services/skills/git_skill_source_manager.py +32 -2
  70. claude_mpm/services/skills/selective_skill_deployer.py +704 -0
  71. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  72. claude_mpm/services/skills_deployer.py +126 -9
  73. claude_mpm/services/socketio/event_normalizer.py +15 -1
  74. claude_mpm/services/socketio/server/core.py +160 -21
  75. claude_mpm/services/version_control/git_operations.py +103 -0
  76. claude_mpm/utils/agent_filters.py +17 -44
  77. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/METADATA +47 -84
  78. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/RECORD +82 -161
  79. claude_mpm-5.4.22.dist-info/entry_points.txt +5 -0
  80. claude_mpm-5.4.22.dist-info/licenses/LICENSE +94 -0
  81. claude_mpm-5.4.22.dist-info/licenses/LICENSE-FAQ.md +153 -0
  82. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
  83. claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
  84. claude_mpm/agents/BASE_ENGINEER.md +0 -658
  85. claude_mpm/agents/BASE_OPS.md +0 -219
  86. claude_mpm/agents/BASE_PM.md +0 -480
  87. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
  88. claude_mpm/agents/BASE_QA.md +0 -167
  89. claude_mpm/agents/BASE_RESEARCH.md +0 -53
  90. claude_mpm/agents/base_agent.json +0 -31
  91. claude_mpm/agents/base_agent_loader.py +0 -601
  92. claude_mpm/cli/commands/agents_detect.py +0 -380
  93. claude_mpm/cli/commands/agents_recommend.py +0 -309
  94. claude_mpm/cli/ticket_cli.py +0 -35
  95. claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
  96. claude_mpm/commands/mpm-agents-detect.md +0 -177
  97. claude_mpm/commands/mpm-agents-list.md +0 -131
  98. claude_mpm/commands/mpm-agents-recommend.md +0 -223
  99. claude_mpm/commands/mpm-config-view.md +0 -150
  100. claude_mpm/commands/mpm-ticket-organize.md +0 -304
  101. claude_mpm/dashboard/analysis_runner.py +0 -455
  102. claude_mpm/dashboard/index.html +0 -13
  103. claude_mpm/dashboard/open_dashboard.py +0 -66
  104. claude_mpm/dashboard/static/css/activity.css +0 -1958
  105. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  106. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  107. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  108. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  109. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  110. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  111. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  112. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  113. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  114. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  115. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  116. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  117. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  118. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  119. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  120. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  121. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  122. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  123. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  124. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  125. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  126. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  127. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  128. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  129. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  130. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  131. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  132. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  133. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  134. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  135. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  136. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  137. claude_mpm/dashboard/templates/code_simple.html +0 -153
  138. claude_mpm/dashboard/templates/index.html +0 -606
  139. claude_mpm/dashboard/test_dashboard.html +0 -372
  140. claude_mpm/scripts/mcp_server.py +0 -75
  141. claude_mpm/scripts/mcp_wrapper.py +0 -39
  142. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  143. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  144. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  145. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  146. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  147. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  148. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  149. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  150. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  151. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  152. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
  153. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  154. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  155. claude_mpm/services/mcp_gateway/main.py +0 -589
  156. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  157. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  158. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  159. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  160. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  161. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  162. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  163. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  164. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  165. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  166. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  167. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  168. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  169. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  170. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  171. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  172. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  173. claude_mpm-5.1.9.dist-info/entry_points.txt +0 -10
  174. claude_mpm-5.1.9.dist-info/licenses/LICENSE +0 -21
  175. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/WHEEL +0 -0
  176. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/top_level.txt +0 -0
@@ -1,243 +0,0 @@
1
- """
2
- MCP Gateway Configuration Schema
3
- ================================
4
-
5
- Defines and validates the configuration schema for MCP Gateway.
6
-
7
- Part of ISS-0034: Infrastructure Setup - MCP Gateway Project Foundation
8
- """
9
-
10
- from dataclasses import dataclass
11
- from typing import Any, Dict, List, Optional
12
-
13
-
14
- @dataclass
15
- class MCPConfigSchema:
16
- """
17
- Configuration schema definition for MCP Gateway.
18
-
19
- This class defines the structure and validation rules for
20
- MCP Gateway configuration.
21
- """
22
-
23
- # Schema version for migration support
24
- SCHEMA_VERSION = "1.0.0"
25
-
26
- # Configuration schema definition
27
- SCHEMA = {
28
- "mcp": {
29
- "type": "object",
30
- "required": True,
31
- "properties": {
32
- "server": {
33
- "type": "object",
34
- "required": True,
35
- "properties": {
36
- "name": {"type": "string", "required": True},
37
- "version": {"type": "string", "required": True},
38
- "description": {"type": "string", "required": False},
39
- "communication": {
40
- "type": "object",
41
- "required": True,
42
- "properties": {
43
- "type": {
44
- "type": "string",
45
- "required": True,
46
- "enum": ["stdio", "websocket", "http"],
47
- },
48
- "timeout": {
49
- "type": "number",
50
- "required": False,
51
- "min": 1,
52
- "max": 3600,
53
- },
54
- "buffer_size": {
55
- "type": "integer",
56
- "required": False,
57
- "min": 1024,
58
- "max": 1048576,
59
- },
60
- },
61
- },
62
- "capabilities": {
63
- "type": "object",
64
- "required": False,
65
- "properties": {
66
- "tools": {"type": "boolean", "required": False},
67
- "resources": {"type": "boolean", "required": False},
68
- "prompts": {"type": "boolean", "required": False},
69
- },
70
- },
71
- },
72
- },
73
- "tools": {
74
- "type": "object",
75
- "required": False,
76
- "properties": {
77
- "enabled": {"type": "boolean", "required": False},
78
- "auto_discover": {"type": "boolean", "required": False},
79
- "discovery_paths": {
80
- "type": "array",
81
- "required": False,
82
- "items": {"type": "string"},
83
- },
84
- "timeout_default": {
85
- "type": "number",
86
- "required": False,
87
- "min": 1,
88
- "max": 300,
89
- },
90
- "max_concurrent": {
91
- "type": "integer",
92
- "required": False,
93
- "min": 1,
94
- "max": 100,
95
- },
96
- },
97
- },
98
- "logging": {
99
- "type": "object",
100
- "required": False,
101
- "properties": {
102
- "level": {
103
- "type": "string",
104
- "required": False,
105
- "enum": ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
106
- },
107
- "file": {"type": "string", "required": False},
108
- "max_size": {"type": "string", "required": False},
109
- "max_files": {
110
- "type": "integer",
111
- "required": False,
112
- "min": 1,
113
- "max": 100,
114
- },
115
- "format": {
116
- "type": "string",
117
- "required": False,
118
- "enum": ["json", "text"],
119
- },
120
- },
121
- },
122
- "security": {
123
- "type": "object",
124
- "required": False,
125
- "properties": {
126
- "validate_schemas": {"type": "boolean", "required": False},
127
- "sanitize_inputs": {"type": "boolean", "required": False},
128
- "max_request_size": {
129
- "type": "integer",
130
- "required": False,
131
- "min": 1024,
132
- "max": 104857600, # 100MB max
133
- },
134
- "allowed_tools": {
135
- "type": "array",
136
- "required": False,
137
- "items": {"type": "string"},
138
- },
139
- "blocked_tools": {
140
- "type": "array",
141
- "required": False,
142
- "items": {"type": "string"},
143
- },
144
- },
145
- },
146
- },
147
- }
148
- }
149
-
150
-
151
- def validate_config(
152
- config: Dict[str, Any], schema: Optional[Dict[str, Any]] = None
153
- ) -> List[str]:
154
- """
155
- Validate configuration against schema.
156
-
157
- Args:
158
- config: Configuration dictionary to validate
159
- schema: Schema to validate against (uses default if not provided)
160
-
161
- Returns:
162
- List of validation errors (empty if valid)
163
- """
164
- if schema is None:
165
- schema = MCPConfigSchema.SCHEMA
166
-
167
- errors = []
168
-
169
- def validate_value(value: Any, spec: Dict[str, Any], path: str) -> None:
170
- """Recursively validate a value against its specification."""
171
-
172
- # Check type
173
- expected_type = spec.get("type")
174
- if expected_type:
175
- if expected_type == "object" and not isinstance(value, dict):
176
- errors.append(f"{path}: Expected object, got {type(value).__name__}")
177
- return
178
- if expected_type == "array" and not isinstance(value, list):
179
- errors.append(f"{path}: Expected array, got {type(value).__name__}")
180
- return
181
- if expected_type == "string" and not isinstance(value, str):
182
- errors.append(f"{path}: Expected string, got {type(value).__name__}")
183
- return
184
- if expected_type == "number" and not isinstance(value, (int, float)):
185
- errors.append(f"{path}: Expected number, got {type(value).__name__}")
186
- return
187
- if expected_type == "integer" and not isinstance(value, int):
188
- errors.append(f"{path}: Expected integer, got {type(value).__name__}")
189
- return
190
- if expected_type == "boolean" and not isinstance(value, bool):
191
- errors.append(f"{path}: Expected boolean, got {type(value).__name__}")
192
- return
193
-
194
- # Check enum values
195
- if "enum" in spec and value not in spec["enum"]:
196
- errors.append(
197
- f"{path}: Value '{value}' not in allowed values: {spec['enum']}"
198
- )
199
-
200
- # Check numeric constraints
201
- if isinstance(value, (int, float)):
202
- if "min" in spec and value < spec["min"]:
203
- errors.append(
204
- f"{path}: Value {value} is less than minimum {spec['min']}"
205
- )
206
- if "max" in spec and value > spec["max"]:
207
- errors.append(
208
- f"{path}: Value {value} is greater than maximum {spec['max']}"
209
- )
210
-
211
- # Validate object properties
212
- if expected_type == "object" and isinstance(value, dict):
213
- properties = spec.get("properties", {})
214
- for prop_name, prop_spec in properties.items():
215
- prop_path = f"{path}.{prop_name}"
216
-
217
- if prop_name in value:
218
- validate_value(value[prop_name], prop_spec, prop_path)
219
- elif prop_spec.get("required", False):
220
- errors.append(f"{prop_path}: Required field missing")
221
-
222
- # Validate array items
223
- if expected_type == "array" and isinstance(value, list):
224
- item_spec = spec.get("items", {})
225
- for i, item in enumerate(value):
226
- validate_value(item, item_spec, f"{path}[{i}]")
227
-
228
- # Start validation from root
229
- validate_value(config, {"type": "object", "properties": schema}, "config")
230
-
231
- return errors
232
-
233
-
234
- def generate_config_template() -> Dict[str, Any]:
235
- """
236
- Generate a configuration template with all possible options.
237
-
238
- Returns:
239
- Configuration template dictionary
240
- """
241
- from ..config.configuration import MCPConfiguration
242
-
243
- return MCPConfiguration.DEFAULT_CONFIG
@@ -1,429 +0,0 @@
1
- from pathlib import Path
2
-
3
- """
4
- MCP Gateway Configuration Implementation
5
- ========================================
6
-
7
- Manages configuration for the MCP Gateway service.
8
-
9
- Part of ISS-0034: Infrastructure Setup - MCP Gateway Project Foundation
10
- """
11
-
12
- import os
13
- from typing import Any, Dict, Optional
14
-
15
- import yaml
16
-
17
- from claude_mpm.services.mcp_gateway.core.base import BaseMCPService
18
- from claude_mpm.services.mcp_gateway.core.exceptions import MCPConfigurationError
19
- from claude_mpm.services.mcp_gateway.core.interfaces import IMCPConfiguration
20
- from claude_mpm.services.shared import ConfigServiceBase
21
-
22
-
23
- class MCPConfiguration(BaseMCPService, IMCPConfiguration):
24
- """
25
- MCP Gateway configuration management service.
26
-
27
- This service handles loading, validation, and access to MCP Gateway configuration.
28
- It supports YAML-based configuration files and environment variable overrides.
29
-
30
- WHY: Configuration is centralized in a service to ensure consistent access
31
- patterns, validation, and the ability to reload configuration at runtime.
32
- The service pattern also allows for dependency injection of configuration
33
- into other MCP services.
34
- """
35
-
36
- DEFAULT_CONFIG = {
37
- "mcp": {
38
- "server": {
39
- "name": "claude-mpm-gateway",
40
- "version": "1.0.0",
41
- "description": "Claude MPM MCP Gateway Server",
42
- "communication": {
43
- "type": "stdio", # stdio, websocket, or http
44
- "timeout": 30, # seconds
45
- "buffer_size": 8192,
46
- },
47
- "capabilities": {
48
- "tools": True,
49
- "resources": False, # Not yet implemented
50
- "prompts": False, # Not yet implemented
51
- },
52
- },
53
- "tools": {
54
- "enabled": True,
55
- "auto_discover": True,
56
- "discovery_paths": [
57
- "~/.claude/mcp/tools",
58
- "./mcp_tools",
59
- ],
60
- "timeout_default": 30, # seconds
61
- "max_concurrent": 10,
62
- },
63
- "external_services": {
64
- "enabled": True,
65
- "auto_install": True,
66
- "services": [
67
- {
68
- "name": "mcp-vector-search",
69
- "package": "mcp-vector-search",
70
- "enabled": True,
71
- "auto_index": True,
72
- },
73
- {
74
- "name": "mcp-browser",
75
- "package": "mcp-browser",
76
- "enabled": True,
77
- },
78
- ],
79
- },
80
- "logging": {
81
- "level": "INFO",
82
- "file": "~/.claude/logs/mcp_gateway.log",
83
- "max_size": "10MB",
84
- "max_files": 5,
85
- "format": "json", # json or text
86
- },
87
- "security": {
88
- "validate_schemas": True,
89
- "sanitize_inputs": True,
90
- "max_request_size": 1048576, # 1MB
91
- "allowed_tools": [], # Empty means all tools allowed
92
- "blocked_tools": [],
93
- },
94
- }
95
- }
96
-
97
- def __init__(self, config_path: Optional[Path] = None):
98
- """
99
- Initialize MCP configuration service.
100
-
101
- Args:
102
- config_path: Optional path to configuration file
103
- """
104
- super().__init__("MCPConfiguration")
105
- self._config_path = config_path
106
- self._config_data: Dict[str, Any] = {}
107
- self._is_loaded = False
108
-
109
- # Initialize shared configuration utilities
110
- self._config_helper = ConfigServiceBase("mcp_configuration")
111
-
112
- # Merge environment configuration
113
- self._config_helper.merge_env_config("CLAUDE_MPM_MCP_")
114
-
115
- async def _do_initialize(self) -> bool:
116
- """
117
- Initialize the configuration service.
118
-
119
- Returns:
120
- True if initialization successful
121
- """
122
- # Start with default configuration
123
- self._config_data = self.DEFAULT_CONFIG.copy()
124
-
125
- # Load from file if path provided
126
- if self._config_path and not self.load_config(self._config_path):
127
- return False
128
-
129
- # Apply environment variable overrides
130
- self._apply_env_overrides()
131
-
132
- # Validate configuration
133
- if not self.validate():
134
- return False
135
-
136
- self._is_loaded = True
137
- self.log_info("Configuration initialized successfully")
138
- return True
139
-
140
- def load_config(self, config_path: Path) -> bool:
141
- """
142
- Load configuration from a file.
143
-
144
- Args:
145
- config_path: Path to configuration file
146
-
147
- Returns:
148
- True if configuration loaded successfully
149
- """
150
- try:
151
- # Expand user path
152
- config_path = Path(config_path).expanduser()
153
-
154
- if not config_path.exists():
155
- self.log_warning(f"Configuration file not found: {config_path}")
156
- return True # Not an error, use defaults
157
-
158
- with config_path.open() as f:
159
- if config_path.suffix in [".yaml", ".yml"]:
160
- loaded_config = yaml.safe_load(f) or {}
161
- else:
162
- raise MCPConfigurationError(
163
- f"Unsupported configuration file format: {config_path.suffix}"
164
- )
165
-
166
- # Merge with existing configuration
167
- self._merge_config(self._config_data, loaded_config)
168
- self._config_path = config_path
169
-
170
- self.log_info(f"Configuration loaded from {config_path}")
171
- return True
172
-
173
- except yaml.YAMLError as e:
174
- raise MCPConfigurationError(
175
- f"Failed to parse YAML configuration: {e}"
176
- ) from e
177
- except Exception as e:
178
- self.log_error(f"Failed to load configuration: {e}")
179
- return False
180
-
181
- def _merge_config(self, base: Dict[str, Any], overlay: Dict[str, Any]) -> None:
182
- """
183
- Recursively merge overlay configuration into base.
184
-
185
- Args:
186
- base: Base configuration dictionary
187
- overlay: Configuration to merge in
188
- """
189
- for key, value in overlay.items():
190
- if key in base and isinstance(base[key], dict) and isinstance(value, dict):
191
- self._merge_config(base[key], value)
192
- else:
193
- base[key] = value
194
-
195
- def _apply_env_overrides(self) -> None:
196
- """
197
- Apply environment variable overrides to configuration.
198
-
199
- Environment variables follow the pattern: MCP_GATEWAY_<SECTION>_<KEY>
200
- For example: MCP_GATEWAY_SERVER_NAME=my-server
201
- """
202
- prefix = "MCP_GATEWAY_"
203
-
204
- for env_key, env_value in os.environ.items():
205
- if not env_key.startswith(prefix):
206
- continue
207
-
208
- # Parse environment variable into configuration path
209
- config_path = env_key[len(prefix) :].lower().split("_")
210
-
211
- # Navigate to the configuration location
212
- current = self._config_data
213
- for i, part in enumerate(config_path[:-1]):
214
- if part not in current:
215
- current[part] = {}
216
- elif not isinstance(current[part], dict):
217
- self.log_warning(
218
- f"Cannot override non-dict config at {'.'.join(config_path[: i + 1])}"
219
- )
220
- break
221
- current = current[part]
222
- else:
223
- # Set the value
224
- key = config_path[-1]
225
- # Try to parse as JSON for complex types
226
- try:
227
- import json
228
-
229
- current[key] = json.loads(env_value)
230
- except Exception:
231
- # Fall back to string value
232
- current[key] = env_value
233
-
234
- self.log_debug(f"Applied environment override: {env_key}")
235
-
236
- def get(self, key: str, default: Any = None) -> Any:
237
- """
238
- Get configuration value by key.
239
-
240
- Args:
241
- key: Configuration key (supports dot notation, e.g., "mcp.server.name")
242
- default: Default value if key not found
243
-
244
- Returns:
245
- Configuration value or default
246
- """
247
- parts = key.split(".")
248
- current = self._config_data
249
-
250
- for part in parts:
251
- if isinstance(current, dict) and part in current:
252
- current = current[part]
253
- else:
254
- return default
255
-
256
- return current
257
-
258
- def set(self, key: str, value: Any) -> None:
259
- """
260
- Set configuration value.
261
-
262
- Args:
263
- key: Configuration key (supports dot notation)
264
- value: Configuration value
265
- """
266
- parts = key.split(".")
267
- current = self._config_data
268
-
269
- # Navigate to parent
270
- for part in parts[:-1]:
271
- if part not in current:
272
- current[part] = {}
273
- elif not isinstance(current[part], dict):
274
- raise MCPConfigurationError(
275
- f"Cannot set value at {key}: parent is not a dictionary"
276
- )
277
- current = current[part]
278
-
279
- # Set the value
280
- current[parts[-1]] = value
281
- self.log_debug(f"Configuration updated: {key} = {value}")
282
-
283
- def validate(self) -> bool:
284
- """
285
- Validate the current configuration.
286
-
287
- Returns:
288
- True if configuration is valid
289
- """
290
- try:
291
- # Check required fields
292
- required_fields = [
293
- "mcp.server.name",
294
- "mcp.server.version",
295
- "mcp.server.communication.type",
296
- ]
297
-
298
- for field in required_fields:
299
- if self.get(field) is None:
300
- raise MCPConfigurationError(
301
- f"Required configuration field missing: {field}",
302
- config_key=field,
303
- )
304
-
305
- # Validate communication type
306
- comm_type = self.get("mcp.server.communication.type")
307
- if comm_type not in ["stdio", "websocket", "http"]:
308
- raise MCPConfigurationError(
309
- f"Invalid communication type: {comm_type}",
310
- config_key="mcp.server.communication.type",
311
- expected_type="stdio|websocket|http",
312
- )
313
-
314
- # Validate numeric fields
315
- timeout = self.get("mcp.server.communication.timeout")
316
- if not isinstance(timeout, (int, float)) or timeout <= 0:
317
- raise MCPConfigurationError(
318
- "Invalid timeout value",
319
- config_key="mcp.server.communication.timeout",
320
- expected_type="positive number",
321
- )
322
-
323
- self.log_debug("Configuration validation successful")
324
- return True
325
-
326
- except MCPConfigurationError:
327
- raise
328
- except Exception as e:
329
- self.log_error(f"Configuration validation failed: {e}")
330
- return False
331
-
332
- def get_server_config(self) -> Dict[str, Any]:
333
- """
334
- Get MCP server configuration.
335
-
336
- Returns:
337
- Server configuration dictionary
338
- """
339
- return self.get("mcp.server", {})
340
-
341
- def get_tools_config(self) -> Dict[str, Any]:
342
- """
343
- Get tools configuration.
344
-
345
- Returns:
346
- Tools configuration dictionary
347
- """
348
- return self.get("mcp.tools", {})
349
-
350
- def save_config(self, path: Optional[Path] = None) -> bool:
351
- """
352
- Save current configuration to file.
353
-
354
- Args:
355
- path: Path to save configuration (uses loaded path if not specified)
356
-
357
- Returns:
358
- True if save successful
359
- """
360
- save_path = path or self._config_path
361
- if not save_path:
362
- self.log_error("No path specified for saving configuration")
363
- return False
364
-
365
- try:
366
- save_path = Path(save_path).expanduser()
367
- save_path.parent.mkdir(parents=True, exist_ok=True)
368
-
369
- with save_path.open("w") as f:
370
- yaml.dump(
371
- self._config_data, f, default_flow_style=False, sort_keys=True
372
- )
373
-
374
- self.log_info(f"Configuration saved to {save_path}")
375
- return True
376
-
377
- except Exception as e:
378
- self.log_error(f"Failed to save configuration: {e}")
379
- return False
380
-
381
- def reload(self) -> bool:
382
- """
383
- Reload configuration from file.
384
-
385
- Returns:
386
- True if reload successful
387
- """
388
- if not self._config_path:
389
- self.log_warning("No configuration file to reload")
390
- return True
391
-
392
- # Reset to defaults
393
- self._config_data = self.DEFAULT_CONFIG.copy()
394
-
395
- # Reload from file
396
- if not self.load_config(self._config_path):
397
- return False
398
-
399
- # Reapply environment overrides
400
- self._apply_env_overrides()
401
-
402
- # Revalidate
403
- return self.validate()
404
-
405
- def get_config_with_validation(
406
- self, key: str, default: Any = None, config_type: Optional[type] = None
407
- ) -> Any:
408
- """
409
- Get configuration value with validation using shared utilities.
410
-
411
- Args:
412
- key: Configuration key (supports dot notation)
413
- default: Default value if not found
414
- config_type: Expected type for validation
415
-
416
- Returns:
417
- Configuration value
418
- """
419
- try:
420
- return self._config_helper.get_config_value(
421
- key, default, config_type=config_type
422
- )
423
- except ValueError:
424
- # Fall back to standard get method
425
- return self.get(key, default)
426
-
427
- def get_config_summary(self) -> Dict[str, Any]:
428
- """Get configuration summary using shared utilities."""
429
- return self._config_helper.get_config_summary()