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,6 +14,8 @@ from abc import ABC, abstractmethod
14
14
  from typing import Dict, Any, Optional
15
15
  from pathlib import Path
16
16
 
17
+ from .logging import get_global_logger
18
+
17
19
  logger = logging.getLogger(__name__)
18
20
 
19
21
 
@@ -41,38 +43,10 @@ class ServerEngine(ABC):
41
43
  pass
42
44
 
43
45
  @abstractmethod
44
- def get_config_schema(self) -> Dict[str, Any]:
45
- """
46
- Get configuration schema for this server engine.
47
-
48
- Returns:
49
- Dictionary describing the configuration options
50
- """
51
- pass
52
46
 
53
47
  @abstractmethod
54
- def validate_config(self, config: Dict[str, Any]) -> bool:
55
- """
56
- Validate configuration for this server engine.
57
-
58
- Args:
59
- config: Configuration dictionary
60
-
61
- Returns:
62
- True if configuration is valid, False otherwise
63
- """
64
- pass
65
48
 
66
49
  @abstractmethod
67
- def run_server(self, app: Any, config: Dict[str, Any]) -> None:
68
- """
69
- Run the server with the given configuration.
70
-
71
- Args:
72
- app: ASGI application
73
- config: Server configuration
74
- """
75
- pass
76
50
 
77
51
 
78
52
  class HypercornEngine(ServerEngine):
@@ -96,120 +70,8 @@ class HypercornEngine(ServerEngine):
96
70
  "reload": True,
97
71
  }
98
72
 
99
- def get_config_schema(self) -> Dict[str, Any]:
100
- return {
101
- "host": {"type": "string", "default": "127.0.0.1"},
102
- "port": {"type": "integer", "default": 8000},
103
- "log_level": {"type": "string", "default": "INFO"},
104
- "certfile": {"type": "string", "optional": True},
105
- "keyfile": {"type": "string", "optional": True},
106
- "ca_certs": {"type": "string", "optional": True},
107
- "verify_mode": {"type": "string", "optional": True},
108
- "reload": {"type": "boolean", "default": False},
109
- "workers": {"type": "integer", "optional": True},
110
- }
111
-
112
- def validate_config(self, config: Dict[str, Any]) -> bool:
113
- """Validate hypercorn configuration."""
114
- required_fields = ["host", "port"]
115
-
116
- for field in required_fields:
117
- if field not in config:
118
- get_global_logger().error(f"Missing required field: {field}")
119
- return False
120
73
 
121
- # Validate SSL files exist if specified
122
- ssl_files = ["certfile", "keyfile", "ca_certs"]
123
- for ssl_file in ssl_files:
124
- if ssl_file in config and config[ssl_file]:
125
- if not Path(config[ssl_file]).exists():
126
- get_global_logger().error(f"SSL file not found: {config[ssl_file]}")
127
- return False
128
74
 
129
- return True
130
-
131
- def run_server(self, app: Any, config: Dict[str, Any]) -> None:
132
- """Run hypercorn server."""
133
- try:
134
- import hypercorn.asyncio
135
- import asyncio
136
-
137
- # Prepare hypercorn config
138
- hypercorn_config = {
139
- "bind": f"{config.get('host', '127.0.0.1')}:{config.get('port', 8000)}",
140
- "log_level": config.get("log_level", "INFO"),
141
- "reload": config.get("reload", False),
142
- }
143
-
144
- # Add SSL configuration if provided
145
- get_global_logger().info(f"🔍 DEBUG: Input config keys: {list(config.keys())}")
146
- get_global_logger().info(
147
- f"🔍 DEBUG: Input config certfile: {config.get('certfile', 'NOT_FOUND')}"
148
- )
149
- get_global_logger().info(
150
- f"🔍 DEBUG: Input config keyfile: {config.get('keyfile', 'NOT_FOUND')}"
151
- )
152
- get_global_logger().info(
153
- f"🔍 DEBUG: Input config ca_certs: {config.get('ca_certs', 'NOT_FOUND')}"
154
- )
155
- get_global_logger().info(
156
- f"🔍 DEBUG: Input config verify_mode: {config.get('verify_mode', 'NOT_FOUND')}"
157
- )
158
-
159
- if "certfile" in config and config["certfile"]:
160
- hypercorn_config["certfile"] = config["certfile"]
161
- if "keyfile" in config and config["keyfile"]:
162
- hypercorn_config["keyfile"] = config["keyfile"]
163
- if "ca_certs" in config and config["ca_certs"]:
164
- hypercorn_config["ca_certs"] = config["ca_certs"]
165
- if "verify_mode" in config and config["verify_mode"]:
166
- # Convert verify_mode string to SSL constant
167
- verify_mode_str = config["verify_mode"]
168
- if verify_mode_str == "CERT_NONE":
169
- import ssl
170
-
171
- hypercorn_config["verify_mode"] = ssl.CERT_NONE
172
- elif verify_mode_str == "CERT_REQUIRED":
173
- import ssl
174
-
175
- hypercorn_config["verify_mode"] = ssl.CERT_REQUIRED
176
- elif verify_mode_str == "CERT_OPTIONAL":
177
- import ssl
178
-
179
- hypercorn_config["verify_mode"] = ssl.CERT_OPTIONAL
180
- else:
181
- hypercorn_config["verify_mode"] = verify_mode_str
182
-
183
- # Add workers if specified
184
- if "workers" in config and config["workers"]:
185
- hypercorn_config["workers"] = config["workers"]
186
-
187
- get_global_logger().info(f"Starting hypercorn server with config: {hypercorn_config}")
188
- get_global_logger().info(f"SSL config from input: {config.get('ssl', 'NOT_FOUND')}")
189
- get_global_logger().info(
190
- f"Security SSL config: {config.get('security', {}).get('ssl', 'NOT_FOUND')}"
191
- )
192
- get_global_logger().info(
193
- f"🔍 DEBUG: Hypercorn verify_mode: {hypercorn_config.get('verify_mode', 'NOT_SET')}"
194
- )
195
- get_global_logger().info(
196
- f"🔍 DEBUG: Hypercorn ca_certs: {hypercorn_config.get('ca_certs', 'NOT_SET')}"
197
- )
198
-
199
- # Create config object
200
- config_obj = hypercorn.Config()
201
- for key, value in hypercorn_config.items():
202
- setattr(config_obj, key, value)
203
-
204
- # Run server
205
- asyncio.run(hypercorn.asyncio.serve(app, config_obj))
206
-
207
- except ImportError:
208
- get_global_logger().error("hypercorn not installed. Install with: pip install hypercorn")
209
- raise
210
- except Exception as e:
211
- get_global_logger().error(f"Failed to start hypercorn server: {e}")
212
- raise
213
75
 
214
76
 
215
77
  class ServerEngineFactory:
@@ -233,43 +95,10 @@ class ServerEngineFactory:
233
95
  get_global_logger().info(f"Registered server engine: {engine.get_name()}")
234
96
 
235
97
  @classmethod
236
- def get_engine(cls, name: str) -> Optional[ServerEngine]:
237
- """
238
- Get a server engine by name.
239
-
240
- Args:
241
- name: Name of the server engine
242
-
243
- Returns:
244
- Server engine instance or None if not found
245
- """
246
- return cls._engines.get(name)
247
98
 
248
99
  @classmethod
249
- def get_available_engines(cls) -> Dict[str, ServerEngine]:
250
- """
251
- Get all available server engines.
252
-
253
- Returns:
254
- Dictionary mapping engine names to engine instances
255
- """
256
- return cls._engines.copy()
257
100
 
258
101
  @classmethod
259
- def get_engine_with_feature(cls, feature: str) -> Optional[ServerEngine]:
260
- """
261
- Get the first available engine that supports a specific feature.
262
-
263
- Args:
264
- feature: Name of the feature to check
265
-
266
- Returns:
267
- Server engine that supports the feature or None
268
- """
269
- for engine in cls._engines.values():
270
- if engine.get_supported_features().get(feature, False):
271
- return engine
272
- return None
273
102
 
274
103
  @classmethod
275
104
  def initialize_default_engines(cls) -> None:
@@ -3,7 +3,6 @@ Settings management for the MCP Proxy Adapter framework.
3
3
  Provides utilities for reading and managing framework settings from configuration.
4
4
  """
5
5
 
6
- from typing import Any, Dict, Optional, Union
7
6
  from mcp_proxy_adapter.config import config
8
7
 
9
8
 
@@ -69,65 +68,10 @@ class Settings:
69
68
  cls._custom_settings.clear()
70
69
 
71
70
  @staticmethod
72
- def get_server_settings() -> Dict[str, Any]:
73
- """
74
- Get server configuration settings.
75
-
76
- Returns:
77
- Dictionary with server settings
78
- """
79
- return {
80
- "host": config.get("server.host", "0.0.0.0"),
81
- "port": config.get("server.port", 8000),
82
- "debug": config.get("server.debug", False),
83
- "log_level": config.get("server.log_level", "INFO"),
84
- }
85
71
 
86
72
  @staticmethod
87
- def get_logging_settings() -> Dict[str, Any]:
88
- """
89
- Get logging configuration settings.
90
-
91
- Returns:
92
- Dictionary with logging settings
93
- """
94
- return {
95
- "level": config.get("logging.level", "INFO"),
96
- "file": config.get("logging.file"),
97
- "log_dir": config.get("logging.log_dir", "./logs"),
98
- "log_file": config.get("logging.log_file", "mcp_proxy_adapter.log"),
99
- "error_log_file": config.get(
100
- "logging.error_log_file", "mcp_proxy_adapter_error.log"
101
- ),
102
- "access_log_file": config.get(
103
- "logging.access_log_file", "mcp_proxy_adapter_access.log"
104
- ),
105
- "max_file_size": config.get("logging.max_file_size", "10MB"),
106
- "backup_count": config.get("logging.backup_count", 5),
107
- "format": config.get(
108
- "logging.format", "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
109
- ),
110
- "date_format": config.get("logging.date_format", "%Y-%m-%d %H:%M:%S"),
111
- "console_output": config.get("logging.console_output", True),
112
- "file_output": config.get("logging.file_output", True),
113
- }
114
73
 
115
74
  @staticmethod
116
- def get_commands_settings() -> Dict[str, Any]:
117
- """
118
- Get commands configuration settings.
119
-
120
- Returns:
121
- Dictionary with commands settings
122
- """
123
- return {
124
- "auto_discovery": config.get("commands.auto_discovery", True),
125
- "discovery_path": config.get(
126
- "commands.discovery_path", "mcp_proxy_adapter.commands"
127
- ),
128
- "auto_commands_path": config.get("commands.auto_commands_path"),
129
- "custom_commands_path": config.get("commands.custom_commands_path"),
130
- }
131
75
 
132
76
  @staticmethod
133
77
  def get_custom_setting(key: str, default: Any = None) -> Any:
@@ -144,16 +88,6 @@ class Settings:
144
88
  return config.get(key, default)
145
89
 
146
90
  @staticmethod
147
- def get_all_settings() -> Dict[str, Any]:
148
- """
149
- Get all configuration settings including custom settings.
150
-
151
- Returns:
152
- Dictionary with all configuration settings
153
- """
154
- all_settings = config.get_all()
155
- all_settings["custom_settings"] = Settings._custom_settings
156
- return all_settings
157
91
 
158
92
  @staticmethod
159
93
  def set_custom_setting(key: str, value: Any) -> None:
@@ -194,11 +128,6 @@ class ServerSettings:
194
128
  """Get debug mode."""
195
129
  return config.get("server.debug", False)
196
130
 
197
- @staticmethod
198
- def get_log_level() -> str:
199
- """Get log level."""
200
- return config.get("server.log_level", "INFO")
201
-
202
131
 
203
132
  class LoggingSettings:
204
133
  """
@@ -216,51 +145,22 @@ class LoggingSettings:
216
145
  return config.get("logging.log_dir", "./logs")
217
146
 
218
147
  @staticmethod
219
- def get_log_file() -> Optional[str]:
220
- """Get main log file name."""
221
- return config.get("logging.log_file", "mcp_proxy_adapter.log")
222
148
 
223
149
  @staticmethod
224
- def get_error_log_file() -> Optional[str]:
225
- """Get error log file name."""
226
- return config.get("logging.error_log_file", "mcp_proxy_adapter_error.log")
227
150
 
228
151
  @staticmethod
229
- def get_access_log_file() -> Optional[str]:
230
- """Get access log file name."""
231
- return config.get("logging.access_log_file", "mcp_proxy_adapter_access.log")
232
152
 
233
153
  @staticmethod
234
- def get_max_file_size() -> str:
235
- """Get max file size."""
236
- return config.get("logging.max_file_size", "10MB")
237
154
 
238
155
  @staticmethod
239
- def get_backup_count() -> int:
240
- """Get backup count."""
241
- return config.get("logging.backup_count", 5)
242
156
 
243
157
  @staticmethod
244
- def get_format() -> str:
245
- """Get log format."""
246
- return config.get(
247
- "logging.format", "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
248
- )
249
158
 
250
159
  @staticmethod
251
- def get_date_format() -> str:
252
- """Get date format."""
253
- return config.get("logging.date_format", "%Y-%m-%d %H:%M:%S")
254
160
 
255
161
  @staticmethod
256
- def get_console_output() -> bool:
257
- """Get console output setting."""
258
- return config.get("logging.console_output", True)
259
162
 
260
163
  @staticmethod
261
- def get_file_output() -> bool:
262
- """Get file output setting."""
263
- return config.get("logging.file_output", True)
264
164
 
265
165
 
266
166
  class CommandsSettings:
@@ -279,35 +179,17 @@ class CommandsSettings:
279
179
  return config.get("commands.discovery_path", "mcp_proxy_adapter.commands")
280
180
 
281
181
  @staticmethod
282
- def get_custom_commands_path() -> Optional[str]:
283
- """Get custom commands path."""
284
- return config.get("commands.custom_commands_path")
285
182
 
286
183
 
287
184
  # Convenience functions for easy access
288
- def get_server_host() -> str:
289
- """Get server host."""
290
- return ServerSettings.get_host()
291
185
 
292
186
 
293
- def get_server_port() -> int:
294
- """Get server port."""
295
- return ServerSettings.get_port()
296
187
 
297
188
 
298
- def get_server_debug() -> bool:
299
- """Get server debug mode."""
300
- return ServerSettings.get_debug()
301
189
 
302
190
 
303
- def get_logging_level() -> str:
304
- """Get logging level."""
305
- return LoggingSettings.get_level()
306
191
 
307
192
 
308
- def get_logging_dir() -> str:
309
- """Get logging directory."""
310
- return LoggingSettings.get_log_dir()
311
193
 
312
194
 
313
195
  def get_auto_discovery() -> bool:
@@ -320,19 +202,10 @@ def get_discovery_path() -> str:
320
202
  return CommandsSettings.get_discovery_path()
321
203
 
322
204
 
323
- def get_setting(key: str, default: Any = None) -> Any:
324
- """Get any setting by key."""
325
- return Settings.get_custom_setting(key, default)
326
205
 
327
206
 
328
- def set_setting(key: str, value: Any) -> None:
329
- """Set any setting by key."""
330
- Settings.set_custom_setting(key, value)
331
207
 
332
208
 
333
- def reload_settings() -> None:
334
- """Reload all settings from configuration."""
335
- Settings.reload_config()
336
209
 
337
210
 
338
211
  def add_custom_settings(settings: Dict[str, Any]) -> None:
@@ -9,7 +9,6 @@ email: vasilyvz@gmail.com
9
9
  """
10
10
 
11
11
  import signal
12
- import asyncio
13
12
  import threading
14
13
  from typing import Optional, Callable, Any
15
14
  from mcp_proxy_adapter.core.logging import get_global_logger
@@ -56,42 +55,7 @@ class SignalHandler:
56
55
 
57
56
  get_global_logger().info("Signal handlers installed for SIGTERM, SIGINT, SIGHUP")
58
57
 
59
- def _handle_shutdown_signal(self, signum: int, frame: Any):
60
- """
61
- Handle shutdown signals.
62
-
63
- Args:
64
- signum: Signal number
65
- frame: Current stack frame
66
- """
67
- signal_name = signal.Signals(signum).name
68
- get_global_logger().info(f"🛑 Received {signal_name} signal, initiating graceful shutdown...")
69
-
70
- # Set shutdown event
71
- self._shutdown_event.set()
72
-
73
- # Call shutdown callback if set
74
- if self._shutdown_callback:
75
- try:
76
- get_global_logger().info("🔄 Executing shutdown callback...")
77
- self._shutdown_callback()
78
- get_global_logger().info("✅ Shutdown callback completed successfully")
79
- except Exception as e:
80
- get_global_logger().error(f"❌ Shutdown callback failed: {e}")
81
-
82
- # Force exit immediately to avoid server hang
83
- get_global_logger().info("🔄 Force exiting to avoid server hang...")
84
- import os
85
- # Use os._exit for immediate termination
86
- get_global_logger().warning("⚠️ Using os._exit for immediate termination...")
87
- os._exit(0)
88
58
 
89
- def _force_exit(self):
90
- """Force exit if graceful shutdown takes too long."""
91
- if self._shutdown_event.is_set():
92
- get_global_logger().warning("⚠️ Forcing exit after timeout")
93
- import os
94
- os._exit(1)
95
59
 
96
60
  def wait_for_shutdown(self, timeout: Optional[float] = None) -> bool:
97
61
  """
@@ -114,12 +78,6 @@ class SignalHandler:
114
78
  """
115
79
  return self._shutdown_event.is_set()
116
80
 
117
- def restore_handlers(self):
118
- """Restore original signal handlers."""
119
- for sig, handler in self._original_handlers.items():
120
- if handler is not None:
121
- signal.signal(sig, handler)
122
- get_global_logger().info("Original signal handlers restored")
123
81
 
124
82
 
125
83
  # Global signal handler instance
@@ -134,31 +92,8 @@ def get_signal_handler() -> SignalHandler:
134
92
  return _signal_handler
135
93
 
136
94
 
137
- def setup_signal_handling(shutdown_callback: Optional[Callable] = None):
138
- """
139
- Setup signal handling for graceful shutdown.
140
-
141
- Args:
142
- shutdown_callback: Optional callback to execute during shutdown
143
- """
144
- handler = get_signal_handler()
145
- if shutdown_callback:
146
- handler.set_shutdown_callback(shutdown_callback)
147
- get_global_logger().info("Signal handling setup completed")
148
95
 
149
96
 
150
- def wait_for_shutdown_signal(timeout: Optional[float] = None) -> bool:
151
- """
152
- Wait for shutdown signal.
153
-
154
- Args:
155
- timeout: Maximum time to wait in seconds
156
-
157
- Returns:
158
- True if shutdown signal received, False if timeout
159
- """
160
- handler = get_signal_handler()
161
- return handler.wait_for_shutdown(timeout)
162
97
 
163
98
 
164
99
  def is_shutdown_requested() -> bool:
@@ -45,91 +45,6 @@ class SSLUtils:
45
45
  }
46
46
 
47
47
  @staticmethod
48
- def create_ssl_context(
49
- cert_file: str,
50
- key_file: str,
51
- ca_cert: Optional[str] = None,
52
- verify_client: bool = False,
53
- cipher_suites: Optional[List[str]] = None,
54
- min_tls_version: str = "1.2",
55
- max_tls_version: str = "1.3",
56
- crl_config: Optional[Dict[str, Any]] = None,
57
- ) -> ssl.SSLContext:
58
- """
59
- Create SSL context with specified configuration.
60
-
61
- Args:
62
- cert_file: Path to certificate file
63
- key_file: Path to private key file
64
- ca_cert: Path to CA certificate file (optional)
65
- verify_client: Whether to verify client certificates
66
- cipher_suites: List of cipher suites to use
67
- min_tls_version: Minimum TLS version
68
- max_tls_version: Maximum TLS version
69
- crl_config: CRL configuration dictionary (optional)
70
-
71
- Returns:
72
- Configured SSL context
73
-
74
- Raises:
75
- ValueError: If certificate validation fails
76
- FileNotFoundError: If certificate or key files not found
77
- """
78
- # Validate certificate using AuthValidator
79
- validator = AuthValidator()
80
- result = validator.validate_certificate(cert_file)
81
- if not result.is_valid:
82
- raise ValueError(f"Invalid certificate: {result.error_message}")
83
-
84
- # Check CRL if configured
85
- if crl_config:
86
- try:
87
- crl_manager = CRLManager(crl_config)
88
- if crl_manager.is_certificate_revoked(cert_file):
89
- raise ValueError(
90
- f"Certificate is revoked according to CRL: {cert_file}"
91
- )
92
- except Exception as e:
93
- get_global_logger().error(f"CRL check failed: {e}")
94
- # For security, fail if CRL check fails
95
- raise ValueError(f"CRL validation failed: {e}")
96
-
97
- # Check if files exist
98
- if not Path(cert_file).exists():
99
- raise FileNotFoundError(f"Certificate file not found: {cert_file}")
100
- if not Path(key_file).exists():
101
- raise FileNotFoundError(f"Key file not found: {key_file}")
102
- if ca_cert and not Path(ca_cert).exists():
103
- raise FileNotFoundError(f"CA certificate file not found: {ca_cert}")
104
-
105
- # Create SSL context
106
- if verify_client:
107
- context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
108
- else:
109
- context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
110
-
111
- # Load certificate and key
112
- context.load_cert_chain(cert_file, key_file)
113
-
114
- # Load CA certificate if provided
115
- if ca_cert:
116
- context.load_verify_locations(ca_cert)
117
-
118
- # Configure client verification
119
- if verify_client:
120
- context.verify_mode = ssl.CERT_REQUIRED
121
- context.check_hostname = False
122
- else:
123
- context.verify_mode = ssl.CERT_NONE
124
-
125
- # Setup cipher suites
126
- SSLUtils.setup_cipher_suites(context, cipher_suites or [])
127
-
128
- # Setup TLS versions
129
- SSLUtils.setup_tls_versions(context, min_tls_version, max_tls_version)
130
-
131
- get_global_logger().info(f"SSL context created successfully with cert: {cert_file}")
132
- return context
133
48
 
134
49
  @staticmethod
135
50
  def validate_certificate(
@@ -225,55 +140,22 @@ class SSLUtils:
225
140
  get_global_logger().error(f"Failed to set TLS versions: {e}")
226
141
 
227
142
  @staticmethod
228
- def check_tls_version(min_version: str, max_version: str) -> bool:
229
- """
230
- Check if TLS version range is valid.
231
-
232
- Args:
233
- min_version: Minimum TLS version
234
- max_version: Maximum TLS version
235
-
236
- Returns:
237
- True if version range is valid, False otherwise
238
- """
239
- min_tls = SSLUtils.TLS_VERSIONS.get(min_version)
240
- max_tls = SSLUtils.TLS_VERSIONS.get(max_version)
241
-
242
- if not min_tls or not max_tls:
243
- return False
244
-
245
- # Check if min version is less than or equal to max version
246
- return min_tls <= max_tls
247
-
248
- @staticmethod
249
- def get_ssl_config_for_hypercorn(ssl_config: Dict[str, Any]) -> Dict[str, Any]:
250
- """
251
- Get SSL configuration for hypercorn from transport configuration.
252
-
253
- Args:
254
- ssl_config: SSL configuration from transport manager
255
-
256
- Returns:
257
- Configuration for hypercorn
258
- """
259
- hypercorn_ssl = {}
260
-
261
- if not ssl_config:
262
- return hypercorn_ssl
263
-
264
- # Basic SSL parameters
265
- if ssl_config.get("cert_file"):
266
- hypercorn_ssl["certfile"] = ssl_config["cert_file"]
267
-
268
- if ssl_config.get("key_file"):
269
- hypercorn_ssl["keyfile"] = ssl_config["key_file"]
270
-
271
- if ssl_config.get("ca_cert"):
272
- hypercorn_ssl["ca_certs"] = ssl_config["ca_cert"]
273
-
274
- # Client verification mode
275
- if ssl_config.get("verify_client", False):
276
- hypercorn_ssl["verify_mode"] = "CERT_REQUIRED"
277
-
278
- get_global_logger().info(f"Generated hypercorn SSL config: {hypercorn_ssl}")
279
- return hypercorn_ssl
143
+ def create_ssl_context(
144
+ cert_file: Optional[str] = None,
145
+ key_file: Optional[str] = None,
146
+ ca_file: Optional[str] = None,
147
+ verify_mode: int = ssl.CERT_REQUIRED,
148
+ check_hostname: bool = True,
149
+ ) -> ssl.SSLContext:
150
+ """Create SSL context with proper configuration."""
151
+ context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
152
+ context.check_hostname = check_hostname
153
+ context.verify_mode = verify_mode
154
+
155
+ if cert_file and key_file:
156
+ context.load_cert_chain(cert_file, key_file)
157
+
158
+ if ca_file:
159
+ context.load_verify_locations(ca_file)
160
+
161
+ return context