mcp-proxy-adapter 6.0.0__py3-none-any.whl → 6.0.1__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.
Files changed (212) hide show
  1. mcp_proxy_adapter/__main__.py +27 -7
  2. mcp_proxy_adapter/api/app.py +209 -79
  3. mcp_proxy_adapter/api/handlers.py +16 -5
  4. mcp_proxy_adapter/api/middleware/__init__.py +14 -9
  5. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  6. mcp_proxy_adapter/api/middleware/factory.py +36 -12
  7. mcp_proxy_adapter/api/middleware/protocol_middleware.py +84 -18
  8. mcp_proxy_adapter/api/middleware/unified_security.py +197 -0
  9. mcp_proxy_adapter/api/middleware/user_info_middleware.py +158 -0
  10. mcp_proxy_adapter/commands/__init__.py +7 -1
  11. mcp_proxy_adapter/commands/base.py +7 -4
  12. mcp_proxy_adapter/commands/builtin_commands.py +8 -2
  13. mcp_proxy_adapter/commands/command_registry.py +8 -0
  14. mcp_proxy_adapter/commands/echo_command.py +81 -0
  15. mcp_proxy_adapter/commands/health_command.py +1 -1
  16. mcp_proxy_adapter/commands/help_command.py +21 -14
  17. mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
  18. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  19. mcp_proxy_adapter/commands/security_command.py +488 -0
  20. mcp_proxy_adapter/commands/ssl_setup_command.py +234 -351
  21. mcp_proxy_adapter/commands/token_management_command.py +1 -1
  22. mcp_proxy_adapter/config.py +323 -40
  23. mcp_proxy_adapter/core/app_factory.py +410 -0
  24. mcp_proxy_adapter/core/app_runner.py +272 -0
  25. mcp_proxy_adapter/core/certificate_utils.py +291 -73
  26. mcp_proxy_adapter/core/client.py +574 -0
  27. mcp_proxy_adapter/core/client_manager.py +284 -0
  28. mcp_proxy_adapter/core/client_security.py +384 -0
  29. mcp_proxy_adapter/core/logging.py +8 -3
  30. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  31. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  32. mcp_proxy_adapter/core/protocol_manager.py +169 -10
  33. mcp_proxy_adapter/core/proxy_client.py +602 -0
  34. mcp_proxy_adapter/core/proxy_registration.py +299 -47
  35. mcp_proxy_adapter/core/security_adapter.py +12 -15
  36. mcp_proxy_adapter/core/security_integration.py +286 -0
  37. mcp_proxy_adapter/core/server_adapter.py +282 -0
  38. mcp_proxy_adapter/core/server_engine.py +270 -0
  39. mcp_proxy_adapter/core/ssl_utils.py +13 -12
  40. mcp_proxy_adapter/core/transport_manager.py +5 -5
  41. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  42. mcp_proxy_adapter/examples/__init__.py +13 -4
  43. mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
  44. mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
  45. mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
  46. mcp_proxy_adapter/examples/basic_framework/main.py +44 -0
  47. mcp_proxy_adapter/examples/commands/__init__.py +5 -0
  48. mcp_proxy_adapter/examples/create_certificates_simple.py +550 -0
  49. mcp_proxy_adapter/examples/debug_request_state.py +112 -0
  50. mcp_proxy_adapter/examples/debug_role_chain.py +158 -0
  51. mcp_proxy_adapter/examples/demo_client.py +275 -0
  52. mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
  53. mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
  54. mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
  55. mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
  56. mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
  57. mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
  58. mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
  59. mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  60. mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
  61. mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
  62. mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  63. mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
  64. mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
  65. mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
  66. mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
  67. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +80 -0
  68. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  69. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
  70. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +75 -0
  71. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  72. mcp_proxy_adapter/examples/full_application/main.py +173 -0
  73. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
  74. mcp_proxy_adapter/examples/generate_all_certificates.py +362 -0
  75. mcp_proxy_adapter/examples/generate_certificates.py +177 -0
  76. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
  77. mcp_proxy_adapter/examples/generate_test_configs.py +331 -0
  78. mcp_proxy_adapter/examples/proxy_registration_example.py +334 -0
  79. mcp_proxy_adapter/examples/run_example.py +59 -0
  80. mcp_proxy_adapter/examples/run_full_test_suite.py +318 -0
  81. mcp_proxy_adapter/examples/run_proxy_server.py +146 -0
  82. mcp_proxy_adapter/examples/run_security_tests.py +544 -0
  83. mcp_proxy_adapter/examples/run_security_tests_fixed.py +247 -0
  84. mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
  85. mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
  86. mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
  87. mcp_proxy_adapter/examples/security_test_client.py +782 -0
  88. mcp_proxy_adapter/examples/setup_test_environment.py +328 -0
  89. mcp_proxy_adapter/examples/test_config.py +148 -0
  90. mcp_proxy_adapter/examples/test_config_generator.py +86 -0
  91. mcp_proxy_adapter/examples/test_examples.py +281 -0
  92. mcp_proxy_adapter/examples/universal_client.py +620 -0
  93. mcp_proxy_adapter/main.py +66 -148
  94. mcp_proxy_adapter/utils/config_generator.py +1008 -0
  95. mcp_proxy_adapter/version.py +5 -2
  96. mcp_proxy_adapter-6.0.1.dist-info/METADATA +679 -0
  97. mcp_proxy_adapter-6.0.1.dist-info/RECORD +140 -0
  98. mcp_proxy_adapter-6.0.1.dist-info/entry_points.txt +2 -0
  99. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/licenses/LICENSE +2 -2
  100. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  101. mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
  102. mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
  103. mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
  104. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  105. mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
  106. mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
  107. mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
  108. mcp_proxy_adapter/api/middleware/security.py +0 -376
  109. mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
  110. mcp_proxy_adapter/examples/README.md +0 -124
  111. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  112. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  113. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  114. mcp_proxy_adapter/examples/basic_server/config.json +0 -70
  115. mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
  116. mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
  117. mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
  118. mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
  119. mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
  120. mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
  121. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  122. mcp_proxy_adapter/examples/basic_server/server.py +0 -114
  123. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  124. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  125. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
  126. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  127. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  128. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  129. mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
  130. mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
  131. mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
  132. mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
  133. mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
  134. mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
  135. mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
  136. mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
  137. mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
  138. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  139. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  140. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  141. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  142. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  143. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  144. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  145. mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
  146. mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
  147. mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
  148. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  149. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  150. mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
  151. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  152. mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
  153. mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
  154. mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
  155. mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
  156. mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
  157. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  158. mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
  159. mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
  160. mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
  161. mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
  162. mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
  163. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  164. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  165. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  166. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  167. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  168. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  169. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  170. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  171. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  172. mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
  173. mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
  174. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  175. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  176. mcp_proxy_adapter/schemas/roles_schema.json +0 -162
  177. mcp_proxy_adapter/tests/__init__.py +0 -0
  178. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  179. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  180. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  181. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  182. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  183. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  184. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  185. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  186. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  187. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  188. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  189. mcp_proxy_adapter/tests/conftest.py +0 -131
  190. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  191. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  192. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  193. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  194. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  195. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  196. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  197. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  198. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  199. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  200. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  201. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  202. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  203. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  204. mcp_proxy_adapter/tests/test_config.py +0 -127
  205. mcp_proxy_adapter/tests/test_utils.py +0 -65
  206. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  207. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  208. mcp_proxy_adapter/tests/unit/test_config.py +0 -270
  209. mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
  210. mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
  211. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/WHEEL +0 -0
  212. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,270 @@
1
+ """
2
+ Server Engine Abstraction
3
+
4
+ This module provides an abstraction layer for the hypercorn ASGI server engine,
5
+ providing full mTLS support and SSL capabilities.
6
+
7
+ Author: Vasiliy Zdanovskiy
8
+ email: vasilyvz@gmail.com
9
+ """
10
+
11
+ import logging
12
+ from abc import ABC, abstractmethod
13
+ from typing import Dict, Any, Optional
14
+ from pathlib import Path
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class ServerEngine(ABC):
20
+ """
21
+ Abstract base class for server engines.
22
+
23
+ This class defines the interface that all server engines must implement,
24
+ allowing the framework to work with different ASGI servers transparently.
25
+ """
26
+
27
+ @abstractmethod
28
+ def get_name(self) -> str:
29
+ """Get the name of the server engine."""
30
+ pass
31
+
32
+ @abstractmethod
33
+ def get_supported_features(self) -> Dict[str, bool]:
34
+ """
35
+ Get supported features of this server engine.
36
+
37
+ Returns:
38
+ Dictionary mapping feature names to boolean support status
39
+ """
40
+ pass
41
+
42
+ @abstractmethod
43
+ def get_config_schema(self) -> Dict[str, Any]:
44
+ """
45
+ Get configuration schema for this server engine.
46
+
47
+ Returns:
48
+ Dictionary describing the configuration options
49
+ """
50
+ pass
51
+
52
+ @abstractmethod
53
+ def validate_config(self, config: Dict[str, Any]) -> bool:
54
+ """
55
+ Validate configuration for this server engine.
56
+
57
+ Args:
58
+ config: Configuration dictionary
59
+
60
+ Returns:
61
+ True if configuration is valid, False otherwise
62
+ """
63
+ pass
64
+
65
+ @abstractmethod
66
+ def run_server(self, app: Any, config: Dict[str, Any]) -> None:
67
+ """
68
+ Run the server with the given configuration.
69
+
70
+ Args:
71
+ app: ASGI application
72
+ config: Server configuration
73
+ """
74
+ pass
75
+
76
+
77
+ class HypercornEngine(ServerEngine):
78
+ """
79
+ Hypercorn server engine implementation.
80
+
81
+ Provides full mTLS support and better SSL capabilities.
82
+ """
83
+
84
+ def get_name(self) -> str:
85
+ return "hypercorn"
86
+
87
+ def get_supported_features(self) -> Dict[str, bool]:
88
+ return {
89
+ "ssl_tls": True,
90
+ "mtls_client_certs": True, # Full support
91
+ "ssl_scope_info": True, # SSL info in request scope
92
+ "client_cert_verification": True,
93
+ "websockets": True,
94
+ "http2": True,
95
+ "reload": True
96
+ }
97
+
98
+ def get_config_schema(self) -> Dict[str, Any]:
99
+ return {
100
+ "host": {"type": "string", "default": "127.0.0.1"},
101
+ "port": {"type": "integer", "default": 8000},
102
+ "log_level": {"type": "string", "default": "INFO"},
103
+ "certfile": {"type": "string", "optional": True},
104
+ "keyfile": {"type": "string", "optional": True},
105
+ "ca_certs": {"type": "string", "optional": True},
106
+ "verify_mode": {"type": "string", "optional": True},
107
+ "reload": {"type": "boolean", "default": False},
108
+ "workers": {"type": "integer", "optional": True}
109
+ }
110
+
111
+ def validate_config(self, config: Dict[str, Any]) -> bool:
112
+ """Validate hypercorn configuration."""
113
+ required_fields = ["host", "port"]
114
+
115
+ for field in required_fields:
116
+ if field not in config:
117
+ logger.error(f"Missing required field: {field}")
118
+ return False
119
+
120
+ # Validate SSL files exist if specified
121
+ ssl_files = ["certfile", "keyfile", "ca_certs"]
122
+ for ssl_file in ssl_files:
123
+ if ssl_file in config and config[ssl_file]:
124
+ if not Path(config[ssl_file]).exists():
125
+ logger.error(f"SSL file not found: {config[ssl_file]}")
126
+ return False
127
+
128
+ return True
129
+
130
+ def run_server(self, app: Any, config: Dict[str, Any]) -> None:
131
+ """Run hypercorn server."""
132
+ try:
133
+ import hypercorn.asyncio
134
+ import asyncio
135
+
136
+ # Prepare hypercorn config
137
+ hypercorn_config = {
138
+ "bind": f"{config.get('host', '127.0.0.1')}:{config.get('port', 8000)}",
139
+ "log_level": config.get("log_level", "INFO"),
140
+ "reload": config.get("reload", False)
141
+ }
142
+
143
+ # Add SSL configuration if provided
144
+ logger.info(f"🔍 DEBUG: Input config keys: {list(config.keys())}")
145
+ logger.info(f"🔍 DEBUG: Input config certfile: {config.get('certfile', 'NOT_FOUND')}")
146
+ logger.info(f"🔍 DEBUG: Input config keyfile: {config.get('keyfile', 'NOT_FOUND')}")
147
+ logger.info(f"🔍 DEBUG: Input config ca_certs: {config.get('ca_certs', 'NOT_FOUND')}")
148
+ logger.info(f"🔍 DEBUG: Input config verify_mode: {config.get('verify_mode', 'NOT_FOUND')}")
149
+
150
+ if "certfile" in config and config["certfile"]:
151
+ hypercorn_config["certfile"] = config["certfile"]
152
+ if "keyfile" in config and config["keyfile"]:
153
+ hypercorn_config["keyfile"] = config["keyfile"]
154
+ if "ca_certs" in config and config["ca_certs"]:
155
+ hypercorn_config["ca_certs"] = config["ca_certs"]
156
+ if "verify_mode" in config and config["verify_mode"]:
157
+ # Convert verify_mode string to SSL constant
158
+ verify_mode_str = config["verify_mode"]
159
+ if verify_mode_str == "CERT_NONE":
160
+ import ssl
161
+ hypercorn_config["verify_mode"] = ssl.CERT_NONE
162
+ elif verify_mode_str == "CERT_REQUIRED":
163
+ import ssl
164
+ hypercorn_config["verify_mode"] = ssl.CERT_REQUIRED
165
+ elif verify_mode_str == "CERT_OPTIONAL":
166
+ import ssl
167
+ hypercorn_config["verify_mode"] = ssl.CERT_OPTIONAL
168
+ else:
169
+ hypercorn_config["verify_mode"] = verify_mode_str
170
+
171
+ # Add workers if specified
172
+ if "workers" in config and config["workers"]:
173
+ hypercorn_config["workers"] = config["workers"]
174
+
175
+ logger.info(f"Starting hypercorn server with config: {hypercorn_config}")
176
+ logger.info(f"SSL config from input: {config.get('ssl', 'NOT_FOUND')}")
177
+ logger.info(f"Security SSL config: {config.get('security', {}).get('ssl', 'NOT_FOUND')}")
178
+ logger.info(f"🔍 DEBUG: Hypercorn verify_mode: {hypercorn_config.get('verify_mode', 'NOT_SET')}")
179
+ logger.info(f"🔍 DEBUG: Hypercorn ca_certs: {hypercorn_config.get('ca_certs', 'NOT_SET')}")
180
+
181
+ # Create config object
182
+ config_obj = hypercorn.Config()
183
+ for key, value in hypercorn_config.items():
184
+ setattr(config_obj, key, value)
185
+
186
+ # Run server
187
+ asyncio.run(hypercorn.asyncio.serve(app, config_obj))
188
+
189
+ except ImportError:
190
+ logger.error("hypercorn not installed. Install with: pip install hypercorn")
191
+ raise
192
+ except Exception as e:
193
+ logger.error(f"Failed to start hypercorn server: {e}")
194
+ raise
195
+
196
+
197
+ class ServerEngineFactory:
198
+ """
199
+ Factory for creating server engines.
200
+
201
+ This class manages the creation and configuration of different server engines.
202
+ """
203
+
204
+ _engines: Dict[str, ServerEngine] = {}
205
+
206
+ @classmethod
207
+ def register_engine(cls, engine: ServerEngine) -> None:
208
+ """
209
+ Register a server engine.
210
+
211
+ Args:
212
+ engine: Server engine instance to register
213
+ """
214
+ cls._engines[engine.get_name()] = engine
215
+ logger.info(f"Registered server engine: {engine.get_name()}")
216
+
217
+ @classmethod
218
+ def get_engine(cls, name: str) -> Optional[ServerEngine]:
219
+ """
220
+ Get a server engine by name.
221
+
222
+ Args:
223
+ name: Name of the server engine
224
+
225
+ Returns:
226
+ Server engine instance or None if not found
227
+ """
228
+ return cls._engines.get(name)
229
+
230
+ @classmethod
231
+ def get_available_engines(cls) -> Dict[str, ServerEngine]:
232
+ """
233
+ Get all available server engines.
234
+
235
+ Returns:
236
+ Dictionary mapping engine names to engine instances
237
+ """
238
+ return cls._engines.copy()
239
+
240
+ @classmethod
241
+ def get_engine_with_feature(cls, feature: str) -> Optional[ServerEngine]:
242
+ """
243
+ Get the first available engine that supports a specific feature.
244
+
245
+ Args:
246
+ feature: Name of the feature to check
247
+
248
+ Returns:
249
+ Server engine that supports the feature or None
250
+ """
251
+ for engine in cls._engines.values():
252
+ if engine.get_supported_features().get(feature, False):
253
+ return engine
254
+ return None
255
+
256
+ @classmethod
257
+ def initialize_default_engines(cls) -> None:
258
+ """Initialize default server engines."""
259
+ # Register hypercorn engine (only supported engine)
260
+ try:
261
+ import hypercorn
262
+ cls.register_engine(HypercornEngine())
263
+ logger.info("Hypercorn engine registered (full mTLS support available)")
264
+ except ImportError:
265
+ logger.error("Hypercorn not available - this is required for the framework")
266
+ raise
267
+
268
+
269
+ # Initialize default engines
270
+ ServerEngineFactory.initialize_default_engines()
@@ -201,33 +201,34 @@ class SSLUtils:
201
201
  return min_tls <= max_tls
202
202
 
203
203
  @staticmethod
204
- def get_ssl_config_for_uvicorn(ssl_config: Dict[str, Any]) -> Dict[str, Any]:
204
+ def get_ssl_config_for_hypercorn(ssl_config: Dict[str, Any]) -> Dict[str, Any]:
205
205
  """
206
- Get SSL configuration for uvicorn from transport configuration.
206
+ Get SSL configuration for hypercorn from transport configuration.
207
207
 
208
208
  Args:
209
209
  ssl_config: SSL configuration from transport manager
210
210
 
211
211
  Returns:
212
- Configuration for uvicorn
212
+ Configuration for hypercorn
213
213
  """
214
- uvicorn_ssl = {}
214
+ hypercorn_ssl = {}
215
215
 
216
216
  if not ssl_config:
217
- return uvicorn_ssl
217
+ return hypercorn_ssl
218
218
 
219
219
  # Basic SSL parameters
220
220
  if ssl_config.get("cert_file"):
221
- uvicorn_ssl["ssl_certfile"] = ssl_config["cert_file"]
221
+ hypercorn_ssl["certfile"] = ssl_config["cert_file"]
222
222
 
223
223
  if ssl_config.get("key_file"):
224
- uvicorn_ssl["ssl_keyfile"] = ssl_config["key_file"]
224
+ hypercorn_ssl["keyfile"] = ssl_config["key_file"]
225
225
 
226
226
  if ssl_config.get("ca_cert"):
227
- uvicorn_ssl["ssl_ca_certs"] = ssl_config["ca_cert"]
227
+ hypercorn_ssl["ca_certs"] = ssl_config["ca_cert"]
228
228
 
229
- # Note: uvicorn doesn't support ssl_verify_mode parameter
230
- # Client verification is handled at the application level
229
+ # Client verification mode
230
+ if ssl_config.get("verify_client", False):
231
+ hypercorn_ssl["verify_mode"] = "CERT_REQUIRED"
231
232
 
232
- logger.info(f"Generated uvicorn SSL config: {uvicorn_ssl}")
233
- return uvicorn_ssl
233
+ logger.info(f"Generated hypercorn SSL config: {hypercorn_ssl}")
234
+ return hypercorn_ssl
@@ -265,12 +265,12 @@ class TransportManager:
265
265
  logger.info(f"All SSL files validated successfully: {files_to_check}")
266
266
  return True
267
267
 
268
- def get_uvicorn_config(self) -> Dict[str, Any]:
268
+ def get_hypercorn_config(self) -> Dict[str, Any]:
269
269
  """
270
- Get configuration for uvicorn.
270
+ Get configuration for hypercorn.
271
271
 
272
272
  Returns:
273
- Uvicorn configuration dictionary
273
+ Hypercorn configuration dictionary
274
274
  """
275
275
  config = {
276
276
  "host": "0.0.0.0", # Can be moved to settings
@@ -282,8 +282,8 @@ class TransportManager:
282
282
  ssl_config = self.get_ssl_config()
283
283
  if ssl_config:
284
284
  from mcp_proxy_adapter.core.ssl_utils import SSLUtils
285
- uvicorn_ssl = SSLUtils.get_ssl_config_for_uvicorn(ssl_config)
286
- config.update(uvicorn_ssl)
285
+ hypercorn_ssl = SSLUtils.get_ssl_config_for_hypercorn(ssl_config)
286
+ config.update(hypercorn_ssl)
287
287
 
288
288
  return config
289
289