mcp-proxy-adapter 6.9.17__py3-none-any.whl → 6.9.19__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 +52 -52
- 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 +68 -67
- 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_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/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/main.py +3 -0
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-6.9.17.dist-info → mcp_proxy_adapter-6.9.19.dist-info}/METADATA +1 -1
- mcp_proxy_adapter-6.9.19.dist-info/RECORD +149 -0
- mcp_proxy_adapter-6.9.17.dist-info/RECORD +0 -144
- {mcp_proxy_adapter-6.9.17.dist-info → mcp_proxy_adapter-6.9.19.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.9.17.dist-info → mcp_proxy_adapter-6.9.19.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.9.17.dist-info → mcp_proxy_adapter-6.9.19.dist-info}/top_level.txt +0 -0
@@ -89,7 +89,7 @@ class CertificateUtils:
|
|
89
89
|
OSError: If files cannot be created
|
90
90
|
"""
|
91
91
|
if not SECURITY_FRAMEWORK_AVAILABLE:
|
92
|
-
|
92
|
+
get_global_logger().warning(
|
93
93
|
"mcp_security_framework not available, using fallback method"
|
94
94
|
)
|
95
95
|
return CertificateUtils._create_ca_certificate_fallback(
|
@@ -141,7 +141,7 @@ class CertificateUtils:
|
|
141
141
|
}
|
142
142
|
|
143
143
|
except Exception as e:
|
144
|
-
|
144
|
+
get_global_logger().error(f"Failed to create CA certificate: {e}")
|
145
145
|
raise
|
146
146
|
|
147
147
|
@staticmethod
|
@@ -231,7 +231,7 @@ class CertificateUtils:
|
|
231
231
|
return {"cert_path": str(cert_path), "key_path": str(key_path)}
|
232
232
|
|
233
233
|
except Exception as e:
|
234
|
-
|
234
|
+
get_global_logger().error(f"Failed to create CA certificate (fallback): {e}")
|
235
235
|
raise
|
236
236
|
|
237
237
|
@staticmethod
|
@@ -379,7 +379,7 @@ class CertificateUtils:
|
|
379
379
|
)
|
380
380
|
)
|
381
381
|
|
382
|
-
|
382
|
+
get_global_logger().info(f"Server certificate created: {cert_path}")
|
383
383
|
|
384
384
|
return {
|
385
385
|
"cert_path": cert_path,
|
@@ -390,7 +390,7 @@ class CertificateUtils:
|
|
390
390
|
}
|
391
391
|
|
392
392
|
except Exception as e:
|
393
|
-
|
393
|
+
get_global_logger().error(f"Failed to create server certificate: {e}")
|
394
394
|
raise
|
395
395
|
|
396
396
|
@staticmethod
|
@@ -423,7 +423,7 @@ class CertificateUtils:
|
|
423
423
|
OSError: If files cannot be created
|
424
424
|
"""
|
425
425
|
if not SECURITY_FRAMEWORK_AVAILABLE:
|
426
|
-
|
426
|
+
get_global_logger().warning(
|
427
427
|
"mcp_security_framework not available, using fallback method"
|
428
428
|
)
|
429
429
|
return CertificateUtils._create_client_certificate_fallback(
|
@@ -489,7 +489,7 @@ class CertificateUtils:
|
|
489
489
|
}
|
490
490
|
|
491
491
|
except Exception as e:
|
492
|
-
|
492
|
+
get_global_logger().error(f"Failed to create client certificate: {e}")
|
493
493
|
raise
|
494
494
|
|
495
495
|
@staticmethod
|
@@ -612,7 +612,7 @@ class CertificateUtils:
|
|
612
612
|
return {"cert_path": str(cert_path), "key_path": str(key_path)}
|
613
613
|
|
614
614
|
except Exception as e:
|
615
|
-
|
615
|
+
get_global_logger().error(f"Failed to create client certificate (fallback): {e}")
|
616
616
|
raise
|
617
617
|
|
618
618
|
@staticmethod
|
@@ -627,7 +627,7 @@ class CertificateUtils:
|
|
627
627
|
List of roles found in certificate
|
628
628
|
"""
|
629
629
|
if not SECURITY_FRAMEWORK_AVAILABLE:
|
630
|
-
|
630
|
+
get_global_logger().warning(
|
631
631
|
"mcp_security_framework not available, using fallback method"
|
632
632
|
)
|
633
633
|
return RoleUtils.extract_roles_from_certificate(cert_path)
|
@@ -635,7 +635,7 @@ class CertificateUtils:
|
|
635
635
|
try:
|
636
636
|
return extract_roles_from_certificate(cert_path)
|
637
637
|
except Exception as e:
|
638
|
-
|
638
|
+
get_global_logger().error(f"Failed to extract roles from certificate: {e}")
|
639
639
|
return []
|
640
640
|
|
641
641
|
@staticmethod
|
@@ -650,7 +650,7 @@ class CertificateUtils:
|
|
650
650
|
List of roles found in certificate
|
651
651
|
"""
|
652
652
|
if not SECURITY_FRAMEWORK_AVAILABLE:
|
653
|
-
|
653
|
+
get_global_logger().warning(
|
654
654
|
"mcp_security_framework not available, using fallback method"
|
655
655
|
)
|
656
656
|
return RoleUtils.extract_roles_from_certificate_object(cert)
|
@@ -660,7 +660,7 @@ class CertificateUtils:
|
|
660
660
|
cert_pem = cert.public_bytes(serialization.Encoding.PEM)
|
661
661
|
return extract_roles_from_certificate(cert_pem)
|
662
662
|
except Exception as e:
|
663
|
-
|
663
|
+
get_global_logger().error(f"Failed to extract roles from certificate object: {e}")
|
664
664
|
return []
|
665
665
|
|
666
666
|
@staticmethod
|
@@ -675,7 +675,7 @@ class CertificateUtils:
|
|
675
675
|
List of permissions found in certificate
|
676
676
|
"""
|
677
677
|
if not SECURITY_FRAMEWORK_AVAILABLE:
|
678
|
-
|
678
|
+
get_global_logger().warning(
|
679
679
|
"mcp_security_framework not available, permissions extraction not supported"
|
680
680
|
)
|
681
681
|
return []
|
@@ -683,7 +683,7 @@ class CertificateUtils:
|
|
683
683
|
try:
|
684
684
|
return extract_permissions_from_certificate(cert_path)
|
685
685
|
except Exception as e:
|
686
|
-
|
686
|
+
get_global_logger().error(f"Failed to extract permissions from certificate: {e}")
|
687
687
|
return []
|
688
688
|
|
689
689
|
@staticmethod
|
@@ -699,7 +699,7 @@ class CertificateUtils:
|
|
699
699
|
True if chain is valid, False otherwise
|
700
700
|
"""
|
701
701
|
if not SECURITY_FRAMEWORK_AVAILABLE:
|
702
|
-
|
702
|
+
get_global_logger().warning(
|
703
703
|
"mcp_security_framework not available, using fallback validation"
|
704
704
|
)
|
705
705
|
return CertificateUtils._validate_certificate_chain_fallback(
|
@@ -709,7 +709,7 @@ class CertificateUtils:
|
|
709
709
|
try:
|
710
710
|
return validate_certificate_chain(cert_path, ca_cert_path)
|
711
711
|
except Exception as e:
|
712
|
-
|
712
|
+
get_global_logger().error(f"Failed to validate certificate chain: {e}")
|
713
713
|
return False
|
714
714
|
|
715
715
|
@staticmethod
|
@@ -727,7 +727,7 @@ class CertificateUtils:
|
|
727
727
|
return cert.issuer == ca_cert.subject
|
728
728
|
|
729
729
|
except Exception as e:
|
730
|
-
|
730
|
+
get_global_logger().error(f"Failed to validate certificate chain (fallback): {e}")
|
731
731
|
return False
|
732
732
|
|
733
733
|
@staticmethod
|
@@ -742,7 +742,7 @@ class CertificateUtils:
|
|
742
742
|
Certificate expiry date or None if not available
|
743
743
|
"""
|
744
744
|
if not SECURITY_FRAMEWORK_AVAILABLE:
|
745
|
-
|
745
|
+
get_global_logger().warning(
|
746
746
|
"mcp_security_framework not available, using fallback method"
|
747
747
|
)
|
748
748
|
return CertificateUtils._get_certificate_expiry_fallback(cert_path)
|
@@ -753,7 +753,7 @@ class CertificateUtils:
|
|
753
753
|
return expiry_info["expiry_date"]
|
754
754
|
return None
|
755
755
|
except Exception as e:
|
756
|
-
|
756
|
+
get_global_logger().error(f"Failed to get certificate expiry: {e}")
|
757
757
|
return None
|
758
758
|
|
759
759
|
@staticmethod
|
@@ -766,7 +766,7 @@ class CertificateUtils:
|
|
766
766
|
return cert.not_valid_after
|
767
767
|
|
768
768
|
except Exception as e:
|
769
|
-
|
769
|
+
get_global_logger().error(f"Failed to get certificate expiry (fallback): {e}")
|
770
770
|
return None
|
771
771
|
|
772
772
|
@staticmethod
|
@@ -785,7 +785,7 @@ class CertificateUtils:
|
|
785
785
|
result = validator.validate_certificate(cert_path)
|
786
786
|
return result.is_valid
|
787
787
|
except Exception as e:
|
788
|
-
|
788
|
+
get_global_logger().error(f"Failed to validate certificate: {e}")
|
789
789
|
return False
|
790
790
|
|
791
791
|
@staticmethod
|
@@ -836,7 +836,7 @@ class CertificateUtils:
|
|
836
836
|
}
|
837
837
|
|
838
838
|
except Exception as e:
|
839
|
-
|
839
|
+
get_global_logger().error(f"Failed to get certificate info: {e}")
|
840
840
|
return {}
|
841
841
|
|
842
842
|
@staticmethod
|
@@ -912,7 +912,7 @@ class CertificateUtils:
|
|
912
912
|
}
|
913
913
|
|
914
914
|
except Exception as e:
|
915
|
-
|
915
|
+
get_global_logger().error(f"Failed to generate private key: {e}")
|
916
916
|
return {"success": False, "error": f"Key generation failed: {str(e)}"}
|
917
917
|
|
918
918
|
@staticmethod
|
@@ -958,7 +958,7 @@ class CertificateUtils:
|
|
958
958
|
return {"success": False, "error": f"Invalid private key: {str(e)}"}
|
959
959
|
|
960
960
|
except Exception as e:
|
961
|
-
|
961
|
+
get_global_logger().error(f"Failed to validate private key: {e}")
|
962
962
|
return {"success": False, "error": f"Key validation failed: {str(e)}"}
|
963
963
|
|
964
964
|
@staticmethod
|
@@ -1003,7 +1003,7 @@ class CertificateUtils:
|
|
1003
1003
|
return {"success": True, "backup_path": backup_path}
|
1004
1004
|
|
1005
1005
|
except Exception as e:
|
1006
|
-
|
1006
|
+
get_global_logger().error(f"Failed to create encrypted backup: {e}")
|
1007
1007
|
return {"success": False, "error": f"Encryption failed: {str(e)}"}
|
1008
1008
|
|
1009
1009
|
@staticmethod
|
@@ -1047,7 +1047,7 @@ class CertificateUtils:
|
|
1047
1047
|
return {"success": True, "key_path": key_path}
|
1048
1048
|
|
1049
1049
|
except Exception as e:
|
1050
|
-
|
1050
|
+
get_global_logger().error(f"Failed to restore encrypted backup: {e}")
|
1051
1051
|
return {"success": False, "error": f"Decryption failed: {str(e)}"}
|
1052
1052
|
|
1053
1053
|
@staticmethod
|
@@ -1084,5 +1084,5 @@ class CertificateUtils:
|
|
1084
1084
|
return context
|
1085
1085
|
|
1086
1086
|
except Exception as e:
|
1087
|
-
|
1087
|
+
get_global_logger().error(f"Failed to create SSL context: {e}")
|
1088
1088
|
raise
|
@@ -46,7 +46,7 @@ class ClientManager:
|
|
46
46
|
self.retry_attempts = config.get("retry_attempts", 3)
|
47
47
|
self.retry_delay = config.get("retry_delay", 1)
|
48
48
|
|
49
|
-
self.
|
49
|
+
self.get_global_logger().info("Client manager initialized")
|
50
50
|
|
51
51
|
async def create_client(self, client_id: str, config_file: str) -> UniversalClient:
|
52
52
|
"""
|
@@ -61,7 +61,7 @@ class ClientManager:
|
|
61
61
|
"""
|
62
62
|
try:
|
63
63
|
if client_id in self.clients:
|
64
|
-
self.
|
64
|
+
self.get_global_logger().warning(
|
65
65
|
f"Client {client_id} already exists, reusing existing connection"
|
66
66
|
)
|
67
67
|
return self.clients[client_id]
|
@@ -69,11 +69,11 @@ class ClientManager:
|
|
69
69
|
client = create_client_from_config(config_file)
|
70
70
|
self.clients[client_id] = client
|
71
71
|
|
72
|
-
self.
|
72
|
+
self.get_global_logger().info(f"Client {client_id} created successfully")
|
73
73
|
return client
|
74
74
|
|
75
75
|
except Exception as e:
|
76
|
-
self.
|
76
|
+
self.get_global_logger().error(f"Failed to create client {client_id}: {e}")
|
77
77
|
raise
|
78
78
|
|
79
79
|
async def get_client(self, client_id: str) -> Optional[UniversalClient]:
|
@@ -102,7 +102,7 @@ class ClientManager:
|
|
102
102
|
client = self.clients[client_id]
|
103
103
|
await client.disconnect()
|
104
104
|
del self.clients[client_id]
|
105
|
-
self.
|
105
|
+
self.get_global_logger().info(f"Client {client_id} removed")
|
106
106
|
return True
|
107
107
|
return False
|
108
108
|
|
@@ -118,13 +118,13 @@ class ClientManager:
|
|
118
118
|
"""
|
119
119
|
client = await self.get_client(client_id)
|
120
120
|
if not client:
|
121
|
-
self.
|
121
|
+
self.get_global_logger().error(f"Client {client_id} not found")
|
122
122
|
return False
|
123
123
|
|
124
124
|
try:
|
125
125
|
return await client.test_connection()
|
126
126
|
except Exception as e:
|
127
|
-
self.
|
127
|
+
self.get_global_logger().error(f"Connection test failed for client {client_id}: {e}")
|
128
128
|
return False
|
129
129
|
|
130
130
|
async def register_proxy(
|
@@ -146,10 +146,10 @@ class ClientManager:
|
|
146
146
|
|
147
147
|
try:
|
148
148
|
result = await client.register_proxy(proxy_config)
|
149
|
-
self.
|
149
|
+
self.get_global_logger().info(f"Proxy registration completed for client {client_id}")
|
150
150
|
return result
|
151
151
|
except Exception as e:
|
152
|
-
self.
|
152
|
+
self.get_global_logger().error(f"Proxy registration failed for client {client_id}: {e}")
|
153
153
|
return {"error": str(e)}
|
154
154
|
|
155
155
|
async def execute_command(
|
@@ -172,10 +172,10 @@ class ClientManager:
|
|
172
172
|
|
173
173
|
try:
|
174
174
|
result = await client.execute_command(command, params or {})
|
175
|
-
self.
|
175
|
+
self.get_global_logger().info(f"Command {command} executed for client {client_id}")
|
176
176
|
return result
|
177
177
|
except Exception as e:
|
178
|
-
self.
|
178
|
+
self.get_global_logger().error(f"Command execution failed for client {client_id}: {e}")
|
179
179
|
return {"error": str(e)}
|
180
180
|
|
181
181
|
async def get_client_status(self, client_id: str) -> Dict[str, Any]:
|
@@ -210,7 +210,7 @@ class ClientManager:
|
|
210
210
|
|
211
211
|
return status
|
212
212
|
except Exception as e:
|
213
|
-
self.
|
213
|
+
self.get_global_logger().error(f"Failed to get status for client {client_id}: {e}")
|
214
214
|
return {"error": str(e)}
|
215
215
|
|
216
216
|
async def list_clients(self) -> List[str]:
|
@@ -226,7 +226,7 @@ class ClientManager:
|
|
226
226
|
"""Clean up all client connections."""
|
227
227
|
for client_id in list(self.clients.keys()):
|
228
228
|
await self.remove_client(client_id)
|
229
|
-
self.
|
229
|
+
self.get_global_logger().info("All client connections cleaned up")
|
230
230
|
|
231
231
|
async def __aenter__(self):
|
232
232
|
"""Async context manager entry."""
|
@@ -39,7 +39,7 @@ except ImportError:
|
|
39
39
|
AuthResult = None
|
40
40
|
ValidationResult = None
|
41
41
|
|
42
|
-
from mcp_proxy_adapter.core.logging import
|
42
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
43
43
|
|
44
44
|
|
45
45
|
class ClientSecurityManager:
|
@@ -69,7 +69,7 @@ class ClientSecurityManager:
|
|
69
69
|
ssl_config = self._create_ssl_config()
|
70
70
|
self.ssl_manager = SSLManager(ssl_config)
|
71
71
|
|
72
|
-
|
72
|
+
get_global_logger().info("Client security manager initialized")
|
73
73
|
|
74
74
|
def _create_ssl_config(self) -> SSLConfig:
|
75
75
|
"""Create SSL configuration for client connections."""
|
@@ -114,13 +114,13 @@ class ClientSecurityManager:
|
|
114
114
|
# Create client SSL context
|
115
115
|
context = self.ssl_manager.create_client_context()
|
116
116
|
|
117
|
-
|
117
|
+
get_global_logger().info(
|
118
118
|
f"Client SSL context created for {server_hostname or 'unknown server'}"
|
119
119
|
)
|
120
120
|
return context
|
121
121
|
|
122
122
|
except Exception as e:
|
123
|
-
|
123
|
+
get_global_logger().error(f"Failed to create client SSL context: {e}")
|
124
124
|
return None
|
125
125
|
|
126
126
|
def generate_client_api_key(self, user_id: str, prefix: str = "mcp_proxy") -> str:
|
@@ -136,10 +136,10 @@ class ClientSecurityManager:
|
|
136
136
|
"""
|
137
137
|
try:
|
138
138
|
api_key = generate_api_key(prefix=prefix)
|
139
|
-
|
139
|
+
get_global_logger().info(f"Generated API key for user: {user_id}")
|
140
140
|
return api_key
|
141
141
|
except Exception as e:
|
142
|
-
|
142
|
+
get_global_logger().error(f"Failed to generate API key: {e}")
|
143
143
|
raise
|
144
144
|
|
145
145
|
def create_client_jwt_token(
|
@@ -173,11 +173,11 @@ class ClientSecurityManager:
|
|
173
173
|
expiry_hours=expiry_hours,
|
174
174
|
)
|
175
175
|
|
176
|
-
|
176
|
+
get_global_logger().info(f"Created JWT token for user: {user_id}")
|
177
177
|
return token
|
178
178
|
|
179
179
|
except Exception as e:
|
180
|
-
|
180
|
+
get_global_logger().error(f"Failed to create JWT token: {e}")
|
181
181
|
raise
|
182
182
|
|
183
183
|
def validate_server_certificate(
|
@@ -196,26 +196,26 @@ class ClientSecurityManager:
|
|
196
196
|
try:
|
197
197
|
# Validate certificate format
|
198
198
|
if not validate_certificate_format(cert_path):
|
199
|
-
|
199
|
+
get_global_logger().error(f"Invalid certificate format: {cert_path}")
|
200
200
|
return False
|
201
201
|
|
202
202
|
# Validate certificate chain if CA provided
|
203
203
|
if ca_cert_path:
|
204
204
|
if not validate_certificate_chain(cert_path, ca_cert_path):
|
205
|
-
|
205
|
+
get_global_logger().error(f"Invalid certificate chain: {cert_path}")
|
206
206
|
return False
|
207
207
|
|
208
208
|
# Parse certificate and check basic properties
|
209
209
|
cert_info = parse_certificate(cert_path)
|
210
210
|
if not cert_info:
|
211
|
-
|
211
|
+
get_global_logger().error(f"Failed to parse certificate: {cert_path}")
|
212
212
|
return False
|
213
213
|
|
214
|
-
|
214
|
+
get_global_logger().info(f"Server certificate validated: {cert_path}")
|
215
215
|
return True
|
216
216
|
|
217
217
|
except Exception as e:
|
218
|
-
|
218
|
+
get_global_logger().error(f"Failed to validate server certificate: {e}")
|
219
219
|
return False
|
220
220
|
|
221
221
|
def extract_server_roles(self, cert_path: str) -> List[str]:
|
@@ -230,10 +230,10 @@ class ClientSecurityManager:
|
|
230
230
|
"""
|
231
231
|
try:
|
232
232
|
roles = extract_roles_from_certificate(cert_path)
|
233
|
-
|
233
|
+
get_global_logger().info(f"Extracted roles from server certificate: {roles}")
|
234
234
|
return roles
|
235
235
|
except Exception as e:
|
236
|
-
|
236
|
+
get_global_logger().error(f"Failed to extract roles from certificate: {e}")
|
237
237
|
return []
|
238
238
|
|
239
239
|
def get_client_auth_headers(
|
@@ -271,11 +271,11 @@ class ClientSecurityManager:
|
|
271
271
|
headers["X-Proxy-Type"] = "mcp_proxy_adapter"
|
272
272
|
headers["X-Client-Type"] = "proxy_client"
|
273
273
|
|
274
|
-
|
274
|
+
get_global_logger().debug(f"Created auth headers for method: {auth_method}")
|
275
275
|
return headers
|
276
276
|
|
277
277
|
except Exception as e:
|
278
|
-
|
278
|
+
get_global_logger().error(f"Failed to create auth headers: {e}")
|
279
279
|
return {}
|
280
280
|
|
281
281
|
def prepare_client_connection(
|
@@ -307,13 +307,13 @@ class ClientSecurityManager:
|
|
307
307
|
token=server_config.get("token"),
|
308
308
|
)
|
309
309
|
|
310
|
-
|
310
|
+
get_global_logger().info(
|
311
311
|
f"Prepared client connection for {server_config.get('hostname', 'unknown')}"
|
312
312
|
)
|
313
313
|
return ssl_context, auth_headers
|
314
314
|
|
315
315
|
except Exception as e:
|
316
|
-
|
316
|
+
get_global_logger().error(f"Failed to prepare client connection: {e}")
|
317
317
|
return None, {}
|
318
318
|
|
319
319
|
def validate_server_response(self, response_headers: Dict[str, str]) -> bool:
|
@@ -331,23 +331,23 @@ class ClientSecurityManager:
|
|
331
331
|
required_headers = ["Content-Type"]
|
332
332
|
for header in required_headers:
|
333
333
|
if header not in response_headers:
|
334
|
-
|
334
|
+
get_global_logger().warning(f"Missing required header: {header}")
|
335
335
|
|
336
336
|
# Check for security headers
|
337
337
|
security_headers = ["X-Frame-Options", "X-Content-Type-Options"]
|
338
338
|
for header in security_headers:
|
339
339
|
if header in response_headers:
|
340
|
-
|
340
|
+
get_global_logger().debug(f"Found security header: {header}")
|
341
341
|
|
342
342
|
# Validate content type
|
343
343
|
content_type = response_headers.get("Content-Type", "")
|
344
344
|
if "application/json" not in content_type:
|
345
|
-
|
345
|
+
get_global_logger().warning(f"Unexpected content type: {content_type}")
|
346
346
|
|
347
347
|
return True
|
348
348
|
|
349
349
|
except Exception as e:
|
350
|
-
|
350
|
+
get_global_logger().error(f"Failed to validate server response: {e}")
|
351
351
|
return False
|
352
352
|
|
353
353
|
def get_client_certificate_info(self) -> Optional[Dict[str, Any]]:
|
@@ -373,7 +373,7 @@ class ClientSecurityManager:
|
|
373
373
|
return None
|
374
374
|
|
375
375
|
except Exception as e:
|
376
|
-
|
376
|
+
get_global_logger().error(f"Failed to get client certificate info: {e}")
|
377
377
|
return None
|
378
378
|
|
379
379
|
def is_ssl_enabled(self) -> bool:
|
@@ -401,8 +401,8 @@ def create_client_security_manager(
|
|
401
401
|
try:
|
402
402
|
return ClientSecurityManager(config)
|
403
403
|
except ImportError:
|
404
|
-
|
404
|
+
get_global_logger().warning("mcp_security_framework not available, client security disabled")
|
405
405
|
return None
|
406
406
|
except Exception as e:
|
407
|
-
|
407
|
+
get_global_logger().error(f"Failed to create client security manager: {e}")
|
408
408
|
return None
|
@@ -10,7 +10,7 @@ import logging
|
|
10
10
|
from typing import Dict, Any, Optional
|
11
11
|
from pathlib import Path
|
12
12
|
|
13
|
-
from mcp_proxy_adapter.core.logging import
|
13
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
14
14
|
|
15
15
|
|
16
16
|
class ConfigConverter:
|
@@ -174,13 +174,13 @@ class ConfigConverter:
|
|
174
174
|
}
|
175
175
|
)
|
176
176
|
|
177
|
-
|
177
|
+
get_global_logger().info(
|
178
178
|
"Configuration converted to security framework format successfully"
|
179
179
|
)
|
180
180
|
return security_config
|
181
181
|
|
182
182
|
except Exception as e:
|
183
|
-
|
183
|
+
get_global_logger().error(
|
184
184
|
f"Failed to convert configuration to security framework format: {e}"
|
185
185
|
)
|
186
186
|
return ConfigConverter._get_default_security_config()
|
@@ -255,13 +255,13 @@ class ConfigConverter:
|
|
255
255
|
"by_user": rate_limit_config.get("by_user", True),
|
256
256
|
}
|
257
257
|
|
258
|
-
|
258
|
+
get_global_logger().info(
|
259
259
|
"Configuration converted from security framework format successfully"
|
260
260
|
)
|
261
261
|
return mcp_config
|
262
262
|
|
263
263
|
except Exception as e:
|
264
|
-
|
264
|
+
get_global_logger().error(
|
265
265
|
f"Failed to convert configuration from security framework format: {e}"
|
266
266
|
)
|
267
267
|
return ConfigConverter._get_default_mcp_config()
|
@@ -299,11 +299,11 @@ class ConfigConverter:
|
|
299
299
|
with open(output_path, "w", encoding="utf-8") as f:
|
300
300
|
json.dump(legacy_config, f, indent=2, ensure_ascii=False)
|
301
301
|
|
302
|
-
|
302
|
+
get_global_logger().info(f"Configuration migrated successfully to {output_path}")
|
303
303
|
return True
|
304
304
|
|
305
305
|
except Exception as e:
|
306
|
-
|
306
|
+
get_global_logger().error(f"Failed to migrate configuration: {e}")
|
307
307
|
return False
|
308
308
|
|
309
309
|
@staticmethod
|
@@ -320,7 +320,7 @@ class ConfigConverter:
|
|
320
320
|
try:
|
321
321
|
# Check if security section exists
|
322
322
|
if "security" not in config:
|
323
|
-
|
323
|
+
get_global_logger().error("Security section not found in configuration")
|
324
324
|
return False
|
325
325
|
|
326
326
|
security_section = config["security"]
|
@@ -329,52 +329,52 @@ class ConfigConverter:
|
|
329
329
|
required_sections = ["auth", "ssl", "permissions", "rate_limit"]
|
330
330
|
for section in required_sections:
|
331
331
|
if section not in security_section:
|
332
|
-
|
332
|
+
get_global_logger().error(
|
333
333
|
f"Required section '{section}' not found in security configuration"
|
334
334
|
)
|
335
335
|
return False
|
336
336
|
|
337
337
|
if not isinstance(security_section[section], dict):
|
338
|
-
|
338
|
+
get_global_logger().error(f"Section '{section}' must be a dictionary")
|
339
339
|
return False
|
340
340
|
|
341
341
|
# Validate auth configuration
|
342
342
|
auth_config = security_section["auth"]
|
343
343
|
if not isinstance(auth_config.get("methods", []), list):
|
344
|
-
|
344
|
+
get_global_logger().error("Auth methods must be a list")
|
345
345
|
return False
|
346
346
|
|
347
347
|
if not isinstance(auth_config.get("api_keys", {}), dict):
|
348
|
-
|
348
|
+
get_global_logger().error("API keys must be a dictionary")
|
349
349
|
return False
|
350
350
|
|
351
351
|
# Validate SSL configuration
|
352
352
|
ssl_config = security_section["ssl"]
|
353
353
|
if not isinstance(ssl_config.get("enabled", False), bool):
|
354
|
-
|
354
|
+
get_global_logger().error("SSL enabled must be a boolean")
|
355
355
|
return False
|
356
356
|
|
357
357
|
# Validate permissions configuration
|
358
358
|
permissions_config = security_section["permissions"]
|
359
359
|
if not isinstance(permissions_config.get("enabled", True), bool):
|
360
|
-
|
360
|
+
get_global_logger().error("Permissions enabled must be a boolean")
|
361
361
|
return False
|
362
362
|
|
363
363
|
# Validate rate limit configuration
|
364
364
|
rate_limit_config = security_section["rate_limit"]
|
365
365
|
if not isinstance(rate_limit_config.get("enabled", True), bool):
|
366
|
-
|
366
|
+
get_global_logger().error("Rate limit enabled must be a boolean")
|
367
367
|
return False
|
368
368
|
|
369
369
|
if not isinstance(rate_limit_config.get("requests_per_minute", 60), int):
|
370
|
-
|
370
|
+
get_global_logger().error("Requests per minute must be an integer")
|
371
371
|
return False
|
372
372
|
|
373
|
-
|
373
|
+
get_global_logger().info("Security configuration validation passed")
|
374
374
|
return True
|
375
375
|
|
376
376
|
except Exception as e:
|
377
|
-
|
377
|
+
get_global_logger().error(f"Configuration validation failed: {e}")
|
378
378
|
return False
|
379
379
|
|
380
380
|
@staticmethod
|
@@ -674,6 +674,11 @@ class ConfigValidator:
|
|
674
674
|
ssl_enabled = self._get_nested_value_safe("ssl.enabled", False)
|
675
675
|
|
676
676
|
if ssl_enabled:
|
677
|
+
# Initialize variables
|
678
|
+
cert_file = None
|
679
|
+
key_file = None
|
680
|
+
ca_cert = None
|
681
|
+
|
677
682
|
if not self._has_nested_key("ssl.cert_file"):
|
678
683
|
self.validation_results.append(ValidationResult(
|
679
684
|
level="error",
|
@@ -695,7 +700,6 @@ class ConfigValidator:
|
|
695
700
|
key_file = self._get_nested_value("ssl.key_file")
|
696
701
|
|
697
702
|
# CA cert is optional for HTTPS but may be required for mTLS with client verification
|
698
|
-
ca_cert = None
|
699
703
|
if self._has_nested_key("ssl.ca_cert"):
|
700
704
|
ca_cert = self._get_nested_value_safe("ssl.ca_cert")
|
701
705
|
|