mcp-proxy-adapter 6.0.0__py3-none-any.whl → 6.1.1__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 (264) 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 +9 -4
  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/protocol_middleware.py +32 -13
  7. mcp_proxy_adapter/api/middleware/unified_security.py +160 -0
  8. mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
  9. mcp_proxy_adapter/commands/__init__.py +7 -1
  10. mcp_proxy_adapter/commands/base.py +7 -4
  11. mcp_proxy_adapter/commands/builtin_commands.py +8 -2
  12. mcp_proxy_adapter/commands/command_registry.py +8 -0
  13. mcp_proxy_adapter/commands/echo_command.py +81 -0
  14. mcp_proxy_adapter/commands/help_command.py +21 -14
  15. mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
  16. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  17. mcp_proxy_adapter/commands/security_command.py +488 -0
  18. mcp_proxy_adapter/commands/ssl_setup_command.py +2 -2
  19. mcp_proxy_adapter/commands/token_management_command.py +1 -1
  20. mcp_proxy_adapter/config.py +81 -21
  21. mcp_proxy_adapter/core/app_factory.py +326 -0
  22. mcp_proxy_adapter/core/client_security.py +384 -0
  23. mcp_proxy_adapter/core/logging.py +8 -3
  24. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  25. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  26. mcp_proxy_adapter/core/protocol_manager.py +139 -8
  27. mcp_proxy_adapter/core/proxy_client.py +602 -0
  28. mcp_proxy_adapter/core/proxy_registration.py +299 -47
  29. mcp_proxy_adapter/core/security_adapter.py +12 -15
  30. mcp_proxy_adapter/core/security_integration.py +285 -0
  31. mcp_proxy_adapter/core/server_adapter.py +345 -0
  32. mcp_proxy_adapter/core/server_engine.py +364 -0
  33. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  34. mcp_proxy_adapter/docs/EN/TROUBLESHOOTING.md +285 -0
  35. mcp_proxy_adapter/docs/RU/TROUBLESHOOTING.md +285 -0
  36. mcp_proxy_adapter/examples/README.md +230 -97
  37. mcp_proxy_adapter/examples/README_EN.md +258 -0
  38. mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
  39. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
  40. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
  41. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +43 -0
  42. mcp_proxy_adapter/examples/basic_framework/configs/https_no_protocol_middleware.json +36 -0
  43. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +29 -0
  44. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_protocol_middleware.json +34 -0
  45. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
  46. mcp_proxy_adapter/examples/basic_framework/configs/mtls_simple.json +35 -0
  47. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
  48. mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
  49. mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
  50. mcp_proxy_adapter/examples/cert_config.json +9 -0
  51. mcp_proxy_adapter/examples/certs/admin.crt +32 -0
  52. mcp_proxy_adapter/examples/certs/admin.key +52 -0
  53. mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
  54. mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
  55. mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
  56. mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
  57. mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
  58. mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
  59. mcp_proxy_adapter/examples/certs/client.crt +32 -0
  60. mcp_proxy_adapter/examples/certs/client.key +52 -0
  61. mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
  62. mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
  63. mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
  64. mcp_proxy_adapter/examples/certs/client_user.key +52 -0
  65. mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
  66. mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
  67. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
  68. mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
  69. mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
  70. mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
  71. mcp_proxy_adapter/examples/certs/readonly.key +52 -0
  72. mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
  73. mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
  74. mcp_proxy_adapter/examples/certs/server.crt +32 -0
  75. mcp_proxy_adapter/examples/certs/server.key +52 -0
  76. mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
  77. mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
  78. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
  79. mcp_proxy_adapter/examples/certs/user.crt +32 -0
  80. mcp_proxy_adapter/examples/certs/user.key +52 -0
  81. mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
  82. mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
  83. mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
  84. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
  85. mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
  86. mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
  87. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
  88. mcp_proxy_adapter/examples/commands/__init__.py +1 -0
  89. mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
  90. mcp_proxy_adapter/examples/debug_request_state.py +144 -0
  91. mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
  92. mcp_proxy_adapter/examples/demo_client.py +341 -0
  93. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
  94. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
  95. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
  96. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
  97. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
  98. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
  99. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
  100. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
  101. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
  102. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
  103. mcp_proxy_adapter/examples/full_application/main.py +138 -0
  104. mcp_proxy_adapter/examples/full_application/roles.json +21 -0
  105. mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
  106. mcp_proxy_adapter/examples/generate_certificates.py +121 -0
  107. mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
  108. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
  109. mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
  110. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
  111. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
  112. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
  113. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
  114. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
  115. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
  116. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
  117. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
  118. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
  119. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
  120. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
  121. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
  122. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
  123. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
  124. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
  125. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
  126. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
  127. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
  128. mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
  129. mcp_proxy_adapter/examples/roles.json +38 -0
  130. mcp_proxy_adapter/examples/run_example.py +81 -0
  131. mcp_proxy_adapter/examples/run_security_tests.py +326 -0
  132. mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
  133. mcp_proxy_adapter/examples/security_test_client.py +743 -0
  134. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
  135. mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
  136. mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
  137. mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
  138. mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
  139. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
  140. mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
  141. mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
  142. mcp_proxy_adapter/examples/test_config_generator.py +110 -0
  143. mcp_proxy_adapter/examples/test_examples.py +344 -0
  144. mcp_proxy_adapter/examples/universal_client.py +628 -0
  145. mcp_proxy_adapter/main.py +21 -10
  146. mcp_proxy_adapter/utils/config_generator.py +727 -0
  147. mcp_proxy_adapter/version.py +5 -2
  148. mcp_proxy_adapter-6.1.1.dist-info/METADATA +205 -0
  149. mcp_proxy_adapter-6.1.1.dist-info/RECORD +197 -0
  150. mcp_proxy_adapter-6.1.1.dist-info/entry_points.txt +2 -0
  151. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/licenses/LICENSE +2 -2
  152. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  153. mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
  154. mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
  155. mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
  156. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  157. mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
  158. mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
  159. mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
  160. mcp_proxy_adapter/api/middleware/security.py +0 -376
  161. mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
  162. mcp_proxy_adapter/examples/__init__.py +0 -7
  163. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  164. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  165. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  166. mcp_proxy_adapter/examples/basic_server/config.json +0 -70
  167. mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
  168. mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
  169. mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
  170. mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
  171. mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
  172. mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
  173. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  174. mcp_proxy_adapter/examples/basic_server/server.py +0 -114
  175. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  176. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  177. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
  178. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  179. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  180. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  181. mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
  182. mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
  183. mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
  184. mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
  185. mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
  186. mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
  187. mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
  188. mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
  189. mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
  190. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  191. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  192. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  193. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  194. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  195. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  196. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  197. mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
  198. mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
  199. mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
  200. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  201. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  202. mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
  203. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  204. mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
  205. mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
  206. mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
  207. mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
  208. mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
  209. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  210. mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
  211. mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
  212. mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
  213. mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
  214. mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
  215. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  216. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  217. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  218. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  219. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  220. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  221. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  222. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  223. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  224. mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
  225. mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
  226. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  227. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  228. mcp_proxy_adapter/schemas/roles_schema.json +0 -162
  229. mcp_proxy_adapter/tests/__init__.py +0 -0
  230. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  231. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  232. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  233. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  234. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  235. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  236. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  237. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  238. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  239. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  240. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  241. mcp_proxy_adapter/tests/conftest.py +0 -131
  242. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  243. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  244. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  245. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  246. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  247. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  248. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  249. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  250. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  251. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  252. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  253. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  254. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  255. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  256. mcp_proxy_adapter/tests/test_config.py +0 -127
  257. mcp_proxy_adapter/tests/test_utils.py +0 -65
  258. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  259. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  260. mcp_proxy_adapter/tests/unit/test_config.py +0 -270
  261. mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
  262. mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
  263. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/WHEEL +0 -0
  264. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.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}")