claude-mpm 5.0.2__py3-none-any.whl → 5.4.3__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 (184) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +2002 -0
  3. claude_mpm/agents/PM_INSTRUCTIONS.md +1218 -905
  4. claude_mpm/agents/agent_loader.py +10 -17
  5. claude_mpm/agents/base_agent_loader.py +10 -35
  6. claude_mpm/agents/frontmatter_validator.py +68 -0
  7. claude_mpm/agents/templates/circuit-breakers.md +431 -45
  8. claude_mpm/cli/__init__.py +0 -1
  9. claude_mpm/cli/commands/__init__.py +2 -0
  10. claude_mpm/cli/commands/agent_state_manager.py +67 -23
  11. claude_mpm/cli/commands/agents.py +446 -25
  12. claude_mpm/cli/commands/auto_configure.py +535 -233
  13. claude_mpm/cli/commands/configure.py +1500 -147
  14. claude_mpm/cli/commands/configure_agent_display.py +13 -6
  15. claude_mpm/cli/commands/mpm_init/core.py +158 -1
  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/postmortem.py +401 -0
  19. claude_mpm/cli/commands/run.py +1 -39
  20. claude_mpm/cli/commands/skills.py +322 -19
  21. claude_mpm/cli/commands/summarize.py +413 -0
  22. claude_mpm/cli/executor.py +8 -0
  23. claude_mpm/cli/interactive/agent_wizard.py +302 -195
  24. claude_mpm/cli/parsers/agents_parser.py +137 -0
  25. claude_mpm/cli/parsers/auto_configure_parser.py +13 -0
  26. claude_mpm/cli/parsers/base_parser.py +9 -0
  27. claude_mpm/cli/parsers/skills_parser.py +7 -0
  28. claude_mpm/cli/startup.py +133 -85
  29. claude_mpm/commands/mpm-agents-auto-configure.md +2 -2
  30. claude_mpm/commands/mpm-agents-list.md +2 -2
  31. claude_mpm/commands/mpm-config-view.md +2 -2
  32. claude_mpm/commands/mpm-help.md +3 -0
  33. claude_mpm/commands/{mpm-ticket-organize.md → mpm-organize.md} +4 -5
  34. claude_mpm/commands/mpm-postmortem.md +123 -0
  35. claude_mpm/commands/mpm-session-resume.md +2 -2
  36. claude_mpm/commands/mpm-ticket-view.md +2 -2
  37. claude_mpm/config/agent_presets.py +312 -82
  38. claude_mpm/config/agent_sources.py +27 -0
  39. claude_mpm/config/skill_presets.py +392 -0
  40. claude_mpm/constants.py +1 -0
  41. claude_mpm/core/claude_runner.py +2 -25
  42. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  43. claude_mpm/core/framework/loaders/file_loader.py +54 -101
  44. claude_mpm/core/interactive_session.py +19 -5
  45. claude_mpm/core/oneshot_session.py +16 -4
  46. claude_mpm/core/output_style_manager.py +173 -43
  47. claude_mpm/core/protocols/__init__.py +23 -0
  48. claude_mpm/core/protocols/runner_protocol.py +103 -0
  49. claude_mpm/core/protocols/session_protocol.py +131 -0
  50. claude_mpm/core/shared/singleton_manager.py +11 -4
  51. claude_mpm/core/socketio_pool.py +3 -3
  52. claude_mpm/core/system_context.py +38 -0
  53. claude_mpm/core/unified_agent_registry.py +134 -16
  54. claude_mpm/core/unified_config.py +22 -0
  55. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  56. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-313.pyc +0 -0
  57. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  58. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  59. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  60. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  61. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  62. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  63. claude_mpm/hooks/claude_hooks/event_handlers.py +35 -2
  64. claude_mpm/hooks/claude_hooks/hook_handler.py +4 -0
  65. claude_mpm/hooks/claude_hooks/memory_integration.py +12 -1
  66. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  67. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  68. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  69. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  70. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  71. claude_mpm/hooks/claude_hooks/services/connection_manager.py +4 -0
  72. claude_mpm/models/agent_definition.py +7 -0
  73. claude_mpm/scripts/launch_monitor.py +93 -13
  74. claude_mpm/services/agents/agent_recommendation_service.py +279 -0
  75. claude_mpm/services/agents/cache_git_manager.py +621 -0
  76. claude_mpm/services/agents/deployment/agent_template_builder.py +3 -2
  77. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +110 -3
  78. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +518 -55
  79. claude_mpm/services/agents/git_source_manager.py +20 -0
  80. claude_mpm/services/agents/sources/git_source_sync_service.py +45 -6
  81. claude_mpm/services/agents/toolchain_detector.py +6 -5
  82. claude_mpm/services/analysis/__init__.py +35 -0
  83. claude_mpm/services/analysis/clone_detector.py +1030 -0
  84. claude_mpm/services/analysis/postmortem_reporter.py +474 -0
  85. claude_mpm/services/analysis/postmortem_service.py +765 -0
  86. claude_mpm/services/command_deployment_service.py +106 -5
  87. claude_mpm/services/core/base.py +7 -2
  88. claude_mpm/services/diagnostics/checks/mcp_services_check.py +7 -15
  89. claude_mpm/services/event_bus/config.py +3 -1
  90. claude_mpm/services/git/git_operations_service.py +8 -8
  91. claude_mpm/services/mcp_config_manager.py +75 -145
  92. claude_mpm/services/mcp_service_verifier.py +6 -3
  93. claude_mpm/services/monitor/daemon.py +37 -10
  94. claude_mpm/services/monitor/daemon_manager.py +134 -21
  95. claude_mpm/services/monitor/server.py +225 -19
  96. claude_mpm/services/project/project_organizer.py +4 -0
  97. claude_mpm/services/runner_configuration_service.py +16 -3
  98. claude_mpm/services/session_management_service.py +16 -4
  99. claude_mpm/services/socketio/event_normalizer.py +15 -1
  100. claude_mpm/services/socketio/server/core.py +160 -21
  101. claude_mpm/services/version_control/git_operations.py +103 -0
  102. claude_mpm/utils/agent_filters.py +261 -0
  103. claude_mpm/utils/gitignore.py +3 -0
  104. claude_mpm/utils/migration.py +372 -0
  105. claude_mpm/utils/progress.py +5 -1
  106. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/METADATA +69 -84
  107. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/RECORD +112 -153
  108. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/entry_points.txt +0 -2
  109. claude_mpm/dashboard/analysis_runner.py +0 -455
  110. claude_mpm/dashboard/index.html +0 -13
  111. claude_mpm/dashboard/open_dashboard.py +0 -66
  112. claude_mpm/dashboard/static/css/activity.css +0 -1958
  113. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  114. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  115. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  116. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  117. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  118. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  119. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  120. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  121. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  122. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  123. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  124. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  125. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  126. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  127. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  128. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  129. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  130. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  131. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  132. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  133. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  134. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  135. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  136. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  137. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  138. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  139. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  140. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  141. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  142. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  143. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  144. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  145. claude_mpm/dashboard/templates/code_simple.html +0 -153
  146. claude_mpm/dashboard/templates/index.html +0 -606
  147. claude_mpm/dashboard/test_dashboard.html +0 -372
  148. claude_mpm/scripts/mcp_server.py +0 -75
  149. claude_mpm/scripts/mcp_wrapper.py +0 -39
  150. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  151. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  152. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  153. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  154. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  155. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  156. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  157. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  158. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  159. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  160. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -971
  161. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  162. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  163. claude_mpm/services/mcp_gateway/main.py +0 -589
  164. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  165. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  166. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  167. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  168. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  169. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  170. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  171. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  172. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  173. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  174. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  175. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  176. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  177. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  178. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  179. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  180. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  181. /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
  182. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/WHEEL +0 -0
  183. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/licenses/LICENSE +0 -0
  184. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.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()