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
@@ -23,10 +23,17 @@ from typing import Dict, List, Optional, Any, Tuple
23
23
 
24
24
  import psutil
25
25
  import requests
26
+
26
27
  # Import security test client with proper module path
27
- from mcp_proxy_adapter.examples.security_test_client import SecurityTestClient, TestResult
28
+ from mcp_proxy_adapter.examples.security_test_client import (
29
+ SecurityTestClient,
30
+ TestResult,
31
+ )
32
+
33
+
28
34
  class SecurityTestRunner:
29
35
  """Main test runner for security testing."""
36
+
30
37
  def __init__(self):
31
38
  """Initialize test runner."""
32
39
  self.servers = {}
@@ -43,32 +50,32 @@ class SecurityTestRunner:
43
50
  "config": "configs/http_simple.json",
44
51
  "port": self.base_port + 0,
45
52
  "url": f"http://localhost:{self.base_port + 0}",
46
- "auth": "none"
53
+ "auth": "none",
47
54
  },
48
55
  "http_token": {
49
56
  "config": "configs/http_token.json",
50
57
  "port": self.base_port + 1,
51
58
  "url": f"http://localhost:{self.base_port + 1}",
52
- "auth": "api_key"
59
+ "auth": "api_key",
53
60
  },
54
61
  "https": {
55
62
  "config": "configs/https_simple.json",
56
63
  "port": self.base_port + 2,
57
64
  "url": f"https://localhost:{self.base_port + 2}",
58
- "auth": "none"
65
+ "auth": "none",
59
66
  },
60
67
  "https_token": {
61
68
  "config": "configs/https_token.json",
62
69
  "port": self.base_port + 3,
63
70
  "url": f"https://localhost:{self.base_port + 3}",
64
- "auth": "api_key"
71
+ "auth": "api_key",
65
72
  },
66
73
  "mtls": {
67
74
  "config": "configs/mtls_no_roles.json",
68
75
  "port": self.base_port + 4,
69
76
  "url": f"https://localhost:{self.base_port + 4}",
70
- "auth": "certificate"
71
- }
77
+ "auth": "certificate",
78
+ },
72
79
  }
73
80
 
74
81
  def _port_in_use(self, port: int, host: str = "127.0.0.1") -> bool:
@@ -159,7 +166,11 @@ class SecurityTestRunner:
159
166
  ssl = data.get("ssl", {})
160
167
  for key in ("cert_file", "key_file", "ca_cert"):
161
168
  exists, abs_path = self._validate_file(base, ssl.get(key))
162
- if ssl.get("enabled") and key in ("cert_file", "key_file") and not exists:
169
+ if (
170
+ ssl.get("enabled")
171
+ and key in ("cert_file", "key_file")
172
+ and not exists
173
+ ):
163
174
  ok = False
164
175
  missing.append(f"{name}:{key} -> {abs_path}")
165
176
  sec = data.get("security", {})
@@ -178,20 +189,23 @@ class SecurityTestRunner:
178
189
  else:
179
190
  print("āœ… Configuration file paths validated")
180
191
  return ok
192
+
181
193
  def check_prerequisites(self) -> bool:
182
194
  """Check if all prerequisites are met."""
183
195
  print("šŸ” Checking prerequisites...")
184
196
  # Check if we're in the right directory
185
197
  if not Path("configs").exists():
186
- print("āŒ configs directory not found. Please run from the test environment root directory.")
198
+ print(
199
+ "āŒ configs directory not found. Please run from the test environment root directory."
200
+ )
187
201
  return False
188
202
  # Check if certificates exist
189
203
  cert_files = [
190
- "certs/mcp_proxy_adapter_ca_ca.crt",
204
+ "certs/mcp_proxy_adapter_ca_ca.crt",
191
205
  "certs/localhost_server.crt",
192
- "keys/localhost_server.key"
206
+ "keys/localhost_server.key",
193
207
  ]
194
-
208
+
195
209
  missing_certs = []
196
210
  # Check if roles.json exists
197
211
  roles_file = "configs/roles.json"
@@ -202,11 +216,16 @@ class SecurityTestRunner:
202
216
  missing_certs.append(cert_file)
203
217
  if missing_certs:
204
218
  print(f"āŒ Missing certificates: {missing_certs}")
205
- print("šŸ’” Run: python -m mcp_proxy_adapter.examples.setup_test_environment to generate certificates")
219
+ print(
220
+ "šŸ’” Run: python -m mcp_proxy_adapter.examples.setup_test_environment to generate certificates"
221
+ )
206
222
  return False
207
223
  print("āœ… Prerequisites check passed")
208
224
  return True
209
- def start_server(self, name: str, config_path: str, port: int) -> Optional[subprocess.Popen]:
225
+
226
+ def start_server(
227
+ self, name: str, config_path: str, port: int
228
+ ) -> Optional[subprocess.Popen]:
210
229
  """Start a server in background."""
211
230
  try:
212
231
  print(f"šŸš€ Starting {name} server on port {port}...")
@@ -222,14 +241,22 @@ class SecurityTestRunner:
222
241
  print(f"āŒ Port {port} is in use, cannot start {name}")
223
242
  return None
224
243
  # Start server in background
225
- logs_dir = Path("logs"); logs_dir.mkdir(exist_ok=True)
244
+ logs_dir = Path("logs")
245
+ logs_dir.mkdir(exist_ok=True)
226
246
  log_path = logs_dir / f"{name}.log"
227
247
  log_file = open(log_path, "wb")
228
248
  self.server_logs[name] = log_file
229
- process = subprocess.Popen([
230
- sys.executable, "-m", "mcp_proxy_adapter.main",
231
- "--config", config_path
232
- ], stdout=log_file, stderr=subprocess.STDOUT)
249
+ process = subprocess.Popen(
250
+ [
251
+ sys.executable,
252
+ "-m",
253
+ "mcp_proxy_adapter.main",
254
+ "--config",
255
+ config_path,
256
+ ],
257
+ stdout=log_file,
258
+ stderr=subprocess.STDOUT,
259
+ )
233
260
  # Wait a bit for server to start
234
261
  time.sleep(3)
235
262
  # Check if process is still running
@@ -242,6 +269,7 @@ class SecurityTestRunner:
242
269
  except Exception as e:
243
270
  print(f"āŒ Error starting {name} server: {e}")
244
271
  return None
272
+
245
273
  def stop_server(self, name: str, process: subprocess.Popen):
246
274
  """Stop a server."""
247
275
  try:
@@ -276,21 +304,30 @@ class SecurityTestRunner:
276
304
  return False
277
305
 
278
306
  # Start proxy server
279
- cmd = [sys.executable, str(proxy_script), "--host", "127.0.0.1", "--port", str(self.proxy_port)]
280
- logs_dir = Path("logs"); logs_dir.mkdir(exist_ok=True)
307
+ cmd = [
308
+ sys.executable,
309
+ str(proxy_script),
310
+ "--host",
311
+ "127.0.0.1",
312
+ "--port",
313
+ str(self.proxy_port),
314
+ ]
315
+ logs_dir = Path("logs")
316
+ logs_dir.mkdir(exist_ok=True)
281
317
  proxy_log_path = logs_dir / "proxy_server.log"
282
318
  self.proxy_log = open(proxy_log_path, "wb")
283
319
  process = subprocess.Popen(
284
- cmd,
285
- stdout=self.proxy_log,
286
- stderr=subprocess.STDOUT,
287
- cwd=Path.cwd()
320
+ cmd, stdout=self.proxy_log, stderr=subprocess.STDOUT, cwd=Path.cwd()
288
321
  )
289
322
 
290
323
  # Check readiness
291
- if process.poll() is None and self.wait_for_http(f"http://127.0.0.1:{self.proxy_port}"):
324
+ if process.poll() is None and self.wait_for_http(
325
+ f"http://127.0.0.1:{self.proxy_port}"
326
+ ):
292
327
  self.proxy_server = process
293
- print("āœ… Proxy server started successfully (PID: {})".format(process.pid))
328
+ print(
329
+ "āœ… Proxy server started successfully (PID: {})".format(process.pid)
330
+ )
294
331
  return True
295
332
  else:
296
333
  print("āŒ Failed to start proxy server (see logs/proxy_server.log)")
@@ -304,7 +341,11 @@ class SecurityTestRunner:
304
341
  """Stop the proxy server."""
305
342
  if self.proxy_server:
306
343
  try:
307
- print("šŸ›‘ Stopping proxy server (PID: {})...".format(self.proxy_server.pid))
344
+ print(
345
+ "šŸ›‘ Stopping proxy server (PID: {})...".format(
346
+ self.proxy_server.pid
347
+ )
348
+ )
308
349
  self.proxy_server.terminate()
309
350
  try:
310
351
  self.proxy_server.wait(timeout=5)
@@ -323,6 +364,7 @@ class SecurityTestRunner:
323
364
  self.proxy_log = None
324
365
  except Exception:
325
366
  pass
367
+
326
368
  async def test_server(self, name: str, config: Dict[str, Any]) -> List[TestResult]:
327
369
  """Test a specific server configuration."""
328
370
  print(f"\n🧪 Testing {name} server...")
@@ -335,21 +377,18 @@ class SecurityTestRunner:
335
377
  client.create_ssl_context = client.create_ssl_context_for_mtls
336
378
  async with client as client_session:
337
379
  results = await client_session.run_security_tests(
338
- config["url"],
339
- config["auth"]
380
+ config["url"], config["auth"]
340
381
  )
341
382
  else:
342
383
  # For other auth types, use default SSL context
343
384
  async with SecurityTestClient(config["url"]) as client:
344
- results = await client.run_security_tests(
345
- config["url"],
346
- config["auth"]
347
- )
385
+ results = await client.run_security_tests(config["url"], config["auth"])
348
386
  # Print summary for this server
349
387
  passed = sum(1 for r in results if r.success)
350
388
  total = len(results)
351
389
  print(f"\nšŸ“Š {name} Results: {passed}/{total} tests passed")
352
390
  return results
391
+
353
392
  async def run_all_tests(self) -> Dict[str, List[TestResult]]:
354
393
  """Run tests against all server configurations."""
355
394
  print("šŸš€ Starting comprehensive security testing")
@@ -385,6 +424,7 @@ class SecurityTestRunner:
385
424
  print(f"āš ļø Skipping {name} tests (server not running)")
386
425
  all_results[name] = []
387
426
  return all_results
427
+
388
428
  def print_final_summary(self, all_results: Dict[str, List[TestResult]]):
389
429
  """Print final test summary."""
390
430
  print("\n" + "=" * 80)
@@ -419,11 +459,17 @@ class SecurityTestRunner:
419
459
  print("=" * 60)
420
460
  print("\nšŸ“‹ NEXT STEPS:")
421
461
  print("1. Start basic framework example:")
422
- print(" python -m mcp_proxy_adapter.examples.basic_framework.main --config configs/https_simple.json")
462
+ print(
463
+ " python -m mcp_proxy_adapter.examples.basic_framework.main --config configs/https_simple.json"
464
+ )
423
465
  print("\n2. Start full application example:")
424
- print(" python -m mcp_proxy_adapter.examples.full_application.main --config configs/mtls_with_roles.json")
466
+ print(
467
+ " python -m mcp_proxy_adapter.examples.full_application.main --config configs/mtls_with_roles.json"
468
+ )
425
469
  print("\n3. Test with custom configurations:")
426
- print(" python -m mcp_proxy_adapter.examples.basic_framework.main --config configs/http_simple.json")
470
+ print(
471
+ " python -m mcp_proxy_adapter.examples.basic_framework.main --config configs/http_simple.json"
472
+ )
427
473
  print("=" * 60)
428
474
  elif total_passed > 0:
429
475
  print("āš ļø SOME TESTS FAILED")
@@ -433,7 +479,9 @@ class SecurityTestRunner:
433
479
  print("\n2. Check if certificates are generated:")
434
480
  print(" python -m mcp_proxy_adapter.examples.generate_certificates")
435
481
  print("\n3. Verify configuration files exist:")
436
- print(" python -m mcp_proxy_adapter.examples.generate_test_configs --output-dir configs")
482
+ print(
483
+ " python -m mcp_proxy_adapter.examples.generate_test_configs --output-dir configs"
484
+ )
437
485
  print("\n4. Check if ports are available (3004, 8000-8005)")
438
486
  print("=" * 60)
439
487
  else:
@@ -444,10 +492,13 @@ class SecurityTestRunner:
444
492
  print("\n2. Generate certificates:")
445
493
  print(" python -m mcp_proxy_adapter.examples.generate_certificates")
446
494
  print("\n3. Generate configurations:")
447
- print(" python -m mcp_proxy_adapter.examples.generate_test_configs --output-dir configs")
495
+ print(
496
+ " python -m mcp_proxy_adapter.examples.generate_test_configs --output-dir configs"
497
+ )
448
498
  print("\n4. Start proxy server manually if needed:")
449
499
  print(" python /path/to/run_proxy_server.py --host 127.0.0.1 --port 3004")
450
500
  print("=" * 60)
501
+
451
502
  def cleanup(self):
452
503
  """Cleanup all running servers and proxy."""
453
504
  print("\n🧹 Cleaning up...")
@@ -457,11 +508,13 @@ class SecurityTestRunner:
457
508
  self.servers.clear()
458
509
  # Stop proxy server
459
510
  self.stop_proxy_server()
511
+
460
512
  def signal_handler(self, signum, frame):
461
513
  """Handle interrupt signals."""
462
514
  print(f"\nāš ļø Received signal {signum}, cleaning up...")
463
515
  self.cleanup()
464
516
  sys.exit(0)
517
+
465
518
  async def run(self):
466
519
  """Main run method."""
467
520
  # Set up signal handlers
@@ -501,12 +554,19 @@ class SecurityTestRunner:
501
554
  finally:
502
555
  # Always cleanup
503
556
  self.cleanup()
557
+
558
+
504
559
  def main():
505
560
  """Main function."""
506
561
  import argparse
507
- parser = argparse.ArgumentParser(description="Security Test Runner for MCP Proxy Adapter")
562
+
563
+ parser = argparse.ArgumentParser(
564
+ description="Security Test Runner for MCP Proxy Adapter"
565
+ )
508
566
  parser.add_argument("--config", help="Test specific configuration")
509
- parser.add_argument("--no-cleanup", action="store_true", help="Don't cleanup servers after tests")
567
+ parser.add_argument(
568
+ "--no-cleanup", action="store_true", help="Don't cleanup servers after tests"
569
+ )
510
570
  parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
511
571
  args = parser.parse_args()
512
572
 
@@ -540,5 +600,7 @@ def main():
540
600
  except Exception as e:
541
601
  print(f"āŒ Unexpected error: {e}")
542
602
  sys.exit(1)
603
+
604
+
543
605
  if __name__ == "__main__":
544
606
  main()
@@ -15,26 +15,30 @@ import sys
15
15
  import time
16
16
  from pathlib import Path
17
17
  from typing import Dict, List, Optional, Tuple
18
+
18
19
  # Add project root to path
19
20
  project_root = Path(__file__).parent.parent.parent
20
21
  sys.path.insert(0, str(project_root))
21
22
  from security_test_client import SecurityTestClient, TestResult
23
+
24
+
22
25
  class SecurityTestRunner:
23
26
  """Security test runner with proper port management."""
27
+
24
28
  def __init__(self):
25
29
  self.project_root = Path(__file__).parent.parent.parent
26
- self.configs_dir = self.project_root / "mcp_proxy_adapter" / "examples" / "server_configs"
30
+ self.configs_dir = (
31
+ self.project_root / "mcp_proxy_adapter" / "examples" / "server_configs"
32
+ )
27
33
  self.server_processes = {}
28
34
  self.test_results = []
35
+
29
36
  def kill_process_on_port(self, port: int) -> bool:
30
37
  """Kill process using specific port."""
31
38
  try:
32
39
  # Find process using the port
33
40
  result = subprocess.run(
34
- ["lsof", "-ti", f":{port}"],
35
- capture_output=True,
36
- text=True,
37
- timeout=5
41
+ ["lsof", "-ti", f":{port}"], capture_output=True, text=True, timeout=5
38
42
  )
39
43
  if result.returncode == 0 and result.stdout.strip():
40
44
  pid = result.stdout.strip()
@@ -52,7 +56,10 @@ class SecurityTestRunner:
52
56
  except Exception as e:
53
57
  print(f"āŒ Error killing process on port {port}: {e}")
54
58
  return False
55
- def start_server(self, config_name: str, config_path: Path) -> Optional[subprocess.Popen]:
59
+
60
+ def start_server(
61
+ self, config_name: str, config_path: Path
62
+ ) -> Optional[subprocess.Popen]:
56
63
  """Start server with proper error handling."""
57
64
  try:
58
65
  # Get port from config
@@ -63,8 +70,11 @@ class SecurityTestRunner:
63
70
  self.kill_process_on_port(port)
64
71
  # Start server
65
72
  cmd = [
66
- sys.executable, "-m", "mcp_proxy_adapter.main",
67
- "--config", str(config_path)
73
+ sys.executable,
74
+ "-m",
75
+ "mcp_proxy_adapter.main",
76
+ "--config",
77
+ str(config_path),
68
78
  ]
69
79
  # For mTLS, start from examples directory
70
80
  if config_name == "mtls":
@@ -73,11 +83,7 @@ class SecurityTestRunner:
73
83
  cwd = self.project_root
74
84
  print(f"šŸš€ Starting {config_name} on port {port}...")
75
85
  process = subprocess.Popen(
76
- cmd,
77
- cwd=cwd,
78
- stdout=subprocess.PIPE,
79
- stderr=subprocess.PIPE,
80
- text=True
86
+ cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
81
87
  )
82
88
  # Wait a bit for server to start
83
89
  time.sleep(3)
@@ -94,6 +100,7 @@ class SecurityTestRunner:
94
100
  except Exception as e:
95
101
  print(f"āŒ Error starting {config_name}: {e}")
96
102
  return None
103
+
97
104
  def stop_server(self, config_name: str, process: subprocess.Popen):
98
105
  """Stop server gracefully."""
99
106
  try:
@@ -107,32 +114,43 @@ class SecurityTestRunner:
107
114
  process.wait()
108
115
  except Exception as e:
109
116
  print(f"āŒ Error stopping {config_name}: {e}")
110
- async def test_server(self, config_name: str, config_path: Path) -> List[TestResult]:
117
+
118
+ async def test_server(
119
+ self, config_name: str, config_path: Path
120
+ ) -> List[TestResult]:
111
121
  """Test a single server configuration."""
112
122
  results = []
113
123
  # Start server
114
124
  process = self.start_server(config_name, config_path)
115
125
  if not process:
116
- return [TestResult(
117
- test_name=f"{config_name}_startup",
118
- server_url=f"http://localhost:{port}",
119
- auth_type="none",
120
- success=False,
121
- error_message="Server failed to start"
122
- )]
126
+ return [
127
+ TestResult(
128
+ test_name=f"{config_name}_startup",
129
+ server_url=f"http://localhost:{port}",
130
+ auth_type="none",
131
+ success=False,
132
+ error_message="Server failed to start",
133
+ )
134
+ ]
123
135
  try:
124
136
  # Get config for client setup
125
137
  with open(config_path) as f:
126
138
  config = json.load(f)
127
139
  port = config.get("server", {}).get("port", 8000)
128
- auth_enabled = config.get("security", {}).get("auth", {}).get("enabled", False)
140
+ auth_enabled = (
141
+ config.get("security", {}).get("auth", {}).get("enabled", False)
142
+ )
129
143
  auth_methods = config.get("security", {}).get("auth", {}).get("methods", [])
130
144
  # Create test client with correct protocol
131
- protocol = "https" if config.get("ssl", {}).get("enabled", False) else "http"
145
+ protocol = (
146
+ "https" if config.get("ssl", {}).get("enabled", False) else "http"
147
+ )
132
148
  client = SecurityTestClient(base_url=f"{protocol}://localhost:{port}")
133
149
  client.auth_enabled = auth_enabled
134
150
  client.auth_methods = auth_methods
135
- client.api_keys = config.get("security", {}).get("auth", {}).get("api_keys", {})
151
+ client.api_keys = (
152
+ config.get("security", {}).get("auth", {}).get("api_keys", {})
153
+ )
136
154
  # For mTLS, override SSL context creation and change working directory
137
155
  if config_name == "mtls":
138
156
  client.create_ssl_context = client.create_ssl_context_for_mtls
@@ -140,6 +158,7 @@ class SecurityTestRunner:
140
158
  client.auth_methods = ["certificate"]
141
159
  # Change to examples directory for mTLS tests
142
160
  import os
161
+
143
162
  os.chdir(self.project_root / "mcp_proxy_adapter" / "examples")
144
163
  # Run tests
145
164
  async with client:
@@ -157,13 +176,19 @@ class SecurityTestRunner:
157
176
  result = await client.test_negative_authentication()
158
177
  results.append(result)
159
178
  # Test 5: Role-based access
160
- result = await client.test_role_based_access(client.base_url, "api_key")
179
+ result = await client.test_role_based_access(
180
+ client.base_url, "api_key"
181
+ )
161
182
  results.append(result)
162
183
  # Test 6: Role permissions
163
- result = await client.test_role_permissions(client.base_url, "api_key")
184
+ result = await client.test_role_permissions(
185
+ client.base_url, "api_key"
186
+ )
164
187
  results.append(result)
165
188
  # Test 7: Multiple roles test
166
- result = await client.test_multiple_roles(client.base_url, "api_key")
189
+ result = await client.test_multiple_roles(
190
+ client.base_url, "api_key"
191
+ )
167
192
  results.append(result)
168
193
  else:
169
194
  # Test 3: No authentication required
@@ -173,17 +198,20 @@ class SecurityTestRunner:
173
198
  result = await client.test_negative_authentication()
174
199
  results.append(result)
175
200
  except Exception as e:
176
- results.append(TestResult(
177
- test_name=f"{config_name}_client_error",
178
- server_url=f"http://localhost:{port}",
179
- auth_type="none",
180
- success=False,
181
- error_message=str(e)
182
- ))
201
+ results.append(
202
+ TestResult(
203
+ test_name=f"{config_name}_client_error",
204
+ server_url=f"http://localhost:{port}",
205
+ auth_type="none",
206
+ success=False,
207
+ error_message=str(e),
208
+ )
209
+ )
183
210
  finally:
184
211
  # Stop server
185
212
  self.stop_server(config_name, process)
186
213
  return results
214
+
187
215
  async def run_all_tests(self):
188
216
  """Run all security tests."""
189
217
  print("šŸ”’ Starting Security Testing Suite")
@@ -194,7 +222,7 @@ class SecurityTestRunner:
194
222
  ("http_token", "config_http_token.json"),
195
223
  ("https", "config_https.json"),
196
224
  ("https_token", "config_https_token.json"),
197
- ("mtls", "config_mtls.json")
225
+ ("mtls", "config_mtls.json"),
198
226
  ]
199
227
  total_tests = 0
200
228
  passed_tests = 0
@@ -221,7 +249,11 @@ class SecurityTestRunner:
221
249
  print(f"Total tests: {total_tests}")
222
250
  print(f"Passed: {passed_tests}")
223
251
  print(f"Failed: {total_tests - passed_tests}")
224
- print(f"Success rate: {(passed_tests/total_tests*100):.1f}%" if total_tests > 0 else "N/A")
252
+ print(
253
+ f"Success rate: {(passed_tests/total_tests*100):.1f}%"
254
+ if total_tests > 0
255
+ else "N/A"
256
+ )
225
257
  # Detailed results
226
258
  print("\nšŸ“‹ DETAILED RESULTS")
227
259
  print("-" * 30)
@@ -231,6 +263,8 @@ class SecurityTestRunner:
231
263
  if not result.success and result.error_message:
232
264
  print(f" Error: {result.error_message}")
233
265
  return passed_tests == total_tests
266
+
267
+
234
268
  async def main():
235
269
  """Main function."""
236
270
  runner = SecurityTestRunner()
@@ -243,5 +277,7 @@ async def main():
243
277
  except Exception as e:
244
278
  print(f"\nāŒ Testing failed: {e}")
245
279
  sys.exit(1)
280
+
281
+
246
282
  if __name__ == "__main__":
247
283
  asyncio.run(main())