mcp-proxy-adapter 6.9.16__py3-none-any.whl → 6.9.18__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.
- mcp_proxy_adapter/api/app.py +57 -55
- mcp_proxy_adapter/api/handlers.py +5 -5
- mcp_proxy_adapter/api/middleware/__init__.py +8 -8
- mcp_proxy_adapter/api/middleware/base.py +14 -14
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +7 -7
- mcp_proxy_adapter/api/middleware/error_handling.py +9 -9
- mcp_proxy_adapter/api/middleware/factory.py +17 -17
- mcp_proxy_adapter/api/middleware/logging.py +6 -6
- mcp_proxy_adapter/api/middleware/performance.py +3 -3
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +19 -19
- mcp_proxy_adapter/api/middleware/transport_middleware.py +3 -3
- mcp_proxy_adapter/api/middleware/unified_security.py +11 -11
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +21 -21
- mcp_proxy_adapter/api/tool_integration.py +3 -2
- mcp_proxy_adapter/api/tools.py +4 -3
- mcp_proxy_adapter/commands/auth_validation_command.py +6 -5
- mcp_proxy_adapter/commands/base.py +10 -10
- mcp_proxy_adapter/commands/builtin_commands.py +6 -6
- mcp_proxy_adapter/commands/catalog_manager.py +74 -74
- mcp_proxy_adapter/commands/cert_monitor_command.py +13 -12
- mcp_proxy_adapter/commands/certificate_management_command.py +20 -19
- mcp_proxy_adapter/commands/command_registry.py +88 -80
- mcp_proxy_adapter/commands/config_command.py +3 -1
- mcp_proxy_adapter/commands/dependency_manager.py +10 -10
- mcp_proxy_adapter/commands/help_command.py +21 -20
- mcp_proxy_adapter/commands/hooks.py +27 -27
- mcp_proxy_adapter/commands/key_management_command.py +19 -18
- mcp_proxy_adapter/commands/plugins_command.py +2 -1
- mcp_proxy_adapter/commands/protocol_management_command.py +6 -6
- mcp_proxy_adapter/commands/proxy_registration_command.py +9 -9
- mcp_proxy_adapter/commands/registration_status_command.py +4 -4
- mcp_proxy_adapter/commands/reload_command.py +5 -5
- mcp_proxy_adapter/commands/role_test_command.py +2 -1
- mcp_proxy_adapter/commands/roles_management_command.py +9 -8
- mcp_proxy_adapter/commands/security_command.py +3 -2
- mcp_proxy_adapter/commands/ssl_setup_command.py +7 -6
- mcp_proxy_adapter/commands/token_management_command.py +12 -11
- mcp_proxy_adapter/commands/transport_management_command.py +2 -2
- mcp_proxy_adapter/config.py +3 -3
- mcp_proxy_adapter/core/__init__.py +1 -1
- mcp_proxy_adapter/core/app_factory.py +1 -1
- mcp_proxy_adapter/core/app_runner.py +3 -3
- mcp_proxy_adapter/core/auth_validator.py +9 -9
- mcp_proxy_adapter/core/certificate_utils.py +27 -27
- mcp_proxy_adapter/core/client_manager.py +13 -13
- mcp_proxy_adapter/core/client_security.py +26 -26
- mcp_proxy_adapter/core/config_converter.py +18 -18
- mcp_proxy_adapter/core/config_validator.py +5 -1
- mcp_proxy_adapter/core/crl_utils.py +22 -22
- mcp_proxy_adapter/core/logging.py +21 -13
- mcp_proxy_adapter/core/mtls_asgi.py +7 -7
- mcp_proxy_adapter/core/mtls_asgi_app.py +9 -9
- mcp_proxy_adapter/core/mtls_proxy.py +9 -9
- mcp_proxy_adapter/core/mtls_server.py +18 -18
- mcp_proxy_adapter/core/protocol_manager.py +29 -29
- mcp_proxy_adapter/core/proxy_registration.py +67 -67
- mcp_proxy_adapter/core/security_adapter.py +18 -18
- mcp_proxy_adapter/core/security_factory.py +16 -16
- mcp_proxy_adapter/core/security_integration.py +6 -6
- mcp_proxy_adapter/core/server_adapter.py +12 -12
- mcp_proxy_adapter/core/server_engine.py +17 -17
- mcp_proxy_adapter/core/signal_handler.py +12 -12
- mcp_proxy_adapter/core/ssl_utils.py +12 -12
- mcp_proxy_adapter/core/transport_manager.py +14 -14
- mcp_proxy_adapter/core/unified_config_adapter.py +6 -6
- mcp_proxy_adapter/core/utils.py +5 -5
- mcp_proxy_adapter/custom_openapi.py +7 -7
- mcp_proxy_adapter/examples/cert_manager_bugfix.py +2 -2
- mcp_proxy_adapter/examples/full_application/commands/__init__.py +6 -5
- mcp_proxy_adapter/examples/full_application/commands/echo_command.py +44 -0
- mcp_proxy_adapter/examples/full_application/commands/help_command.py +66 -0
- mcp_proxy_adapter/examples/full_application/commands/list_command.py +64 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +21 -21
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +6 -6
- mcp_proxy_adapter/examples/full_application/main.py +28 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +38 -38
- mcp_proxy_adapter/examples/run_proxy_server.py +20 -10
- mcp_proxy_adapter/examples/test_framework_complete.py +35 -35
- mcp_proxy_adapter/examples/test_mcp_server.py +2 -2
- mcp_proxy_adapter/examples/validate_generator_compatibility.py +386 -0
- mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +248 -0
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-6.9.16.dist-info → mcp_proxy_adapter-6.9.18.dist-info}/METADATA +1 -1
- mcp_proxy_adapter-6.9.18.dist-info/RECORD +149 -0
- mcp_proxy_adapter-6.9.16.dist-info/RECORD +0 -144
- {mcp_proxy_adapter-6.9.16.dist-info → mcp_proxy_adapter-6.9.18.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.9.16.dist-info → mcp_proxy_adapter-6.9.18.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.9.16.dist-info → mcp_proxy_adapter-6.9.18.dist-info}/top_level.txt +0 -0
@@ -32,7 +32,7 @@ except ImportError:
|
|
32
32
|
PermissionConfig = None
|
33
33
|
RateLimitConfig = None
|
34
34
|
|
35
|
-
from mcp_proxy_adapter.core.logging import
|
35
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
36
36
|
|
37
37
|
|
38
38
|
class SecurityAdapter:
|
@@ -55,9 +55,9 @@ class SecurityAdapter:
|
|
55
55
|
|
56
56
|
if SECURITY_FRAMEWORK_AVAILABLE:
|
57
57
|
self.security_manager = self._create_security_manager()
|
58
|
-
|
58
|
+
get_global_logger().info("Security adapter initialized with mcp_security_framework")
|
59
59
|
else:
|
60
|
-
|
60
|
+
get_global_logger().warning("mcp_security_framework not available, using fallback mode")
|
61
61
|
|
62
62
|
def _create_security_manager(self) -> Optional[SecurityManager]:
|
63
63
|
"""
|
@@ -73,7 +73,7 @@ class SecurityAdapter:
|
|
73
73
|
security_config = self._convert_config()
|
74
74
|
return SecurityManager(security_config)
|
75
75
|
except Exception as e:
|
76
|
-
|
76
|
+
get_global_logger().error(f"Failed to create SecurityManager: {e}")
|
77
77
|
return None
|
78
78
|
|
79
79
|
def _convert_config(self) -> SecurityConfig:
|
@@ -225,10 +225,10 @@ class SecurityAdapter:
|
|
225
225
|
Returns:
|
226
226
|
Validation result dictionary
|
227
227
|
"""
|
228
|
-
|
228
|
+
get_global_logger().debug(f"Security manager available: {self.security_manager is not None}")
|
229
229
|
if not self.security_manager:
|
230
230
|
# Fallback validation when framework is not available
|
231
|
-
|
231
|
+
get_global_logger().debug("Using fallback validation")
|
232
232
|
return self._fallback_validation(request_data)
|
233
233
|
|
234
234
|
try:
|
@@ -241,7 +241,7 @@ class SecurityAdapter:
|
|
241
241
|
return result.to_dict()
|
242
242
|
|
243
243
|
except Exception as e:
|
244
|
-
|
244
|
+
get_global_logger().error(f"Security validation failed: {e}")
|
245
245
|
return {
|
246
246
|
"is_valid": False,
|
247
247
|
"error_code": -32603,
|
@@ -286,21 +286,21 @@ class SecurityAdapter:
|
|
286
286
|
|
287
287
|
# Check for API key in headers (FastAPI converts headers to lowercase)
|
288
288
|
api_key = headers.get("x-api-key") or headers.get("X-API-Key")
|
289
|
-
|
289
|
+
get_global_logger().debug(f"API key from headers: {api_key}")
|
290
290
|
|
291
291
|
# Check for API key in query parameters
|
292
292
|
if not api_key:
|
293
293
|
api_key = query_params.get("api_key")
|
294
|
-
|
294
|
+
get_global_logger().debug(f"API key from query params: {api_key}")
|
295
295
|
|
296
296
|
# Check for API key in JSON-RPC body
|
297
297
|
if not api_key and isinstance(body, dict):
|
298
298
|
api_key = body.get("params", {}).get("api_key")
|
299
|
-
|
299
|
+
get_global_logger().debug(f"API key from body: {api_key}")
|
300
300
|
|
301
301
|
# Get API keys from config
|
302
302
|
api_keys = self._get_api_keys()
|
303
|
-
|
303
|
+
get_global_logger().debug(f"Available API keys: {list(api_keys.keys())}")
|
304
304
|
|
305
305
|
if api_key and api_key in api_keys:
|
306
306
|
return {
|
@@ -331,17 +331,17 @@ class SecurityAdapter:
|
|
331
331
|
auth_config = security_config.get("auth", {})
|
332
332
|
api_keys = auth_config.get("api_keys", {})
|
333
333
|
|
334
|
-
|
335
|
-
|
336
|
-
|
334
|
+
get_global_logger().debug(f"Security config: {security_config}")
|
335
|
+
get_global_logger().debug(f"Auth config: {auth_config}")
|
336
|
+
get_global_logger().debug(f"API keys from security config: {api_keys}")
|
337
337
|
|
338
338
|
# Fallback to legacy SSL config
|
339
339
|
if not api_keys:
|
340
340
|
ssl_config = self.config.get("ssl", {})
|
341
341
|
api_keys = ssl_config.get("api_keys", {})
|
342
|
-
|
342
|
+
get_global_logger().debug(f"API keys from SSL config: {api_keys}")
|
343
343
|
|
344
|
-
|
344
|
+
get_global_logger().info(f"Total API keys loaded: {len(api_keys)}")
|
345
345
|
return api_keys
|
346
346
|
|
347
347
|
def create_middleware(self, framework: str = "fastapi"):
|
@@ -355,7 +355,7 @@ class SecurityAdapter:
|
|
355
355
|
Middleware instance
|
356
356
|
"""
|
357
357
|
if not self.security_manager:
|
358
|
-
|
358
|
+
get_global_logger().warning("Cannot create middleware: security framework not available")
|
359
359
|
return None
|
360
360
|
|
361
361
|
try:
|
@@ -368,7 +368,7 @@ class SecurityAdapter:
|
|
368
368
|
else:
|
369
369
|
raise ValueError(f"Unsupported framework: {framework}")
|
370
370
|
except Exception as e:
|
371
|
-
|
371
|
+
get_global_logger().error(f"Failed to create middleware: {e}")
|
372
372
|
return None
|
373
373
|
|
374
374
|
def is_available(self) -> bool:
|
@@ -8,7 +8,7 @@ and middleware components with proper configuration and error handling.
|
|
8
8
|
import logging
|
9
9
|
from typing import Dict, Any, Optional
|
10
10
|
|
11
|
-
from mcp_proxy_adapter.core.logging import
|
11
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
12
12
|
from .security_adapter import SecurityAdapter
|
13
13
|
|
14
14
|
|
@@ -33,10 +33,10 @@ class SecurityFactory:
|
|
33
33
|
"""
|
34
34
|
try:
|
35
35
|
adapter = SecurityAdapter(config)
|
36
|
-
|
36
|
+
get_global_logger().info("Security adapter created successfully")
|
37
37
|
return adapter
|
38
38
|
except Exception as e:
|
39
|
-
|
39
|
+
get_global_logger().error(f"Failed to create security adapter: {e}")
|
40
40
|
raise
|
41
41
|
|
42
42
|
@staticmethod
|
@@ -54,7 +54,7 @@ class SecurityFactory:
|
|
54
54
|
adapter = SecurityFactory.create_security_adapter(config)
|
55
55
|
return adapter.security_manager
|
56
56
|
except Exception as e:
|
57
|
-
|
57
|
+
get_global_logger().error(f"Failed to create security manager: {e}")
|
58
58
|
return None
|
59
59
|
|
60
60
|
@staticmethod
|
@@ -74,14 +74,14 @@ class SecurityFactory:
|
|
74
74
|
middleware = adapter.create_middleware(framework)
|
75
75
|
|
76
76
|
if middleware:
|
77
|
-
|
77
|
+
get_global_logger().info(f"Security middleware created for {framework}")
|
78
78
|
else:
|
79
|
-
|
79
|
+
get_global_logger().warning(f"Failed to create security middleware for {framework}")
|
80
80
|
|
81
81
|
return middleware
|
82
82
|
|
83
83
|
except Exception as e:
|
84
|
-
|
84
|
+
get_global_logger().error(f"Failed to create security middleware: {e}")
|
85
85
|
return None
|
86
86
|
|
87
87
|
@staticmethod
|
@@ -114,38 +114,38 @@ class SecurityFactory:
|
|
114
114
|
|
115
115
|
# Validate required fields
|
116
116
|
if not isinstance(security_config, dict):
|
117
|
-
|
117
|
+
get_global_logger().error("Security configuration must be a dictionary")
|
118
118
|
return False
|
119
119
|
|
120
120
|
# Validate auth configuration
|
121
121
|
auth_config = security_config.get("auth", {})
|
122
122
|
if not isinstance(auth_config, dict):
|
123
|
-
|
123
|
+
get_global_logger().error("Auth configuration must be a dictionary")
|
124
124
|
return False
|
125
125
|
|
126
126
|
# Validate SSL configuration
|
127
127
|
ssl_config = security_config.get("ssl", {})
|
128
128
|
if not isinstance(ssl_config, dict):
|
129
|
-
|
129
|
+
get_global_logger().error("SSL configuration must be a dictionary")
|
130
130
|
return False
|
131
131
|
|
132
132
|
# Validate permissions configuration
|
133
133
|
permissions_config = security_config.get("permissions", {})
|
134
134
|
if not isinstance(permissions_config, dict):
|
135
|
-
|
135
|
+
get_global_logger().error("Permissions configuration must be a dictionary")
|
136
136
|
return False
|
137
137
|
|
138
138
|
# Validate rate limit configuration
|
139
139
|
rate_limit_config = security_config.get("rate_limit", {})
|
140
140
|
if not isinstance(rate_limit_config, dict):
|
141
|
-
|
141
|
+
get_global_logger().error("Rate limit configuration must be a dictionary")
|
142
142
|
return False
|
143
143
|
|
144
|
-
|
144
|
+
get_global_logger().info("Security configuration validation passed")
|
145
145
|
return True
|
146
146
|
|
147
147
|
except Exception as e:
|
148
|
-
|
148
|
+
get_global_logger().error(f"Configuration validation failed: {e}")
|
149
149
|
return False
|
150
150
|
|
151
151
|
@staticmethod
|
@@ -218,11 +218,11 @@ class SecurityFactory:
|
|
218
218
|
# Deep merge security configuration
|
219
219
|
SecurityFactory._deep_merge(merged_config["security"], security_config)
|
220
220
|
|
221
|
-
|
221
|
+
get_global_logger().info("Security configuration merged successfully")
|
222
222
|
return merged_config
|
223
223
|
|
224
224
|
except Exception as e:
|
225
|
-
|
225
|
+
get_global_logger().error(f"Failed to merge security configuration: {e}")
|
226
226
|
return base_config
|
227
227
|
|
228
228
|
@staticmethod
|
@@ -48,7 +48,7 @@ except ImportError:
|
|
48
48
|
RateLimiter = None
|
49
49
|
FastAPISecurityMiddleware = None
|
50
50
|
|
51
|
-
from mcp_proxy_adapter.core.logging import
|
51
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
52
52
|
|
53
53
|
|
54
54
|
class SecurityIntegration:
|
@@ -81,7 +81,7 @@ class SecurityIntegration:
|
|
81
81
|
self.certificate_manager = CertificateManager(self.security_config.certificates)
|
82
82
|
self.rate_limiter = RateLimiter(self.security_config.rate_limit)
|
83
83
|
|
84
|
-
|
84
|
+
get_global_logger().info("Security integration initialized with mcp_security_framework")
|
85
85
|
|
86
86
|
def _create_security_config(self) -> SecurityConfig:
|
87
87
|
"""Create SecurityConfig from project configuration."""
|
@@ -126,7 +126,7 @@ class SecurityIntegration:
|
|
126
126
|
if permissions_enabled:
|
127
127
|
# If roles_file is None or empty string, don't pass it to avoid framework errors
|
128
128
|
if roles_file is None or roles_file == "":
|
129
|
-
|
129
|
+
get_global_logger().warning(
|
130
130
|
"roles_file is None or empty, permissions will use default configuration"
|
131
131
|
)
|
132
132
|
roles_file = None
|
@@ -292,7 +292,7 @@ class SecurityIntegration:
|
|
292
292
|
return await self.certificate_manager.validate_certificate(cert_path)
|
293
293
|
|
294
294
|
except Exception as e:
|
295
|
-
|
295
|
+
get_global_logger().error(f"Certificate validation failed: {e}")
|
296
296
|
return False
|
297
297
|
|
298
298
|
async def extract_roles_from_certificate(self, cert_path: str) -> List[str]:
|
@@ -371,8 +371,8 @@ def create_security_integration(config: Dict[str, Any]) -> SecurityIntegration:
|
|
371
371
|
try:
|
372
372
|
return SecurityIntegration(config)
|
373
373
|
except ImportError as e:
|
374
|
-
|
374
|
+
get_global_logger().error(f"mcp_security_framework not available: {e}")
|
375
375
|
raise RuntimeError("Security framework is required but not available") from e
|
376
376
|
except Exception as e:
|
377
|
-
|
377
|
+
get_global_logger().error(f"Failed to create security integration: {e}")
|
378
378
|
raise RuntimeError(f"Security integration failed: {e}") from e
|
@@ -41,13 +41,13 @@ class ServerConfigAdapter:
|
|
41
41
|
"""
|
42
42
|
engine = ServerEngineFactory.get_engine(target_engine)
|
43
43
|
if not engine:
|
44
|
-
|
44
|
+
get_global_logger().error(f"Unknown server engine: {target_engine}")
|
45
45
|
return {}
|
46
46
|
|
47
47
|
if target_engine == "hypercorn":
|
48
48
|
return ServerConfigAdapter._convert_to_hypercorn_ssl(ssl_config)
|
49
49
|
else:
|
50
|
-
|
50
|
+
get_global_logger().warning(f"No SSL conversion available for engine: {target_engine}")
|
51
51
|
return {}
|
52
52
|
|
53
53
|
@staticmethod
|
@@ -71,7 +71,7 @@ class ServerConfigAdapter:
|
|
71
71
|
if "chk_hostname" in ssl_config:
|
72
72
|
hypercorn_ssl["check_hostname"] = ssl_config["chk_hostname"]
|
73
73
|
|
74
|
-
|
74
|
+
get_global_logger().debug(f"Converted SSL config to hypercorn: {hypercorn_ssl}")
|
75
75
|
return hypercorn_ssl
|
76
76
|
|
77
77
|
@staticmethod
|
@@ -104,7 +104,7 @@ class ServerConfigAdapter:
|
|
104
104
|
"""
|
105
105
|
engine = ServerEngineFactory.get_engine(engine_name)
|
106
106
|
if not engine:
|
107
|
-
|
107
|
+
get_global_logger().error(f"Unknown engine: {engine_name}")
|
108
108
|
return False
|
109
109
|
|
110
110
|
# Check SSL requirements
|
@@ -115,7 +115,7 @@ class ServerConfigAdapter:
|
|
115
115
|
|
116
116
|
if ssl_config.get("verify_client", False):
|
117
117
|
if not engine.get_supported_features().get("mtls_client_certs", False):
|
118
|
-
|
118
|
+
get_global_logger().error(
|
119
119
|
f"Engine {engine_name} doesn't support mTLS client certificates"
|
120
120
|
)
|
121
121
|
return False
|
@@ -163,8 +163,8 @@ class UnifiedServerRunner:
|
|
163
163
|
self.default_engine = default_engine
|
164
164
|
self.available_engines = ServerEngineFactory.get_available_engines()
|
165
165
|
|
166
|
-
|
167
|
-
|
166
|
+
get_global_logger().info(f"Available engines: {list(self.available_engines.keys())}")
|
167
|
+
get_global_logger().info(f"Default engine: {default_engine}")
|
168
168
|
|
169
169
|
def run_server(
|
170
170
|
self, app: Any, config: Dict[str, Any], engine_name: Optional[str] = None
|
@@ -179,7 +179,7 @@ class UnifiedServerRunner:
|
|
179
179
|
"""
|
180
180
|
# Use hypercorn as the only supported engine
|
181
181
|
selected_engine = "hypercorn"
|
182
|
-
|
182
|
+
get_global_logger().info(f"Using hypercorn engine")
|
183
183
|
|
184
184
|
# Validate compatibility
|
185
185
|
if not ServerConfigAdapter.validate_engine_compatibility(
|
@@ -198,16 +198,16 @@ class UnifiedServerRunner:
|
|
198
198
|
converted_config = self._prepare_config_for_engine(config, selected_engine)
|
199
199
|
|
200
200
|
# Run server
|
201
|
-
|
201
|
+
get_global_logger().info(f"Starting server with {selected_engine} engine")
|
202
202
|
engine.run_server(app, converted_config)
|
203
203
|
|
204
204
|
def _prepare_config_for_engine(
|
205
205
|
self, config: Dict[str, Any], engine_name: str
|
206
206
|
) -> Dict[str, Any]:
|
207
|
-
|
207
|
+
get_global_logger().info(
|
208
208
|
f"🔍 Debug: _prepare_config_for_engine called with config keys: {list(config.keys())}"
|
209
209
|
)
|
210
|
-
|
210
|
+
get_global_logger().info(f"🔍 Debug: SSL config in input: {config.get('ssl', 'NOT_FOUND')}")
|
211
211
|
"""
|
212
212
|
Prepare configuration for a specific engine.
|
213
213
|
|
@@ -234,7 +234,7 @@ class UnifiedServerRunner:
|
|
234
234
|
or "ca_certs" in config
|
235
235
|
or "verify_mode" in config
|
236
236
|
):
|
237
|
-
|
237
|
+
get_global_logger().info(f"🔍 DEBUG: Direct SSL parameters found in config")
|
238
238
|
if "certfile" in config:
|
239
239
|
engine_config["certfile"] = config["certfile"]
|
240
240
|
if "keyfile" in config:
|
@@ -115,7 +115,7 @@ class HypercornEngine(ServerEngine):
|
|
115
115
|
|
116
116
|
for field in required_fields:
|
117
117
|
if field not in config:
|
118
|
-
|
118
|
+
get_global_logger().error(f"Missing required field: {field}")
|
119
119
|
return False
|
120
120
|
|
121
121
|
# Validate SSL files exist if specified
|
@@ -123,7 +123,7 @@ class HypercornEngine(ServerEngine):
|
|
123
123
|
for ssl_file in ssl_files:
|
124
124
|
if ssl_file in config and config[ssl_file]:
|
125
125
|
if not Path(config[ssl_file]).exists():
|
126
|
-
|
126
|
+
get_global_logger().error(f"SSL file not found: {config[ssl_file]}")
|
127
127
|
return False
|
128
128
|
|
129
129
|
return True
|
@@ -142,17 +142,17 @@ class HypercornEngine(ServerEngine):
|
|
142
142
|
}
|
143
143
|
|
144
144
|
# Add SSL configuration if provided
|
145
|
-
|
146
|
-
|
145
|
+
get_global_logger().info(f"🔍 DEBUG: Input config keys: {list(config.keys())}")
|
146
|
+
get_global_logger().info(
|
147
147
|
f"🔍 DEBUG: Input config certfile: {config.get('certfile', 'NOT_FOUND')}"
|
148
148
|
)
|
149
|
-
|
149
|
+
get_global_logger().info(
|
150
150
|
f"🔍 DEBUG: Input config keyfile: {config.get('keyfile', 'NOT_FOUND')}"
|
151
151
|
)
|
152
|
-
|
152
|
+
get_global_logger().info(
|
153
153
|
f"🔍 DEBUG: Input config ca_certs: {config.get('ca_certs', 'NOT_FOUND')}"
|
154
154
|
)
|
155
|
-
|
155
|
+
get_global_logger().info(
|
156
156
|
f"🔍 DEBUG: Input config verify_mode: {config.get('verify_mode', 'NOT_FOUND')}"
|
157
157
|
)
|
158
158
|
|
@@ -184,15 +184,15 @@ class HypercornEngine(ServerEngine):
|
|
184
184
|
if "workers" in config and config["workers"]:
|
185
185
|
hypercorn_config["workers"] = config["workers"]
|
186
186
|
|
187
|
-
|
188
|
-
|
189
|
-
|
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
190
|
f"Security SSL config: {config.get('security', {}).get('ssl', 'NOT_FOUND')}"
|
191
191
|
)
|
192
|
-
|
192
|
+
get_global_logger().info(
|
193
193
|
f"🔍 DEBUG: Hypercorn verify_mode: {hypercorn_config.get('verify_mode', 'NOT_SET')}"
|
194
194
|
)
|
195
|
-
|
195
|
+
get_global_logger().info(
|
196
196
|
f"🔍 DEBUG: Hypercorn ca_certs: {hypercorn_config.get('ca_certs', 'NOT_SET')}"
|
197
197
|
)
|
198
198
|
|
@@ -205,10 +205,10 @@ class HypercornEngine(ServerEngine):
|
|
205
205
|
asyncio.run(hypercorn.asyncio.serve(app, config_obj))
|
206
206
|
|
207
207
|
except ImportError:
|
208
|
-
|
208
|
+
get_global_logger().error("hypercorn not installed. Install with: pip install hypercorn")
|
209
209
|
raise
|
210
210
|
except Exception as e:
|
211
|
-
|
211
|
+
get_global_logger().error(f"Failed to start hypercorn server: {e}")
|
212
212
|
raise
|
213
213
|
|
214
214
|
|
@@ -230,7 +230,7 @@ class ServerEngineFactory:
|
|
230
230
|
engine: Server engine instance to register
|
231
231
|
"""
|
232
232
|
cls._engines[engine.get_name()] = engine
|
233
|
-
|
233
|
+
get_global_logger().info(f"Registered server engine: {engine.get_name()}")
|
234
234
|
|
235
235
|
@classmethod
|
236
236
|
def get_engine(cls, name: str) -> Optional[ServerEngine]:
|
@@ -279,9 +279,9 @@ class ServerEngineFactory:
|
|
279
279
|
import hypercorn
|
280
280
|
|
281
281
|
cls.register_engine(HypercornEngine())
|
282
|
-
|
282
|
+
get_global_logger().info("Hypercorn engine registered (full mTLS support available)")
|
283
283
|
except ImportError:
|
284
|
-
|
284
|
+
get_global_logger().error("Hypercorn not available - this is required for the framework")
|
285
285
|
raise
|
286
286
|
|
287
287
|
|
@@ -12,7 +12,7 @@ import signal
|
|
12
12
|
import asyncio
|
13
13
|
import threading
|
14
14
|
from typing import Optional, Callable, Any
|
15
|
-
from mcp_proxy_adapter.core.logging import
|
15
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
16
16
|
|
17
17
|
|
18
18
|
class SignalHandler:
|
@@ -35,7 +35,7 @@ class SignalHandler:
|
|
35
35
|
callback: Function to call during shutdown
|
36
36
|
"""
|
37
37
|
self._shutdown_callback = callback
|
38
|
-
|
38
|
+
get_global_logger().info("Shutdown callback set for signal handler")
|
39
39
|
|
40
40
|
def _setup_signal_handlers(self):
|
41
41
|
"""Setup signal handlers for graceful shutdown."""
|
@@ -54,7 +54,7 @@ class SignalHandler:
|
|
54
54
|
signal.SIGHUP, self._handle_shutdown_signal
|
55
55
|
)
|
56
56
|
|
57
|
-
|
57
|
+
get_global_logger().info("Signal handlers installed for SIGTERM, SIGINT, SIGHUP")
|
58
58
|
|
59
59
|
def _handle_shutdown_signal(self, signum: int, frame: Any):
|
60
60
|
"""
|
@@ -65,7 +65,7 @@ class SignalHandler:
|
|
65
65
|
frame: Current stack frame
|
66
66
|
"""
|
67
67
|
signal_name = signal.Signals(signum).name
|
68
|
-
|
68
|
+
get_global_logger().info(f"🛑 Received {signal_name} signal, initiating graceful shutdown...")
|
69
69
|
|
70
70
|
# Set shutdown event
|
71
71
|
self._shutdown_event.set()
|
@@ -73,23 +73,23 @@ class SignalHandler:
|
|
73
73
|
# Call shutdown callback if set
|
74
74
|
if self._shutdown_callback:
|
75
75
|
try:
|
76
|
-
|
76
|
+
get_global_logger().info("🔄 Executing shutdown callback...")
|
77
77
|
self._shutdown_callback()
|
78
|
-
|
78
|
+
get_global_logger().info("✅ Shutdown callback completed successfully")
|
79
79
|
except Exception as e:
|
80
|
-
|
80
|
+
get_global_logger().error(f"❌ Shutdown callback failed: {e}")
|
81
81
|
|
82
82
|
# Force exit immediately to avoid server hang
|
83
|
-
|
83
|
+
get_global_logger().info("🔄 Force exiting to avoid server hang...")
|
84
84
|
import os
|
85
85
|
# Use os._exit for immediate termination
|
86
|
-
|
86
|
+
get_global_logger().warning("⚠️ Using os._exit for immediate termination...")
|
87
87
|
os._exit(0)
|
88
88
|
|
89
89
|
def _force_exit(self):
|
90
90
|
"""Force exit if graceful shutdown takes too long."""
|
91
91
|
if self._shutdown_event.is_set():
|
92
|
-
|
92
|
+
get_global_logger().warning("⚠️ Forcing exit after timeout")
|
93
93
|
import os
|
94
94
|
os._exit(1)
|
95
95
|
|
@@ -119,7 +119,7 @@ class SignalHandler:
|
|
119
119
|
for sig, handler in self._original_handlers.items():
|
120
120
|
if handler is not None:
|
121
121
|
signal.signal(sig, handler)
|
122
|
-
|
122
|
+
get_global_logger().info("Original signal handlers restored")
|
123
123
|
|
124
124
|
|
125
125
|
# Global signal handler instance
|
@@ -144,7 +144,7 @@ def setup_signal_handling(shutdown_callback: Optional[Callable] = None):
|
|
144
144
|
handler = get_signal_handler()
|
145
145
|
if shutdown_callback:
|
146
146
|
handler.set_shutdown_callback(shutdown_callback)
|
147
|
-
|
147
|
+
get_global_logger().info("Signal handling setup completed")
|
148
148
|
|
149
149
|
|
150
150
|
def wait_for_shutdown_signal(timeout: Optional[float] = None) -> bool:
|
@@ -90,7 +90,7 @@ class SSLUtils:
|
|
90
90
|
f"Certificate is revoked according to CRL: {cert_file}"
|
91
91
|
)
|
92
92
|
except Exception as e:
|
93
|
-
|
93
|
+
get_global_logger().error(f"CRL check failed: {e}")
|
94
94
|
# For security, fail if CRL check fails
|
95
95
|
raise ValueError(f"CRL validation failed: {e}")
|
96
96
|
|
@@ -128,7 +128,7 @@ class SSLUtils:
|
|
128
128
|
# Setup TLS versions
|
129
129
|
SSLUtils.setup_tls_versions(context, min_tls_version, max_tls_version)
|
130
130
|
|
131
|
-
|
131
|
+
get_global_logger().info(f"SSL context created successfully with cert: {cert_file}")
|
132
132
|
return context
|
133
133
|
|
134
134
|
@staticmethod
|
@@ -156,18 +156,18 @@ class SSLUtils:
|
|
156
156
|
try:
|
157
157
|
crl_manager = CRLManager(crl_config)
|
158
158
|
if crl_manager.is_certificate_revoked(cert_file):
|
159
|
-
|
159
|
+
get_global_logger().warning(
|
160
160
|
f"Certificate is revoked according to CRL: {cert_file}"
|
161
161
|
)
|
162
162
|
return False
|
163
163
|
except Exception as e:
|
164
|
-
|
164
|
+
get_global_logger().error(f"CRL check failed: {e}")
|
165
165
|
# For security, consider certificate invalid if CRL check fails
|
166
166
|
return False
|
167
167
|
|
168
168
|
return True
|
169
169
|
except Exception as e:
|
170
|
-
|
170
|
+
get_global_logger().error(f"Certificate validation failed: {e}")
|
171
171
|
return False
|
172
172
|
|
173
173
|
@staticmethod
|
@@ -188,14 +188,14 @@ class SSLUtils:
|
|
188
188
|
if cipher_name in SSLUtils.CIPHER_SUITES:
|
189
189
|
actual_ciphers.append(SSLUtils.CIPHER_SUITES[cipher_name])
|
190
190
|
else:
|
191
|
-
|
191
|
+
get_global_logger().warning(f"Unknown cipher suite: {cipher_name}")
|
192
192
|
|
193
193
|
if actual_ciphers:
|
194
194
|
try:
|
195
195
|
context.set_ciphers(":".join(actual_ciphers))
|
196
|
-
|
196
|
+
get_global_logger().info(f"Cipher suites configured: {actual_ciphers}")
|
197
197
|
except ssl.SSLError as e:
|
198
|
-
|
198
|
+
get_global_logger().error(f"Failed to set cipher suites: {e}")
|
199
199
|
|
200
200
|
@staticmethod
|
201
201
|
def setup_tls_versions(
|
@@ -216,13 +216,13 @@ class SSLUtils:
|
|
216
216
|
if min_tls and max_tls:
|
217
217
|
context.minimum_version = min_tls
|
218
218
|
context.maximum_version = max_tls
|
219
|
-
|
219
|
+
get_global_logger().info(f"TLS versions configured: {min_version} - {max_version}")
|
220
220
|
else:
|
221
|
-
|
221
|
+
get_global_logger().warning(
|
222
222
|
f"Invalid TLS version range: {min_version} - {max_version}"
|
223
223
|
)
|
224
224
|
except Exception as e:
|
225
|
-
|
225
|
+
get_global_logger().error(f"Failed to set TLS versions: {e}")
|
226
226
|
|
227
227
|
@staticmethod
|
228
228
|
def check_tls_version(min_version: str, max_version: str) -> bool:
|
@@ -275,5 +275,5 @@ class SSLUtils:
|
|
275
275
|
if ssl_config.get("verify_client", False):
|
276
276
|
hypercorn_ssl["verify_mode"] = "CERT_REQUIRED"
|
277
277
|
|
278
|
-
|
278
|
+
get_global_logger().info(f"Generated hypercorn SSL config: {hypercorn_ssl}")
|
279
279
|
return hypercorn_ssl
|