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
@@ -54,80 +54,7 @@ class TransportManager:
54
54
  self._config: Optional[TransportConfig] = None
55
55
  self._current_transport: Optional[TransportType] = None
56
56
 
57
- def load_config(self, config: Dict[str, Any]) -> bool:
58
- """
59
- Load transport configuration from config dict.
60
-
61
- Args:
62
- config: Configuration dictionary
63
57
 
64
- Returns:
65
- True if config loaded successfully, False otherwise
66
- """
67
- try:
68
- transport_config = config.get("transport", {})
69
-
70
- # Get transport type
71
- transport_type_str = transport_config.get("type", "http").lower()
72
- try:
73
- transport_type = TransportType(transport_type_str)
74
- except ValueError:
75
- get_global_logger().error(f"Invalid transport type: {transport_type_str}")
76
- return False
77
-
78
- # Get port (use default if not specified)
79
- port = transport_config.get("port")
80
- if port is None:
81
- port = self.DEFAULT_PORTS.get(transport_type, 8000)
82
-
83
- # Get SSL configuration
84
- ssl_config = transport_config.get("ssl", {})
85
- ssl_enabled = ssl_config.get("enabled", False)
86
-
87
- # Validate SSL requirements
88
- if (
89
- transport_type in [TransportType.HTTPS, TransportType.MTLS]
90
- and not ssl_enabled
91
- ):
92
- get_global_logger().error(
93
- f"SSL must be enabled for transport type: {transport_type.value}"
94
- )
95
- return False
96
-
97
- if transport_type == TransportType.HTTP and ssl_enabled:
98
- get_global_logger().warning("SSL enabled for HTTP transport - this may cause issues")
99
-
100
- # Create transport config
101
- self._config = TransportConfig(
102
- type=transport_type,
103
- port=port,
104
- ssl_enabled=ssl_enabled,
105
- cert_file=ssl_config.get("cert_file") if ssl_enabled else None,
106
- key_file=ssl_config.get("key_file") if ssl_enabled else None,
107
- ca_cert=ssl_config.get("ca_cert") if ssl_enabled else None,
108
- verify_client=ssl_config.get("verify_client", False),
109
- client_cert_required=ssl_config.get("client_cert_required", False),
110
- )
111
-
112
- self._current_transport = transport_type
113
-
114
- get_global_logger().info(
115
- f"Transport config loaded: {transport_type.value} on port {port}"
116
- )
117
- return True
118
-
119
- except Exception as e:
120
- get_global_logger().error(f"Failed to load transport config: {e}")
121
- return False
122
-
123
- def get_transport_type(self) -> Optional[TransportType]:
124
- """
125
- Get current transport type.
126
-
127
- Returns:
128
- Current transport type or None if not configured
129
- """
130
- return self._current_transport
131
58
 
132
59
  def get_port(self) -> Optional[int]:
133
60
  """
@@ -192,63 +119,7 @@ class TransportManager:
192
119
  """
193
120
  return self._current_transport == TransportType.HTTP
194
121
 
195
- def get_transport_info(self) -> Dict[str, Any]:
196
- """
197
- Get transport information.
198
-
199
- Returns:
200
- Dictionary with transport information
201
- """
202
- if not self._config:
203
- return {"error": "Transport not configured"}
204
-
205
- return {
206
- "type": self._config.type.value,
207
- "port": self._config.port,
208
- "ssl_enabled": self._config.ssl_enabled,
209
- "is_mtls": self.is_mtls(),
210
- "is_https": self.is_https(),
211
- "is_http": self.is_http(),
212
- "ssl_config": self.get_ssl_config(),
213
- }
214
-
215
- def validate_config(self) -> bool:
216
- """
217
- Validate current transport configuration.
218
-
219
- Returns:
220
- True if configuration is valid, False otherwise
221
- """
222
- if not self._config:
223
- get_global_logger().error("Transport not configured")
224
- return False
225
-
226
- # Validate SSL requirements
227
- if self._config.type in [TransportType.HTTPS, TransportType.MTLS]:
228
- if not self._config.ssl_enabled:
229
- get_global_logger().error(f"SSL must be enabled for {self._config.type.value}")
230
- return False
231
-
232
- if not self._config.cert_file or not self._config.key_file:
233
- get_global_logger().error(
234
- f"SSL certificate and key required for {self._config.type.value}"
235
- )
236
- return False
237
-
238
- # Validate SSL files exist
239
- if not self.validate_ssl_files():
240
- return False
241
-
242
- # Validate MTLS requirements
243
- if self._config.type == TransportType.MTLS:
244
- if not self._config.verify_client:
245
- get_global_logger().warning("MTLS transport should have client verification enabled")
246
122
 
247
- if not self._config.ca_cert:
248
- get_global_logger().warning("CA certificate recommended for MTLS transport")
249
-
250
- get_global_logger().info(f"Transport configuration validated: {self._config.type.value}")
251
- return True
252
123
 
253
124
  def validate_ssl_files(self) -> bool:
254
125
  """
@@ -276,28 +147,6 @@ class TransportManager:
276
147
  get_global_logger().info(f"All SSL files validated successfully: {files_to_check}")
277
148
  return True
278
149
 
279
- def get_hypercorn_config(self) -> Dict[str, Any]:
280
- """
281
- Get configuration for hypercorn.
282
-
283
- Returns:
284
- Hypercorn configuration dictionary
285
- """
286
- config = {
287
- "host": "0.0.0.0", # Can be moved to settings
288
- "port": self.get_port(),
289
- "log_level": "info",
290
- }
291
-
292
- if self.is_ssl_enabled():
293
- ssl_config = self.get_ssl_config()
294
- if ssl_config:
295
- from mcp_proxy_adapter.core.ssl_utils import SSLUtils
296
-
297
- hypercorn_ssl = SSLUtils.get_ssl_config_for_hypercorn(ssl_config)
298
- config.update(hypercorn_ssl)
299
-
300
- return config
301
150
 
302
151
 
303
152
  # Global transport manager instance
@@ -10,9 +10,9 @@ to SecurityConfig format used by mcp_security_framework.
10
10
 
11
11
  import json
12
12
  import logging
13
- from typing import Dict, Any, Optional, List, Union
14
13
  from pathlib import Path
15
14
  from dataclasses import dataclass
15
+ from typing import List, Dict, Any
16
16
 
17
17
  # Import mcp_security_framework components
18
18
  try:
@@ -70,53 +70,6 @@ class UnifiedConfigAdapter:
70
70
  self.validation_errors = []
71
71
  self.validation_warnings = []
72
72
 
73
- def convert_to_security_config(
74
- self, config: Dict[str, Any]
75
- ) -> Optional[SecurityConfig]:
76
- """
77
- Convert mcp_proxy_adapter configuration to SecurityConfig.
78
-
79
- Args:
80
- config: mcp_proxy_adapter configuration dictionary
81
-
82
- Returns:
83
- SecurityConfig instance or None if conversion failed
84
- """
85
- if not SECURITY_FRAMEWORK_AVAILABLE:
86
- get_global_logger().error(
87
- "mcp_security_framework not available, cannot convert configuration"
88
- )
89
- return None
90
-
91
- try:
92
- # Validate configuration first
93
- validation_result = self.validate_configuration(config)
94
- if not validation_result.is_valid:
95
- get_global_logger().error(
96
- f"Configuration validation failed: {validation_result.errors}"
97
- )
98
- return None
99
-
100
- # Convert configuration sections
101
- auth_config = self._convert_auth_config(config)
102
- ssl_config = self._convert_ssl_config(config)
103
- permission_config = self._convert_permission_config(config)
104
- rate_limit_config = self._convert_rate_limit_config(config)
105
-
106
- # Create SecurityConfig
107
- security_config = SecurityConfig(
108
- auth=auth_config,
109
- ssl=ssl_config,
110
- permissions=permission_config,
111
- rate_limit=rate_limit_config,
112
- )
113
-
114
- get_global_logger().info("Configuration successfully converted to SecurityConfig")
115
- return security_config
116
-
117
- except Exception as e:
118
- get_global_logger().error(f"Failed to convert configuration: {e}")
119
- return None
120
73
 
121
74
  def validate_configuration(self, config: Dict[str, Any]) -> ValidationResult:
122
75
  """
@@ -479,151 +432,6 @@ class UnifiedConfigAdapter:
479
432
 
480
433
  return rate_limit_config
481
434
 
482
- def get_public_paths(self, config: Dict[str, Any]) -> List[str]:
483
- """
484
- Get public paths from configuration.
485
435
 
486
- Args:
487
- config: Configuration dictionary
488
436
 
489
- Returns:
490
- List of public paths
491
- """
492
- security_config = config.get("security", {})
493
-
494
- # Ensure security_config is a dictionary
495
- if not isinstance(security_config, dict):
496
- return ["/health", "/docs", "/redoc", "/openapi.json", "/favicon.ico"]
497
-
498
- public_paths = security_config.get("public_paths", [])
499
-
500
- # Add default public paths if none specified
501
- if not public_paths:
502
- public_paths = [
503
- "/health",
504
- "/docs",
505
- "/redoc",
506
- "/openapi.json",
507
- "/favicon.ico",
508
- ]
509
437
 
510
- return public_paths
511
-
512
- def get_security_enabled(self, config: Dict[str, Any]) -> bool:
513
- """
514
- Check if security is enabled in configuration.
515
-
516
- Args:
517
- config: Configuration dictionary
518
-
519
- Returns:
520
- True if security is enabled
521
- """
522
- security_config = config.get("security", {})
523
-
524
- # Ensure security_config is a dictionary
525
- if not isinstance(security_config, dict):
526
- return True # Default to enabled for safety
527
-
528
- return security_config.get("enabled", True)
529
-
530
- def migrate_legacy_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
531
- """
532
- Migrate legacy configuration to new format.
533
-
534
- Args:
535
- config: Legacy configuration dictionary
536
-
537
- Returns:
538
- Migrated configuration dictionary
539
- """
540
- migrated_config = config.copy()
541
-
542
- # Create security section if it doesn't exist
543
- if "security" not in migrated_config:
544
- migrated_config["security"] = {}
545
-
546
- security_config = migrated_config["security"]
547
-
548
- # Migrate SSL configuration
549
- if "ssl" in migrated_config and "ssl" not in security_config:
550
- security_config["ssl"] = migrated_config["ssl"]
551
- # Don't remove legacy SSL yet for backward compatibility
552
-
553
- # Migrate roles configuration
554
- if "roles" in migrated_config and "permissions" not in security_config:
555
- security_config["permissions"] = migrated_config["roles"]
556
- # Don't remove legacy roles yet for backward compatibility
557
-
558
- # Migrate auth_enabled flag
559
- if "auth_enabled" in migrated_config and "auth" not in security_config:
560
- security_config["auth"] = {"enabled": migrated_config["auth_enabled"]}
561
-
562
- # Migrate rate_limit_enabled flag
563
- if (
564
- "rate_limit_enabled" in migrated_config
565
- and "rate_limit" not in security_config
566
- ):
567
- security_config["rate_limit"] = {
568
- "enabled": migrated_config["rate_limit_enabled"]
569
- }
570
-
571
- get_global_logger().info("Legacy configuration migrated to new format")
572
- return migrated_config
573
-
574
- def get_default_config(self) -> Dict[str, Any]:
575
- """
576
- Get default configuration.
577
-
578
- Returns:
579
- Default configuration dictionary
580
- """
581
- return {
582
- "security": {
583
- "enabled": True,
584
- "auth": {
585
- "enabled": True,
586
- "methods": ["api_key"],
587
- "api_keys": {},
588
- "jwt_secret": "",
589
- "jwt_algorithm": "HS256",
590
- "jwt_expiration": 3600,
591
- },
592
- "ssl": {
593
- "enabled": False,
594
- "cert_file": None,
595
- "key_file": None,
596
- "ca_cert": None,
597
- "min_tls_version": "TLSv1.2",
598
- "verify_client": False,
599
- "client_cert_required": False,
600
- "cipher_suites": [],
601
- },
602
- "permissions": {
603
- "enabled": True,
604
- "roles_file": "roles.json",
605
- "default_role": "user",
606
- "deny_by_default": True,
607
- "role_mappings": {},
608
- },
609
- "rate_limit": {
610
- "enabled": True,
611
- "requests_per_minute": 60,
612
- "requests_per_hour": 1000,
613
- "requests_per_day": 10000,
614
- "burst_limit": 10,
615
- "by_ip": True,
616
- "by_user": True,
617
- "by_endpoint": False,
618
- "exempt_roles": [],
619
- "exempt_endpoints": [],
620
- },
621
- "public_paths": [
622
- "/health",
623
- "/docs",
624
- "/redoc",
625
- "/openapi.json",
626
- "/favicon.ico",
627
- ],
628
- }
629
- }
@@ -10,135 +10,25 @@ import sys
10
10
  import time
11
11
  import uuid
12
12
  from datetime import datetime, timezone
13
- from typing import Any, Dict, List, Optional, Tuple, Union
13
+ from typing import Optional, Dict, Any, List
14
14
 
15
15
  from mcp_proxy_adapter.core.logging import get_global_logger
16
16
 
17
17
 
18
- def generate_id() -> str:
19
- """
20
- Generates a unique identifier.
21
18
 
22
- Returns:
23
- String with unique identifier.
24
- """
25
- return str(uuid.uuid4())
26
19
 
27
20
 
28
- def get_timestamp() -> int:
29
- """
30
- Returns current timestamp in milliseconds.
31
21
 
32
- Returns:
33
- Integer - timestamp in milliseconds.
34
- """
35
- return int(time.time() * 1000)
36
22
 
37
23
 
38
- def format_datetime(
39
- dt: Optional[datetime] = None, format_str: str = "%Y-%m-%dT%H:%M:%S.%fZ"
40
- ) -> str:
41
- """
42
- Formats date and time as string.
43
24
 
44
- Args:
45
- dt: Datetime object to format. If None, current time is used.
46
- format_str: Format string for output.
47
25
 
48
- Returns:
49
- Formatted date/time string.
50
- """
51
- dt = dt or datetime.now(timezone.utc)
52
- return dt.strftime(format_str)
53
26
 
54
27
 
55
- def parse_datetime(dt_str: str, format_str: str = "%Y-%m-%dT%H:%M:%S.%fZ") -> datetime:
56
- """
57
- Parses date/time string into datetime object.
58
28
 
59
- Args:
60
- dt_str: Date/time string.
61
- format_str: Date/time string format.
62
29
 
63
- Returns:
64
- Datetime object.
65
- """
66
- return datetime.strptime(dt_str, format_str)
67
30
 
68
31
 
69
- def safe_json_loads(s: str, default: Any = None) -> Any:
70
- """
71
- Safe JSON string loading.
72
-
73
- Args:
74
- s: JSON string to load.
75
- default: Default value on parsing error.
76
-
77
- Returns:
78
- Loaded object or default value on error.
79
- """
80
- try:
81
- return json.loads(s)
82
- except Exception as e:
83
- get_global_logger().error(f"Error parsing JSON: {e}")
84
- return default
85
-
86
-
87
- def safe_json_dumps(obj: Any, default: str = "{}", indent: Optional[int] = None) -> str:
88
- """
89
- Safe object conversion to JSON string.
90
-
91
- Args:
92
- obj: Object to convert.
93
- default: Default string on serialization error.
94
- indent: Indentation for JSON formatting.
95
-
96
- Returns:
97
- JSON string or default string on error.
98
- """
99
- try:
100
- return json.dumps(obj, ensure_ascii=False, indent=indent)
101
- except Exception as e:
102
- get_global_logger().error(f"Error serializing to JSON: {e}")
103
- return default
104
-
105
-
106
- def calculate_hash(data: Union[str, bytes], algorithm: str = "sha256") -> str:
107
- """
108
- Calculates hash for data.
109
-
110
- Args:
111
- data: Data to hash (string or bytes).
112
- algorithm: Hashing algorithm (md5, sha1, sha256, etc.).
113
-
114
- Returns:
115
- String with hash in hexadecimal format.
116
- """
117
- if isinstance(data, str):
118
- data = data.encode("utf-8")
119
-
120
- hash_obj = hashlib.new(algorithm)
121
- hash_obj.update(data)
122
- return hash_obj.hexdigest()
123
-
124
-
125
- def ensure_directory(path: str) -> bool:
126
- """
127
- Checks directory existence and creates it if necessary.
128
-
129
- Args:
130
- path: Path to directory.
131
-
132
- Returns:
133
- True if directory exists or was successfully created, otherwise False.
134
- """
135
- try:
136
- if not os.path.exists(path):
137
- os.makedirs(path, exist_ok=True)
138
- return True
139
- except Exception as e:
140
- get_global_logger().error(f"Error creating directory {path}: {e}")
141
- return False
142
32
 
143
33
 
144
34
  def check_port_availability(host: str, port: int, timeout: float = 1.0) -> bool:
@@ -207,76 +97,5 @@ def get_port_usage_info(port: int) -> str:
207
97
  return f"Port {port} is in use (unable to get process info: {e})"
208
98
 
209
99
 
210
- def handle_port_conflict(host: str, port: int) -> None:
211
- """
212
- Handles port conflict with user-friendly error message and suggestions.
213
- This is for MAIN server port conflicts - application must exit.
214
-
215
- Args:
216
- host: Host address
217
- port: Port number that's in conflict
218
- """
219
- print(f"❌ ERROR: Port {port} is already in use on {host}")
220
- print(f"💡 Suggestions:")
221
- print(f" 1. Choose a different port: --port <different_port>")
222
- print(f" 2. Stop the conflicting service")
223
- print(f" 3. Check what's using the port:")
224
- print(f" lsof -i :{port}")
225
- print(f" netstat -tulpn | grep :{port}")
226
-
227
- # Try to get more detailed info about port usage
228
- usage_info = get_port_usage_info(port)
229
- print(f"🔍 Port usage details:")
230
- print(f" {usage_info}")
231
-
232
- # Try to find an alternative port
233
- alt_port = find_available_port(host, port + 1, 10)
234
- if alt_port:
235
- print(f"💡 Alternative port suggestion: {alt_port}")
236
- print(f" Try: --port {alt_port}")
237
- else:
238
- print(f"💡 Try ports in range {port + 1}-{port + 20}")
239
-
240
- print(f"🛑 Application cannot start due to port conflict")
241
- sys.exit(1)
242
100
 
243
101
 
244
- def find_port_for_internal_server(host: str, preferred_port: int) -> int:
245
- """
246
- Finds an available port for internal server (mTLS, etc.).
247
- If preferred port is occupied, finds any available port.
248
-
249
- Args:
250
- host: Host address
251
- preferred_port: Preferred port number
252
-
253
- Returns:
254
- Available port number
255
- """
256
- # First try the preferred port
257
- if check_port_availability(host, preferred_port):
258
- print(f"✅ Internal server port {preferred_port} is available")
259
- return preferred_port
260
-
261
- # If preferred port is occupied, find any available port
262
- print(f"⚠️ Internal server preferred port {preferred_port} is occupied, searching for alternative...")
263
-
264
- alt_port = find_available_port(host, preferred_port + 1, 50)
265
- if alt_port:
266
- print(f"✅ Found alternative port for internal server: {alt_port}")
267
- return alt_port
268
-
269
- # If no port found in range, try from 9000
270
- alt_port = find_available_port(host, 9000, 100)
271
- if alt_port:
272
- print(f"✅ Found alternative port for internal server: {alt_port}")
273
- return alt_port
274
-
275
- # Last resort - try from 10000
276
- alt_port = find_available_port(host, 10000, 100)
277
- if alt_port:
278
- print(f"✅ Found alternative port for internal server: {alt_port}")
279
- return alt_port
280
-
281
- # If still no port found, raise error
282
- raise RuntimeError(f"Unable to find available port for internal server on {host}")
@@ -0,0 +1,21 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Configuration validation package for MCP Proxy Adapter.
6
+ """
7
+
8
+ from .config_validator import ConfigValidator
9
+ from .validation_result import ValidationResult, ValidationLevel
10
+ from .file_validator import FileValidator
11
+ from .security_validator import SecurityValidator
12
+ from .protocol_validator import ProtocolValidator
13
+
14
+ __all__ = [
15
+ "ConfigValidator",
16
+ "ValidationResult",
17
+ "ValidationLevel",
18
+ "FileValidator",
19
+ "SecurityValidator",
20
+ "ProtocolValidator",
21
+ ]