mcp-proxy-adapter 6.3.4__py3-none-any.whl → 6.3.6__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 (131) hide show
  1. mcp_proxy_adapter/__init__.py +9 -5
  2. mcp_proxy_adapter/__main__.py +1 -1
  3. mcp_proxy_adapter/api/app.py +227 -176
  4. mcp_proxy_adapter/api/handlers.py +68 -60
  5. mcp_proxy_adapter/api/middleware/__init__.py +7 -5
  6. mcp_proxy_adapter/api/middleware/base.py +19 -16
  7. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +44 -34
  8. mcp_proxy_adapter/api/middleware/error_handling.py +57 -67
  9. mcp_proxy_adapter/api/middleware/factory.py +50 -52
  10. mcp_proxy_adapter/api/middleware/logging.py +46 -30
  11. mcp_proxy_adapter/api/middleware/performance.py +19 -16
  12. mcp_proxy_adapter/api/middleware/protocol_middleware.py +80 -50
  13. mcp_proxy_adapter/api/middleware/transport_middleware.py +26 -24
  14. mcp_proxy_adapter/api/middleware/unified_security.py +70 -51
  15. mcp_proxy_adapter/api/middleware/user_info_middleware.py +43 -34
  16. mcp_proxy_adapter/api/schemas.py +69 -43
  17. mcp_proxy_adapter/api/tool_integration.py +83 -63
  18. mcp_proxy_adapter/api/tools.py +60 -50
  19. mcp_proxy_adapter/commands/__init__.py +15 -6
  20. mcp_proxy_adapter/commands/auth_validation_command.py +107 -110
  21. mcp_proxy_adapter/commands/base.py +108 -112
  22. mcp_proxy_adapter/commands/builtin_commands.py +28 -18
  23. mcp_proxy_adapter/commands/catalog_manager.py +394 -265
  24. mcp_proxy_adapter/commands/cert_monitor_command.py +222 -204
  25. mcp_proxy_adapter/commands/certificate_management_command.py +210 -213
  26. mcp_proxy_adapter/commands/command_registry.py +275 -226
  27. mcp_proxy_adapter/commands/config_command.py +48 -33
  28. mcp_proxy_adapter/commands/dependency_container.py +22 -23
  29. mcp_proxy_adapter/commands/dependency_manager.py +65 -56
  30. mcp_proxy_adapter/commands/echo_command.py +15 -15
  31. mcp_proxy_adapter/commands/health_command.py +31 -29
  32. mcp_proxy_adapter/commands/help_command.py +97 -61
  33. mcp_proxy_adapter/commands/hooks.py +65 -49
  34. mcp_proxy_adapter/commands/key_management_command.py +148 -147
  35. mcp_proxy_adapter/commands/load_command.py +58 -40
  36. mcp_proxy_adapter/commands/plugins_command.py +80 -54
  37. mcp_proxy_adapter/commands/protocol_management_command.py +60 -48
  38. mcp_proxy_adapter/commands/proxy_registration_command.py +107 -115
  39. mcp_proxy_adapter/commands/reload_command.py +43 -37
  40. mcp_proxy_adapter/commands/result.py +26 -33
  41. mcp_proxy_adapter/commands/role_test_command.py +26 -26
  42. mcp_proxy_adapter/commands/roles_management_command.py +176 -173
  43. mcp_proxy_adapter/commands/security_command.py +134 -122
  44. mcp_proxy_adapter/commands/settings_command.py +47 -56
  45. mcp_proxy_adapter/commands/ssl_setup_command.py +109 -129
  46. mcp_proxy_adapter/commands/token_management_command.py +129 -158
  47. mcp_proxy_adapter/commands/transport_management_command.py +41 -36
  48. mcp_proxy_adapter/commands/unload_command.py +42 -37
  49. mcp_proxy_adapter/config.py +36 -35
  50. mcp_proxy_adapter/core/__init__.py +19 -21
  51. mcp_proxy_adapter/core/app_factory.py +30 -9
  52. mcp_proxy_adapter/core/app_runner.py +81 -64
  53. mcp_proxy_adapter/core/auth_validator.py +176 -182
  54. mcp_proxy_adapter/core/certificate_utils.py +469 -426
  55. mcp_proxy_adapter/core/client.py +155 -126
  56. mcp_proxy_adapter/core/client_manager.py +60 -54
  57. mcp_proxy_adapter/core/client_security.py +120 -91
  58. mcp_proxy_adapter/core/config_converter.py +176 -143
  59. mcp_proxy_adapter/core/config_validator.py +12 -4
  60. mcp_proxy_adapter/core/crl_utils.py +21 -7
  61. mcp_proxy_adapter/core/errors.py +64 -20
  62. mcp_proxy_adapter/core/logging.py +34 -29
  63. mcp_proxy_adapter/core/mtls_asgi.py +29 -25
  64. mcp_proxy_adapter/core/mtls_asgi_app.py +66 -54
  65. mcp_proxy_adapter/core/protocol_manager.py +154 -104
  66. mcp_proxy_adapter/core/proxy_client.py +202 -144
  67. mcp_proxy_adapter/core/proxy_registration.py +7 -3
  68. mcp_proxy_adapter/core/role_utils.py +139 -125
  69. mcp_proxy_adapter/core/security_adapter.py +88 -77
  70. mcp_proxy_adapter/core/security_factory.py +50 -44
  71. mcp_proxy_adapter/core/security_integration.py +72 -24
  72. mcp_proxy_adapter/core/server_adapter.py +68 -64
  73. mcp_proxy_adapter/core/server_engine.py +71 -53
  74. mcp_proxy_adapter/core/settings.py +68 -58
  75. mcp_proxy_adapter/core/ssl_utils.py +69 -56
  76. mcp_proxy_adapter/core/transport_manager.py +72 -60
  77. mcp_proxy_adapter/core/unified_config_adapter.py +201 -150
  78. mcp_proxy_adapter/core/utils.py +4 -2
  79. mcp_proxy_adapter/custom_openapi.py +107 -99
  80. mcp_proxy_adapter/examples/basic_framework/main.py +9 -2
  81. mcp_proxy_adapter/examples/commands/__init__.py +1 -1
  82. mcp_proxy_adapter/examples/create_certificates_simple.py +182 -71
  83. mcp_proxy_adapter/examples/debug_request_state.py +38 -19
  84. mcp_proxy_adapter/examples/debug_role_chain.py +53 -20
  85. mcp_proxy_adapter/examples/demo_client.py +48 -36
  86. mcp_proxy_adapter/examples/examples/basic_framework/main.py +9 -2
  87. mcp_proxy_adapter/examples/examples/full_application/__init__.py +1 -0
  88. mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +22 -10
  89. mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +24 -17
  90. mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +16 -3
  91. mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +13 -3
  92. mcp_proxy_adapter/examples/examples/full_application/main.py +27 -2
  93. mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +48 -14
  94. mcp_proxy_adapter/examples/full_application/__init__.py +1 -0
  95. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +22 -10
  96. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +24 -17
  97. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +16 -3
  98. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +13 -3
  99. mcp_proxy_adapter/examples/full_application/main.py +27 -2
  100. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +48 -14
  101. mcp_proxy_adapter/examples/generate_all_certificates.py +198 -73
  102. mcp_proxy_adapter/examples/generate_certificates.py +31 -16
  103. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +220 -74
  104. mcp_proxy_adapter/examples/generate_test_configs.py +68 -91
  105. mcp_proxy_adapter/examples/proxy_registration_example.py +76 -75
  106. mcp_proxy_adapter/examples/run_example.py +23 -5
  107. mcp_proxy_adapter/examples/run_full_test_suite.py +109 -71
  108. mcp_proxy_adapter/examples/run_proxy_server.py +22 -9
  109. mcp_proxy_adapter/examples/run_security_tests.py +103 -41
  110. mcp_proxy_adapter/examples/run_security_tests_fixed.py +72 -36
  111. mcp_proxy_adapter/examples/scripts/config_generator.py +288 -187
  112. mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +185 -72
  113. mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +220 -74
  114. mcp_proxy_adapter/examples/security_test_client.py +196 -127
  115. mcp_proxy_adapter/examples/setup_test_environment.py +17 -29
  116. mcp_proxy_adapter/examples/test_config.py +19 -4
  117. mcp_proxy_adapter/examples/test_config_generator.py +23 -7
  118. mcp_proxy_adapter/examples/test_examples.py +84 -56
  119. mcp_proxy_adapter/examples/universal_client.py +119 -62
  120. mcp_proxy_adapter/openapi.py +108 -115
  121. mcp_proxy_adapter/utils/config_generator.py +429 -274
  122. mcp_proxy_adapter/version.py +1 -2
  123. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/METADATA +1 -1
  124. mcp_proxy_adapter-6.3.6.dist-info/RECORD +144 -0
  125. mcp_proxy_adapter-6.3.6.dist-info/top_level.txt +2 -0
  126. mcp_proxy_adapter_issue_package/demonstrate_issue.py +178 -0
  127. mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
  128. mcp_proxy_adapter-6.3.4.dist-info/top_level.txt +0 -1
  129. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/WHEEL +0 -0
  130. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/entry_points.txt +0 -0
  131. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/licenses/LICENSE +0 -0
@@ -33,7 +33,9 @@ try:
33
33
  ValidationResult,
34
34
  CertificatePair,
35
35
  )
36
- from mcp_security_framework.middleware.fastapi_middleware import FastAPISecurityMiddleware
36
+ from mcp_security_framework.middleware.fastapi_middleware import (
37
+ FastAPISecurityMiddleware,
38
+ )
37
39
 
38
40
  SECURITY_FRAMEWORK_AVAILABLE = True
39
41
  except ImportError:
@@ -73,7 +75,9 @@ class SecurityIntegration:
73
75
  # Initialize framework components
74
76
  self.security_manager = SecurityManager(self.security_config)
75
77
  self.permission_manager = PermissionManager(self.security_config.permissions)
76
- self.auth_manager = AuthManager(self.security_config.auth, self.permission_manager)
78
+ self.auth_manager = AuthManager(
79
+ self.security_config.auth, self.permission_manager
80
+ )
77
81
  self.certificate_manager = CertificateManager(self.security_config.certificates)
78
82
  self.rate_limiter = RateLimiter(self.security_config.rate_limit)
79
83
 
@@ -92,11 +96,17 @@ class SecurityIntegration:
92
96
  ca_cert_file=security_section.get("ssl", {}).get("ca_cert_file"),
93
97
  client_cert_file=security_section.get("ssl", {}).get("client_cert_file"),
94
98
  client_key_file=security_section.get("ssl", {}).get("client_key_file"),
95
- verify_mode=security_section.get("ssl", {}).get("verify_mode", "CERT_REQUIRED"),
96
- min_tls_version=security_section.get("ssl", {}).get("min_tls_version", "TLSv1.2"),
99
+ verify_mode=security_section.get("ssl", {}).get(
100
+ "verify_mode", "CERT_REQUIRED"
101
+ ),
102
+ min_tls_version=security_section.get("ssl", {}).get(
103
+ "min_tls_version", "TLSv1.2"
104
+ ),
97
105
  check_hostname=security_section.get("ssl", {}).get("check_hostname", True),
98
106
  check_expiry=security_section.get("ssl", {}).get("check_expiry", True),
99
- expiry_warning_days=security_section.get("ssl", {}).get("expiry_warning_days", 30),
107
+ expiry_warning_days=security_section.get("ssl", {}).get(
108
+ "expiry_warning_days", 30
109
+ ),
100
110
  )
101
111
 
102
112
  # Create auth config
@@ -106,9 +116,15 @@ class SecurityIntegration:
106
116
  api_keys=security_section.get("auth", {}).get("api_keys", {}),
107
117
  user_roles=security_section.get("auth", {}).get("user_roles", {}),
108
118
  jwt_secret=security_section.get("auth", {}).get("jwt_secret"),
109
- jwt_algorithm=security_section.get("auth", {}).get("jwt_algorithm", "HS256"),
110
- jwt_expiry_hours=security_section.get("auth", {}).get("jwt_expiry_hours", 24),
111
- certificate_auth=security_section.get("auth", {}).get("certificate_auth", False),
119
+ jwt_algorithm=security_section.get("auth", {}).get(
120
+ "jwt_algorithm", "HS256"
121
+ ),
122
+ jwt_expiry_hours=security_section.get("auth", {}).get(
123
+ "jwt_expiry_hours", 24
124
+ ),
125
+ certificate_auth=security_section.get("auth", {}).get(
126
+ "certificate_auth", False
127
+ ),
112
128
  public_paths=security_section.get("auth", {}).get("public_paths", []),
113
129
  )
114
130
 
@@ -132,9 +148,15 @@ class SecurityIntegration:
132
148
  default_role=permissions_section.get("default_role", "guest"),
133
149
  admin_role=permissions_section.get("admin_role", "admin"),
134
150
  role_hierarchy=permissions_section.get("role_hierarchy", {}),
135
- permission_cache_enabled=permissions_section.get("permission_cache_enabled", True),
136
- permission_cache_ttl=permissions_section.get("permission_cache_ttl", 300),
137
- wildcard_permissions=permissions_section.get("wildcard_permissions", False),
151
+ permission_cache_enabled=permissions_section.get(
152
+ "permission_cache_enabled", True
153
+ ),
154
+ permission_cache_ttl=permissions_section.get(
155
+ "permission_cache_ttl", 300
156
+ ),
157
+ wildcard_permissions=permissions_section.get(
158
+ "wildcard_permissions", False
159
+ ),
138
160
  strict_mode=permissions_section.get("strict_mode", True),
139
161
  roles=permissions_section.get("roles"),
140
162
  )
@@ -166,7 +188,9 @@ class SecurityIntegration:
166
188
  window_size_seconds=security_section.get("rate_limit", {}).get(
167
189
  "window_size_seconds", 60
168
190
  ),
169
- storage_backend=security_section.get("rate_limit", {}).get("storage_backend", "memory"),
191
+ storage_backend=security_section.get("rate_limit", {}).get(
192
+ "storage_backend", "memory"
193
+ ),
170
194
  exempt_paths=security_section.get("rate_limit", {}).get("exempt_paths", []),
171
195
  exempt_roles=security_section.get("rate_limit", {}).get("exempt_roles", []),
172
196
  )
@@ -186,7 +210,9 @@ class SecurityIntegration:
186
210
  "default_validity_days", 365
187
211
  ),
188
212
  key_size=security_section.get("certificates", {}).get("key_size", 2048),
189
- hash_algorithm=security_section.get("certificates", {}).get("hash_algorithm", "sha256"),
213
+ hash_algorithm=security_section.get("certificates", {}).get(
214
+ "hash_algorithm", "sha256"
215
+ ),
190
216
  )
191
217
 
192
218
  # Create logging config
@@ -196,7 +222,9 @@ class SecurityIntegration:
196
222
  format=security_section.get("logging", {}).get(
197
223
  "format", "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
198
224
  ),
199
- console_output=security_section.get("logging", {}).get("console_output", True),
225
+ console_output=security_section.get("logging", {}).get(
226
+ "console_output", True
227
+ ),
200
228
  file_path=security_section.get("logging", {}).get("file_path"),
201
229
  )
202
230
 
@@ -231,17 +259,29 @@ class SecurityIntegration:
231
259
  return await self.security_manager.validate_request(request_data)
232
260
 
233
261
  # Certificate methods - direct calls to CertificateManager
234
- async def create_ca_certificate(self, common_name: str, **kwargs) -> CertificatePair:
262
+ async def create_ca_certificate(
263
+ self, common_name: str, **kwargs
264
+ ) -> CertificatePair:
235
265
  """Create CA certificate."""
236
- return await self.certificate_manager.create_ca_certificate(common_name, **kwargs)
266
+ return await self.certificate_manager.create_ca_certificate(
267
+ common_name, **kwargs
268
+ )
237
269
 
238
- async def create_client_certificate(self, common_name: str, **kwargs) -> CertificatePair:
270
+ async def create_client_certificate(
271
+ self, common_name: str, **kwargs
272
+ ) -> CertificatePair:
239
273
  """Create client certificate."""
240
- return await self.certificate_manager.create_client_certificate(common_name, **kwargs)
274
+ return await self.certificate_manager.create_client_certificate(
275
+ common_name, **kwargs
276
+ )
241
277
 
242
- async def create_server_certificate(self, common_name: str, **kwargs) -> CertificatePair:
278
+ async def create_server_certificate(
279
+ self, common_name: str, **kwargs
280
+ ) -> CertificatePair:
243
281
  """Create server certificate."""
244
- return await self.certificate_manager.create_server_certificate(common_name, **kwargs)
282
+ return await self.certificate_manager.create_server_certificate(
283
+ common_name, **kwargs
284
+ )
245
285
 
246
286
  async def validate_certificate(self, cert_path: str) -> bool:
247
287
  """Validate certificate with CRL check if enabled."""
@@ -261,12 +301,16 @@ class SecurityIntegration:
261
301
  "crl_enabled": cert_config.crl_enabled,
262
302
  "crl_path": getattr(cert_config, "crl_path", None),
263
303
  "crl_url": getattr(cert_config, "crl_url", None),
264
- "crl_validity_days": getattr(cert_config, "crl_validity_days", 30),
304
+ "crl_validity_days": getattr(
305
+ cert_config, "crl_validity_days", 30
306
+ ),
265
307
  }
266
308
 
267
309
  # Use mcp_security_framework's validate_certificate_chain with CRL
268
310
  if crl_config and crl_config.get("crl_enabled"):
269
- from mcp_security_framework.utils.cert_utils import validate_certificate_chain
311
+ from mcp_security_framework.utils.cert_utils import (
312
+ validate_certificate_chain,
313
+ )
270
314
  from .crl_utils import CRLManager
271
315
 
272
316
  # Get CRL data
@@ -276,7 +320,9 @@ class SecurityIntegration:
276
320
  # Validate with CRL
277
321
  if crl_data:
278
322
  return validate_certificate_chain(
279
- cert_path, self.security_config.certificates.ca_cert_path, crl_data
323
+ cert_path,
324
+ self.security_config.certificates.ca_cert_path,
325
+ crl_data,
280
326
  )
281
327
 
282
328
  # Fallback to standard validation
@@ -312,7 +358,9 @@ class SecurityIntegration:
312
358
  return await self.permission_manager.remove_user_role(user_id, role)
313
359
 
314
360
  # Rate limiting methods - direct calls to RateLimiter
315
- async def check_rate_limit(self, identifier: str, limit_type: str = "per_minute") -> bool:
361
+ async def check_rate_limit(
362
+ self, identifier: str, limit_type: str = "per_minute"
363
+ ) -> bool:
316
364
  """Check rate limit."""
317
365
  return await self.rate_limiter.check_rate_limit(identifier, limit_type)
318
366
 
@@ -20,23 +20,22 @@ logger = logging.getLogger(__name__)
20
20
  class ServerConfigAdapter:
21
21
  """
22
22
  Adapter for converting server configurations between different engines.
23
-
23
+
24
24
  This class handles the mapping of configuration parameters between
25
25
  different server engines and provides unified configuration management.
26
26
  """
27
-
27
+
28
28
  @staticmethod
29
29
  def convert_ssl_config_for_engine(
30
- ssl_config: Dict[str, Any],
31
- target_engine: str
30
+ ssl_config: Dict[str, Any], target_engine: str
32
31
  ) -> Dict[str, Any]:
33
32
  """
34
33
  Convert SSL configuration for a specific server engine.
35
-
34
+
36
35
  Args:
37
36
  ssl_config: Source SSL configuration
38
37
  target_engine: Target engine name (hypercorn)
39
-
38
+
40
39
  Returns:
41
40
  Converted SSL configuration for the target engine
42
41
  """
@@ -44,18 +43,18 @@ class ServerConfigAdapter:
44
43
  if not engine:
45
44
  logger.error(f"Unknown server engine: {target_engine}")
46
45
  return {}
47
-
46
+
48
47
  if target_engine == "hypercorn":
49
48
  return ServerConfigAdapter._convert_to_hypercorn_ssl(ssl_config)
50
49
  else:
51
50
  logger.warning(f"No SSL conversion available for engine: {target_engine}")
52
51
  return {}
53
-
52
+
54
53
  @staticmethod
55
54
  def _convert_to_hypercorn_ssl(ssl_config: Dict[str, Any]) -> Dict[str, Any]:
56
55
  """Convert SSL configuration to hypercorn format."""
57
56
  hypercorn_ssl = {}
58
-
57
+
59
58
  # Map SSL parameters
60
59
  if ssl_config.get("cert_file"):
61
60
  hypercorn_ssl["certfile"] = ssl_config["cert_file"]
@@ -63,42 +62,39 @@ class ServerConfigAdapter:
63
62
  hypercorn_ssl["keyfile"] = ssl_config["key_file"]
64
63
  if ssl_config.get("ca_cert"):
65
64
  hypercorn_ssl["ca_certs"] = ssl_config["ca_cert"]
66
-
65
+
67
66
  # Map verification mode
68
67
  if ssl_config.get("verify_client", False):
69
68
  hypercorn_ssl["verify_mode"] = "CERT_REQUIRED"
70
-
69
+
71
70
  logger.debug(f"Converted SSL config to hypercorn: {hypercorn_ssl}")
72
71
  return hypercorn_ssl
73
-
72
+
74
73
  @staticmethod
75
74
  def get_optimal_engine_for_config(config: Dict[str, Any]) -> Optional[str]:
76
75
  """
77
76
  Determine the optimal server engine for a given configuration.
78
-
77
+
79
78
  Currently only hypercorn is supported.
80
-
79
+
81
80
  Args:
82
81
  config: Server configuration
83
-
82
+
84
83
  Returns:
85
84
  Optimal engine name (currently always "hypercorn")
86
85
  """
87
86
  # Currently only hypercorn is supported
88
87
  return "hypercorn"
89
-
88
+
90
89
  @staticmethod
91
- def validate_engine_compatibility(
92
- config: Dict[str, Any],
93
- engine_name: str
94
- ) -> bool:
90
+ def validate_engine_compatibility(config: Dict[str, Any], engine_name: str) -> bool:
95
91
  """
96
92
  Validate if a configuration is compatible with a specific engine.
97
-
93
+
98
94
  Args:
99
95
  config: Server configuration
100
96
  engine_name: Name of the server engine
101
-
97
+
102
98
  Returns:
103
99
  True if compatible, False otherwise
104
100
  """
@@ -106,73 +102,72 @@ class ServerConfigAdapter:
106
102
  if not engine:
107
103
  logger.error(f"Unknown engine: {engine_name}")
108
104
  return False
109
-
105
+
110
106
  # Check SSL requirements
111
107
  ssl_config = config.get("ssl", {})
112
108
  if not ssl_config:
113
109
  # Try to get SSL config from security section
114
110
  ssl_config = config.get("security", {}).get("ssl", {})
115
-
111
+
116
112
  if ssl_config.get("verify_client", False):
117
113
  if not engine.get_supported_features().get("mtls_client_certs", False):
118
- logger.error(f"Engine {engine_name} doesn't support mTLS client certificates")
114
+ logger.error(
115
+ f"Engine {engine_name} doesn't support mTLS client certificates"
116
+ )
119
117
  return False
120
-
118
+
121
119
  # Validate engine-specific configuration
122
120
  return engine.validate_config(config)
123
-
121
+
124
122
  @staticmethod
125
123
  def get_engine_capabilities(engine_name: str) -> Dict[str, Any]:
126
124
  """
127
125
  Get capabilities of a specific server engine.
128
-
126
+
129
127
  Args:
130
128
  engine_name: Name of the server engine
131
-
129
+
132
130
  Returns:
133
131
  Dictionary of engine capabilities
134
132
  """
135
133
  engine = ServerEngineFactory.get_engine(engine_name)
136
134
  if not engine:
137
135
  return {}
138
-
136
+
139
137
  return {
140
138
  "name": engine.get_name(),
141
139
  "features": engine.get_supported_features(),
142
- "config_schema": engine.get_config_schema()
140
+ "config_schema": engine.get_config_schema(),
143
141
  }
144
142
 
145
143
 
146
144
  class UnifiedServerRunner:
147
145
  """
148
146
  Unified server runner that uses hypercorn as the default engine.
149
-
147
+
150
148
  This class provides a unified interface for running servers using hypercorn
151
149
  as the underlying engine.
152
150
  """
153
-
151
+
154
152
  def __init__(self, default_engine: str = "hypercorn"):
155
153
  """
156
154
  Initialize the unified server runner.
157
-
155
+
158
156
  Args:
159
157
  default_engine: Default engine to use (currently only hypercorn is supported)
160
158
  """
161
159
  self.default_engine = default_engine
162
160
  self.available_engines = ServerEngineFactory.get_available_engines()
163
-
161
+
164
162
  logger.info(f"Available engines: {list(self.available_engines.keys())}")
165
163
  logger.info(f"Default engine: {default_engine}")
166
-
164
+
167
165
  def run_server(
168
- self,
169
- app: Any,
170
- config: Dict[str, Any],
171
- engine_name: Optional[str] = None
166
+ self, app: Any, config: Dict[str, Any], engine_name: Optional[str] = None
172
167
  ) -> None:
173
168
  """
174
169
  Run server with hypercorn engine.
175
-
170
+
176
171
  Args:
177
172
  app: ASGI application
178
173
  config: Server configuration
@@ -181,29 +176,33 @@ class UnifiedServerRunner:
181
176
  # Use hypercorn as the only supported engine
182
177
  selected_engine = "hypercorn"
183
178
  logger.info(f"Using hypercorn engine")
184
-
179
+
185
180
  # Validate compatibility
186
- if not ServerConfigAdapter.validate_engine_compatibility(config, selected_engine):
187
- raise ValueError(f"Configuration not compatible with engine: {selected_engine}")
188
-
181
+ if not ServerConfigAdapter.validate_engine_compatibility(
182
+ config, selected_engine
183
+ ):
184
+ raise ValueError(
185
+ f"Configuration not compatible with engine: {selected_engine}"
186
+ )
187
+
189
188
  # Get engine instance
190
189
  engine = ServerEngineFactory.get_engine(selected_engine)
191
190
  if not engine:
192
191
  raise ValueError(f"Engine not available: {selected_engine}")
193
-
192
+
194
193
  # Convert configuration if needed
195
194
  converted_config = self._prepare_config_for_engine(config, selected_engine)
196
-
195
+
197
196
  # Run server
198
197
  logger.info(f"Starting server with {selected_engine} engine")
199
198
  engine.run_server(app, converted_config)
200
-
199
+
201
200
  def _prepare_config_for_engine(
202
- self,
203
- config: Dict[str, Any],
204
- engine_name: str
201
+ self, config: Dict[str, Any], engine_name: str
205
202
  ) -> Dict[str, Any]:
206
- logger.info(f"🔍 Debug: _prepare_config_for_engine called with config keys: {list(config.keys())}")
203
+ logger.info(
204
+ f"🔍 Debug: _prepare_config_for_engine called with config keys: {list(config.keys())}"
205
+ )
207
206
  logger.info(f"🔍 Debug: SSL config in input: {config.get('ssl', 'NOT_FOUND')}")
208
207
  """
209
208
  Prepare configuration for a specific engine.
@@ -220,12 +219,17 @@ class UnifiedServerRunner:
220
219
  "host": config.get("host", "127.0.0.1"),
221
220
  "port": config.get("port", 8000),
222
221
  "log_level": config.get("log_level", "info"),
223
- "reload": config.get("reload", False)
222
+ "reload": config.get("reload", False),
224
223
  }
225
-
224
+
226
225
  # Add SSL configuration if present
227
226
  # First check for direct SSL parameters (from app_factory.py)
228
- if "certfile" in config or "keyfile" in config or "ca_certs" in config or "verify_mode" in config:
227
+ if (
228
+ "certfile" in config
229
+ or "keyfile" in config
230
+ or "ca_certs" in config
231
+ or "verify_mode" in config
232
+ ):
229
233
  logger.info(f"🔍 DEBUG: Direct SSL parameters found in config")
230
234
  if "certfile" in config:
231
235
  engine_config["certfile"] = config["certfile"]
@@ -241,35 +245,35 @@ class UnifiedServerRunner:
241
245
  if not ssl_config:
242
246
  # Try to get SSL config from security section
243
247
  ssl_config = config.get("security", {}).get("ssl", {})
244
-
248
+
245
249
  if ssl_config:
246
250
  converted_ssl = ServerConfigAdapter.convert_ssl_config_for_engine(
247
251
  ssl_config, engine_name
248
252
  )
249
253
  engine_config.update(converted_ssl)
250
-
254
+
251
255
  # Add engine-specific configuration
252
256
  if "workers" in config:
253
257
  engine_config["workers"] = config["workers"]
254
-
258
+
255
259
  return engine_config
256
-
260
+
257
261
  def get_engine_info(self, engine_name: str) -> Dict[str, Any]:
258
262
  """
259
263
  Get information about a specific engine.
260
-
264
+
261
265
  Args:
262
266
  engine_name: Name of the engine
263
-
267
+
264
268
  Returns:
265
269
  Engine information dictionary
266
270
  """
267
271
  return ServerConfigAdapter.get_engine_capabilities(engine_name)
268
-
272
+
269
273
  def list_available_engines(self) -> Dict[str, Dict[str, Any]]:
270
274
  """
271
275
  List all available engines with their capabilities.
272
-
276
+
273
277
  Returns:
274
278
  Dictionary mapping engine names to their capabilities
275
279
  """
@@ -277,6 +281,6 @@ class UnifiedServerRunner:
277
281
  for name, engine in self.available_engines.items():
278
282
  engines_info[name] = {
279
283
  "features": engine.get_supported_features(),
280
- "config_schema": engine.get_config_schema()
284
+ "config_schema": engine.get_config_schema(),
281
285
  }
282
286
  return engines_info