mcp-proxy-adapter 6.9.28__py3-none-any.whl → 6.9.30__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.

Potentially problematic release.


This version of mcp-proxy-adapter might be problematic. Click here for more details.

Files changed (212) hide show
  1. mcp_proxy_adapter/__init__.py +10 -0
  2. mcp_proxy_adapter/__main__.py +8 -21
  3. mcp_proxy_adapter/api/app.py +10 -913
  4. mcp_proxy_adapter/api/core/__init__.py +18 -0
  5. mcp_proxy_adapter/api/core/app_factory.py +243 -0
  6. mcp_proxy_adapter/api/core/lifespan_manager.py +55 -0
  7. mcp_proxy_adapter/api/core/registration_manager.py +166 -0
  8. mcp_proxy_adapter/api/core/ssl_context_factory.py +88 -0
  9. mcp_proxy_adapter/api/handlers.py +78 -199
  10. mcp_proxy_adapter/api/middleware/__init__.py +1 -44
  11. mcp_proxy_adapter/api/middleware/base.py +0 -42
  12. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +0 -85
  13. mcp_proxy_adapter/api/middleware/error_handling.py +1 -127
  14. mcp_proxy_adapter/api/middleware/factory.py +0 -94
  15. mcp_proxy_adapter/api/middleware/logging.py +0 -112
  16. mcp_proxy_adapter/api/middleware/performance.py +0 -35
  17. mcp_proxy_adapter/api/middleware/protocol_middleware.py +2 -98
  18. mcp_proxy_adapter/api/middleware/transport_middleware.py +0 -37
  19. mcp_proxy_adapter/api/middleware/unified_security.py +10 -10
  20. mcp_proxy_adapter/api/middleware/user_info_middleware.py +0 -118
  21. mcp_proxy_adapter/api/openapi/__init__.py +21 -0
  22. mcp_proxy_adapter/api/openapi/command_integration.py +105 -0
  23. mcp_proxy_adapter/api/openapi/openapi_generator.py +40 -0
  24. mcp_proxy_adapter/api/openapi/openapi_registry.py +62 -0
  25. mcp_proxy_adapter/api/openapi/schema_loader.py +116 -0
  26. mcp_proxy_adapter/api/schemas.py +0 -61
  27. mcp_proxy_adapter/api/tool_integration.py +0 -117
  28. mcp_proxy_adapter/api/tools.py +0 -46
  29. mcp_proxy_adapter/cli/__init__.py +12 -0
  30. mcp_proxy_adapter/cli/commands/__init__.py +15 -0
  31. mcp_proxy_adapter/cli/commands/client.py +100 -0
  32. mcp_proxy_adapter/cli/commands/config_generate.py +21 -0
  33. mcp_proxy_adapter/cli/commands/config_validate.py +36 -0
  34. mcp_proxy_adapter/cli/commands/generate.py +259 -0
  35. mcp_proxy_adapter/cli/commands/server.py +174 -0
  36. mcp_proxy_adapter/cli/commands/sets.py +128 -0
  37. mcp_proxy_adapter/cli/commands/testconfig.py +177 -0
  38. mcp_proxy_adapter/cli/examples/__init__.py +8 -0
  39. mcp_proxy_adapter/cli/examples/http_basic.py +82 -0
  40. mcp_proxy_adapter/cli/examples/https_token.py +96 -0
  41. mcp_proxy_adapter/cli/examples/mtls_roles.py +103 -0
  42. mcp_proxy_adapter/cli/main.py +63 -0
  43. mcp_proxy_adapter/cli/parser.py +324 -0
  44. mcp_proxy_adapter/cli/validators.py +231 -0
  45. mcp_proxy_adapter/client/jsonrpc_client.py +406 -0
  46. mcp_proxy_adapter/client/proxy.py +45 -0
  47. mcp_proxy_adapter/commands/__init__.py +44 -28
  48. mcp_proxy_adapter/commands/auth_validation_command.py +7 -344
  49. mcp_proxy_adapter/commands/base.py +19 -43
  50. mcp_proxy_adapter/commands/builtin_commands.py +0 -75
  51. mcp_proxy_adapter/commands/catalog/__init__.py +20 -0
  52. mcp_proxy_adapter/commands/catalog/catalog_loader.py +34 -0
  53. mcp_proxy_adapter/commands/catalog/catalog_manager.py +122 -0
  54. mcp_proxy_adapter/commands/catalog/catalog_syncer.py +149 -0
  55. mcp_proxy_adapter/commands/catalog/command_catalog.py +43 -0
  56. mcp_proxy_adapter/commands/catalog/dependency_manager.py +37 -0
  57. mcp_proxy_adapter/commands/catalog_manager.py +58 -928
  58. mcp_proxy_adapter/commands/cert_monitor_command.py +0 -88
  59. mcp_proxy_adapter/commands/certificate_management_command.py +0 -45
  60. mcp_proxy_adapter/commands/command_registry.py +172 -904
  61. mcp_proxy_adapter/commands/config_command.py +0 -28
  62. mcp_proxy_adapter/commands/dependency_container.py +1 -70
  63. mcp_proxy_adapter/commands/dependency_manager.py +0 -128
  64. mcp_proxy_adapter/commands/echo_command.py +0 -34
  65. mcp_proxy_adapter/commands/health_command.py +0 -3
  66. mcp_proxy_adapter/commands/help_command.py +0 -159
  67. mcp_proxy_adapter/commands/hooks.py +0 -137
  68. mcp_proxy_adapter/commands/key_management_command.py +0 -25
  69. mcp_proxy_adapter/commands/load_command.py +7 -78
  70. mcp_proxy_adapter/commands/plugins_command.py +0 -16
  71. mcp_proxy_adapter/commands/protocol_management_command.py +0 -28
  72. mcp_proxy_adapter/commands/proxy_registration_command.py +0 -88
  73. mcp_proxy_adapter/commands/queue_commands.py +750 -0
  74. mcp_proxy_adapter/commands/registration_status_command.py +0 -43
  75. mcp_proxy_adapter/commands/registry/__init__.py +18 -0
  76. mcp_proxy_adapter/commands/registry/command_info.py +103 -0
  77. mcp_proxy_adapter/commands/registry/command_loader.py +207 -0
  78. mcp_proxy_adapter/commands/registry/command_manager.py +119 -0
  79. mcp_proxy_adapter/commands/registry/command_registry.py +217 -0
  80. mcp_proxy_adapter/commands/reload_command.py +0 -80
  81. mcp_proxy_adapter/commands/result.py +25 -77
  82. mcp_proxy_adapter/commands/role_test_command.py +0 -44
  83. mcp_proxy_adapter/commands/roles_management_command.py +0 -199
  84. mcp_proxy_adapter/commands/security_command.py +0 -30
  85. mcp_proxy_adapter/commands/settings_command.py +0 -68
  86. mcp_proxy_adapter/commands/ssl_setup_command.py +0 -42
  87. mcp_proxy_adapter/commands/token_management_command.py +0 -1
  88. mcp_proxy_adapter/commands/transport_management_command.py +0 -20
  89. mcp_proxy_adapter/commands/unload_command.py +0 -71
  90. mcp_proxy_adapter/config.py +15 -626
  91. mcp_proxy_adapter/core/__init__.py +5 -39
  92. mcp_proxy_adapter/core/app_factory.py +14 -36
  93. mcp_proxy_adapter/core/app_runner.py +0 -27
  94. mcp_proxy_adapter/core/auth_validator.py +1 -93
  95. mcp_proxy_adapter/core/certificate/__init__.py +20 -0
  96. mcp_proxy_adapter/core/certificate/certificate_creator.py +371 -0
  97. mcp_proxy_adapter/core/certificate/certificate_extractor.py +183 -0
  98. mcp_proxy_adapter/core/certificate/certificate_utils.py +249 -0
  99. mcp_proxy_adapter/core/certificate/certificate_validator.py +110 -0
  100. mcp_proxy_adapter/core/certificate/ssl_context_manager.py +70 -0
  101. mcp_proxy_adapter/core/certificate_utils.py +64 -903
  102. mcp_proxy_adapter/core/client.py +10 -9
  103. mcp_proxy_adapter/core/client_manager.py +0 -19
  104. mcp_proxy_adapter/core/client_security.py +0 -2
  105. mcp_proxy_adapter/core/config/__init__.py +18 -0
  106. mcp_proxy_adapter/core/config/config.py +195 -0
  107. mcp_proxy_adapter/core/config/config_factory.py +22 -0
  108. mcp_proxy_adapter/core/config/config_loader.py +66 -0
  109. mcp_proxy_adapter/core/config/feature_manager.py +31 -0
  110. mcp_proxy_adapter/core/config/simple_config.py +112 -0
  111. mcp_proxy_adapter/core/config/simple_config_generator.py +50 -0
  112. mcp_proxy_adapter/core/config/simple_config_validator.py +96 -0
  113. mcp_proxy_adapter/core/config_converter.py +0 -186
  114. mcp_proxy_adapter/core/config_validator.py +96 -1238
  115. mcp_proxy_adapter/core/errors.py +7 -42
  116. mcp_proxy_adapter/core/job_manager.py +54 -0
  117. mcp_proxy_adapter/core/logging.py +2 -22
  118. mcp_proxy_adapter/core/mtls_asgi.py +0 -20
  119. mcp_proxy_adapter/core/mtls_asgi_app.py +0 -12
  120. mcp_proxy_adapter/core/mtls_proxy.py +0 -80
  121. mcp_proxy_adapter/core/mtls_server.py +3 -173
  122. mcp_proxy_adapter/core/protocol_manager.py +1 -191
  123. mcp_proxy_adapter/core/proxy/__init__.py +22 -0
  124. mcp_proxy_adapter/core/proxy/auth_manager.py +27 -0
  125. mcp_proxy_adapter/core/proxy/proxy_registration_manager.py +137 -0
  126. mcp_proxy_adapter/core/proxy/registration_client.py +60 -0
  127. mcp_proxy_adapter/core/proxy/ssl_manager.py +101 -0
  128. mcp_proxy_adapter/core/proxy_client.py +0 -1
  129. mcp_proxy_adapter/core/proxy_registration.py +36 -913
  130. mcp_proxy_adapter/core/role_utils.py +0 -308
  131. mcp_proxy_adapter/core/security_adapter.py +1 -36
  132. mcp_proxy_adapter/core/security_factory.py +1 -150
  133. mcp_proxy_adapter/core/security_integration.py +0 -33
  134. mcp_proxy_adapter/core/server_adapter.py +1 -40
  135. mcp_proxy_adapter/core/server_engine.py +2 -173
  136. mcp_proxy_adapter/core/settings.py +0 -127
  137. mcp_proxy_adapter/core/signal_handler.py +0 -65
  138. mcp_proxy_adapter/core/ssl_utils.py +19 -137
  139. mcp_proxy_adapter/core/transport_manager.py +0 -151
  140. mcp_proxy_adapter/core/unified_config_adapter.py +1 -193
  141. mcp_proxy_adapter/core/utils.py +1 -182
  142. mcp_proxy_adapter/core/validation/__init__.py +21 -0
  143. mcp_proxy_adapter/core/validation/config_validator.py +211 -0
  144. mcp_proxy_adapter/core/validation/file_validator.py +73 -0
  145. mcp_proxy_adapter/core/validation/protocol_validator.py +191 -0
  146. mcp_proxy_adapter/core/validation/security_validator.py +58 -0
  147. mcp_proxy_adapter/core/validation/validation_result.py +27 -0
  148. mcp_proxy_adapter/custom_openapi.py +33 -652
  149. mcp_proxy_adapter/examples/bugfix_certificate_config.py +0 -23
  150. mcp_proxy_adapter/examples/check_config.py +0 -2
  151. mcp_proxy_adapter/examples/client_usage_example.py +164 -0
  152. mcp_proxy_adapter/examples/config_builder.py +13 -2
  153. mcp_proxy_adapter/examples/config_cli.py +0 -1
  154. mcp_proxy_adapter/examples/create_test_configs.py +0 -46
  155. mcp_proxy_adapter/examples/debug_request_state.py +0 -1
  156. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +0 -47
  157. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +0 -45
  158. mcp_proxy_adapter/examples/full_application/commands/echo_command.py +0 -12
  159. mcp_proxy_adapter/examples/full_application/commands/help_command.py +0 -12
  160. mcp_proxy_adapter/examples/full_application/commands/list_command.py +0 -7
  161. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +0 -2
  162. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +0 -59
  163. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +0 -54
  164. mcp_proxy_adapter/examples/full_application/main.py +186 -150
  165. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +0 -107
  166. mcp_proxy_adapter/examples/full_application/test_minimal_server.py +0 -24
  167. mcp_proxy_adapter/examples/full_application/test_server.py +0 -58
  168. mcp_proxy_adapter/examples/generate_config.py +65 -11
  169. mcp_proxy_adapter/examples/queue_demo_simple.py +632 -0
  170. mcp_proxy_adapter/examples/queue_integration_example.py +578 -0
  171. mcp_proxy_adapter/examples/queue_server_demo.py +82 -0
  172. mcp_proxy_adapter/examples/queue_server_example.py +85 -0
  173. mcp_proxy_adapter/examples/queue_server_simple.py +173 -0
  174. mcp_proxy_adapter/examples/required_certificates.py +0 -2
  175. mcp_proxy_adapter/examples/run_full_test_suite.py +0 -29
  176. mcp_proxy_adapter/examples/run_proxy_server.py +31 -71
  177. mcp_proxy_adapter/examples/run_security_tests_fixed.py +0 -27
  178. mcp_proxy_adapter/examples/security_test/__init__.py +18 -0
  179. mcp_proxy_adapter/examples/security_test/auth_manager.py +14 -0
  180. mcp_proxy_adapter/examples/security_test/ssl_context_manager.py +28 -0
  181. mcp_proxy_adapter/examples/security_test/test_client.py +159 -0
  182. mcp_proxy_adapter/examples/security_test/test_result.py +22 -0
  183. mcp_proxy_adapter/examples/security_test_client.py +24 -1075
  184. mcp_proxy_adapter/examples/setup/__init__.py +24 -0
  185. mcp_proxy_adapter/examples/setup/certificate_manager.py +215 -0
  186. mcp_proxy_adapter/examples/setup/config_generator.py +12 -0
  187. mcp_proxy_adapter/examples/setup/config_validator.py +118 -0
  188. mcp_proxy_adapter/examples/setup/environment_setup.py +62 -0
  189. mcp_proxy_adapter/examples/setup/test_files_generator.py +10 -0
  190. mcp_proxy_adapter/examples/setup/test_runner.py +89 -0
  191. mcp_proxy_adapter/examples/setup_test_environment.py +133 -1425
  192. mcp_proxy_adapter/examples/test_config.py +0 -3
  193. mcp_proxy_adapter/examples/test_config_builder.py +25 -405
  194. mcp_proxy_adapter/examples/test_examples.py +0 -1
  195. mcp_proxy_adapter/examples/test_framework_complete.py +0 -2
  196. mcp_proxy_adapter/examples/test_mcp_server.py +0 -1
  197. mcp_proxy_adapter/examples/test_protocol_examples.py +0 -1
  198. mcp_proxy_adapter/examples/universal_client.py +0 -6
  199. mcp_proxy_adapter/examples/update_config_certificates.py +0 -1
  200. mcp_proxy_adapter/examples/validate_generator_compatibility.py +0 -1
  201. mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +0 -187
  202. mcp_proxy_adapter/integrations/__init__.py +25 -0
  203. mcp_proxy_adapter/integrations/queuemgr_integration.py +462 -0
  204. mcp_proxy_adapter/main.py +70 -62
  205. mcp_proxy_adapter/openapi.py +0 -22
  206. mcp_proxy_adapter/version.py +1 -1
  207. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/METADATA +2 -1
  208. mcp_proxy_adapter-6.9.30.dist-info/RECORD +235 -0
  209. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/entry_points.txt +1 -1
  210. mcp_proxy_adapter-6.9.28.dist-info/RECORD +0 -149
  211. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/WHEEL +0 -0
  212. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/top_level.txt +0 -0
@@ -1,42 +1,8 @@
1
1
  """
2
- Core functionality for MCP Proxy Adapter.
3
- """
2
+ Core package marker for MCP Proxy Adapter.
4
3
 
5
- from .errors import *
6
- from .logging import *
7
- from .settings import *
4
+ Intentionally lightweight: avoids importing submodules at package import time
5
+ to prevent heavy side effects and circular imports.
6
+ """
8
7
 
9
- __all__ = [
10
- # Errors
11
- "NotFoundError",
12
- "InvalidParamsError",
13
- "CommandExecutionError",
14
- "ConfigurationError",
15
- # Logging
16
- "setup_logging",
17
- "get_logger",
18
- "get_global_logger()",
19
- "RequestLogger",
20
- "CustomFormatter",
21
- "RequestContextFilter",
22
- # Settings
23
- "Settings",
24
- "ServerSettings",
25
- "LoggingSettings",
26
- "CommandsSettings",
27
- "get_server_host",
28
- "get_server_port",
29
- "get_server_debug",
30
- "get_logging_level",
31
- "get_logging_dir",
32
- "get_auto_discovery",
33
- "get_discovery_path",
34
- "get_setting",
35
- "set_setting",
36
- "reload_settings",
37
- "add_custom_settings",
38
- "get_custom_settings",
39
- "get_custom_setting_value",
40
- "set_custom_setting_value",
41
- "clear_custom_settings",
42
- ]
8
+ __all__ = []
@@ -14,18 +14,11 @@ from typing import Optional, Dict, Any
14
14
 
15
15
  from fastapi import FastAPI
16
16
  from mcp_proxy_adapter.api.app import create_app
17
- from mcp_proxy_adapter.core.logging import setup_logging, get_logger
17
+ from mcp_proxy_adapter.core.logging import setup_logging, get_global_logger
18
18
  from mcp_proxy_adapter.config import config
19
- from mcp_proxy_adapter.commands.builtin_commands import (
20
- register_builtin_commands,
21
- )
22
- from mcp_proxy_adapter.core.utils import (
23
- check_port_availability,
24
- handle_port_conflict,
25
- find_port_for_internal_server,
26
- )
19
+ # Built-in command registration is temporarily disabled in this startup path
27
20
 
28
- logger = get_logger("app_factory")
21
+ logger = get_global_logger()
29
22
 
30
23
 
31
24
  async def create_and_run_server(
@@ -253,13 +246,8 @@ async def create_and_run_server(
253
246
  print(f"❌ Failed to setup logging: {e}")
254
247
  sys.exit(1)
255
248
 
256
- # 3. Register built-in commands
257
- try:
258
- builtin_count = register_builtin_commands()
259
- print(f"✅ Registered {builtin_count} built-in commands")
260
- except Exception as e:
261
- print(f"❌ Failed to register built-in commands: {e}")
262
- sys.exit(1)
249
+ # 3. Register built-in commands (disabled)
250
+ print("⚠️ Built-in command registration disabled for simplified startup")
263
251
 
264
252
  # 4. Create FastAPI application with configuration
265
253
  try:
@@ -449,13 +437,7 @@ async def create_and_run_server(
449
437
  else:
450
438
  print(f"🌐 Starting HTTP server with hypercorn...")
451
439
 
452
- # Final port check before starting hypercorn
453
- print(f"🔍 Final port check before starting hypercorn: {host}:{server_port}")
454
- if not check_port_availability(host, server_port):
455
- print(f"❌ CRITICAL: Port {server_port} is occupied during final check")
456
- handle_port_conflict(host, server_port)
457
- return # Exit immediately
458
- print(f"✅ Final port check passed: {host}:{server_port}")
440
+ # Final port check disabled in this refactor; rely on OS errors
459
441
 
460
442
  # Run the server
461
443
  # hypercorn.asyncio.serve() should be run with asyncio.run(), not awaited
@@ -469,18 +451,14 @@ async def create_and_run_server(
469
451
  print("🛑 Stopping internal mTLS server...")
470
452
  mtls_server.stop()
471
453
  except OSError as e:
472
- if e.errno == 98: # Address already in use
473
- print(f"\n❌ Port conflict detected: {e}")
474
- handle_port_conflict(host, server_port)
475
- else:
476
- print(f"\n❌ Failed to start server: {e}")
477
- # Stop mTLS server if running
478
- if mtls_server:
479
- print("🛑 Stopping mTLS server...")
480
- mtls_server.stop()
481
- import traceback
482
- traceback.print_exc()
483
- sys.exit(1)
454
+ print(f"\n❌ Failed to start server: {e}")
455
+ # Stop mTLS server if running
456
+ if mtls_server:
457
+ print("🛑 Stopping mTLS server...")
458
+ mtls_server.stop()
459
+ import traceback
460
+ traceback.print_exc()
461
+ sys.exit(1)
484
462
  except Exception as e:
485
463
  print(f"\n❌ Failed to start server: {e}")
486
464
  # Stop internal mTLS server if running
@@ -11,7 +11,6 @@ with full configuration validation and error handling.
11
11
  import socket
12
12
  import sys
13
13
  from pathlib import Path
14
- from typing import Dict, Any, List, Optional
15
14
 
16
15
  from fastapi import FastAPI
17
16
 
@@ -227,16 +226,9 @@ class ApplicationRunner:
227
226
 
228
227
  # Add startup event
229
228
  @self.app.on_event("startup")
230
- async def startup_event():
231
- get_global_logger().info("Application starting up")
232
- get_global_logger().info(
233
- f"Configuration validation passed with {len(self.errors)} errors"
234
- )
235
229
 
236
230
  # Add shutdown event
237
231
  @self.app.on_event("shutdown")
238
- async def shutdown_event():
239
- get_global_logger().info("Application shutting down")
240
232
 
241
233
  def run(self) -> None:
242
234
  """
@@ -252,25 +244,6 @@ class ApplicationRunner:
252
244
  sys.exit(1)
253
245
 
254
246
  # Setup signal handling for graceful shutdown
255
- def shutdown_callback():
256
- """Callback for graceful shutdown with proxy unregistration."""
257
- print("\n🛑 Graceful shutdown initiated...")
258
- try:
259
- from mcp_proxy_adapter.core.async_proxy_registration import (
260
- stop_async_registration,
261
- get_registration_status,
262
- )
263
-
264
- # Get final status
265
- final_status = get_registration_status()
266
- print(f"📊 Final registration status: {final_status}")
267
-
268
- # Stop async registration (this will unregister from proxy)
269
- stop_async_registration()
270
- print("✅ Proxy unregistration completed")
271
-
272
- except Exception as e:
273
- print(f"❌ Error during shutdown: {e}")
274
247
 
275
248
  setup_signal_handling(shutdown_callback)
276
249
  print("🔧 Signal handling configured for graceful shutdown")
@@ -12,12 +12,10 @@ import json
12
12
  import logging
13
13
  import os
14
14
  from datetime import datetime
15
- from typing import Dict, List, Optional, Any, Union
16
15
  from pathlib import Path
16
+ from typing import Optional, Dict, Any, List
17
17
 
18
18
  from cryptography import x509
19
- from cryptography.hazmat.primitives import hashes
20
- from cryptography.hazmat.primitives.asymmetric import rsa, padding
21
19
 
22
20
 
23
21
  # Standard JSON-RPC error codes
@@ -71,36 +69,7 @@ class AuthValidationResult:
71
69
  self.error_message = error_message
72
70
  self.roles = roles or []
73
71
 
74
- def to_json_rpc_error(self) -> Dict[str, Any]:
75
- """
76
- Convert to JSON-RPC error format.
77
-
78
- Returns:
79
- JSON-RPC error dictionary
80
- """
81
- if self.is_valid:
82
- return {}
83
-
84
- return {
85
- "code": self.error_code or -32603,
86
- "message": self.error_message
87
- or JSON_RPC_ERRORS.get(self.error_code, "Unknown error"),
88
- "data": {"validation_type": "authentication", "roles": self.roles},
89
- }
90
72
 
91
- def to_dict(self) -> Dict[str, Any]:
92
- """
93
- Convert to dictionary format.
94
-
95
- Returns:
96
- Dictionary representation
97
- """
98
- return {
99
- "is_valid": self.is_valid,
100
- "error_code": self.error_code,
101
- "error_message": self.error_message,
102
- "roles": self.roles,
103
- }
104
73
 
105
74
 
106
75
  class AuthValidator:
@@ -127,67 +96,6 @@ class AuthValidator:
127
96
  # Custom OID for roles
128
97
  self.role_oid = "1.3.6.1.4.1.99999.1"
129
98
 
130
- def validate_auth(
131
- self, auth_data: Dict[str, Any], auth_type: str = "auto"
132
- ) -> AuthValidationResult:
133
- """
134
- Universal authentication validation.
135
-
136
- Logic:
137
- 1. Check SSL configuration
138
- 2. If SSL disabled - return success
139
- 3. Determine validation type (auto/ssl/mtls/token)
140
- 4. Check for required data
141
- 5. Perform validation
142
- 6. Return result with JSON-RPC error code
143
-
144
- Args:
145
- auth_data: Authentication data dictionary
146
- auth_type: Type of authentication to validate
147
-
148
- Returns:
149
- Authentication validation result
150
- """
151
- try:
152
- # Determine validation type
153
- if auth_type == "auto":
154
- auth_type = self._get_validation_mode()
155
-
156
- # Check for unsupported types first
157
- if auth_type not in ["certificate", "token", "mtls", "ssl"]:
158
- return AuthValidationResult(
159
- is_valid=False,
160
- error_code=-32602,
161
- error_message=f"Unsupported authentication type: {auth_type}",
162
- )
163
-
164
- # Check if authentication is enabled for the determined type
165
- if not self._check_config(auth_type):
166
- return AuthValidationResult(is_valid=True, roles=[])
167
-
168
- # Validate based on type
169
- if auth_type == "certificate":
170
- return self.validate_certificate(
171
- auth_data.get("cert_path"), auth_data.get("cert_type", "server")
172
- )
173
- elif auth_type == "token":
174
- return self.validate_token(
175
- auth_data.get("token"), auth_data.get("token_type", "jwt")
176
- )
177
- elif auth_type == "mtls":
178
- return self.validate_mtls(
179
- auth_data.get("client_cert"), auth_data.get("ca_cert")
180
- )
181
- elif auth_type == "ssl":
182
- return self.validate_ssl(auth_data.get("server_cert"))
183
-
184
- except Exception as e:
185
- self.get_global_logger().error(f"Authentication validation error: {e}")
186
- return AuthValidationResult(
187
- is_valid=False,
188
- error_code=-32603,
189
- error_message=f"Internal validation error: {str(e)}",
190
- )
191
99
 
192
100
  def validate_certificate(
193
101
  self, cert_path: Optional[str], cert_type: str = "server"
@@ -0,0 +1,20 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Certificate utilities package for MCP Proxy Adapter.
6
+ """
7
+
8
+ from .certificate_utils import CertificateUtils
9
+ from .certificate_creator import CertificateCreator
10
+ from .certificate_validator import CertificateValidator
11
+ from .certificate_extractor import CertificateExtractor
12
+ from .ssl_context_manager import SSLContextManager
13
+
14
+ __all__ = [
15
+ "CertificateUtils",
16
+ "CertificateCreator",
17
+ "CertificateValidator",
18
+ "CertificateExtractor",
19
+ "SSLContextManager",
20
+ ]
@@ -0,0 +1,371 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Certificate creation utilities for MCP Proxy Adapter.
6
+ """
7
+
8
+ import logging
9
+ from datetime import datetime, timedelta, timezone
10
+ from pathlib import Path
11
+ from typing import Dict, List, Optional
12
+
13
+ # Import mcp_security_framework
14
+ try:
15
+ from mcp_security_framework.core.cert_manager import CertificateManager
16
+ CertificateConfig,
17
+ CAConfig,
18
+ ClientCertConfig,
19
+ ServerCertConfig,
20
+ )
21
+ SECURITY_FRAMEWORK_AVAILABLE = True
22
+ except ImportError:
23
+ SECURITY_FRAMEWORK_AVAILABLE = False
24
+ # Fallback to cryptography if mcp_security_framework is not available
25
+ from cryptography import x509
26
+ from cryptography.hazmat.primitives import hashes, serialization
27
+ from cryptography.hazmat.primitives.asymmetric import rsa
28
+ from cryptography.x509.oid import NameOID
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ class CertificateCreator:
34
+ """Creator for various types of certificates."""
35
+
36
+ # Default certificate validity period (1 year)
37
+ DEFAULT_VALIDITY_DAYS = 365
38
+
39
+ # Default key size
40
+ DEFAULT_KEY_SIZE = 2048
41
+
42
+ @staticmethod
43
+ def create_ca_certificate(
44
+ common_name: str,
45
+ output_dir: str,
46
+ validity_days: int = DEFAULT_VALIDITY_DAYS,
47
+ key_size: int = DEFAULT_KEY_SIZE,
48
+ ) -> Dict[str, str]:
49
+ """
50
+ Create a CA certificate and private key using mcp_security_framework.
51
+
52
+ Args:
53
+ common_name: Common name for the CA certificate
54
+ output_dir: Directory to save certificate and key files
55
+ validity_days: Certificate validity period in days
56
+ key_size: RSA key size in bits
57
+
58
+ Returns:
59
+ Dictionary with paths to created files
60
+
61
+ Raises:
62
+ ValueError: If parameters are invalid
63
+ OSError: If files cannot be created
64
+ """
65
+ if not SECURITY_FRAMEWORK_AVAILABLE:
66
+ logger.warning("mcp_security_framework not available, using fallback method")
67
+ return CertificateCreator._create_ca_certificate_fallback(
68
+ common_name, output_dir, validity_days, key_size
69
+ )
70
+
71
+ try:
72
+ # Validate parameters
73
+ if not common_name or not common_name.strip():
74
+ raise ValueError("Common name cannot be empty")
75
+
76
+ if validity_days <= 0:
77
+ raise ValueError("Validity days must be positive")
78
+
79
+ if key_size < 1024:
80
+ raise ValueError("Key size must be at least 1024 bits")
81
+
82
+ # Create output directory if it doesn't exist
83
+ Path(output_dir).mkdir(parents=True, exist_ok=True)
84
+
85
+ # Configure CA using mcp_security_framework
86
+ ca_config = CAConfig(
87
+ common_name=common_name,
88
+ organization="MCP Proxy Adapter CA",
89
+ organizational_unit="Certificate Authority",
90
+ country="US",
91
+ state="Default State",
92
+ locality="Default City",
93
+ validity_days=validity_days,
94
+ key_size=key_size,
95
+ key_type="RSA",
96
+ )
97
+
98
+ # Create certificate manager
99
+ cert_config = CertificateConfig(
100
+ output_dir=output_dir,
101
+ ca_cert_path=str(Path(output_dir) / f"{common_name}.crt"),
102
+ ca_key_path=str(Path(output_dir) / f"{common_name}.key"),
103
+ )
104
+
105
+ cert_manager = CertificateManager(cert_config)
106
+
107
+ # Generate CA certificate
108
+ ca_pair = cert_manager.create_ca_certificate(ca_config)
109
+
110
+ return {
111
+ "cert_path": str(ca_pair.cert_path),
112
+ "key_path": str(ca_pair.key_path),
113
+ }
114
+
115
+ except Exception as e:
116
+ logger.error(f"Failed to create CA certificate: {e}")
117
+ raise
118
+
119
+ @staticmethod
120
+ def _create_ca_certificate_fallback(
121
+ common_name: str, output_dir: str, validity_days: int, key_size: int
122
+ ) -> Dict[str, str]:
123
+ """Fallback CA certificate creation using cryptography."""
124
+ try:
125
+ # Create output directory if it doesn't exist
126
+ Path(output_dir).mkdir(parents=True, exist_ok=True)
127
+
128
+ # Generate private key
129
+ private_key = rsa.generate_private_key(
130
+ public_exponent=65537,
131
+ key_size=key_size,
132
+ )
133
+
134
+ # Create certificate
135
+ subject = issuer = x509.Name([
136
+ x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
137
+ x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Default State"),
138
+ x509.NameAttribute(NameOID.LOCALITY_NAME, "Default City"),
139
+ x509.NameAttribute(NameOID.ORGANIZATION_NAME, "MCP Proxy Adapter CA"),
140
+ x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "Certificate Authority"),
141
+ x509.NameAttribute(NameOID.COMMON_NAME, common_name),
142
+ ])
143
+
144
+ cert = x509.CertificateBuilder().subject_name(
145
+ subject
146
+ ).issuer_name(
147
+ issuer
148
+ ).public_key(
149
+ private_key.public_key()
150
+ ).serial_number(
151
+ x509.random_serial_number()
152
+ ).not_valid_before(
153
+ datetime.now(timezone.utc)
154
+ ).not_valid_after(
155
+ datetime.now(timezone.utc) + timedelta(days=validity_days)
156
+ ).add_extension(
157
+ x509.BasicConstraints(ca=True, path_length=None),
158
+ critical=True,
159
+ ).add_extension(
160
+ x509.KeyUsage(
161
+ key_cert_sign=True,
162
+ crl_sign=True,
163
+ digital_signature=True,
164
+ key_encipherment=False,
165
+ data_encipherment=False,
166
+ key_agreement=False,
167
+ encipher_only=False,
168
+ decipher_only=False,
169
+ content_commitment=False,
170
+ ),
171
+ critical=True,
172
+ ).sign(private_key, hashes.SHA256())
173
+
174
+ # Save certificate and key
175
+ cert_path = Path(output_dir) / f"{common_name}.crt"
176
+ key_path = Path(output_dir) / f"{common_name}.key"
177
+
178
+ with open(cert_path, "wb") as f:
179
+ f.write(cert.public_bytes(serialization.Encoding.PEM))
180
+
181
+ with open(key_path, "wb") as f:
182
+ f.write(private_key.private_bytes(
183
+ encoding=serialization.Encoding.PEM,
184
+ format=serialization.PrivateFormat.PKCS8,
185
+ encryption_algorithm=serialization.NoEncryption()
186
+ ))
187
+
188
+ return {
189
+ "cert_path": str(cert_path),
190
+ "key_path": str(key_path),
191
+ }
192
+
193
+ except Exception as e:
194
+ logger.error(f"Failed to create CA certificate (fallback): {e}")
195
+ raise
196
+
197
+ @staticmethod
198
+ def create_server_certificate(
199
+ common_name: str,
200
+ output_dir: str,
201
+ ca_cert_path: str,
202
+ ca_key_path: str,
203
+ validity_days: int = DEFAULT_VALIDITY_DAYS,
204
+ key_size: int = DEFAULT_KEY_SIZE,
205
+ san_dns: Optional[List[str]] = None,
206
+ san_ip: Optional[List[str]] = None,
207
+ ) -> Dict[str, str]:
208
+ """
209
+ Create a server certificate signed by CA.
210
+
211
+ Args:
212
+ common_name: Common name for the server certificate
213
+ output_dir: Directory to save certificate and key files
214
+ ca_cert_path: Path to CA certificate
215
+ ca_key_path: Path to CA private key
216
+ validity_days: Certificate validity period in days
217
+ key_size: RSA key size in bits
218
+ san_dns: List of DNS names for SAN extension
219
+ san_ip: List of IP addresses for SAN extension
220
+
221
+ Returns:
222
+ Dictionary with paths to created files
223
+ """
224
+ if not SECURITY_FRAMEWORK_AVAILABLE:
225
+ logger.warning("mcp_security_framework not available, using fallback method")
226
+ return CertificateCreator._create_server_certificate_fallback(
227
+ common_name, output_dir, ca_cert_path, ca_key_path,
228
+ validity_days, key_size, san_dns, san_ip
229
+ )
230
+
231
+ try:
232
+ # Validate parameters
233
+ if not common_name or not common_name.strip():
234
+ raise ValueError("Common name cannot be empty")
235
+
236
+ if not Path(ca_cert_path).exists():
237
+ raise FileNotFoundError(f"CA certificate not found: {ca_cert_path}")
238
+
239
+ if not Path(ca_key_path).exists():
240
+ raise FileNotFoundError(f"CA key not found: {ca_key_path}")
241
+
242
+ # Create output directory if it doesn't exist
243
+ Path(output_dir).mkdir(parents=True, exist_ok=True)
244
+
245
+ # Configure server certificate using mcp_security_framework
246
+ server_config = ServerCertConfig(
247
+ common_name=common_name,
248
+ organization="MCP Proxy Adapter",
249
+ organizational_unit="Server",
250
+ country="US",
251
+ state="Default State",
252
+ locality="Default City",
253
+ validity_days=validity_days,
254
+ key_size=key_size,
255
+ key_type="RSA",
256
+ san_dns=san_dns or [],
257
+ san_ip=san_ip or [],
258
+ )
259
+
260
+ # Create certificate manager
261
+ cert_config = CertificateConfig(
262
+ output_dir=output_dir,
263
+ ca_cert_path=ca_cert_path,
264
+ ca_key_path=ca_key_path,
265
+ )
266
+
267
+ cert_manager = CertificateManager(cert_config)
268
+
269
+ # Generate server certificate
270
+ server_pair = cert_manager.create_server_certificate(server_config)
271
+
272
+ return {
273
+ "cert_path": str(server_pair.cert_path),
274
+ "key_path": str(server_pair.key_path),
275
+ }
276
+
277
+ except Exception as e:
278
+ logger.error(f"Failed to create server certificate: {e}")
279
+ raise
280
+
281
+ @staticmethod
282
+ def _create_server_certificate_fallback(
283
+ common_name: str,
284
+ output_dir: str,
285
+ ca_cert_path: str,
286
+ ca_key_path: str,
287
+ validity_days: int,
288
+ key_size: int,
289
+ san_dns: Optional[List[str]],
290
+ san_ip: Optional[List[str]],
291
+ ) -> Dict[str, str]:
292
+ """Fallback server certificate creation using cryptography."""
293
+ try:
294
+ # Create output directory if it doesn't exist
295
+ Path(output_dir).mkdir(parents=True, exist_ok=True)
296
+
297
+ # Load CA certificate and key
298
+ with open(ca_cert_path, "rb") as f:
299
+ ca_cert = x509.load_pem_x509_certificate(f.read())
300
+
301
+ with open(ca_key_path, "rb") as f:
302
+ ca_key = serialization.load_pem_private_key(f.read(), password=None)
303
+
304
+ # Generate server private key
305
+ private_key = rsa.generate_private_key(
306
+ public_exponent=65537,
307
+ key_size=key_size,
308
+ )
309
+
310
+ # Create certificate
311
+ subject = x509.Name([
312
+ x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
313
+ x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Default State"),
314
+ x509.NameAttribute(NameOID.LOCALITY_NAME, "Default City"),
315
+ x509.NameAttribute(NameOID.ORGANIZATION_NAME, "MCP Proxy Adapter"),
316
+ x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "Server"),
317
+ x509.NameAttribute(NameOID.COMMON_NAME, common_name),
318
+ ])
319
+
320
+ # Build certificate
321
+ cert_builder = x509.CertificateBuilder().subject_name(
322
+ subject
323
+ ).issuer_name(
324
+ ca_cert.subject
325
+ ).public_key(
326
+ private_key.public_key()
327
+ ).serial_number(
328
+ x509.random_serial_number()
329
+ ).not_valid_before(
330
+ datetime.now(timezone.utc)
331
+ ).not_valid_after(
332
+ datetime.now(timezone.utc) + timedelta(days=validity_days)
333
+ )
334
+
335
+ # Add SAN extension if provided
336
+ if san_dns or san_ip:
337
+ san_list = []
338
+ if san_dns:
339
+ san_list.extend([x509.DNSName(name) for name in san_dns])
340
+ if san_ip:
341
+ san_list.extend([x509.IPAddress(ip) for ip in san_ip])
342
+
343
+ cert_builder = cert_builder.add_extension(
344
+ x509.SubjectAlternativeName(san_list),
345
+ critical=False,
346
+ )
347
+
348
+ cert = cert_builder.sign(ca_key, hashes.SHA256())
349
+
350
+ # Save certificate and key
351
+ cert_path = Path(output_dir) / f"{common_name}_server.crt"
352
+ key_path = Path(output_dir) / f"{common_name}_server.key"
353
+
354
+ with open(cert_path, "wb") as f:
355
+ f.write(cert.public_bytes(serialization.Encoding.PEM))
356
+
357
+ with open(key_path, "wb") as f:
358
+ f.write(private_key.private_bytes(
359
+ encoding=serialization.Encoding.PEM,
360
+ format=serialization.PrivateFormat.PKCS8,
361
+ encryption_algorithm=serialization.NoEncryption()
362
+ ))
363
+
364
+ return {
365
+ "cert_path": str(cert_path),
366
+ "key_path": str(key_path),
367
+ }
368
+
369
+ except Exception as e:
370
+ logger.error(f"Failed to create server certificate (fallback): {e}")
371
+ raise