mcp-proxy-adapter 6.9.28__py3-none-any.whl → 6.9.30__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 mcp-proxy-adapter might be problematic. Click here for more details.

Files changed (212) hide show
  1. mcp_proxy_adapter/__init__.py +10 -0
  2. mcp_proxy_adapter/__main__.py +8 -21
  3. mcp_proxy_adapter/api/app.py +10 -913
  4. mcp_proxy_adapter/api/core/__init__.py +18 -0
  5. mcp_proxy_adapter/api/core/app_factory.py +243 -0
  6. mcp_proxy_adapter/api/core/lifespan_manager.py +55 -0
  7. mcp_proxy_adapter/api/core/registration_manager.py +166 -0
  8. mcp_proxy_adapter/api/core/ssl_context_factory.py +88 -0
  9. mcp_proxy_adapter/api/handlers.py +78 -199
  10. mcp_proxy_adapter/api/middleware/__init__.py +1 -44
  11. mcp_proxy_adapter/api/middleware/base.py +0 -42
  12. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +0 -85
  13. mcp_proxy_adapter/api/middleware/error_handling.py +1 -127
  14. mcp_proxy_adapter/api/middleware/factory.py +0 -94
  15. mcp_proxy_adapter/api/middleware/logging.py +0 -112
  16. mcp_proxy_adapter/api/middleware/performance.py +0 -35
  17. mcp_proxy_adapter/api/middleware/protocol_middleware.py +2 -98
  18. mcp_proxy_adapter/api/middleware/transport_middleware.py +0 -37
  19. mcp_proxy_adapter/api/middleware/unified_security.py +10 -10
  20. mcp_proxy_adapter/api/middleware/user_info_middleware.py +0 -118
  21. mcp_proxy_adapter/api/openapi/__init__.py +21 -0
  22. mcp_proxy_adapter/api/openapi/command_integration.py +105 -0
  23. mcp_proxy_adapter/api/openapi/openapi_generator.py +40 -0
  24. mcp_proxy_adapter/api/openapi/openapi_registry.py +62 -0
  25. mcp_proxy_adapter/api/openapi/schema_loader.py +116 -0
  26. mcp_proxy_adapter/api/schemas.py +0 -61
  27. mcp_proxy_adapter/api/tool_integration.py +0 -117
  28. mcp_proxy_adapter/api/tools.py +0 -46
  29. mcp_proxy_adapter/cli/__init__.py +12 -0
  30. mcp_proxy_adapter/cli/commands/__init__.py +15 -0
  31. mcp_proxy_adapter/cli/commands/client.py +100 -0
  32. mcp_proxy_adapter/cli/commands/config_generate.py +21 -0
  33. mcp_proxy_adapter/cli/commands/config_validate.py +36 -0
  34. mcp_proxy_adapter/cli/commands/generate.py +259 -0
  35. mcp_proxy_adapter/cli/commands/server.py +174 -0
  36. mcp_proxy_adapter/cli/commands/sets.py +128 -0
  37. mcp_proxy_adapter/cli/commands/testconfig.py +177 -0
  38. mcp_proxy_adapter/cli/examples/__init__.py +8 -0
  39. mcp_proxy_adapter/cli/examples/http_basic.py +82 -0
  40. mcp_proxy_adapter/cli/examples/https_token.py +96 -0
  41. mcp_proxy_adapter/cli/examples/mtls_roles.py +103 -0
  42. mcp_proxy_adapter/cli/main.py +63 -0
  43. mcp_proxy_adapter/cli/parser.py +324 -0
  44. mcp_proxy_adapter/cli/validators.py +231 -0
  45. mcp_proxy_adapter/client/jsonrpc_client.py +406 -0
  46. mcp_proxy_adapter/client/proxy.py +45 -0
  47. mcp_proxy_adapter/commands/__init__.py +44 -28
  48. mcp_proxy_adapter/commands/auth_validation_command.py +7 -344
  49. mcp_proxy_adapter/commands/base.py +19 -43
  50. mcp_proxy_adapter/commands/builtin_commands.py +0 -75
  51. mcp_proxy_adapter/commands/catalog/__init__.py +20 -0
  52. mcp_proxy_adapter/commands/catalog/catalog_loader.py +34 -0
  53. mcp_proxy_adapter/commands/catalog/catalog_manager.py +122 -0
  54. mcp_proxy_adapter/commands/catalog/catalog_syncer.py +149 -0
  55. mcp_proxy_adapter/commands/catalog/command_catalog.py +43 -0
  56. mcp_proxy_adapter/commands/catalog/dependency_manager.py +37 -0
  57. mcp_proxy_adapter/commands/catalog_manager.py +58 -928
  58. mcp_proxy_adapter/commands/cert_monitor_command.py +0 -88
  59. mcp_proxy_adapter/commands/certificate_management_command.py +0 -45
  60. mcp_proxy_adapter/commands/command_registry.py +172 -904
  61. mcp_proxy_adapter/commands/config_command.py +0 -28
  62. mcp_proxy_adapter/commands/dependency_container.py +1 -70
  63. mcp_proxy_adapter/commands/dependency_manager.py +0 -128
  64. mcp_proxy_adapter/commands/echo_command.py +0 -34
  65. mcp_proxy_adapter/commands/health_command.py +0 -3
  66. mcp_proxy_adapter/commands/help_command.py +0 -159
  67. mcp_proxy_adapter/commands/hooks.py +0 -137
  68. mcp_proxy_adapter/commands/key_management_command.py +0 -25
  69. mcp_proxy_adapter/commands/load_command.py +7 -78
  70. mcp_proxy_adapter/commands/plugins_command.py +0 -16
  71. mcp_proxy_adapter/commands/protocol_management_command.py +0 -28
  72. mcp_proxy_adapter/commands/proxy_registration_command.py +0 -88
  73. mcp_proxy_adapter/commands/queue_commands.py +750 -0
  74. mcp_proxy_adapter/commands/registration_status_command.py +0 -43
  75. mcp_proxy_adapter/commands/registry/__init__.py +18 -0
  76. mcp_proxy_adapter/commands/registry/command_info.py +103 -0
  77. mcp_proxy_adapter/commands/registry/command_loader.py +207 -0
  78. mcp_proxy_adapter/commands/registry/command_manager.py +119 -0
  79. mcp_proxy_adapter/commands/registry/command_registry.py +217 -0
  80. mcp_proxy_adapter/commands/reload_command.py +0 -80
  81. mcp_proxy_adapter/commands/result.py +25 -77
  82. mcp_proxy_adapter/commands/role_test_command.py +0 -44
  83. mcp_proxy_adapter/commands/roles_management_command.py +0 -199
  84. mcp_proxy_adapter/commands/security_command.py +0 -30
  85. mcp_proxy_adapter/commands/settings_command.py +0 -68
  86. mcp_proxy_adapter/commands/ssl_setup_command.py +0 -42
  87. mcp_proxy_adapter/commands/token_management_command.py +0 -1
  88. mcp_proxy_adapter/commands/transport_management_command.py +0 -20
  89. mcp_proxy_adapter/commands/unload_command.py +0 -71
  90. mcp_proxy_adapter/config.py +15 -626
  91. mcp_proxy_adapter/core/__init__.py +5 -39
  92. mcp_proxy_adapter/core/app_factory.py +14 -36
  93. mcp_proxy_adapter/core/app_runner.py +0 -27
  94. mcp_proxy_adapter/core/auth_validator.py +1 -93
  95. mcp_proxy_adapter/core/certificate/__init__.py +20 -0
  96. mcp_proxy_adapter/core/certificate/certificate_creator.py +371 -0
  97. mcp_proxy_adapter/core/certificate/certificate_extractor.py +183 -0
  98. mcp_proxy_adapter/core/certificate/certificate_utils.py +249 -0
  99. mcp_proxy_adapter/core/certificate/certificate_validator.py +110 -0
  100. mcp_proxy_adapter/core/certificate/ssl_context_manager.py +70 -0
  101. mcp_proxy_adapter/core/certificate_utils.py +64 -903
  102. mcp_proxy_adapter/core/client.py +10 -9
  103. mcp_proxy_adapter/core/client_manager.py +0 -19
  104. mcp_proxy_adapter/core/client_security.py +0 -2
  105. mcp_proxy_adapter/core/config/__init__.py +18 -0
  106. mcp_proxy_adapter/core/config/config.py +195 -0
  107. mcp_proxy_adapter/core/config/config_factory.py +22 -0
  108. mcp_proxy_adapter/core/config/config_loader.py +66 -0
  109. mcp_proxy_adapter/core/config/feature_manager.py +31 -0
  110. mcp_proxy_adapter/core/config/simple_config.py +112 -0
  111. mcp_proxy_adapter/core/config/simple_config_generator.py +50 -0
  112. mcp_proxy_adapter/core/config/simple_config_validator.py +96 -0
  113. mcp_proxy_adapter/core/config_converter.py +0 -186
  114. mcp_proxy_adapter/core/config_validator.py +96 -1238
  115. mcp_proxy_adapter/core/errors.py +7 -42
  116. mcp_proxy_adapter/core/job_manager.py +54 -0
  117. mcp_proxy_adapter/core/logging.py +2 -22
  118. mcp_proxy_adapter/core/mtls_asgi.py +0 -20
  119. mcp_proxy_adapter/core/mtls_asgi_app.py +0 -12
  120. mcp_proxy_adapter/core/mtls_proxy.py +0 -80
  121. mcp_proxy_adapter/core/mtls_server.py +3 -173
  122. mcp_proxy_adapter/core/protocol_manager.py +1 -191
  123. mcp_proxy_adapter/core/proxy/__init__.py +22 -0
  124. mcp_proxy_adapter/core/proxy/auth_manager.py +27 -0
  125. mcp_proxy_adapter/core/proxy/proxy_registration_manager.py +137 -0
  126. mcp_proxy_adapter/core/proxy/registration_client.py +60 -0
  127. mcp_proxy_adapter/core/proxy/ssl_manager.py +101 -0
  128. mcp_proxy_adapter/core/proxy_client.py +0 -1
  129. mcp_proxy_adapter/core/proxy_registration.py +36 -913
  130. mcp_proxy_adapter/core/role_utils.py +0 -308
  131. mcp_proxy_adapter/core/security_adapter.py +1 -36
  132. mcp_proxy_adapter/core/security_factory.py +1 -150
  133. mcp_proxy_adapter/core/security_integration.py +0 -33
  134. mcp_proxy_adapter/core/server_adapter.py +1 -40
  135. mcp_proxy_adapter/core/server_engine.py +2 -173
  136. mcp_proxy_adapter/core/settings.py +0 -127
  137. mcp_proxy_adapter/core/signal_handler.py +0 -65
  138. mcp_proxy_adapter/core/ssl_utils.py +19 -137
  139. mcp_proxy_adapter/core/transport_manager.py +0 -151
  140. mcp_proxy_adapter/core/unified_config_adapter.py +1 -193
  141. mcp_proxy_adapter/core/utils.py +1 -182
  142. mcp_proxy_adapter/core/validation/__init__.py +21 -0
  143. mcp_proxy_adapter/core/validation/config_validator.py +211 -0
  144. mcp_proxy_adapter/core/validation/file_validator.py +73 -0
  145. mcp_proxy_adapter/core/validation/protocol_validator.py +191 -0
  146. mcp_proxy_adapter/core/validation/security_validator.py +58 -0
  147. mcp_proxy_adapter/core/validation/validation_result.py +27 -0
  148. mcp_proxy_adapter/custom_openapi.py +33 -652
  149. mcp_proxy_adapter/examples/bugfix_certificate_config.py +0 -23
  150. mcp_proxy_adapter/examples/check_config.py +0 -2
  151. mcp_proxy_adapter/examples/client_usage_example.py +164 -0
  152. mcp_proxy_adapter/examples/config_builder.py +13 -2
  153. mcp_proxy_adapter/examples/config_cli.py +0 -1
  154. mcp_proxy_adapter/examples/create_test_configs.py +0 -46
  155. mcp_proxy_adapter/examples/debug_request_state.py +0 -1
  156. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +0 -47
  157. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +0 -45
  158. mcp_proxy_adapter/examples/full_application/commands/echo_command.py +0 -12
  159. mcp_proxy_adapter/examples/full_application/commands/help_command.py +0 -12
  160. mcp_proxy_adapter/examples/full_application/commands/list_command.py +0 -7
  161. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +0 -2
  162. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +0 -59
  163. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +0 -54
  164. mcp_proxy_adapter/examples/full_application/main.py +186 -150
  165. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +0 -107
  166. mcp_proxy_adapter/examples/full_application/test_minimal_server.py +0 -24
  167. mcp_proxy_adapter/examples/full_application/test_server.py +0 -58
  168. mcp_proxy_adapter/examples/generate_config.py +65 -11
  169. mcp_proxy_adapter/examples/queue_demo_simple.py +632 -0
  170. mcp_proxy_adapter/examples/queue_integration_example.py +578 -0
  171. mcp_proxy_adapter/examples/queue_server_demo.py +82 -0
  172. mcp_proxy_adapter/examples/queue_server_example.py +85 -0
  173. mcp_proxy_adapter/examples/queue_server_simple.py +173 -0
  174. mcp_proxy_adapter/examples/required_certificates.py +0 -2
  175. mcp_proxy_adapter/examples/run_full_test_suite.py +0 -29
  176. mcp_proxy_adapter/examples/run_proxy_server.py +31 -71
  177. mcp_proxy_adapter/examples/run_security_tests_fixed.py +0 -27
  178. mcp_proxy_adapter/examples/security_test/__init__.py +18 -0
  179. mcp_proxy_adapter/examples/security_test/auth_manager.py +14 -0
  180. mcp_proxy_adapter/examples/security_test/ssl_context_manager.py +28 -0
  181. mcp_proxy_adapter/examples/security_test/test_client.py +159 -0
  182. mcp_proxy_adapter/examples/security_test/test_result.py +22 -0
  183. mcp_proxy_adapter/examples/security_test_client.py +24 -1075
  184. mcp_proxy_adapter/examples/setup/__init__.py +24 -0
  185. mcp_proxy_adapter/examples/setup/certificate_manager.py +215 -0
  186. mcp_proxy_adapter/examples/setup/config_generator.py +12 -0
  187. mcp_proxy_adapter/examples/setup/config_validator.py +118 -0
  188. mcp_proxy_adapter/examples/setup/environment_setup.py +62 -0
  189. mcp_proxy_adapter/examples/setup/test_files_generator.py +10 -0
  190. mcp_proxy_adapter/examples/setup/test_runner.py +89 -0
  191. mcp_proxy_adapter/examples/setup_test_environment.py +133 -1425
  192. mcp_proxy_adapter/examples/test_config.py +0 -3
  193. mcp_proxy_adapter/examples/test_config_builder.py +25 -405
  194. mcp_proxy_adapter/examples/test_examples.py +0 -1
  195. mcp_proxy_adapter/examples/test_framework_complete.py +0 -2
  196. mcp_proxy_adapter/examples/test_mcp_server.py +0 -1
  197. mcp_proxy_adapter/examples/test_protocol_examples.py +0 -1
  198. mcp_proxy_adapter/examples/universal_client.py +0 -6
  199. mcp_proxy_adapter/examples/update_config_certificates.py +0 -1
  200. mcp_proxy_adapter/examples/validate_generator_compatibility.py +0 -1
  201. mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +0 -187
  202. mcp_proxy_adapter/integrations/__init__.py +25 -0
  203. mcp_proxy_adapter/integrations/queuemgr_integration.py +462 -0
  204. mcp_proxy_adapter/main.py +70 -62
  205. mcp_proxy_adapter/openapi.py +0 -22
  206. mcp_proxy_adapter/version.py +1 -1
  207. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/METADATA +2 -1
  208. mcp_proxy_adapter-6.9.30.dist-info/RECORD +235 -0
  209. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/entry_points.txt +1 -1
  210. mcp_proxy_adapter-6.9.28.dist-info/RECORD +0 -149
  211. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/WHEEL +0 -0
  212. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/top_level.txt +0 -0
@@ -14,13 +14,12 @@ import json
14
14
  import os
15
15
  import ssl
16
16
  import time
17
- from typing import Dict, Any, Optional, List, Union
18
- from urllib.parse import urljoin
19
17
  from pathlib import Path
18
+ from typing import Any, Dict, Optional
19
+ from urllib.parse import urljoin
20
20
 
21
21
  import aiohttp
22
22
  import requests
23
- from requests.exceptions import RequestException
24
23
 
25
24
  # Import security framework components
26
25
  try:
@@ -29,17 +28,11 @@ try:
29
28
  AuthManager,
30
29
  CertificateManager,
31
30
  PermissionManager,
32
- )
33
- from mcp_security_framework.utils import (
34
31
  generate_api_key,
35
32
  create_jwt_token,
36
33
  validate_jwt_token,
37
- )
38
- from mcp_security_framework.utils import (
39
34
  extract_roles_from_cert,
40
35
  validate_certificate_chain,
41
- )
42
- from mcp_security_framework.utils import (
43
36
  create_ssl_context,
44
37
  validate_server_certificate,
45
38
  )
@@ -47,6 +40,14 @@ try:
47
40
  SECURITY_FRAMEWORK_AVAILABLE = True
48
41
  except ImportError:
49
42
  SECURITY_FRAMEWORK_AVAILABLE = False
43
+ # Define stubs for missing imports
44
+ generate_api_key = None
45
+ create_jwt_token = None
46
+ validate_jwt_token = None
47
+ extract_roles_from_cert = None
48
+ validate_certificate_chain = None
49
+ create_ssl_context = None
50
+ validate_server_certificate = None
50
51
 
51
52
 
52
53
  class UniversalClient:
@@ -213,14 +213,6 @@ class ClientManager:
213
213
  self.get_global_logger().error(f"Failed to get status for client {client_id}: {e}")
214
214
  return {"error": str(e)}
215
215
 
216
- async def list_clients(self) -> List[str]:
217
- """
218
- Get list of all client identifiers.
219
-
220
- Returns:
221
- List of client identifiers
222
- """
223
- return list(self.clients.keys())
224
216
 
225
217
  async def cleanup(self):
226
218
  """Clean up all client connections."""
@@ -237,17 +229,6 @@ class ClientManager:
237
229
  await self.cleanup()
238
230
 
239
231
 
240
- def create_client_manager(config: Dict[str, Any]) -> ClientManager:
241
- """
242
- Create a ClientManager instance.
243
-
244
- Args:
245
- config: Client manager configuration
246
-
247
- Returns:
248
- ClientManager instance
249
- """
250
- return ClientManager(config)
251
232
 
252
233
 
253
234
  # Example usage and testing functions
@@ -15,7 +15,6 @@ from pathlib import Path
15
15
 
16
16
  # Import framework utilities
17
17
  try:
18
- from mcp_security_framework.utils.crypto_utils import (
19
18
  generate_api_key,
20
19
  create_jwt_token,
21
20
  verify_jwt_token,
@@ -27,7 +26,6 @@ try:
27
26
  validate_certificate_format,
28
27
  )
29
28
  from mcp_security_framework.core.ssl_manager import SSLManager
30
- from mcp_security_framework.schemas.config import SSLConfig, AuthConfig
31
29
  from mcp_security_framework.schemas.models import AuthResult, ValidationResult
32
30
 
33
31
  SECURITY_FRAMEWORK_AVAILABLE = True
@@ -0,0 +1,18 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Configuration management package for MCP Proxy Adapter.
6
+ """
7
+
8
+ from .config import Config
9
+ from .config_loader import ConfigLoader
10
+ from .feature_manager import FeatureManager
11
+ from .config_factory import ConfigFactory
12
+
13
+ __all__ = [
14
+ "Config",
15
+ "ConfigLoader",
16
+ "FeatureManager",
17
+ "ConfigFactory",
18
+ ]
@@ -0,0 +1,195 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Main configuration class for MCP Proxy Adapter.
6
+ """
7
+
8
+ from typing import Any, Dict, Optional, List
9
+ from pathlib import Path
10
+
11
+ from mcp_proxy_adapter.core.logging import get_global_logger
12
+ from .config_loader import ConfigLoader
13
+ from .feature_manager import FeatureManager
14
+ from .config_factory import ConfigFactory
15
+
16
+ # Import validation if available
17
+ try:
18
+ VALIDATION_AVAILABLE = True
19
+ except ImportError:
20
+ VALIDATION_AVAILABLE = False
21
+
22
+ # Import configuration errors
23
+ from ..errors import ConfigError, ValidationResult
24
+
25
+
26
+ class Config:
27
+ """
28
+ Configuration management class for the microservice.
29
+ Allows loading settings from configuration file and environment variables.
30
+ Supports optional features that can be enabled/disabled.
31
+ """
32
+
33
+ def __init__(self, config_path: Optional[str] = None, validate_on_load: bool = False):
34
+ """
35
+ Initialize configuration.
36
+
37
+ Args:
38
+ config_path: Path to configuration file. If not specified,
39
+ "./config.json" is used.
40
+ validate_on_load: Whether to validate configuration on load (default: False)
41
+
42
+ Raises:
43
+ ConfigError: If configuration validation fails
44
+ """
45
+ self.config_path = config_path or "./config.json"
46
+ self.config_data: Dict[str, Any] = {}
47
+ self.validate_on_load = validate_on_load
48
+ self.validation_results: List[ValidationResult] = []
49
+ self.validator = None
50
+
51
+ # Initialize components
52
+ self.logger = get_global_logger()
53
+ self.loader = ConfigLoader()
54
+ self.feature_manager = FeatureManager(self.config_data)
55
+ self.factory = ConfigFactory()
56
+
57
+ # Load configuration
58
+ self.load_config()
59
+
60
+ def load_config(self) -> None:
61
+ """Load configuration from file and environment variables."""
62
+ try:
63
+ # Load from file if it exists
64
+ if Path(self.config_path).exists():
65
+ file_config = self.loader.load_from_file(self.config_path)
66
+ self.config_data.update(file_config)
67
+
68
+ # Load from environment variables
69
+ try:
70
+ env_config = self.loader.load_from_env()
71
+ self._merge_config(env_config)
72
+ except AttributeError:
73
+ # load_from_env doesn't exist yet, skip
74
+ pass
75
+
76
+ # Validate if required
77
+ if self.validate_on_load and VALIDATION_AVAILABLE:
78
+ self.validate()
79
+
80
+ except Exception as e:
81
+ self.logger.error(f"Failed to load configuration: {e}")
82
+ raise ConfigError(f"Configuration loading failed: {e}")
83
+
84
+ def _merge_config(self, new_config: Dict[str, Any]) -> None:
85
+ """
86
+ Merge new configuration into existing configuration.
87
+
88
+ Args:
89
+ new_config: New configuration to merge
90
+ """
91
+ for section, values in new_config.items():
92
+ if section in self.config_data:
93
+ if isinstance(self.config_data[section], dict) and isinstance(values, dict):
94
+ self.config_data[section].update(values)
95
+ else:
96
+ self.config_data[section] = values
97
+ else:
98
+ self.config_data[section] = values
99
+
100
+
101
+
102
+
103
+
104
+ def enable_feature(self, feature: str) -> None:
105
+ """
106
+ Enable a feature.
107
+
108
+ Args:
109
+ feature: Feature name
110
+ """
111
+ self.feature_manager.enable_feature(feature)
112
+
113
+ def disable_feature(self, feature: str) -> None:
114
+ """
115
+ Disable a feature.
116
+
117
+ Args:
118
+ feature: Feature name
119
+ """
120
+ self.feature_manager.disable_feature(feature)
121
+
122
+ def is_feature_enabled(self, feature: str) -> bool:
123
+ """
124
+ Check if feature is enabled.
125
+
126
+ Args:
127
+ feature: Feature name
128
+
129
+ Returns:
130
+ True if enabled, False otherwise
131
+ """
132
+ return self.feature_manager.is_feature_enabled(feature)
133
+
134
+ def get_enabled_features(self) -> List[str]:
135
+ """
136
+ Get list of enabled features.
137
+
138
+ Returns:
139
+ List of enabled feature names
140
+ """
141
+ return self.feature_manager.get_enabled_features()
142
+
143
+ def validate(self) -> List[ValidationResult]:
144
+ """
145
+ Validate configuration.
146
+
147
+ Returns:
148
+ List of validation results
149
+ """
150
+ if not VALIDATION_AVAILABLE:
151
+ self.logger.warning("Configuration validation not available")
152
+ return []
153
+
154
+ try:
155
+ self.validator = ConfigValidator()
156
+ self.validator.config_data = self.config_data
157
+ self.validation_results = self.validator.validate_config()
158
+ return self.validation_results
159
+ except Exception as e:
160
+ self.logger.error(f"Configuration validation failed: {e}")
161
+ return []
162
+
163
+
164
+ def get_validation_errors(self) -> List[ValidationResult]:
165
+ """Get validation errors."""
166
+ return [result for result in self.validation_results if result.level == "error"]
167
+
168
+ def get_validation_warnings(self) -> List[ValidationResult]:
169
+ """Get validation warnings."""
170
+ return [result for result in self.validation_results if result.level == "warning"]
171
+
172
+ def get_validation_summary(self) -> Dict[str, Any]:
173
+ """Get validation summary."""
174
+ errors = self.get_validation_errors()
175
+ warnings = self.get_validation_warnings()
176
+
177
+ return {
178
+ "total": len(self.validation_results),
179
+ "errors": len(errors),
180
+ "warnings": len(warnings),
181
+ "is_valid": len(errors) == 0
182
+ }
183
+
184
+
185
+ def check_feature_requirements(self, feature: str) -> List[ValidationResult]:
186
+ """
187
+ Check feature requirements.
188
+
189
+ Args:
190
+ feature: Feature name
191
+
192
+ Returns:
193
+ List of validation results
194
+ """
195
+ return self.feature_manager.check_feature_requirements(feature)
@@ -0,0 +1,22 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Configuration factory for creating predefined configurations.
6
+ """
7
+
8
+ from typing import Dict, Any
9
+
10
+ from mcp_proxy_adapter.core.logging import get_global_logger
11
+
12
+
13
+ class ConfigFactory:
14
+ """Factory for creating predefined configurations."""
15
+
16
+ def __init__(self):
17
+ """Initialize config factory."""
18
+ self.logger = get_global_logger()
19
+
20
+
21
+
22
+
@@ -0,0 +1,66 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Configuration loading utilities for MCP Proxy Adapter.
6
+ """
7
+
8
+ import json
9
+ import os
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+ from mcp_proxy_adapter.core.logging import get_global_logger
14
+
15
+
16
+ class ConfigLoader:
17
+ """Loader for configuration files and environment variables."""
18
+
19
+ def __init__(self):
20
+ """Initialize config loader."""
21
+ self.logger = get_global_logger()
22
+
23
+ def load_from_file(self, config_path: str | Path) -> dict:
24
+ """
25
+ Load configuration from JSON file.
26
+
27
+ Args:
28
+ config_path: Path to configuration file
29
+
30
+ Returns:
31
+ Configuration dictionary
32
+
33
+ Raises:
34
+ FileNotFoundError: If config file doesn't exist
35
+ json.JSONDecodeError: If config file is invalid JSON
36
+ """
37
+ path = Path(config_path)
38
+ if not path.exists():
39
+ raise FileNotFoundError(f"Configuration file not found: {config_path}")
40
+
41
+ with open(path, 'r', encoding='utf-8') as f:
42
+ return json.load(f)
43
+
44
+ def _convert_env_value(self, value: str) -> Any:
45
+ """
46
+ Convert environment variable value to appropriate type.
47
+
48
+ Args:
49
+ value: Value as string
50
+
51
+ Returns:
52
+ Converted value
53
+ """
54
+ # Try to convert to appropriate type
55
+ if value.lower() == "true":
56
+ return True
57
+ elif value.lower() == "false":
58
+ return False
59
+ elif value.isdigit():
60
+ return int(value)
61
+ else:
62
+ try:
63
+ return float(value)
64
+ except ValueError:
65
+ return value
66
+
@@ -0,0 +1,31 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Feature management utilities for MCP Proxy Adapter configuration.
6
+ """
7
+
8
+ from typing import Any, Dict, List
9
+
10
+ from mcp_proxy_adapter.core.logging import get_global_logger
11
+
12
+
13
+ class FeatureManager:
14
+ """Manager for configuration features."""
15
+
16
+ def __init__(self, config_data: Dict[str, Any]):
17
+ """
18
+ Initialize feature manager.
19
+
20
+ Args:
21
+ config_data: Configuration data dictionary
22
+ """
23
+ self.config_data = config_data
24
+ self.logger = get_global_logger()
25
+
26
+
27
+
28
+
29
+
30
+
31
+
@@ -0,0 +1,112 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Simple configuration data container and IO helpers for MCP Proxy Adapter.
6
+
7
+ This module provides a minimal, explicit configuration model with three
8
+ sections: server, proxy_client and auth.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import json
14
+ from dataclasses import dataclass, field
15
+ from pathlib import Path
16
+ from typing import Any, Dict, List, Optional
17
+
18
+
19
+ @dataclass
20
+ class ServerConfig:
21
+ host: str
22
+ port: int
23
+ protocol: str # http | https | mtls
24
+ cert_file: Optional[str] = None
25
+ key_file: Optional[str] = None
26
+ ca_cert_file: Optional[str] = None
27
+ crl_file: Optional[str] = None
28
+ log_dir: str = "./logs"
29
+
30
+
31
+ @dataclass
32
+ class HeartbeatConfig:
33
+ endpoint: str = "/heartbeat"
34
+ interval: int = 30
35
+
36
+
37
+ @dataclass
38
+ class RegistrationConfig:
39
+ register_endpoint: str = "/register"
40
+ unregister_endpoint: str = "/unregister"
41
+ auto_on_startup: bool = True
42
+ auto_on_shutdown: bool = True
43
+
44
+
45
+ @dataclass
46
+ class ProxyClientConfig:
47
+ enabled: bool = False
48
+ host: str = "localhost"
49
+ port: int = 3005
50
+ protocol: str = "http"
51
+ cert_file: Optional[str] = None
52
+ key_file: Optional[str] = None
53
+ ca_cert_file: Optional[str] = None
54
+ crl_file: Optional[str] = None
55
+ heartbeat: HeartbeatConfig = field(default_factory=HeartbeatConfig)
56
+ registration: RegistrationConfig = field(default_factory=RegistrationConfig)
57
+
58
+
59
+ @dataclass
60
+ class AuthConfig:
61
+ use_token: bool = False
62
+ use_roles: bool = False
63
+ tokens: Dict[str, List[str]] = field(default_factory=dict)
64
+ roles: Dict[str, List[str]] = field(default_factory=dict)
65
+
66
+
67
+ @dataclass
68
+ class SimpleConfigModel:
69
+ server: ServerConfig
70
+ proxy_client: ProxyClientConfig = field(default_factory=ProxyClientConfig)
71
+ auth: AuthConfig = field(default_factory=AuthConfig)
72
+
73
+
74
+ class SimpleConfig:
75
+ """High-level loader/saver for SimpleConfigModel."""
76
+
77
+ def __init__(self, config_path: str = "config.json") -> None:
78
+ self.config_path: Path = Path(config_path)
79
+ self.model: Optional[SimpleConfigModel] = None
80
+
81
+ def load(self) -> SimpleConfigModel:
82
+ content = json.loads(self.config_path.read_text(encoding="utf-8"))
83
+ server = ServerConfig(**content["server"]) # type: ignore[arg-type]
84
+ proxy_client = ProxyClientConfig(**content.get("proxy_client", {})) # type: ignore[arg-type]
85
+ # Nested structures for proxy client (heartbeat/registration)
86
+ if isinstance(content.get("proxy_client"), dict):
87
+ pc = content["proxy_client"]
88
+ if isinstance(pc.get("heartbeat"), dict):
89
+ proxy_client.heartbeat = HeartbeatConfig(**pc["heartbeat"]) # type: ignore[arg-type]
90
+ if isinstance(pc.get("registration"), dict):
91
+ proxy_client.registration = RegistrationConfig(**pc["registration"]) # type: ignore[arg-type]
92
+ auth = AuthConfig(**content.get("auth", {})) # type: ignore[arg-type]
93
+ self.model = SimpleConfigModel(server=server, proxy_client=proxy_client, auth=auth)
94
+ return self.model
95
+
96
+ def save(self, out_path: Optional[str] = None) -> None:
97
+ if self.model is None:
98
+ raise ValueError("Configuration model is not loaded")
99
+ path = Path(out_path) if out_path else self.config_path
100
+ data: Dict[str, Any] = {
101
+ "server": vars(self.model.server),
102
+ "proxy_client": {
103
+ **{k: v for k, v in vars(self.model.proxy_client).items() if k not in {"heartbeat", "registration"}},
104
+ "heartbeat": vars(self.model.proxy_client.heartbeat),
105
+ "registration": vars(self.model.proxy_client.registration),
106
+ },
107
+ "auth": vars(self.model.auth),
108
+ }
109
+ path.parent.mkdir(parents=True, exist_ok=True)
110
+ path.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8")
111
+
112
+
@@ -0,0 +1,50 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Simple configuration generator for MCP Proxy Adapter.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Optional
11
+
12
+ from .simple_config import (
13
+ SimpleConfig,
14
+ SimpleConfigModel,
15
+ ServerConfig,
16
+ ProxyClientConfig,
17
+ AuthConfig,
18
+ )
19
+
20
+
21
+ class SimpleConfigGenerator:
22
+ """Generate minimal configuration according to the plan."""
23
+
24
+ def generate(self, protocol: str, with_proxy: bool = False, out_path: str = "config.json") -> str:
25
+ server = ServerConfig(host="0.0.0.0", port=8080, protocol=protocol)
26
+ if protocol in ("https", "mtls"):
27
+ server.cert_file = "./certs/server.crt"
28
+ server.key_file = "./certs/server.key"
29
+ if protocol == "mtls":
30
+ server.ca_cert_file = "./certs/ca.crt"
31
+
32
+ proxy = ProxyClientConfig(enabled=with_proxy)
33
+ if with_proxy:
34
+ proxy.protocol = protocol
35
+ proxy.host = "localhost"
36
+ proxy.port = 3005
37
+ if protocol in ("https", "mtls"):
38
+ proxy.cert_file = "./certs/client.crt"
39
+ proxy.key_file = "./certs/client.key"
40
+ if protocol == "mtls":
41
+ proxy.ca_cert_file = "./certs/ca.crt"
42
+
43
+ auth = AuthConfig(use_token=False, use_roles=False, tokens={}, roles={})
44
+
45
+ cfg = SimpleConfig()
46
+ cfg.model = SimpleConfigModel(server=server, proxy_client=proxy, auth=auth)
47
+ cfg.save(out_path)
48
+ return out_path
49
+
50
+
@@ -0,0 +1,96 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Simple configuration validator ensuring required fields and files exist.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ from dataclasses import dataclass
12
+ from typing import List
13
+
14
+ from .simple_config import SimpleConfigModel
15
+
16
+
17
+ @dataclass
18
+ class ValidationError:
19
+ message: str
20
+
21
+
22
+ class SimpleConfigValidator:
23
+ """Validate SimpleConfigModel instances."""
24
+
25
+ def validate(self, model: SimpleConfigModel) -> List[ValidationError]:
26
+ errors: List[ValidationError] = []
27
+ errors.extend(self._validate_server(model))
28
+ errors.extend(self._validate_proxy_client(model))
29
+ errors.extend(self._validate_auth(model))
30
+ return errors
31
+
32
+ def _validate_server(self, model: SimpleConfigModel) -> List[ValidationError]:
33
+ e: List[ValidationError] = []
34
+ s = model.server
35
+ if not s.host:
36
+ e.append(ValidationError("server.host is required"))
37
+ if not isinstance(s.port, int):
38
+ e.append(ValidationError("server.port must be integer"))
39
+ if s.protocol not in ("http", "https", "mtls"):
40
+ e.append(ValidationError("server.protocol must be one of: http, https, mtls"))
41
+ if s.protocol in ("https", "mtls"):
42
+ if not s.cert_file:
43
+ e.append(ValidationError(f"server.cert_file is required for {s.protocol}"))
44
+ if not s.key_file:
45
+ e.append(ValidationError(f"server.key_file is required for {s.protocol}"))
46
+ if s.protocol == "mtls" and not s.ca_cert_file:
47
+ e.append(ValidationError("server.ca_cert_file is required for mtls"))
48
+ # Files existence (if provided)
49
+ for label, path in ("cert_file", s.cert_file), ("key_file", s.key_file), ("ca_cert_file", s.ca_cert_file), ("crl_file", s.crl_file):
50
+ if path and not os.path.exists(path):
51
+ e.append(ValidationError(f"server.{label} not found: {path}"))
52
+ return e
53
+
54
+ def _validate_proxy_client(self, model: SimpleConfigModel) -> List[ValidationError]:
55
+ e: List[ValidationError] = []
56
+ pc = model.proxy_client
57
+ if pc.enabled:
58
+ if not pc.host:
59
+ e.append(ValidationError("proxy_client.host is required when enabled"))
60
+ if not isinstance(pc.port, int):
61
+ e.append(ValidationError("proxy_client.port must be integer"))
62
+ if pc.protocol not in ("http", "https", "mtls"):
63
+ e.append(ValidationError("proxy_client.protocol must be one of: http, https, mtls"))
64
+ if pc.protocol in ("https", "mtls"):
65
+ if not pc.cert_file:
66
+ e.append(ValidationError(f"proxy_client.cert_file is required for {pc.protocol}"))
67
+ if not pc.key_file:
68
+ e.append(ValidationError(f"proxy_client.key_file is required for {pc.protocol}"))
69
+ if pc.protocol == "mtls" and not pc.ca_cert_file:
70
+ e.append(ValidationError("proxy_client.ca_cert_file is required for mtls"))
71
+ # Files existence (if provided)
72
+ for label, path in ("cert_file", pc.cert_file), ("key_file", pc.key_file), ("ca_cert_file", pc.ca_cert_file), ("crl_file", pc.crl_file):
73
+ if path and not os.path.exists(path):
74
+ e.append(ValidationError(f"proxy_client.{label} not found: {path}"))
75
+ # Heartbeat
76
+ if not pc.heartbeat.endpoint:
77
+ e.append(ValidationError("proxy_client.heartbeat.endpoint is required"))
78
+ if not isinstance(pc.heartbeat.interval, int) or pc.heartbeat.interval <= 0:
79
+ e.append(ValidationError("proxy_client.heartbeat.interval must be positive integer"))
80
+ # Registration endpoints
81
+ if not pc.registration.register_endpoint:
82
+ e.append(ValidationError("proxy_client.registration.register_endpoint is required"))
83
+ if not pc.registration.unregister_endpoint:
84
+ e.append(ValidationError("proxy_client.registration.unregister_endpoint is required"))
85
+ return e
86
+
87
+ def _validate_auth(self, model: SimpleConfigModel) -> List[ValidationError]:
88
+ e: List[ValidationError] = []
89
+ a = model.auth
90
+ if a.use_roles and not a.use_token:
91
+ e.append(ValidationError("auth.use_roles requires auth.use_token to be true"))
92
+ if a.use_token and not a.tokens:
93
+ e.append(ValidationError("auth.tokens must be provided when auth.use_token is true"))
94
+ return e
95
+
96
+