mcp-proxy-adapter 2.0.1__py3-none-any.whl → 6.9.50__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 (269) hide show
  1. mcp_proxy_adapter/__init__.py +47 -0
  2. mcp_proxy_adapter/__main__.py +13 -0
  3. mcp_proxy_adapter/api/__init__.py +0 -0
  4. mcp_proxy_adapter/api/app.py +66 -0
  5. mcp_proxy_adapter/api/core/__init__.py +18 -0
  6. mcp_proxy_adapter/api/core/app_factory.py +400 -0
  7. mcp_proxy_adapter/api/core/lifespan_manager.py +55 -0
  8. mcp_proxy_adapter/api/core/registration_context.py +356 -0
  9. mcp_proxy_adapter/api/core/registration_manager.py +307 -0
  10. mcp_proxy_adapter/api/core/registration_tasks.py +84 -0
  11. mcp_proxy_adapter/api/core/ssl_context_factory.py +88 -0
  12. mcp_proxy_adapter/api/handlers.py +181 -0
  13. mcp_proxy_adapter/api/middleware/__init__.py +21 -0
  14. mcp_proxy_adapter/api/middleware/base.py +54 -0
  15. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +73 -0
  16. mcp_proxy_adapter/api/middleware/error_handling.py +76 -0
  17. mcp_proxy_adapter/api/middleware/factory.py +147 -0
  18. mcp_proxy_adapter/api/middleware/logging.py +31 -0
  19. mcp_proxy_adapter/api/middleware/performance.py +51 -0
  20. mcp_proxy_adapter/api/middleware/protocol_middleware.py +140 -0
  21. mcp_proxy_adapter/api/middleware/transport_middleware.py +87 -0
  22. mcp_proxy_adapter/api/middleware/unified_security.py +223 -0
  23. mcp_proxy_adapter/api/middleware/user_info_middleware.py +132 -0
  24. mcp_proxy_adapter/api/openapi/__init__.py +21 -0
  25. mcp_proxy_adapter/api/openapi/command_integration.py +105 -0
  26. mcp_proxy_adapter/api/openapi/openapi_generator.py +40 -0
  27. mcp_proxy_adapter/api/openapi/openapi_registry.py +62 -0
  28. mcp_proxy_adapter/api/openapi/schema_loader.py +116 -0
  29. mcp_proxy_adapter/api/schemas.py +270 -0
  30. mcp_proxy_adapter/api/tool_integration.py +131 -0
  31. mcp_proxy_adapter/api/tools.py +163 -0
  32. mcp_proxy_adapter/cli/__init__.py +12 -0
  33. mcp_proxy_adapter/cli/commands/__init__.py +15 -0
  34. mcp_proxy_adapter/cli/commands/client.py +100 -0
  35. mcp_proxy_adapter/cli/commands/config_generate.py +105 -0
  36. mcp_proxy_adapter/cli/commands/config_validate.py +94 -0
  37. mcp_proxy_adapter/cli/commands/generate.py +259 -0
  38. mcp_proxy_adapter/cli/commands/server.py +174 -0
  39. mcp_proxy_adapter/cli/commands/sets.py +132 -0
  40. mcp_proxy_adapter/cli/commands/testconfig.py +177 -0
  41. mcp_proxy_adapter/cli/examples/__init__.py +8 -0
  42. mcp_proxy_adapter/cli/examples/http_basic.py +82 -0
  43. mcp_proxy_adapter/cli/examples/https_token.py +96 -0
  44. mcp_proxy_adapter/cli/examples/mtls_roles.py +103 -0
  45. mcp_proxy_adapter/cli/main.py +63 -0
  46. mcp_proxy_adapter/cli/parser.py +338 -0
  47. mcp_proxy_adapter/cli/validators.py +231 -0
  48. mcp_proxy_adapter/client/jsonrpc_client/__init__.py +9 -0
  49. mcp_proxy_adapter/client/jsonrpc_client/client.py +42 -0
  50. mcp_proxy_adapter/client/jsonrpc_client/command_api.py +45 -0
  51. mcp_proxy_adapter/client/jsonrpc_client/proxy_api.py +224 -0
  52. mcp_proxy_adapter/client/jsonrpc_client/queue_api.py +60 -0
  53. mcp_proxy_adapter/client/jsonrpc_client/transport.py +108 -0
  54. mcp_proxy_adapter/client/proxy.py +123 -0
  55. mcp_proxy_adapter/commands/__init__.py +66 -0
  56. mcp_proxy_adapter/commands/auth_validation_command.py +69 -0
  57. mcp_proxy_adapter/commands/base.py +389 -0
  58. mcp_proxy_adapter/commands/builtin_commands.py +30 -0
  59. mcp_proxy_adapter/commands/catalog/__init__.py +20 -0
  60. mcp_proxy_adapter/commands/catalog/catalog_loader.py +34 -0
  61. mcp_proxy_adapter/commands/catalog/catalog_manager.py +122 -0
  62. mcp_proxy_adapter/commands/catalog/catalog_syncer.py +149 -0
  63. mcp_proxy_adapter/commands/catalog/command_catalog.py +43 -0
  64. mcp_proxy_adapter/commands/catalog/dependency_manager.py +37 -0
  65. mcp_proxy_adapter/commands/catalog_manager.py +97 -0
  66. mcp_proxy_adapter/commands/cert_monitor_command.py +552 -0
  67. mcp_proxy_adapter/commands/certificate_management_command.py +562 -0
  68. mcp_proxy_adapter/commands/command_registry.py +298 -0
  69. mcp_proxy_adapter/commands/config_command.py +102 -0
  70. mcp_proxy_adapter/commands/dependency_container.py +40 -0
  71. mcp_proxy_adapter/commands/dependency_manager.py +143 -0
  72. mcp_proxy_adapter/commands/echo_command.py +48 -0
  73. mcp_proxy_adapter/commands/health_command.py +142 -0
  74. mcp_proxy_adapter/commands/help_command.py +175 -0
  75. mcp_proxy_adapter/commands/hooks.py +172 -0
  76. mcp_proxy_adapter/commands/key_management_command.py +484 -0
  77. mcp_proxy_adapter/commands/load_command.py +123 -0
  78. mcp_proxy_adapter/commands/plugins_command.py +246 -0
  79. mcp_proxy_adapter/commands/protocol_management_command.py +216 -0
  80. mcp_proxy_adapter/commands/proxy_registration_command.py +319 -0
  81. mcp_proxy_adapter/commands/queue_commands.py +750 -0
  82. mcp_proxy_adapter/commands/registration_status_command.py +76 -0
  83. mcp_proxy_adapter/commands/registry/__init__.py +18 -0
  84. mcp_proxy_adapter/commands/registry/command_info.py +103 -0
  85. mcp_proxy_adapter/commands/registry/command_loader.py +207 -0
  86. mcp_proxy_adapter/commands/registry/command_manager.py +119 -0
  87. mcp_proxy_adapter/commands/registry/command_registry.py +217 -0
  88. mcp_proxy_adapter/commands/reload_command.py +136 -0
  89. mcp_proxy_adapter/commands/result.py +157 -0
  90. mcp_proxy_adapter/commands/role_test_command.py +99 -0
  91. mcp_proxy_adapter/commands/roles_management_command.py +502 -0
  92. mcp_proxy_adapter/commands/security_command.py +472 -0
  93. mcp_proxy_adapter/commands/settings_command.py +113 -0
  94. mcp_proxy_adapter/commands/ssl_setup_command.py +306 -0
  95. mcp_proxy_adapter/commands/token_management_command.py +500 -0
  96. mcp_proxy_adapter/commands/transport_management_command.py +129 -0
  97. mcp_proxy_adapter/commands/unload_command.py +92 -0
  98. mcp_proxy_adapter/config.py +32 -0
  99. mcp_proxy_adapter/core/__init__.py +8 -0
  100. mcp_proxy_adapter/core/app_factory.py +560 -0
  101. mcp_proxy_adapter/core/app_runner.py +318 -0
  102. mcp_proxy_adapter/core/auth_validator.py +508 -0
  103. mcp_proxy_adapter/core/certificate/__init__.py +20 -0
  104. mcp_proxy_adapter/core/certificate/certificate_creator.py +372 -0
  105. mcp_proxy_adapter/core/certificate/certificate_extractor.py +185 -0
  106. mcp_proxy_adapter/core/certificate/certificate_utils.py +249 -0
  107. mcp_proxy_adapter/core/certificate/certificate_validator.py +481 -0
  108. mcp_proxy_adapter/core/certificate/ssl_context_manager.py +65 -0
  109. mcp_proxy_adapter/core/certificate_utils.py +249 -0
  110. mcp_proxy_adapter/core/client.py +608 -0
  111. mcp_proxy_adapter/core/client_manager.py +271 -0
  112. mcp_proxy_adapter/core/client_security.py +411 -0
  113. mcp_proxy_adapter/core/config/__init__.py +18 -0
  114. mcp_proxy_adapter/core/config/config.py +237 -0
  115. mcp_proxy_adapter/core/config/config_factory.py +22 -0
  116. mcp_proxy_adapter/core/config/config_loader.py +66 -0
  117. mcp_proxy_adapter/core/config/feature_manager.py +31 -0
  118. mcp_proxy_adapter/core/config/simple_config.py +204 -0
  119. mcp_proxy_adapter/core/config/simple_config_generator.py +131 -0
  120. mcp_proxy_adapter/core/config/simple_config_validator.py +476 -0
  121. mcp_proxy_adapter/core/config_converter.py +252 -0
  122. mcp_proxy_adapter/core/config_validator.py +211 -0
  123. mcp_proxy_adapter/core/crl_utils.py +362 -0
  124. mcp_proxy_adapter/core/errors.py +276 -0
  125. mcp_proxy_adapter/core/job_manager.py +54 -0
  126. mcp_proxy_adapter/core/logging.py +250 -0
  127. mcp_proxy_adapter/core/mtls_asgi.py +140 -0
  128. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  129. mcp_proxy_adapter/core/mtls_proxy.py +229 -0
  130. mcp_proxy_adapter/core/mtls_server.py +154 -0
  131. mcp_proxy_adapter/core/protocol_manager.py +232 -0
  132. mcp_proxy_adapter/core/proxy/__init__.py +19 -0
  133. mcp_proxy_adapter/core/proxy/auth_manager.py +26 -0
  134. mcp_proxy_adapter/core/proxy/proxy_registration_manager.py +160 -0
  135. mcp_proxy_adapter/core/proxy/registration_client.py +186 -0
  136. mcp_proxy_adapter/core/proxy/ssl_manager.py +101 -0
  137. mcp_proxy_adapter/core/proxy_client.py +184 -0
  138. mcp_proxy_adapter/core/proxy_registration.py +80 -0
  139. mcp_proxy_adapter/core/role_utils.py +103 -0
  140. mcp_proxy_adapter/core/security_adapter.py +343 -0
  141. mcp_proxy_adapter/core/security_factory.py +96 -0
  142. mcp_proxy_adapter/core/security_integration.py +342 -0
  143. mcp_proxy_adapter/core/server_adapter.py +251 -0
  144. mcp_proxy_adapter/core/server_engine.py +217 -0
  145. mcp_proxy_adapter/core/settings.py +260 -0
  146. mcp_proxy_adapter/core/signal_handler.py +107 -0
  147. mcp_proxy_adapter/core/ssl_utils.py +161 -0
  148. mcp_proxy_adapter/core/transport_manager.py +153 -0
  149. mcp_proxy_adapter/core/unified_config_adapter.py +471 -0
  150. mcp_proxy_adapter/core/utils.py +101 -0
  151. mcp_proxy_adapter/core/validation/__init__.py +21 -0
  152. mcp_proxy_adapter/core/validation/config_validator.py +219 -0
  153. mcp_proxy_adapter/core/validation/file_validator.py +131 -0
  154. mcp_proxy_adapter/core/validation/protocol_validator.py +205 -0
  155. mcp_proxy_adapter/core/validation/security_validator.py +140 -0
  156. mcp_proxy_adapter/core/validation/validation_result.py +27 -0
  157. mcp_proxy_adapter/custom_openapi.py +58 -0
  158. mcp_proxy_adapter/examples/__init__.py +16 -0
  159. mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
  160. mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
  161. mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
  162. mcp_proxy_adapter/examples/basic_framework/main.py +52 -0
  163. mcp_proxy_adapter/examples/bugfix_certificate_config.py +261 -0
  164. mcp_proxy_adapter/examples/cert_manager_bugfix.py +203 -0
  165. mcp_proxy_adapter/examples/check_config.py +413 -0
  166. mcp_proxy_adapter/examples/client_usage_example.py +164 -0
  167. mcp_proxy_adapter/examples/commands/__init__.py +5 -0
  168. mcp_proxy_adapter/examples/config_builder.py +234 -0
  169. mcp_proxy_adapter/examples/config_cli.py +282 -0
  170. mcp_proxy_adapter/examples/create_test_configs.py +174 -0
  171. mcp_proxy_adapter/examples/debug_request_state.py +130 -0
  172. mcp_proxy_adapter/examples/debug_role_chain.py +191 -0
  173. mcp_proxy_adapter/examples/demo_client.py +287 -0
  174. mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
  175. mcp_proxy_adapter/examples/full_application/commands/__init__.py +8 -0
  176. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +45 -0
  177. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +52 -0
  178. mcp_proxy_adapter/examples/full_application/commands/echo_command.py +32 -0
  179. mcp_proxy_adapter/examples/full_application/commands/help_command.py +54 -0
  180. mcp_proxy_adapter/examples/full_application/commands/list_command.py +57 -0
  181. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +5 -0
  182. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +29 -0
  183. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +27 -0
  184. mcp_proxy_adapter/examples/full_application/main.py +311 -0
  185. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +161 -0
  186. mcp_proxy_adapter/examples/full_application/run_mtls.py +252 -0
  187. mcp_proxy_adapter/examples/full_application/run_simple.py +152 -0
  188. mcp_proxy_adapter/examples/full_application/test_minimal_server.py +45 -0
  189. mcp_proxy_adapter/examples/full_application/test_server.py +163 -0
  190. mcp_proxy_adapter/examples/full_application/test_simple_server.py +62 -0
  191. mcp_proxy_adapter/examples/generate_config.py +502 -0
  192. mcp_proxy_adapter/examples/proxy_registration_example.py +335 -0
  193. mcp_proxy_adapter/examples/queue_demo_simple.py +632 -0
  194. mcp_proxy_adapter/examples/queue_integration_example.py +578 -0
  195. mcp_proxy_adapter/examples/queue_server_demo.py +82 -0
  196. mcp_proxy_adapter/examples/queue_server_example.py +85 -0
  197. mcp_proxy_adapter/examples/queue_server_simple.py +173 -0
  198. mcp_proxy_adapter/examples/required_certificates.py +208 -0
  199. mcp_proxy_adapter/examples/run_example.py +77 -0
  200. mcp_proxy_adapter/examples/run_full_test_suite.py +619 -0
  201. mcp_proxy_adapter/examples/run_proxy_server.py +153 -0
  202. mcp_proxy_adapter/examples/run_security_tests_fixed.py +435 -0
  203. mcp_proxy_adapter/examples/security_test/__init__.py +18 -0
  204. mcp_proxy_adapter/examples/security_test/auth_manager.py +14 -0
  205. mcp_proxy_adapter/examples/security_test/ssl_context_manager.py +28 -0
  206. mcp_proxy_adapter/examples/security_test/test_client.py +159 -0
  207. mcp_proxy_adapter/examples/security_test/test_result.py +22 -0
  208. mcp_proxy_adapter/examples/security_test_client.py +72 -0
  209. mcp_proxy_adapter/examples/setup/__init__.py +24 -0
  210. mcp_proxy_adapter/examples/setup/certificate_manager.py +215 -0
  211. mcp_proxy_adapter/examples/setup/config_generator.py +12 -0
  212. mcp_proxy_adapter/examples/setup/config_validator.py +118 -0
  213. mcp_proxy_adapter/examples/setup/environment_setup.py +62 -0
  214. mcp_proxy_adapter/examples/setup/test_files_generator.py +10 -0
  215. mcp_proxy_adapter/examples/setup/test_runner.py +89 -0
  216. mcp_proxy_adapter/examples/setup_test_environment.py +235 -0
  217. mcp_proxy_adapter/examples/simple_protocol_test.py +125 -0
  218. mcp_proxy_adapter/examples/test_chk_hostname_automated.py +211 -0
  219. mcp_proxy_adapter/examples/test_config.py +205 -0
  220. mcp_proxy_adapter/examples/test_config_builder.py +110 -0
  221. mcp_proxy_adapter/examples/test_examples.py +308 -0
  222. mcp_proxy_adapter/examples/test_framework_complete.py +267 -0
  223. mcp_proxy_adapter/examples/test_mcp_server.py +187 -0
  224. mcp_proxy_adapter/examples/test_protocol_examples.py +337 -0
  225. mcp_proxy_adapter/examples/universal_client.py +674 -0
  226. mcp_proxy_adapter/examples/update_config_certificates.py +135 -0
  227. mcp_proxy_adapter/examples/validate_generator_compatibility.py +385 -0
  228. mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +61 -0
  229. mcp_proxy_adapter/integrations/__init__.py +25 -0
  230. mcp_proxy_adapter/integrations/queuemgr_integration.py +462 -0
  231. mcp_proxy_adapter/main.py +311 -0
  232. mcp_proxy_adapter/openapi.py +375 -0
  233. mcp_proxy_adapter/schemas/base_schema.json +114 -0
  234. mcp_proxy_adapter/schemas/openapi_schema.json +314 -0
  235. mcp_proxy_adapter/schemas/roles.json +37 -0
  236. mcp_proxy_adapter/schemas/roles_schema.json +162 -0
  237. mcp_proxy_adapter/version.py +5 -0
  238. mcp_proxy_adapter-6.9.50.dist-info/METADATA +1088 -0
  239. mcp_proxy_adapter-6.9.50.dist-info/RECORD +242 -0
  240. {mcp_proxy_adapter-2.0.1.dist-info → mcp_proxy_adapter-6.9.50.dist-info}/WHEEL +1 -1
  241. mcp_proxy_adapter-6.9.50.dist-info/entry_points.txt +14 -0
  242. mcp_proxy_adapter-6.9.50.dist-info/top_level.txt +1 -0
  243. adapters/__init__.py +0 -16
  244. analyzers/__init__.py +0 -14
  245. analyzers/docstring_analyzer.py +0 -199
  246. analyzers/type_analyzer.py +0 -151
  247. cli/__init__.py +0 -12
  248. cli/__main__.py +0 -79
  249. cli/command_runner.py +0 -233
  250. dispatchers/__init__.py +0 -14
  251. dispatchers/base_dispatcher.py +0 -85
  252. dispatchers/json_rpc_dispatcher.py +0 -198
  253. generators/__init__.py +0 -14
  254. generators/endpoint_generator.py +0 -172
  255. generators/openapi_generator.py +0 -254
  256. generators/rest_api_generator.py +0 -207
  257. mcp_proxy_adapter-2.0.1.dist-info/METADATA +0 -272
  258. mcp_proxy_adapter-2.0.1.dist-info/RECORD +0 -28
  259. mcp_proxy_adapter-2.0.1.dist-info/licenses/LICENSE +0 -21
  260. mcp_proxy_adapter-2.0.1.dist-info/top_level.txt +0 -7
  261. openapi_schema/__init__.py +0 -38
  262. openapi_schema/command_registry.py +0 -312
  263. openapi_schema/rest_schema.py +0 -510
  264. openapi_schema/rpc_generator.py +0 -307
  265. openapi_schema/rpc_schema.py +0 -416
  266. validators/__init__.py +0 -14
  267. validators/base_validator.py +0 -23
  268. validators/docstring_validator.py +0 -75
  269. validators/metadata_validator.py +0 -76
@@ -0,0 +1,271 @@
1
+ """
2
+ Client Manager for MCP Proxy Adapter Framework
3
+
4
+ This module provides client management functionality for the MCP Proxy Adapter framework.
5
+ It handles client creation, connection management, and proxy registration.
6
+
7
+ Author: Vasiliy Zdanovskiy
8
+ email: vasilyvz@gmail.com
9
+ """
10
+
11
+ import asyncio
12
+ import json
13
+ import logging
14
+ from typing import Dict, Any, Optional, List
15
+ from pathlib import Path
16
+
17
+ from .client import UniversalClient, create_client_from_config
18
+
19
+
20
+ class ClientManager:
21
+ """
22
+ Manages client connections and proxy registrations.
23
+
24
+ This class provides functionality for:
25
+ - Creating and managing client connections
26
+ - Proxy registration
27
+ - Connection pooling
28
+ - Authentication management
29
+ """
30
+
31
+ def __init__(self, config: Dict[str, Any]):
32
+ """
33
+ Initialize client manager.
34
+
35
+ Args:
36
+ config: Client manager configuration
37
+ """
38
+ self.config = config
39
+ self.logger = logging.getLogger(__name__)
40
+ self.clients: Dict[str, UniversalClient] = {}
41
+ self.connection_pool: Dict[str, UniversalClient] = {}
42
+
43
+ # Client manager settings
44
+ self.max_connections = config.get("max_connections", 10)
45
+ self.connection_timeout = config.get("connection_timeout", 30)
46
+ self.retry_attempts = config.get("retry_attempts", 3)
47
+ self.retry_delay = config.get("retry_delay", 1)
48
+
49
+ self.get_global_logger().info("Client manager initialized")
50
+
51
+ async def create_client(self, client_id: str, config_file: str) -> UniversalClient:
52
+ """
53
+ Create a new client instance.
54
+
55
+ Args:
56
+ client_id: Unique identifier for the client
57
+ config_file: Path to client configuration file
58
+
59
+ Returns:
60
+ UniversalClient instance
61
+ """
62
+ try:
63
+ if client_id in self.clients:
64
+ self.get_global_logger().warning(
65
+ f"Client {client_id} already exists, reusing existing connection"
66
+ )
67
+ return self.clients[client_id]
68
+
69
+ client = create_client_from_config(config_file)
70
+ self.clients[client_id] = client
71
+
72
+ self.get_global_logger().info(f"Client {client_id} created successfully")
73
+ return client
74
+
75
+ except Exception as e:
76
+ self.get_global_logger().error(f"Failed to create client {client_id}: {e}")
77
+ raise
78
+
79
+ async def get_client(self, client_id: str) -> Optional[UniversalClient]:
80
+ """
81
+ Get an existing client instance.
82
+
83
+ Args:
84
+ client_id: Client identifier
85
+
86
+ Returns:
87
+ UniversalClient instance or None if not found
88
+ """
89
+ return self.clients.get(client_id)
90
+
91
+ async def remove_client(self, client_id: str) -> bool:
92
+ """
93
+ Remove a client instance.
94
+
95
+ Args:
96
+ client_id: Client identifier
97
+
98
+ Returns:
99
+ True if client was removed, False otherwise
100
+ """
101
+ if client_id in self.clients:
102
+ client = self.clients[client_id]
103
+ await client.disconnect()
104
+ del self.clients[client_id]
105
+ self.get_global_logger().info(f"Client {client_id} removed")
106
+ return True
107
+ return False
108
+
109
+ async def test_client_connection(self, client_id: str) -> bool:
110
+ """
111
+ Test connection for a specific client.
112
+
113
+ Args:
114
+ client_id: Client identifier
115
+
116
+ Returns:
117
+ True if connection is successful, False otherwise
118
+ """
119
+ client = await self.get_client(client_id)
120
+ if not client:
121
+ self.get_global_logger().error(f"Client {client_id} not found")
122
+ return False
123
+
124
+ try:
125
+ return await client.test_connection()
126
+ except Exception as e:
127
+ self.get_global_logger().error(f"Connection test failed for client {client_id}: {e}")
128
+ return False
129
+
130
+ async def register_proxy(
131
+ self, client_id: str, proxy_config: Dict[str, Any]
132
+ ) -> Dict[str, Any]:
133
+ """
134
+ Register with proxy server using a specific client.
135
+
136
+ Args:
137
+ client_id: Client identifier
138
+ proxy_config: Proxy registration configuration
139
+
140
+ Returns:
141
+ Registration result
142
+ """
143
+ client = await self.get_client(client_id)
144
+ if not client:
145
+ return {"error": f"Client {client_id} not found"}
146
+
147
+ try:
148
+ result = await client.register_proxy(proxy_config)
149
+ self.get_global_logger().info(f"Proxy registration completed for client {client_id}")
150
+ return result
151
+ except Exception as e:
152
+ self.get_global_logger().error(f"Proxy registration failed for client {client_id}: {e}")
153
+ return {"error": str(e)}
154
+
155
+ async def execute_command(
156
+ self, client_id: str, command: str, params: Dict[str, Any] = None
157
+ ) -> Dict[str, Any]:
158
+ """
159
+ Execute a command using a specific client.
160
+
161
+ Args:
162
+ client_id: Client identifier
163
+ command: Command name
164
+ params: Command parameters
165
+
166
+ Returns:
167
+ Command result
168
+ """
169
+ client = await self.get_client(client_id)
170
+ if not client:
171
+ return {"error": f"Client {client_id} not found"}
172
+
173
+ try:
174
+ result = await client.execute_command(command, params or {})
175
+ self.get_global_logger().info(f"Command {command} executed for client {client_id}")
176
+ return result
177
+ except Exception as e:
178
+ self.get_global_logger().error(f"Command execution failed for client {client_id}: {e}")
179
+ return {"error": str(e)}
180
+
181
+ async def get_client_status(self, client_id: str) -> Dict[str, Any]:
182
+ """
183
+ Get status information for a specific client.
184
+
185
+ Args:
186
+ client_id: Client identifier
187
+
188
+ Returns:
189
+ Client status information
190
+ """
191
+ client = await self.get_client(client_id)
192
+ if not client:
193
+ return {"error": f"Client {client_id} not found"}
194
+
195
+ try:
196
+ # Test connection
197
+ connection_ok = await client.test_connection()
198
+
199
+ # Test security features
200
+ security_features = await client.test_security_features()
201
+
202
+ status = {
203
+ "client_id": client_id,
204
+ "base_url": client.base_url,
205
+ "auth_method": client.auth_method,
206
+ "connection_ok": connection_ok,
207
+ "security_features": security_features,
208
+ "session_active": client.session is not None,
209
+ }
210
+
211
+ return status
212
+ except Exception as e:
213
+ self.get_global_logger().error(f"Failed to get status for client {client_id}: {e}")
214
+ return {"error": str(e)}
215
+
216
+
217
+ async def cleanup(self):
218
+ """Clean up all client connections."""
219
+ for client_id in list(self.clients.keys()):
220
+ await self.remove_client(client_id)
221
+ self.get_global_logger().info("All client connections cleaned up")
222
+
223
+ async def __aenter__(self):
224
+ """Async context manager entry."""
225
+ return self
226
+
227
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
228
+ """Async context manager exit."""
229
+ await self.cleanup()
230
+
231
+
232
+
233
+
234
+ # Example usage and testing functions
235
+ async def test_client_manager():
236
+ """Test client manager functionality."""
237
+ # Example configuration
238
+ config = {
239
+ "max_connections": 5,
240
+ "connection_timeout": 30,
241
+ "retry_attempts": 3,
242
+ "retry_delay": 1,
243
+ }
244
+
245
+ async with ClientManager(config) as manager:
246
+ # Create a client
247
+ client_id = "test_client"
248
+ config_file = "configs/http_simple.json"
249
+
250
+ try:
251
+ client = await manager.create_client(client_id, config_file)
252
+ print(f"✅ Client {client_id} created successfully")
253
+
254
+ # Test connection
255
+ connection_ok = await manager.test_client_connection(client_id)
256
+ print(f"✅ Connection test: {connection_ok}")
257
+
258
+ # Get status
259
+ status = await manager.get_client_status(client_id)
260
+ print(f"✅ Client status: {json.dumps(status, indent=2)}")
261
+
262
+ # Execute command
263
+ result = await manager.execute_command(client_id, "help")
264
+ print(f"✅ Command result: {json.dumps(result, indent=2)}")
265
+
266
+ except Exception as e:
267
+ print(f"❌ Test failed: {e}")
268
+
269
+
270
+ if __name__ == "__main__":
271
+ asyncio.run(test_client_manager())
@@ -0,0 +1,411 @@
1
+ """
2
+ Client Security Module
3
+
4
+ This module provides client-side security integration for MCP Proxy Adapter,
5
+ using mcp_security_framework utilities for secure connections to servers.
6
+
7
+ Author: Vasiliy Zdanovskiy
8
+ email: vasilyvz@gmail.com
9
+ """
10
+
11
+ import ssl
12
+ from typing import Dict, Any, Optional, List, Tuple
13
+ from pathlib import Path
14
+
15
+ # Import framework utilities
16
+ try:
17
+ from mcp_security_framework.utils import (
18
+ generate_api_key,
19
+ create_jwt_token,
20
+ )
21
+ from mcp_security_framework.utils.cert_utils import (
22
+ parse_certificate,
23
+ extract_roles_from_certificate,
24
+ validate_certificate_chain,
25
+ validate_certificate_format,
26
+ )
27
+ from mcp_security_framework import SSLConfig
28
+ from mcp_security_framework.core.ssl_manager import SSLManager
29
+ from mcp_security_framework.schemas.models import AuthResult, ValidationResult
30
+
31
+ SECURITY_FRAMEWORK_AVAILABLE = True
32
+ except ImportError as e:
33
+ # NO FALLBACK! mcp_security_framework is REQUIRED
34
+ raise RuntimeError(
35
+ f"CRITICAL: mcp_security_framework is required but not available: {e}. "
36
+ "Install it with: pip install mcp_security_framework>=1.2.8"
37
+ ) from e
38
+ AuthResult = None
39
+ ValidationResult = None
40
+
41
+ from mcp_proxy_adapter.core.logging import get_global_logger
42
+
43
+
44
+ class ClientSecurityManager:
45
+ """
46
+ Client-side security manager for MCP Proxy Adapter.
47
+
48
+ Provides secure client connections using mcp_security_framework utilities.
49
+ Handles authentication, certificate management, and SSL/TLS for client connections.
50
+ """
51
+
52
+ def __init__(self, config: Dict[str, Any]):
53
+ """
54
+ Initialize client security manager.
55
+
56
+ Args:
57
+ config: Security configuration
58
+ """
59
+ if not SECURITY_FRAMEWORK_AVAILABLE:
60
+ raise ImportError("mcp_security_framework is not available")
61
+
62
+ self.config = config
63
+ self.security_config = config.get("security", {})
64
+
65
+ # Initialize SSL manager if needed
66
+ self.ssl_manager = None
67
+ if self.security_config.get("ssl", {}).get("enabled", False):
68
+ ssl_config = self._create_ssl_config()
69
+ self.ssl_manager = SSLManager(ssl_config)
70
+
71
+ get_global_logger().info("Client security manager initialized")
72
+
73
+ def _create_ssl_config(self) -> SSLConfig:
74
+ """Create SSL configuration for client connections."""
75
+ ssl_section = self.security_config.get("ssl", {})
76
+
77
+ # Determine verify_mode based on verify_server setting
78
+ verify_server = ssl_section.get("verify_server", True)
79
+ if verify_server:
80
+ verify_mode = "CERT_REQUIRED"
81
+ check_hostname = True
82
+ else:
83
+ verify_mode = "CERT_NONE"
84
+ check_hostname = False
85
+
86
+ return SSLConfig(
87
+ enabled=ssl_section.get("enabled", False),
88
+ cert_file=ssl_section.get("client_cert_file"),
89
+ key_file=ssl_section.get("client_key_file"),
90
+ ca_cert_file=ssl_section.get("ca_cert_file"),
91
+ verify_mode=verify_mode,
92
+ min_tls_version=ssl_section.get("min_tls_version", "TLSv1.2"),
93
+ check_hostname=check_hostname,
94
+ check_expiry=ssl_section.get("check_expiry", True),
95
+ )
96
+
97
+ def create_client_ssl_context(
98
+ self, server_hostname: Optional[str] = None
99
+ ) -> Optional[ssl.SSLContext]:
100
+ """
101
+ Create SSL context for client connections.
102
+
103
+ Args:
104
+ server_hostname: Server hostname for SNI
105
+
106
+ Returns:
107
+ SSL context or None if SSL not enabled
108
+ """
109
+ if not self.ssl_manager:
110
+ return None
111
+
112
+ try:
113
+ # Create client SSL context
114
+ context = self.ssl_manager.create_client_context()
115
+
116
+ get_global_logger().info(
117
+ f"Client SSL context created for {server_hostname or 'unknown server'}"
118
+ )
119
+ return context
120
+
121
+ except Exception as e:
122
+ get_global_logger().error(f"Failed to create client SSL context: {e}")
123
+ return None
124
+
125
+ def generate_client_api_key(self, user_id: str, prefix: str = "mcp_proxy") -> str:
126
+ """
127
+ Generate API key for client authentication.
128
+
129
+ Args:
130
+ user_id: User identifier
131
+ prefix: Key prefix
132
+
133
+ Returns:
134
+ Generated API key
135
+ """
136
+ try:
137
+ api_key = generate_api_key(prefix=prefix)
138
+ get_global_logger().info(f"Generated API key for user: {user_id}")
139
+ return api_key
140
+ except Exception as e:
141
+ get_global_logger().error(f"Failed to generate API key: {e}")
142
+ raise
143
+
144
+ def create_client_jwt_token(
145
+ self,
146
+ user_id: str,
147
+ roles: List[str],
148
+ secret: str,
149
+ algorithm: str = "HS256",
150
+ expiry_hours: int = 24,
151
+ ) -> str:
152
+ """
153
+ Create JWT token for client authentication.
154
+
155
+ Args:
156
+ user_id: User identifier
157
+ roles: User roles
158
+ secret: JWT secret
159
+ algorithm: JWT algorithm
160
+ expiry_hours: Token expiry in hours
161
+
162
+ Returns:
163
+ JWT token
164
+ """
165
+ try:
166
+ payload = {"user_id": user_id, "roles": roles, "type": "client_proxy"}
167
+
168
+ token = create_jwt_token(
169
+ payload=payload,
170
+ secret=secret,
171
+ algorithm=algorithm,
172
+ expiry_hours=expiry_hours,
173
+ )
174
+
175
+ get_global_logger().info(f"Created JWT token for user: {user_id}")
176
+ return token
177
+
178
+ except Exception as e:
179
+ get_global_logger().error(f"Failed to create JWT token: {e}")
180
+ raise
181
+
182
+ def validate_server_certificate(
183
+ self, cert_path: str, ca_cert_path: Optional[str] = None
184
+ ) -> bool:
185
+ """
186
+ Validate server certificate before connection.
187
+
188
+ Args:
189
+ cert_path: Path to server certificate
190
+ ca_cert_path: Path to CA certificate
191
+
192
+ Returns:
193
+ True if certificate is valid
194
+ """
195
+ try:
196
+ # Validate certificate format
197
+ if not validate_certificate_format(cert_path):
198
+ get_global_logger().error(f"Invalid certificate format: {cert_path}")
199
+ return False
200
+
201
+ # Validate certificate chain if CA provided
202
+ if ca_cert_path:
203
+ if not validate_certificate_chain(cert_path, ca_cert_path):
204
+ get_global_logger().error(f"Invalid certificate chain: {cert_path}")
205
+ return False
206
+
207
+ # Parse certificate and check basic properties
208
+ cert_info = parse_certificate(cert_path)
209
+ if not cert_info:
210
+ get_global_logger().error(f"Failed to parse certificate: {cert_path}")
211
+ return False
212
+
213
+ get_global_logger().info(f"Server certificate validated: {cert_path}")
214
+ return True
215
+
216
+ except Exception as e:
217
+ get_global_logger().error(f"Failed to validate server certificate: {e}")
218
+ return False
219
+
220
+ def extract_server_roles(self, cert_path: str) -> List[str]:
221
+ """
222
+ Extract roles from server certificate.
223
+
224
+ Args:
225
+ cert_path: Path to server certificate
226
+
227
+ Returns:
228
+ List of roles extracted from certificate
229
+ """
230
+ try:
231
+ roles = extract_roles_from_certificate(cert_path)
232
+ get_global_logger().info(
233
+ f"Extracted roles from server certificate: {roles}"
234
+ )
235
+ return roles
236
+ except Exception as e:
237
+ get_global_logger().error(f"Failed to extract roles from certificate: {e}")
238
+ return []
239
+
240
+ def get_client_auth_headers(
241
+ self, auth_method: str = "api_key", **kwargs
242
+ ) -> Dict[str, str]:
243
+ """
244
+ Get authentication headers for client requests.
245
+
246
+ Args:
247
+ auth_method: Authentication method (api_key, jwt, certificate)
248
+ **kwargs: Additional parameters
249
+
250
+ Returns:
251
+ Dictionary of authentication headers
252
+ """
253
+ headers = {}
254
+
255
+ try:
256
+ if auth_method == "api_key":
257
+ api_key = kwargs.get("api_key")
258
+ if api_key:
259
+ headers["X-API-Key"] = api_key
260
+ headers["Authorization"] = f"Bearer {api_key}"
261
+
262
+ elif auth_method == "jwt":
263
+ token = kwargs.get("token")
264
+ if token:
265
+ headers["Authorization"] = f"Bearer {token}"
266
+
267
+ elif auth_method == "certificate":
268
+ # Certificate authentication is handled at SSL level
269
+ headers["X-Auth-Method"] = "certificate"
270
+
271
+ # Add common proxy headers
272
+ headers["X-Proxy-Type"] = "mcp_proxy_adapter"
273
+ headers["X-Client-Type"] = "proxy_client"
274
+
275
+ get_global_logger().debug(f"Created auth headers for method: {auth_method}")
276
+ return headers
277
+
278
+ except Exception as e:
279
+ get_global_logger().error(f"Failed to create auth headers: {e}")
280
+ return {}
281
+
282
+ def prepare_client_connection(
283
+ self, server_config: Dict[str, Any]
284
+ ) -> Tuple[Optional[ssl.SSLContext], Dict[str, str]]:
285
+ """
286
+ Prepare secure client connection to server.
287
+
288
+ Args:
289
+ server_config: Server connection configuration
290
+
291
+ Returns:
292
+ Tuple of (SSL context, auth headers)
293
+ """
294
+ ssl_context = None
295
+ auth_headers = {}
296
+
297
+ try:
298
+ # Create SSL context if needed
299
+ if server_config.get("ssl", False):
300
+ server_hostname = server_config.get("hostname")
301
+ ssl_context = self.create_client_ssl_context(server_hostname)
302
+
303
+ # Create authentication headers
304
+ auth_method = server_config.get("auth_method", "api_key")
305
+ auth_headers = self.get_client_auth_headers(
306
+ auth_method=auth_method,
307
+ api_key=server_config.get("api_key"),
308
+ token=server_config.get("token"),
309
+ )
310
+
311
+ get_global_logger().info(
312
+ f"Prepared client connection for {server_config.get('hostname', 'unknown')}"
313
+ )
314
+ return ssl_context, auth_headers
315
+
316
+ except Exception as e:
317
+ get_global_logger().error(f"Failed to prepare client connection: {e}")
318
+ return None, {}
319
+
320
+ def validate_server_response(self, response_headers: Dict[str, str]) -> bool:
321
+ """
322
+ Validate server response for security compliance.
323
+
324
+ Args:
325
+ response_headers: Server response headers
326
+
327
+ Returns:
328
+ True if response is valid
329
+ """
330
+ try:
331
+ # Check for required security headers
332
+ required_headers = ["Content-Type"]
333
+ for header in required_headers:
334
+ if header not in response_headers:
335
+ get_global_logger().warning(f"Missing required header: {header}")
336
+
337
+ # Check for security headers
338
+ security_headers = ["X-Frame-Options", "X-Content-Type-Options"]
339
+ for header in security_headers:
340
+ if header in response_headers:
341
+ get_global_logger().debug(f"Found security header: {header}")
342
+
343
+ # Validate content type
344
+ content_type = response_headers.get("Content-Type", "")
345
+ if "application/json" not in content_type:
346
+ get_global_logger().warning(f"Unexpected content type: {content_type}")
347
+
348
+ return True
349
+
350
+ except Exception as e:
351
+ get_global_logger().error(f"Failed to validate server response: {e}")
352
+ return False
353
+
354
+ def get_client_certificate_info(self) -> Optional[Dict[str, Any]]:
355
+ """
356
+ Get information about client certificate.
357
+
358
+ Returns:
359
+ Certificate information or None
360
+ """
361
+ try:
362
+ ssl_config = self.security_config.get("ssl", {})
363
+ cert_path = ssl_config.get("client_cert_file")
364
+
365
+ if not cert_path or not Path(cert_path).exists():
366
+ return None
367
+
368
+ cert_info = parse_certificate(cert_path)
369
+ if cert_info:
370
+ roles = extract_roles_from_certificate(cert_path)
371
+ cert_info["roles"] = roles
372
+ return cert_info
373
+
374
+ return None
375
+
376
+ except Exception as e:
377
+ get_global_logger().error(f"Failed to get client certificate info: {e}")
378
+ return None
379
+
380
+ def is_ssl_enabled(self) -> bool:
381
+ """Check if SSL is enabled for client connections."""
382
+ return self.security_config.get("ssl", {}).get("enabled", False)
383
+
384
+ def get_supported_auth_methods(self) -> List[str]:
385
+ """Get list of supported authentication methods."""
386
+ return ["api_key", "jwt", "certificate"]
387
+
388
+
389
+ # Factory function for easy integration
390
+ def create_client_security_manager(
391
+ config: Dict[str, Any],
392
+ ) -> Optional[ClientSecurityManager]:
393
+ """
394
+ Create client security manager instance.
395
+
396
+ Args:
397
+ config: Configuration dictionary
398
+
399
+ Returns:
400
+ ClientSecurityManager instance or None if framework not available
401
+ """
402
+ try:
403
+ return ClientSecurityManager(config)
404
+ except ImportError:
405
+ get_global_logger().warning(
406
+ "mcp_security_framework not available, client security disabled"
407
+ )
408
+ return None
409
+ except Exception as e:
410
+ get_global_logger().error(f"Failed to create client security manager: {e}")
411
+ return None
@@ -0,0 +1,18 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Configuration management package for MCP Proxy Adapter.
6
+ """
7
+
8
+ from .config import Config
9
+ from .config_loader import ConfigLoader
10
+ from .feature_manager import FeatureManager
11
+ from .config_factory import ConfigFactory
12
+
13
+ __all__ = [
14
+ "Config",
15
+ "ConfigLoader",
16
+ "FeatureManager",
17
+ "ConfigFactory",
18
+ ]