mcp-proxy-adapter 6.0.0__py3-none-any.whl → 6.1.0__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 (259) hide show
  1. mcp_proxy_adapter/api/app.py +174 -80
  2. mcp_proxy_adapter/api/handlers.py +16 -5
  3. mcp_proxy_adapter/api/middleware/__init__.py +7 -2
  4. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  5. mcp_proxy_adapter/api/middleware/factory.py +36 -12
  6. mcp_proxy_adapter/api/middleware/unified_security.py +152 -0
  7. mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
  8. mcp_proxy_adapter/commands/__init__.py +7 -1
  9. mcp_proxy_adapter/commands/base.py +7 -4
  10. mcp_proxy_adapter/commands/builtin_commands.py +8 -2
  11. mcp_proxy_adapter/commands/command_registry.py +8 -0
  12. mcp_proxy_adapter/commands/echo_command.py +81 -0
  13. mcp_proxy_adapter/commands/help_command.py +21 -14
  14. mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
  15. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  16. mcp_proxy_adapter/commands/security_command.py +488 -0
  17. mcp_proxy_adapter/commands/ssl_setup_command.py +2 -2
  18. mcp_proxy_adapter/commands/token_management_command.py +1 -1
  19. mcp_proxy_adapter/config.py +81 -21
  20. mcp_proxy_adapter/core/app_factory.py +326 -0
  21. mcp_proxy_adapter/core/client_security.py +384 -0
  22. mcp_proxy_adapter/core/logging.py +8 -3
  23. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  24. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  25. mcp_proxy_adapter/core/protocol_manager.py +9 -0
  26. mcp_proxy_adapter/core/proxy_client.py +602 -0
  27. mcp_proxy_adapter/core/proxy_registration.py +299 -47
  28. mcp_proxy_adapter/core/security_adapter.py +12 -15
  29. mcp_proxy_adapter/core/security_integration.py +277 -0
  30. mcp_proxy_adapter/core/server_adapter.py +345 -0
  31. mcp_proxy_adapter/core/server_engine.py +364 -0
  32. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  33. mcp_proxy_adapter/examples/README.md +230 -97
  34. mcp_proxy_adapter/examples/README_EN.md +258 -0
  35. mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
  36. mcp_proxy_adapter/examples/__pycache__/security_configurations.cpython-312.pyc +0 -0
  37. mcp_proxy_adapter/examples/__pycache__/security_test_client.cpython-312.pyc +0 -0
  38. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
  39. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
  40. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +39 -0
  41. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +25 -0
  42. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
  43. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
  44. mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
  45. mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
  46. mcp_proxy_adapter/examples/cert_config.json +9 -0
  47. mcp_proxy_adapter/examples/certs/admin.crt +32 -0
  48. mcp_proxy_adapter/examples/certs/admin.key +52 -0
  49. mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
  50. mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
  51. mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
  52. mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
  53. mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
  54. mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
  55. mcp_proxy_adapter/examples/certs/client.crt +32 -0
  56. mcp_proxy_adapter/examples/certs/client.key +52 -0
  57. mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
  58. mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
  59. mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
  60. mcp_proxy_adapter/examples/certs/client_user.key +52 -0
  61. mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
  62. mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
  63. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
  64. mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
  65. mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
  66. mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
  67. mcp_proxy_adapter/examples/certs/readonly.key +52 -0
  68. mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
  69. mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
  70. mcp_proxy_adapter/examples/certs/server.crt +32 -0
  71. mcp_proxy_adapter/examples/certs/server.key +52 -0
  72. mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
  73. mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
  74. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
  75. mcp_proxy_adapter/examples/certs/user.crt +32 -0
  76. mcp_proxy_adapter/examples/certs/user.key +52 -0
  77. mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
  78. mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
  79. mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
  80. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
  81. mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
  82. mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
  83. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
  84. mcp_proxy_adapter/examples/commands/__init__.py +1 -0
  85. mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
  86. mcp_proxy_adapter/examples/debug_request_state.py +144 -0
  87. mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
  88. mcp_proxy_adapter/examples/demo_client.py +341 -0
  89. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
  90. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
  91. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
  92. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
  93. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
  94. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
  95. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
  96. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
  97. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
  98. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
  99. mcp_proxy_adapter/examples/full_application/main.py +138 -0
  100. mcp_proxy_adapter/examples/full_application/roles.json +21 -0
  101. mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
  102. mcp_proxy_adapter/examples/generate_certificates.py +121 -0
  103. mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
  104. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
  105. mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
  106. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
  107. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
  108. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
  109. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
  110. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
  111. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
  112. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
  113. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
  114. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
  115. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
  116. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
  117. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
  118. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
  119. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
  120. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
  121. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
  122. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
  123. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
  124. mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
  125. mcp_proxy_adapter/examples/roles.json +38 -0
  126. mcp_proxy_adapter/examples/run_example.py +81 -0
  127. mcp_proxy_adapter/examples/run_security_tests.py +326 -0
  128. mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
  129. mcp_proxy_adapter/examples/security_test_client.py +743 -0
  130. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
  131. mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
  132. mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
  133. mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
  134. mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
  135. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
  136. mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
  137. mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
  138. mcp_proxy_adapter/examples/test_examples.py +344 -0
  139. mcp_proxy_adapter/examples/universal_client.py +628 -0
  140. mcp_proxy_adapter/main.py +21 -10
  141. mcp_proxy_adapter/utils/config_generator.py +639 -0
  142. mcp_proxy_adapter/version.py +2 -1
  143. mcp_proxy_adapter-6.1.0.dist-info/METADATA +205 -0
  144. mcp_proxy_adapter-6.1.0.dist-info/RECORD +193 -0
  145. mcp_proxy_adapter-6.1.0.dist-info/entry_points.txt +2 -0
  146. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/licenses/LICENSE +2 -2
  147. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  148. mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
  149. mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
  150. mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
  151. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  152. mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
  153. mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
  154. mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
  155. mcp_proxy_adapter/api/middleware/security.py +0 -376
  156. mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
  157. mcp_proxy_adapter/examples/__init__.py +0 -7
  158. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  159. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  160. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  161. mcp_proxy_adapter/examples/basic_server/config.json +0 -70
  162. mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
  163. mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
  164. mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
  165. mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
  166. mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
  167. mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
  168. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  169. mcp_proxy_adapter/examples/basic_server/server.py +0 -114
  170. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  171. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  172. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
  173. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  174. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  175. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  176. mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
  177. mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
  178. mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
  179. mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
  180. mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
  181. mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
  182. mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
  183. mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
  184. mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
  185. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  186. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  187. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  188. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  189. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  190. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  191. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  192. mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
  193. mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
  194. mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
  195. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  196. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  197. mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
  198. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  199. mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
  200. mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
  201. mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
  202. mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
  203. mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
  204. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  205. mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
  206. mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
  207. mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
  208. mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
  209. mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
  210. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  211. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  212. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  213. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  214. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  215. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  216. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  217. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  218. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  219. mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
  220. mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
  221. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  222. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  223. mcp_proxy_adapter/schemas/roles_schema.json +0 -162
  224. mcp_proxy_adapter/tests/__init__.py +0 -0
  225. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  226. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  227. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  228. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  229. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  230. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  231. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  232. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  233. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  234. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  235. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  236. mcp_proxy_adapter/tests/conftest.py +0 -131
  237. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  238. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  239. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  240. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  241. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  242. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  243. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  244. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  245. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  246. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  247. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  248. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  249. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  250. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  251. mcp_proxy_adapter/tests/test_config.py +0 -127
  252. mcp_proxy_adapter/tests/test_utils.py +0 -65
  253. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  254. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  255. mcp_proxy_adapter/tests/unit/test_config.py +0 -270
  256. mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
  257. mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
  258. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/WHEEL +0 -0
  259. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,628 @@
1
+ """
2
+ Universal Client Example
3
+
4
+ This module demonstrates all possible secure connection methods to the server
5
+ using the mcp_security_framework. The client supports all authentication methods
6
+ and connection types supported by the security framework.
7
+
8
+ Author: Vasiliy Zdanovskiy
9
+ email: vasilyvz@gmail.com
10
+ """
11
+
12
+ import asyncio
13
+ import json
14
+ import ssl
15
+ import time
16
+ from typing import Dict, Any, Optional, List, Union
17
+ from urllib.parse import urljoin
18
+ from pathlib import Path
19
+
20
+ import aiohttp
21
+ import requests
22
+ from requests.exceptions import RequestException
23
+
24
+ # Import security framework components
25
+ try:
26
+ from mcp_security_framework import SecurityManager, AuthManager, CertificateManager
27
+ from mcp_security_framework.utils import generate_api_key, create_jwt_token, validate_jwt_token
28
+ from mcp_security_framework.utils import extract_roles_from_cert, validate_certificate_chain
29
+ from mcp_security_framework.utils import create_ssl_context, validate_server_certificate
30
+ SECURITY_FRAMEWORK_AVAILABLE = True
31
+ except ImportError:
32
+ SECURITY_FRAMEWORK_AVAILABLE = False
33
+ print("Warning: mcp_security_framework not available. Using basic HTTP client.")
34
+
35
+
36
+ class UniversalClient:
37
+ """
38
+ Universal client that demonstrates all possible secure connection methods.
39
+
40
+ Supports:
41
+ - HTTP/HTTPS connections
42
+ - API Key authentication
43
+ - JWT token authentication
44
+ - Certificate-based authentication
45
+ - SSL/TLS with custom certificates
46
+ - Role-based access control
47
+ - Rate limiting awareness
48
+ """
49
+
50
+ def __init__(self, config: Dict[str, Any]):
51
+ """
52
+ Initialize universal client with configuration.
53
+
54
+ Args:
55
+ config: Client configuration with security settings
56
+ """
57
+ self.config = config
58
+ self.base_url = config.get("server_url", "http://localhost:8000")
59
+ self.timeout = config.get("timeout", 30)
60
+ self.retry_attempts = config.get("retry_attempts", 3)
61
+ self.retry_delay = config.get("retry_delay", 1)
62
+
63
+ # Security configuration
64
+ self.security_config = config.get("security", {})
65
+ self.auth_method = self.security_config.get("auth_method", "none")
66
+
67
+ # Initialize security managers if framework is available
68
+ self.security_manager = None
69
+ self.auth_manager = None
70
+ self.cert_manager = None
71
+
72
+ if SECURITY_FRAMEWORK_AVAILABLE:
73
+ self._initialize_security_managers()
74
+
75
+ # Session management
76
+ self.session: Optional[aiohttp.ClientSession] = None
77
+ self.current_token: Optional[str] = None
78
+ self.token_expiry: Optional[float] = None
79
+
80
+ print(f"Universal client initialized with auth method: {self.auth_method}")
81
+
82
+ def _initialize_security_managers(self) -> None:
83
+ """Initialize security framework managers."""
84
+ try:
85
+ # Initialize security manager
86
+ self.security_manager = SecurityManager(self.security_config)
87
+
88
+ # Initialize auth manager
89
+ auth_config = self.security_config.get("auth", {})
90
+ self.auth_manager = AuthManager(auth_config)
91
+
92
+ # Initialize certificate manager
93
+ cert_config = self.security_config.get("certificates", {})
94
+ self.cert_manager = CertificateManager(cert_config)
95
+
96
+ print("Security framework managers initialized successfully")
97
+ except Exception as e:
98
+ print(f"Warning: Failed to initialize security managers: {e}")
99
+
100
+ async def __aenter__(self):
101
+ """Async context manager entry."""
102
+ await self.connect()
103
+ return self
104
+
105
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
106
+ """Async context manager exit."""
107
+ await self.disconnect()
108
+
109
+ async def connect(self) -> None:
110
+ """Establish connection with authentication."""
111
+ print(f"Connecting to {self.base_url} with {self.auth_method} authentication...")
112
+
113
+ # Perform authentication based on method
114
+ if self.auth_method == "api_key":
115
+ await self._authenticate_api_key()
116
+ elif self.auth_method == "jwt":
117
+ await self._authenticate_jwt()
118
+ elif self.auth_method == "certificate":
119
+ await self._authenticate_certificate()
120
+ elif self.auth_method == "basic":
121
+ await self._authenticate_basic()
122
+ else:
123
+ print("No authentication required")
124
+
125
+ print("Connection established successfully")
126
+
127
+ async def disconnect(self) -> None:
128
+ """Close connection and cleanup."""
129
+ if self.session:
130
+ await self.session.close()
131
+ self.session = None
132
+ print("Connection closed")
133
+
134
+ async def _authenticate_api_key(self) -> None:
135
+ """Authenticate using API key."""
136
+ api_key_config = self.security_config.get("api_key", {})
137
+ api_key = api_key_config.get("key")
138
+
139
+ if not api_key:
140
+ raise ValueError("API key not provided in configuration")
141
+
142
+ # Store API key for requests
143
+ self.current_token = api_key
144
+ print(f"Authenticated with API key: {api_key[:8]}...")
145
+
146
+ async def _authenticate_jwt(self) -> None:
147
+ """Authenticate using JWT token."""
148
+ jwt_config = self.security_config.get("jwt", {})
149
+
150
+ # Check if we have a stored token that's still valid
151
+ if self.current_token and self.token_expiry and time.time() < self.token_expiry:
152
+ print("Using existing JWT token")
153
+ return
154
+
155
+ # Get credentials for JWT
156
+ username = jwt_config.get("username")
157
+ password = jwt_config.get("password")
158
+ secret = jwt_config.get("secret")
159
+
160
+ if not all([username, password, secret]):
161
+ raise ValueError("JWT credentials not provided in configuration")
162
+
163
+ # Create JWT token
164
+ if SECURITY_FRAMEWORK_AVAILABLE:
165
+ self.current_token = create_jwt_token(
166
+ username,
167
+ secret,
168
+ expiry_hours=jwt_config.get("expiry_hours", 24)
169
+ )
170
+ else:
171
+ # Simple JWT creation (for demonstration)
172
+ import jwt
173
+ payload = {
174
+ "username": username,
175
+ "exp": time.time() + (jwt_config.get("expiry_hours", 24) * 3600)
176
+ }
177
+ self.current_token = jwt.encode(payload, secret, algorithm="HS256")
178
+
179
+ self.token_expiry = time.time() + (jwt_config.get("expiry_hours", 24) * 3600)
180
+ print(f"Authenticated with JWT token: {self.current_token[:20]}...")
181
+
182
+ async def _authenticate_certificate(self) -> None:
183
+ """Authenticate using client certificate."""
184
+ cert_config = self.security_config.get("certificate", {})
185
+
186
+ cert_file = cert_config.get("cert_file")
187
+ key_file = cert_config.get("key_file")
188
+
189
+ if not cert_file or not key_file:
190
+ raise ValueError("Certificate files not provided in configuration")
191
+
192
+ # Validate certificate
193
+ if SECURITY_FRAMEWORK_AVAILABLE and self.cert_manager:
194
+ try:
195
+ cert_info = self.cert_manager.validate_certificate(cert_file, key_file)
196
+ print(f"Certificate validated: {cert_info.get('subject', 'Unknown')}")
197
+
198
+ # Extract roles from certificate
199
+ roles = extract_roles_from_cert(cert_file)
200
+ if roles:
201
+ print(f"Certificate roles: {roles}")
202
+ except Exception as e:
203
+ print(f"Warning: Certificate validation failed: {e}")
204
+
205
+ print("Certificate authentication prepared")
206
+
207
+ async def _authenticate_basic(self) -> None:
208
+ """Authenticate using basic authentication."""
209
+ basic_config = self.security_config.get("basic", {})
210
+ username = basic_config.get("username")
211
+ password = basic_config.get("password")
212
+
213
+ if not username or not password:
214
+ raise ValueError("Basic auth credentials not provided in configuration")
215
+
216
+ import base64
217
+ credentials = base64.b64encode(f"{username}:{password}".encode()).decode()
218
+ self.current_token = f"Basic {credentials}"
219
+ print(f"Authenticated with basic auth: {username}")
220
+
221
+ def _get_auth_headers(self) -> Dict[str, str]:
222
+ """Get authentication headers for requests."""
223
+ headers = {"Content-Type": "application/json"}
224
+
225
+ if not self.current_token:
226
+ return headers
227
+
228
+ if self.auth_method == "api_key":
229
+ api_key_config = self.security_config.get("api_key", {})
230
+ header_name = api_key_config.get("header", "X-API-Key")
231
+ headers[header_name] = self.current_token
232
+ elif self.auth_method == "jwt":
233
+ headers["Authorization"] = f"Bearer {self.current_token}"
234
+ elif self.auth_method == "basic":
235
+ headers["Authorization"] = self.current_token
236
+
237
+ return headers
238
+
239
+ def _create_ssl_context(self) -> Optional[ssl.SSLContext]:
240
+ """Create SSL context for secure connections."""
241
+ if not SECURITY_FRAMEWORK_AVAILABLE:
242
+ return None
243
+
244
+ try:
245
+ ssl_config = self.security_config.get("ssl", {})
246
+
247
+ if not ssl_config.get("enabled", False):
248
+ return None
249
+
250
+ # Create SSL context using security framework
251
+ if self.security_manager:
252
+ return self.security_manager.create_client_ssl_context()
253
+
254
+ # Fallback SSL context creation
255
+ context = ssl.create_default_context()
256
+
257
+ # Add client certificate if provided
258
+ cert_config = self.security_config.get("certificate", {})
259
+ if cert_config.get("enabled", False):
260
+ cert_file = cert_config.get("cert_file")
261
+ key_file = cert_config.get("key_file")
262
+ if cert_file and key_file:
263
+ context.load_cert_chain(cert_file, key_file)
264
+
265
+ # Add CA certificate if provided
266
+ ca_cert_file = ssl_config.get("ca_cert_file")
267
+ if ca_cert_file:
268
+ context.load_verify_locations(ca_cert_file)
269
+
270
+ # Configure verification
271
+ if ssl_config.get("check_hostname", True):
272
+ context.check_hostname = True
273
+ context.verify_mode = ssl.CERT_REQUIRED
274
+ else:
275
+ context.check_hostname = False
276
+ context.verify_mode = ssl.CERT_NONE
277
+
278
+ return context
279
+ except Exception as e:
280
+ print(f"Warning: Failed to create SSL context: {e}")
281
+ return None
282
+
283
+ async def request(
284
+ self,
285
+ method: str,
286
+ endpoint: str,
287
+ data: Optional[Dict[str, Any]] = None,
288
+ headers: Optional[Dict[str, str]] = None
289
+ ) -> Dict[str, Any]:
290
+ """
291
+ Make authenticated request to server.
292
+
293
+ Args:
294
+ method: HTTP method (GET, POST, etc.)
295
+ endpoint: API endpoint
296
+ data: Request data
297
+ headers: Additional headers
298
+
299
+ Returns:
300
+ Response data
301
+ """
302
+ url = urljoin(self.base_url, endpoint)
303
+
304
+ # Prepare headers
305
+ request_headers = self._get_auth_headers()
306
+ if headers:
307
+ request_headers.update(headers)
308
+
309
+ # Create SSL context
310
+ ssl_context = self._create_ssl_context()
311
+
312
+ # Create connector with SSL context
313
+ connector = None
314
+ if ssl_context:
315
+ connector = aiohttp.TCPConnector(ssl=ssl_context)
316
+
317
+ try:
318
+ async with aiohttp.ClientSession(connector=connector) as session:
319
+ for attempt in range(self.retry_attempts):
320
+ try:
321
+ async with session.request(
322
+ method,
323
+ url,
324
+ json=data,
325
+ headers=request_headers,
326
+ timeout=aiohttp.ClientTimeout(total=self.timeout)
327
+ ) as response:
328
+ result = await response.json()
329
+
330
+ # Validate response if security framework available
331
+ if SECURITY_FRAMEWORK_AVAILABLE and self.security_manager:
332
+ self.security_manager.validate_server_response(dict(response.headers))
333
+
334
+ if response.status >= 400:
335
+ print(f"Request failed with status {response.status}: {result}")
336
+ return {"error": result, "status": response.status}
337
+
338
+ return result
339
+
340
+ except Exception as e:
341
+ print(f"Request attempt {attempt + 1} failed: {e}")
342
+ if attempt < self.retry_attempts - 1:
343
+ await asyncio.sleep(self.retry_delay)
344
+ else:
345
+ raise
346
+ finally:
347
+ if connector:
348
+ await connector.close()
349
+
350
+ async def get(self, endpoint: str, **kwargs) -> Dict[str, Any]:
351
+ """Make GET request."""
352
+ return await self.request("GET", endpoint, **kwargs)
353
+
354
+ async def post(self, endpoint: str, data: Dict[str, Any], **kwargs) -> Dict[str, Any]:
355
+ """Make POST request."""
356
+ return await self.request("POST", endpoint, data=data, **kwargs)
357
+
358
+ async def put(self, endpoint: str, data: Dict[str, Any], **kwargs) -> Dict[str, Any]:
359
+ """Make PUT request."""
360
+ return await self.request("PUT", endpoint, data=data, **kwargs)
361
+
362
+ async def delete(self, endpoint: str, **kwargs) -> Dict[str, Any]:
363
+ """Make DELETE request."""
364
+ return await self.request("DELETE", endpoint, **kwargs)
365
+
366
+ async def test_connection(self) -> bool:
367
+ """Test connection to server."""
368
+ try:
369
+ result = await self.get("/health")
370
+ if "error" not in result:
371
+ print("✅ Connection test successful")
372
+ return True
373
+ else:
374
+ print(f"❌ Connection test failed: {result}")
375
+ return False
376
+ except Exception as e:
377
+ print(f"❌ Connection test failed: {e}")
378
+ return False
379
+
380
+ async def test_security_features(self) -> Dict[str, bool]:
381
+ """Test various security features."""
382
+ results = {}
383
+
384
+ # Test basic connectivity
385
+ results["connectivity"] = await self.test_connection()
386
+
387
+ # Test authentication
388
+ if self.auth_method != "none":
389
+ try:
390
+ result = await self.get("/api/auth/status")
391
+ results["authentication"] = "error" not in result
392
+ except:
393
+ results["authentication"] = False
394
+
395
+ # Test SSL/TLS
396
+ if self.base_url.startswith("https"):
397
+ results["ssl_tls"] = True
398
+ else:
399
+ results["ssl_tls"] = False
400
+
401
+ # Test certificate validation
402
+ if self.auth_method == "certificate" and SECURITY_FRAMEWORK_AVAILABLE:
403
+ results["certificate_validation"] = True
404
+ else:
405
+ results["certificate_validation"] = False
406
+
407
+ return results
408
+
409
+
410
+ def create_client_config(
411
+ server_url: str,
412
+ auth_method: str = "none",
413
+ **kwargs
414
+ ) -> Dict[str, Any]:
415
+ """
416
+ Create client configuration for different authentication methods.
417
+
418
+ Args:
419
+ server_url: Server URL
420
+ auth_method: Authentication method (none, api_key, jwt, certificate, basic)
421
+ **kwargs: Additional configuration parameters
422
+
423
+ Returns:
424
+ Client configuration dictionary
425
+ """
426
+ config = {
427
+ "server_url": server_url,
428
+ "timeout": 30,
429
+ "retry_attempts": 3,
430
+ "retry_delay": 1,
431
+ "security": {
432
+ "auth_method": auth_method
433
+ }
434
+ }
435
+
436
+ if auth_method == "api_key":
437
+ config["security"]["api_key"] = {
438
+ "key": kwargs.get("api_key", "your_api_key_here"),
439
+ "header": kwargs.get("header", "X-API-Key")
440
+ }
441
+
442
+ elif auth_method == "jwt":
443
+ config["security"]["jwt"] = {
444
+ "username": kwargs.get("username", "user"),
445
+ "password": kwargs.get("password", "password"),
446
+ "secret": kwargs.get("secret", "your_jwt_secret"),
447
+ "expiry_hours": kwargs.get("expiry_hours", 24)
448
+ }
449
+
450
+ elif auth_method == "certificate":
451
+ config["security"]["certificate"] = {
452
+ "enabled": True,
453
+ "cert_file": kwargs.get("cert_file", "./certs/client.crt"),
454
+ "key_file": kwargs.get("key_file", "./keys/client.key"),
455
+ "ca_cert_file": kwargs.get("ca_cert_file", "./certs/ca.crt")
456
+ }
457
+ config["security"]["ssl"] = {
458
+ "enabled": True,
459
+ "check_hostname": kwargs.get("check_hostname", True),
460
+ "ca_cert_file": kwargs.get("ca_cert_file", "./certs/ca.crt")
461
+ }
462
+
463
+ elif auth_method == "basic":
464
+ config["security"]["basic"] = {
465
+ "username": kwargs.get("username", "user"),
466
+ "password": kwargs.get("password", "password")
467
+ }
468
+
469
+ return config
470
+
471
+
472
+ async def demo_all_connection_methods():
473
+ """Demonstrate all possible connection methods."""
474
+ print("🚀 Universal Client Demo - All Connection Methods")
475
+ print("=" * 60)
476
+
477
+ # Test configurations for different auth methods
478
+ test_configs = [
479
+ {
480
+ "name": "No Authentication",
481
+ "config": create_client_config("http://localhost:8000", "none")
482
+ },
483
+ {
484
+ "name": "API Key Authentication",
485
+ "config": create_client_config(
486
+ "http://localhost:8000",
487
+ "api_key",
488
+ api_key="demo_api_key_123"
489
+ )
490
+ },
491
+ {
492
+ "name": "JWT Authentication",
493
+ "config": create_client_config(
494
+ "http://localhost:8000",
495
+ "jwt",
496
+ username="demo_user",
497
+ password="demo_password",
498
+ secret="demo_jwt_secret"
499
+ )
500
+ },
501
+ {
502
+ "name": "Basic Authentication",
503
+ "config": create_client_config(
504
+ "http://localhost:8000",
505
+ "basic",
506
+ username="demo_user",
507
+ password="demo_password"
508
+ )
509
+ },
510
+ {
511
+ "name": "Certificate Authentication (HTTPS)",
512
+ "config": create_client_config(
513
+ "https://localhost:8443",
514
+ "certificate",
515
+ cert_file="./certs/client.crt",
516
+ key_file="./keys/client.key",
517
+ ca_cert_file="./certs/ca.crt"
518
+ )
519
+ }
520
+ ]
521
+
522
+ for test_config in test_configs:
523
+ print(f"\n📋 Testing: {test_config['name']}")
524
+ print("-" * 40)
525
+
526
+ try:
527
+ async with UniversalClient(test_config["config"]) as client:
528
+ # Test connection
529
+ success = await client.test_connection()
530
+
531
+ if success:
532
+ # Test security features
533
+ security_results = await client.test_security_features()
534
+ print("Security Features:")
535
+ for feature, status in security_results.items():
536
+ status_icon = "✅" if status else "❌"
537
+ print(f" {status_icon} {feature}: {status}")
538
+
539
+ # Make a test API call
540
+ try:
541
+ result = await client.get("/api/status")
542
+ print(f"API Status: {result}")
543
+ except Exception as e:
544
+ print(f"API call failed: {e}")
545
+ else:
546
+ print("❌ Connection failed")
547
+
548
+ except Exception as e:
549
+ print(f"❌ Test failed: {e}")
550
+
551
+ print("\n🎉 Demo completed!")
552
+
553
+
554
+ async def demo_specific_connection(auth_method: str, **kwargs):
555
+ """
556
+ Demo specific connection method.
557
+
558
+ Args:
559
+ auth_method: Authentication method to test
560
+ **kwargs: Configuration parameters
561
+ """
562
+ print(f"🚀 Testing {auth_method} connection")
563
+ print("=" * 40)
564
+
565
+ config = create_client_config("http://localhost:8000", auth_method, **kwargs)
566
+
567
+ async with UniversalClient(config) as client:
568
+ # Test connection
569
+ success = await client.test_connection()
570
+
571
+ if success:
572
+ print("✅ Connection successful!")
573
+
574
+ # Make some API calls
575
+ try:
576
+ # Get server status
577
+ status = await client.get("/api/status")
578
+ print(f"Server Status: {status}")
579
+
580
+ # Test command execution
581
+ command_data = {
582
+ "jsonrpc": "2.0",
583
+ "method": "test_command",
584
+ "params": {"message": "Hello from universal client!"},
585
+ "id": 1
586
+ }
587
+
588
+ result = await client.post("/api/jsonrpc", command_data)
589
+ print(f"Command Result: {result}")
590
+
591
+ except Exception as e:
592
+ print(f"API calls failed: {e}")
593
+ else:
594
+ print("❌ Connection failed")
595
+
596
+
597
+ if __name__ == "__main__":
598
+ import sys
599
+
600
+ if len(sys.argv) > 1:
601
+ # Test specific auth method
602
+ auth_method = sys.argv[1]
603
+ kwargs = {}
604
+
605
+ if auth_method == "api_key":
606
+ kwargs["api_key"] = "demo_key_123"
607
+ elif auth_method == "jwt":
608
+ kwargs.update({
609
+ "username": "demo_user",
610
+ "password": "demo_password",
611
+ "secret": "demo_secret"
612
+ })
613
+ elif auth_method == "certificate":
614
+ kwargs.update({
615
+ "cert_file": "./certs/client.crt",
616
+ "key_file": "./keys/client.key",
617
+ "ca_cert_file": "./certs/ca.crt"
618
+ })
619
+ elif auth_method == "basic":
620
+ kwargs.update({
621
+ "username": "demo_user",
622
+ "password": "demo_password"
623
+ })
624
+
625
+ asyncio.run(demo_specific_connection(auth_method, **kwargs))
626
+ else:
627
+ # Demo all connection methods
628
+ asyncio.run(demo_all_connection_methods())
mcp_proxy_adapter/main.py CHANGED
@@ -113,6 +113,14 @@ def main():
113
113
 
114
114
  logger.info("Configuration validation passed")
115
115
 
116
+ # Load commands
117
+ from mcp_proxy_adapter.commands.command_registry import registry
118
+ import asyncio
119
+
120
+ # Reload system to load all commands
121
+ reload_result = asyncio.run(registry.reload_system())
122
+ logger.info(f"Commands loaded: {reload_result}")
123
+
116
124
  # Override settings with command line arguments
117
125
  if args.host:
118
126
  server_settings['host'] = args.host
@@ -154,17 +162,20 @@ def main():
154
162
  version="1.0.0"
155
163
  )
156
164
 
157
- # Get SSL configuration for uvicorn
158
- uvicorn_ssl_config = SSLUtils.get_ssl_config_for_uvicorn(ssl_settings)
165
+ # Create unified server configuration
166
+ server_config = {
167
+ "host": server_settings['host'],
168
+ "port": server_settings['port'],
169
+ "log_level": server_settings.get('log_level', 'info'),
170
+ "ssl": ssl_settings
171
+ }
159
172
 
160
- # Run the server
161
- uvicorn.run(
162
- app,
163
- host=server_settings['host'],
164
- port=server_settings['port'],
165
- log_level=server_settings.get('log_level', 'info').lower(),
166
- **uvicorn_ssl_config
167
- )
173
+ # Use unified server runner
174
+ from mcp_proxy_adapter.core.server_adapter import UnifiedServerRunner
175
+ server_runner = UnifiedServerRunner()
176
+
177
+ # Run the server with optimal engine selection
178
+ server_runner.run_server(app, server_config)
168
179
 
169
180
  except Exception as e:
170
181
  logger.error(f"Failed to start server: {e}")