mcp-proxy-adapter 6.3.4__py3-none-any.whl → 6.3.5__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 (129) 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 +108 -88
  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.5.dist-info}/METADATA +1 -1
  124. mcp_proxy_adapter-6.3.5.dist-info/RECORD +143 -0
  125. mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
  126. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/WHEEL +0 -0
  127. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/entry_points.txt +0 -0
  128. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/licenses/LICENSE +0 -0
  129. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/top_level.txt +0 -0
@@ -16,15 +16,20 @@ from pathlib import Path
16
16
  # Import framework utilities
17
17
  try:
18
18
  from mcp_security_framework.utils.crypto_utils import (
19
- generate_api_key, create_jwt_token, verify_jwt_token
19
+ generate_api_key,
20
+ create_jwt_token,
21
+ verify_jwt_token,
20
22
  )
21
23
  from mcp_security_framework.utils.cert_utils import (
22
- parse_certificate, extract_roles_from_certificate,
23
- validate_certificate_chain, validate_certificate_format
24
+ parse_certificate,
25
+ extract_roles_from_certificate,
26
+ validate_certificate_chain,
27
+ validate_certificate_format,
24
28
  )
25
29
  from mcp_security_framework.core.ssl_manager import SSLManager
26
30
  from mcp_security_framework.schemas.config import SSLConfig, AuthConfig
27
31
  from mcp_security_framework.schemas.models import AuthResult, ValidationResult
32
+
28
33
  SECURITY_FRAMEWORK_AVAILABLE = True
29
34
  except ImportError:
30
35
  SECURITY_FRAMEWORK_AVAILABLE = False
@@ -40,36 +45,36 @@ from mcp_proxy_adapter.core.logging import logger
40
45
  class ClientSecurityManager:
41
46
  """
42
47
  Client-side security manager for MCP Proxy Adapter.
43
-
48
+
44
49
  Provides secure client connections using mcp_security_framework utilities.
45
50
  Handles authentication, certificate management, and SSL/TLS for client connections.
46
51
  """
47
-
52
+
48
53
  def __init__(self, config: Dict[str, Any]):
49
54
  """
50
55
  Initialize client security manager.
51
-
56
+
52
57
  Args:
53
58
  config: Security configuration
54
59
  """
55
60
  if not SECURITY_FRAMEWORK_AVAILABLE:
56
61
  raise ImportError("mcp_security_framework is not available")
57
-
62
+
58
63
  self.config = config
59
64
  self.security_config = config.get("security", {})
60
-
65
+
61
66
  # Initialize SSL manager if needed
62
67
  self.ssl_manager = None
63
68
  if self.security_config.get("ssl", {}).get("enabled", False):
64
69
  ssl_config = self._create_ssl_config()
65
70
  self.ssl_manager = SSLManager(ssl_config)
66
-
71
+
67
72
  logger.info("Client security manager initialized")
68
-
73
+
69
74
  def _create_ssl_config(self) -> SSLConfig:
70
75
  """Create SSL configuration for client connections."""
71
76
  ssl_section = self.security_config.get("ssl", {})
72
-
77
+
73
78
  return SSLConfig(
74
79
  enabled=ssl_section.get("enabled", False),
75
80
  cert_file=ssl_section.get("client_cert_file"),
@@ -78,46 +83,50 @@ class ClientSecurityManager:
78
83
  verify_mode=ssl_section.get("verify_mode", "CERT_REQUIRED"),
79
84
  min_tls_version=ssl_section.get("min_tls_version", "TLSv1.2"),
80
85
  check_hostname=ssl_section.get("check_hostname", True),
81
- check_expiry=ssl_section.get("check_expiry", True)
86
+ check_expiry=ssl_section.get("check_expiry", True),
82
87
  )
83
-
84
- def create_client_ssl_context(self, server_hostname: Optional[str] = None) -> Optional[ssl.SSLContext]:
88
+
89
+ def create_client_ssl_context(
90
+ self, server_hostname: Optional[str] = None
91
+ ) -> Optional[ssl.SSLContext]:
85
92
  """
86
93
  Create SSL context for client connections.
87
-
94
+
88
95
  Args:
89
96
  server_hostname: Server hostname for SNI
90
-
97
+
91
98
  Returns:
92
99
  SSL context or None if SSL not enabled
93
100
  """
94
101
  if not self.ssl_manager:
95
102
  return None
96
-
103
+
97
104
  try:
98
105
  # Create client SSL context
99
106
  context = self.ssl_manager.create_client_context()
100
-
107
+
101
108
  if server_hostname:
102
109
  # Set server hostname for SNI
103
110
  context.check_hostname = True
104
111
  context.verify_mode = ssl.CERT_REQUIRED
105
-
106
- logger.info(f"Client SSL context created for {server_hostname or 'unknown server'}")
112
+
113
+ logger.info(
114
+ f"Client SSL context created for {server_hostname or 'unknown server'}"
115
+ )
107
116
  return context
108
-
117
+
109
118
  except Exception as e:
110
119
  logger.error(f"Failed to create client SSL context: {e}")
111
120
  return None
112
-
121
+
113
122
  def generate_client_api_key(self, user_id: str, prefix: str = "mcp_proxy") -> str:
114
123
  """
115
124
  Generate API key for client authentication.
116
-
125
+
117
126
  Args:
118
127
  user_id: User identifier
119
128
  prefix: Key prefix
120
-
129
+
121
130
  Returns:
122
131
  Generated API key
123
132
  """
@@ -128,52 +137,55 @@ class ClientSecurityManager:
128
137
  except Exception as e:
129
138
  logger.error(f"Failed to generate API key: {e}")
130
139
  raise
131
-
132
- def create_client_jwt_token(self, user_id: str, roles: List[str],
133
- secret: str, algorithm: str = "HS256",
134
- expiry_hours: int = 24) -> str:
140
+
141
+ def create_client_jwt_token(
142
+ self,
143
+ user_id: str,
144
+ roles: List[str],
145
+ secret: str,
146
+ algorithm: str = "HS256",
147
+ expiry_hours: int = 24,
148
+ ) -> str:
135
149
  """
136
150
  Create JWT token for client authentication.
137
-
151
+
138
152
  Args:
139
153
  user_id: User identifier
140
154
  roles: User roles
141
155
  secret: JWT secret
142
156
  algorithm: JWT algorithm
143
157
  expiry_hours: Token expiry in hours
144
-
158
+
145
159
  Returns:
146
160
  JWT token
147
161
  """
148
162
  try:
149
- payload = {
150
- "user_id": user_id,
151
- "roles": roles,
152
- "type": "client_proxy"
153
- }
154
-
163
+ payload = {"user_id": user_id, "roles": roles, "type": "client_proxy"}
164
+
155
165
  token = create_jwt_token(
156
166
  payload=payload,
157
167
  secret=secret,
158
168
  algorithm=algorithm,
159
- expiry_hours=expiry_hours
169
+ expiry_hours=expiry_hours,
160
170
  )
161
-
171
+
162
172
  logger.info(f"Created JWT token for user: {user_id}")
163
173
  return token
164
-
174
+
165
175
  except Exception as e:
166
176
  logger.error(f"Failed to create JWT token: {e}")
167
177
  raise
168
-
169
- def validate_server_certificate(self, cert_path: str, ca_cert_path: Optional[str] = None) -> bool:
178
+
179
+ def validate_server_certificate(
180
+ self, cert_path: str, ca_cert_path: Optional[str] = None
181
+ ) -> bool:
170
182
  """
171
183
  Validate server certificate before connection.
172
-
184
+
173
185
  Args:
174
186
  cert_path: Path to server certificate
175
187
  ca_cert_path: Path to CA certificate
176
-
188
+
177
189
  Returns:
178
190
  True if certificate is valid
179
191
  """
@@ -182,33 +194,33 @@ class ClientSecurityManager:
182
194
  if not validate_certificate_format(cert_path):
183
195
  logger.error(f"Invalid certificate format: {cert_path}")
184
196
  return False
185
-
197
+
186
198
  # Validate certificate chain if CA provided
187
199
  if ca_cert_path:
188
200
  if not validate_certificate_chain(cert_path, ca_cert_path):
189
201
  logger.error(f"Invalid certificate chain: {cert_path}")
190
202
  return False
191
-
203
+
192
204
  # Parse certificate and check basic properties
193
205
  cert_info = parse_certificate(cert_path)
194
206
  if not cert_info:
195
207
  logger.error(f"Failed to parse certificate: {cert_path}")
196
208
  return False
197
-
209
+
198
210
  logger.info(f"Server certificate validated: {cert_path}")
199
211
  return True
200
-
212
+
201
213
  except Exception as e:
202
214
  logger.error(f"Failed to validate server certificate: {e}")
203
215
  return False
204
-
216
+
205
217
  def extract_server_roles(self, cert_path: str) -> List[str]:
206
218
  """
207
219
  Extract roles from server certificate.
208
-
220
+
209
221
  Args:
210
222
  cert_path: Path to server certificate
211
-
223
+
212
224
  Returns:
213
225
  List of roles extracted from certificate
214
226
  """
@@ -219,88 +231,94 @@ class ClientSecurityManager:
219
231
  except Exception as e:
220
232
  logger.error(f"Failed to extract roles from certificate: {e}")
221
233
  return []
222
-
223
- def get_client_auth_headers(self, auth_method: str = "api_key", **kwargs) -> Dict[str, str]:
234
+
235
+ def get_client_auth_headers(
236
+ self, auth_method: str = "api_key", **kwargs
237
+ ) -> Dict[str, str]:
224
238
  """
225
239
  Get authentication headers for client requests.
226
-
240
+
227
241
  Args:
228
242
  auth_method: Authentication method (api_key, jwt, certificate)
229
243
  **kwargs: Additional parameters
230
-
244
+
231
245
  Returns:
232
246
  Dictionary of authentication headers
233
247
  """
234
248
  headers = {}
235
-
249
+
236
250
  try:
237
251
  if auth_method == "api_key":
238
252
  api_key = kwargs.get("api_key")
239
253
  if api_key:
240
254
  headers["X-API-Key"] = api_key
241
255
  headers["Authorization"] = f"Bearer {api_key}"
242
-
256
+
243
257
  elif auth_method == "jwt":
244
258
  token = kwargs.get("token")
245
259
  if token:
246
260
  headers["Authorization"] = f"Bearer {token}"
247
-
261
+
248
262
  elif auth_method == "certificate":
249
263
  # Certificate authentication is handled at SSL level
250
264
  headers["X-Auth-Method"] = "certificate"
251
-
265
+
252
266
  # Add common proxy headers
253
267
  headers["X-Proxy-Type"] = "mcp_proxy_adapter"
254
268
  headers["X-Client-Type"] = "proxy_client"
255
-
269
+
256
270
  logger.debug(f"Created auth headers for method: {auth_method}")
257
271
  return headers
258
-
272
+
259
273
  except Exception as e:
260
274
  logger.error(f"Failed to create auth headers: {e}")
261
275
  return {}
262
-
263
- def prepare_client_connection(self, server_config: Dict[str, Any]) -> Tuple[Optional[ssl.SSLContext], Dict[str, str]]:
276
+
277
+ def prepare_client_connection(
278
+ self, server_config: Dict[str, Any]
279
+ ) -> Tuple[Optional[ssl.SSLContext], Dict[str, str]]:
264
280
  """
265
281
  Prepare secure client connection to server.
266
-
282
+
267
283
  Args:
268
284
  server_config: Server connection configuration
269
-
285
+
270
286
  Returns:
271
287
  Tuple of (SSL context, auth headers)
272
288
  """
273
289
  ssl_context = None
274
290
  auth_headers = {}
275
-
291
+
276
292
  try:
277
293
  # Create SSL context if needed
278
294
  if server_config.get("ssl", False):
279
295
  server_hostname = server_config.get("hostname")
280
296
  ssl_context = self.create_client_ssl_context(server_hostname)
281
-
297
+
282
298
  # Create authentication headers
283
299
  auth_method = server_config.get("auth_method", "api_key")
284
300
  auth_headers = self.get_client_auth_headers(
285
301
  auth_method=auth_method,
286
302
  api_key=server_config.get("api_key"),
287
- token=server_config.get("token")
303
+ token=server_config.get("token"),
304
+ )
305
+
306
+ logger.info(
307
+ f"Prepared client connection for {server_config.get('hostname', 'unknown')}"
288
308
  )
289
-
290
- logger.info(f"Prepared client connection for {server_config.get('hostname', 'unknown')}")
291
309
  return ssl_context, auth_headers
292
-
310
+
293
311
  except Exception as e:
294
312
  logger.error(f"Failed to prepare client connection: {e}")
295
313
  return None, {}
296
-
314
+
297
315
  def validate_server_response(self, response_headers: Dict[str, str]) -> bool:
298
316
  """
299
317
  Validate server response for security compliance.
300
-
318
+
301
319
  Args:
302
320
  response_headers: Server response headers
303
-
321
+
304
322
  Returns:
305
323
  True if response is valid
306
324
  """
@@ -310,67 +328,69 @@ class ClientSecurityManager:
310
328
  for header in required_headers:
311
329
  if header not in response_headers:
312
330
  logger.warning(f"Missing required header: {header}")
313
-
331
+
314
332
  # Check for security headers
315
333
  security_headers = ["X-Frame-Options", "X-Content-Type-Options"]
316
334
  for header in security_headers:
317
335
  if header in response_headers:
318
336
  logger.debug(f"Found security header: {header}")
319
-
337
+
320
338
  # Validate content type
321
339
  content_type = response_headers.get("Content-Type", "")
322
340
  if "application/json" not in content_type:
323
341
  logger.warning(f"Unexpected content type: {content_type}")
324
-
342
+
325
343
  return True
326
-
344
+
327
345
  except Exception as e:
328
346
  logger.error(f"Failed to validate server response: {e}")
329
347
  return False
330
-
348
+
331
349
  def get_client_certificate_info(self) -> Optional[Dict[str, Any]]:
332
350
  """
333
351
  Get information about client certificate.
334
-
352
+
335
353
  Returns:
336
354
  Certificate information or None
337
355
  """
338
356
  try:
339
357
  ssl_config = self.security_config.get("ssl", {})
340
358
  cert_path = ssl_config.get("client_cert_file")
341
-
359
+
342
360
  if not cert_path or not Path(cert_path).exists():
343
361
  return None
344
-
362
+
345
363
  cert_info = parse_certificate(cert_path)
346
364
  if cert_info:
347
365
  roles = extract_roles_from_certificate(cert_path)
348
366
  cert_info["roles"] = roles
349
367
  return cert_info
350
-
368
+
351
369
  return None
352
-
370
+
353
371
  except Exception as e:
354
372
  logger.error(f"Failed to get client certificate info: {e}")
355
373
  return None
356
-
374
+
357
375
  def is_ssl_enabled(self) -> bool:
358
376
  """Check if SSL is enabled for client connections."""
359
377
  return self.security_config.get("ssl", {}).get("enabled", False)
360
-
378
+
361
379
  def get_supported_auth_methods(self) -> List[str]:
362
380
  """Get list of supported authentication methods."""
363
381
  return ["api_key", "jwt", "certificate"]
364
382
 
365
383
 
366
384
  # Factory function for easy integration
367
- def create_client_security_manager(config: Dict[str, Any]) -> Optional[ClientSecurityManager]:
385
+ def create_client_security_manager(
386
+ config: Dict[str, Any]
387
+ ) -> Optional[ClientSecurityManager]:
368
388
  """
369
389
  Create client security manager instance.
370
-
390
+
371
391
  Args:
372
392
  config: Configuration dictionary
373
-
393
+
374
394
  Returns:
375
395
  ClientSecurityManager instance or None if framework not available
376
396
  """