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,224 @@
1
+ """Proxy registration helpers for JsonRpcClient.
2
+
3
+ Author: Vasiliy Zdanovskiy
4
+ email: vasilyvz@gmail.com
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ import re
11
+ from typing import Any, Dict, List, Optional, Tuple, Union, cast
12
+
13
+ import httpx
14
+
15
+ from mcp_proxy_adapter.client.jsonrpc_client.transport import JsonRpcTransport
16
+
17
+
18
+ class ProxyApiMixin(JsonRpcTransport):
19
+ """Mixin providing proxy registration helpers."""
20
+
21
+ async def register_with_proxy(
22
+ self,
23
+ proxy_url: str,
24
+ server_name: str,
25
+ server_url: str,
26
+ capabilities: Optional[List[str]] = None,
27
+ metadata: Optional[Dict[str, Any]] = None,
28
+ cert: Optional[Tuple[str, str]] = None,
29
+ verify: Optional[Union[bool, str]] = None,
30
+ ) -> Dict[str, Any]:
31
+ payload: Dict[str, Any] = {
32
+ "server_id": server_name,
33
+ "server_url": server_url,
34
+ "capabilities": capabilities or [],
35
+ "metadata": metadata or {},
36
+ }
37
+
38
+ ssl_verify = verify if verify is not None else self.verify
39
+ ssl_cert = cert if cert is not None else self.cert
40
+
41
+ proxy_base = proxy_url.rstrip("/")
42
+ register_url = (
43
+ proxy_base if proxy_base.endswith("/register") else f"{proxy_base}/register"
44
+ )
45
+
46
+ logger = logging.getLogger(__name__)
47
+ logger.debug(
48
+ "Registering with proxy: %s, cert=%s, verify=%s",
49
+ register_url,
50
+ ssl_cert is not None,
51
+ ssl_verify,
52
+ )
53
+
54
+ try:
55
+ async with httpx.AsyncClient(
56
+ verify=ssl_verify, cert=ssl_cert, timeout=10.0
57
+ ) as client:
58
+ response = await client.post(register_url, json=payload)
59
+
60
+ if response.status_code == 400:
61
+ error_data = cast(Dict[str, Any], response.json())
62
+ error_msg = error_data.get("error", "").lower()
63
+ if "already registered" in error_msg:
64
+ await self._retry_registration_after_unregister(
65
+ client,
66
+ proxy_base,
67
+ register_url,
68
+ server_name,
69
+ server_url,
70
+ capabilities,
71
+ metadata,
72
+ error_data,
73
+ )
74
+
75
+ if response.status_code >= 400:
76
+ try:
77
+ error_data = cast(Dict[str, Any], response.json())
78
+ error_msg = error_data.get(
79
+ "error",
80
+ error_data.get("message", f"HTTP {response.status_code}"),
81
+ )
82
+ raise httpx.HTTPStatusError(
83
+ f"Registration failed: {error_msg}",
84
+ request=response.request,
85
+ response=response,
86
+ )
87
+ except (ValueError, KeyError):
88
+ response.raise_for_status()
89
+
90
+ response.raise_for_status()
91
+ return cast(Dict[str, Any], response.json())
92
+ except httpx.ConnectError as exc: # noqa: BLE001
93
+ error_msg = f"Connection failed to {register_url}"
94
+ if hasattr(exc, "request"):
95
+ error_msg += f" (request: {exc.request.url})"
96
+ raise ConnectionError(error_msg) from exc
97
+ except httpx.TimeoutException as exc: # noqa: BLE001
98
+ raise TimeoutError(f"Request timeout to {register_url}") from exc
99
+ except httpx.HTTPError as exc: # noqa: BLE001
100
+ raise ConnectionError(
101
+ f"HTTP error connecting to {register_url}: {exc}"
102
+ ) from exc
103
+
104
+ async def unregister_from_proxy(
105
+ self,
106
+ proxy_url: str,
107
+ server_name: str,
108
+ cert: Optional[Tuple[str, str]] = None,
109
+ verify: Optional[Union[bool, str]] = None,
110
+ ) -> Dict[str, Any]:
111
+ payload: Dict[str, Any] = {
112
+ "server_id": server_name,
113
+ "server_url": "",
114
+ "capabilities": [],
115
+ "metadata": {},
116
+ }
117
+
118
+ ssl_verify = verify if verify is not None else self.verify
119
+ ssl_cert = cert if cert is not None else self.cert
120
+
121
+ proxy_base = proxy_url.rstrip("/")
122
+ unregister_url = (
123
+ proxy_base
124
+ if proxy_base.endswith("/unregister")
125
+ else f"{proxy_base}/unregister"
126
+ )
127
+
128
+ async with httpx.AsyncClient(
129
+ verify=ssl_verify, cert=ssl_cert, timeout=10.0
130
+ ) as client:
131
+ response = await client.post(unregister_url, json=payload)
132
+ response.raise_for_status()
133
+ return cast(Dict[str, Any], response.json())
134
+
135
+ async def heartbeat_to_proxy(
136
+ self,
137
+ proxy_url: str,
138
+ server_name: str,
139
+ server_url: str,
140
+ capabilities: Optional[List[str]] = None,
141
+ metadata: Optional[Dict[str, Any]] = None,
142
+ cert: Optional[Tuple[str, str]] = None,
143
+ verify: Optional[Union[bool, str]] = None,
144
+ ) -> None:
145
+ payload: Dict[str, Any] = {
146
+ "server_id": server_name,
147
+ "server_url": server_url,
148
+ "capabilities": capabilities or [],
149
+ "metadata": metadata or {},
150
+ }
151
+
152
+ ssl_verify = verify if verify is not None else self.verify
153
+ ssl_cert = cert if cert is not None else self.cert
154
+
155
+ proxy_base = proxy_url.rstrip("/")
156
+ async with httpx.AsyncClient(
157
+ verify=ssl_verify, cert=ssl_cert, timeout=10.0
158
+ ) as client:
159
+ response = await client.post(
160
+ f"{proxy_base}/proxy/heartbeat",
161
+ json=payload,
162
+ )
163
+ response.raise_for_status()
164
+
165
+ async def list_proxy_servers(self, proxy_url: str) -> Dict[str, Any]:
166
+ proxy_base = proxy_url.rstrip("/")
167
+ async with httpx.AsyncClient(timeout=10.0) as client:
168
+ response = await client.get(f"{proxy_base}/proxy/list")
169
+ response.raise_for_status()
170
+ return cast(Dict[str, Any], response.json())
171
+
172
+ async def get_proxy_health(self, proxy_url: str) -> Dict[str, Any]:
173
+ proxy_base = proxy_url.rstrip("/")
174
+ async with httpx.AsyncClient(timeout=10.0) as client:
175
+ response = await client.get(f"{proxy_base}/proxy/health")
176
+ response.raise_for_status()
177
+ return cast(Dict[str, Any], response.json())
178
+
179
+ async def _retry_registration_after_unregister(
180
+ self,
181
+ client: httpx.AsyncClient,
182
+ proxy_base: str,
183
+ register_url: str,
184
+ server_name: str,
185
+ server_url: str,
186
+ capabilities: Optional[List[str]],
187
+ metadata: Optional[Dict[str, Any]],
188
+ error_data: Dict[str, Any],
189
+ ) -> None:
190
+ match = re.search(
191
+ r"already registered as ([^\s,]+)",
192
+ error_data.get("error", ""),
193
+ re.IGNORECASE,
194
+ )
195
+ if not match:
196
+ return
197
+
198
+ registered_server_key = match.group(1)
199
+ original_server_id = (
200
+ re.sub(r"_\d+$", "", registered_server_key)
201
+ if "_" in registered_server_key
202
+ else registered_server_key
203
+ )
204
+
205
+ unregister_payload: Dict[str, Any] = {
206
+ "server_id": original_server_id,
207
+ "server_url": "",
208
+ "capabilities": [],
209
+ "metadata": {},
210
+ }
211
+ unregister_response = await client.post(
212
+ f"{proxy_base}/unregister",
213
+ json=unregister_payload,
214
+ )
215
+ if unregister_response.status_code != 200:
216
+ return
217
+
218
+ retry_payload: Dict[str, Any] = {
219
+ "server_id": server_name,
220
+ "server_url": server_url,
221
+ "capabilities": capabilities or [],
222
+ "metadata": metadata or {},
223
+ }
224
+ await client.post(register_url, json=retry_payload)
@@ -0,0 +1,60 @@
1
+ """Queue management helpers for JsonRpcClient.
2
+
3
+ Author: Vasiliy Zdanovskiy
4
+ email: vasilyvz@gmail.com
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any, Dict, Optional
10
+
11
+ from mcp_proxy_adapter.client.jsonrpc_client.transport import JsonRpcTransport
12
+
13
+
14
+ class QueueApiMixin(JsonRpcTransport):
15
+ """Mixin with queue-related JSON-RPC shortcuts."""
16
+
17
+ async def queue_health(self) -> Dict[str, Any]:
18
+ response = await self.jsonrpc_call("queue_health", {})
19
+ return self._extract_result(response)
20
+
21
+ async def queue_add_job(
22
+ self,
23
+ job_type: str,
24
+ job_id: str,
25
+ params: Dict[str, Any],
26
+ ) -> Dict[str, Any]:
27
+ payload = {"job_type": job_type, "job_id": job_id, "params": params}
28
+ response = await self.jsonrpc_call("queue_add_job", payload)
29
+ return self._extract_result(response)
30
+
31
+ async def queue_start_job(self, job_id: str) -> Dict[str, Any]:
32
+ response = await self.jsonrpc_call("queue_start_job", {"job_id": job_id})
33
+ return self._extract_result(response)
34
+
35
+ async def queue_stop_job(self, job_id: str) -> Dict[str, Any]:
36
+ response = await self.jsonrpc_call("queue_stop_job", {"job_id": job_id})
37
+ return self._extract_result(response)
38
+
39
+ async def queue_delete_job(self, job_id: str) -> Dict[str, Any]:
40
+ response = await self.jsonrpc_call("queue_delete_job", {"job_id": job_id})
41
+ return self._extract_result(response)
42
+
43
+ async def queue_get_job_status(self, job_id: str) -> Dict[str, Any]:
44
+ response = await self.jsonrpc_call(
45
+ "queue_get_job_status", {"job_id": job_id}
46
+ )
47
+ return self._extract_result(response)
48
+
49
+ async def queue_list_jobs(
50
+ self,
51
+ status: Optional[str] = None,
52
+ job_type: Optional[str] = None,
53
+ ) -> Dict[str, Any]:
54
+ params: Dict[str, Any] = {}
55
+ if status:
56
+ params["status"] = status
57
+ if job_type:
58
+ params["job_type"] = job_type
59
+ response = await self.jsonrpc_call("queue_list_jobs", params)
60
+ return self._extract_result(response)
@@ -0,0 +1,108 @@
1
+ """Transport utilities for asynchronous JSON-RPC client.
2
+
3
+ Author: Vasiliy Zdanovskiy
4
+ email: vasilyvz@gmail.com
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from pathlib import Path
10
+ from typing import Any, Dict, Optional, Tuple, Union, cast
11
+
12
+ import httpx
13
+
14
+
15
+ class JsonRpcTransport:
16
+ """Base transport class providing HTTP primitives."""
17
+
18
+ def __init__(
19
+ self,
20
+ protocol: str = "http",
21
+ host: str = "127.0.0.1",
22
+ port: int = 8080,
23
+ token_header: Optional[str] = None,
24
+ token: Optional[str] = None,
25
+ cert: Optional[str] = None,
26
+ key: Optional[str] = None,
27
+ ca: Optional[str] = None,
28
+ ) -> None:
29
+ scheme = "https" if protocol in ("https", "mtls") else "http"
30
+ self.base_url = f"{scheme}://{host}:{port}"
31
+
32
+ self.headers: Dict[str, str] = {"Content-Type": "application/json"}
33
+ if token_header and token:
34
+ self.headers[token_header] = token
35
+
36
+ self.verify: Union[bool, str] = True
37
+ self.cert: Optional[Tuple[str, str]] = None
38
+
39
+ if protocol in ("https", "mtls"):
40
+ if cert and key:
41
+ self.cert = (str(Path(cert)), str(Path(key)))
42
+ if ca:
43
+ self.verify = str(Path(ca))
44
+ else:
45
+ self.verify = False
46
+
47
+ self.timeout = 30.0
48
+ self._client: Optional[httpx.AsyncClient] = None
49
+
50
+ async def _get_client(self) -> httpx.AsyncClient:
51
+ """Return cached async HTTP client or create it lazily."""
52
+
53
+ if self._client is None:
54
+ self._client = httpx.AsyncClient(
55
+ verify=self.verify,
56
+ cert=self.cert,
57
+ timeout=self.timeout,
58
+ )
59
+ return self._client
60
+
61
+ async def close(self) -> None:
62
+ """Close underlying HTTPX client."""
63
+
64
+ if self._client:
65
+ await self._client.aclose()
66
+ self._client = None
67
+
68
+ async def health(self) -> Dict[str, Any]:
69
+ """Fetch health information from service."""
70
+
71
+ client = await self._get_client()
72
+ response = await client.get(f"{self.base_url}/health", headers=self.headers)
73
+ response.raise_for_status()
74
+ return cast(Dict[str, Any], response.json())
75
+
76
+ async def jsonrpc_call(
77
+ self,
78
+ method: str,
79
+ params: Dict[str, Any],
80
+ req_id: int = 1,
81
+ ) -> Dict[str, Any]:
82
+ """Perform JSON-RPC request and return raw response payload."""
83
+
84
+ payload: Dict[str, Any] = {
85
+ "jsonrpc": "2.0",
86
+ "method": method,
87
+ "params": params,
88
+ "id": req_id,
89
+ }
90
+ client = await self._get_client()
91
+ response = await client.post(
92
+ f"{self.base_url}/api/jsonrpc",
93
+ json=payload,
94
+ headers=self.headers,
95
+ )
96
+ response.raise_for_status()
97
+ return cast(Dict[str, Any], response.json())
98
+
99
+ def _extract_result(self, response: Dict[str, Any]) -> Dict[str, Any]:
100
+ """Extract ``result`` part from JSON-RPC reply raising on error."""
101
+
102
+ if "error" in response:
103
+ error = response["error"]
104
+ message = error.get("message", "Unknown error")
105
+ code = error.get("code", -1)
106
+ raise RuntimeError(f"JSON-RPC error: {message} (code: {code})")
107
+ result_data = response.get("result", {})
108
+ return cast(Dict[str, Any], result_data)
@@ -0,0 +1,123 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ Lightweight asynchronous client library for registry (proxy) server used in examples.
6
+ """
7
+
8
+ from typing import Any, Dict, List, Optional
9
+
10
+ import httpx
11
+
12
+
13
+ class ProxyClient:
14
+ """Asynchronous client for proxy server."""
15
+
16
+ def __init__(self, base_url: str):
17
+ """
18
+ Initialize proxy client.
19
+
20
+ Args:
21
+ base_url: Base URL of the proxy server
22
+ """
23
+ self.base_url = base_url.rstrip("/")
24
+ self._client: Optional[httpx.AsyncClient] = None
25
+
26
+ async def _get_client(self) -> httpx.AsyncClient:
27
+ """Get or create async HTTP client."""
28
+ if self._client is None:
29
+ self._client = httpx.AsyncClient(timeout=5.0)
30
+ return self._client
31
+
32
+ async def close(self) -> None:
33
+ """Close the HTTP client."""
34
+ if self._client:
35
+ await self._client.aclose()
36
+ self._client = None
37
+
38
+ async def health(self) -> Dict[str, Any]:
39
+ """
40
+ Get proxy server health status.
41
+
42
+ Returns:
43
+ Health status information
44
+ """
45
+ client = await self._get_client()
46
+ r = await client.get(f"{self.base_url}/proxy/health")
47
+ r.raise_for_status()
48
+ return r.json() # type: ignore[no-any-return]
49
+
50
+ async def register(
51
+ self,
52
+ name: str,
53
+ url: str,
54
+ capabilities: Optional[List[str]] = None,
55
+ metadata: Optional[Dict[str, Any]] = None,
56
+ ) -> Dict[str, Any]:
57
+ """
58
+ Register a server with the proxy.
59
+
60
+ Args:
61
+ name: Server name
62
+ url: Server URL
63
+ capabilities: Optional list of capabilities
64
+ metadata: Optional metadata dictionary
65
+
66
+ Returns:
67
+ Registration result
68
+ """
69
+ payload = {
70
+ "name": name,
71
+ "url": url,
72
+ "capabilities": capabilities or [],
73
+ "metadata": metadata or {},
74
+ }
75
+ client = await self._get_client()
76
+ r = await client.post(f"{self.base_url}/register", json=payload)
77
+ r.raise_for_status()
78
+ return r.json() # type: ignore[no-any-return]
79
+
80
+ async def unregister(self, name: str) -> Dict[str, Any]:
81
+ """
82
+ Unregister a server from the proxy.
83
+
84
+ Args:
85
+ name: Server name
86
+
87
+ Returns:
88
+ Unregistration result
89
+ """
90
+ payload = {"name": name, "url": "", "capabilities": [], "metadata": {}}
91
+ client = await self._get_client()
92
+ r = await client.post(f"{self.base_url}/unregister", json=payload)
93
+ r.raise_for_status()
94
+ return r.json() # type: ignore[no-any-return]
95
+
96
+ async def list_servers(self) -> Dict[str, Any]:
97
+ """
98
+ List all servers registered with the proxy.
99
+
100
+ Returns:
101
+ List of registered servers
102
+ """
103
+ client = await self._get_client()
104
+ r = await client.get(f"{self.base_url}/proxy/list")
105
+ r.raise_for_status()
106
+ return r.json() # type: ignore[no-any-return]
107
+
108
+ async def heartbeat(self, name: str, url: str) -> Dict[str, Any]:
109
+ """
110
+ Send heartbeat to the proxy.
111
+
112
+ Args:
113
+ name: Server name
114
+ url: Server URL
115
+
116
+ Returns:
117
+ Heartbeat result
118
+ """
119
+ payload = {"name": name, "url": url, "capabilities": [], "metadata": {}}
120
+ client = await self._get_client()
121
+ r = await client.post(f"{self.base_url}/proxy/heartbeat", json=payload)
122
+ r.raise_for_status()
123
+ return r.json() # type: ignore[no-any-return]
@@ -0,0 +1,66 @@
1
+ """
2
+ Commands module initialization file.
3
+ """
4
+
5
+ from mcp_proxy_adapter.commands.base import Command
6
+ from mcp_proxy_adapter.commands.command_registry import registry, CommandRegistry
7
+ from mcp_proxy_adapter.commands.dependency_container import (
8
+ container,
9
+ DependencyContainer,
10
+ )
11
+ from mcp_proxy_adapter.commands.result import CommandResult, SuccessResult, ErrorResult
12
+ # from mcp_proxy_adapter.commands.auth_validation_command import AuthValidationCommand
13
+ # from mcp_proxy_adapter.commands.ssl_setup_command import SSLSetupCommand
14
+ # from mcp_proxy_adapter.commands.certificate_management_command import (
15
+ # CertificateManagementCommand,
16
+ # )
17
+ # from mcp_proxy_adapter.commands.key_management_command import KeyManagementCommand
18
+ # from mcp_proxy_adapter.commands.cert_monitor_command import CertMonitorCommand
19
+ # from mcp_proxy_adapter.commands.transport_management_command import (
20
+ # TransportManagementCommand,
21
+ # )
22
+ # from mcp_proxy_adapter.commands.role_test_command import RoleTestCommand
23
+ # from mcp_proxy_adapter.commands.echo_command import EchoCommand
24
+ # from mcp_proxy_adapter.commands.proxy_registration_command import (
25
+ # ProxyRegistrationCommand,
26
+ # )
27
+ # from mcp_proxy_adapter.commands.registration_status_command import (
28
+ # RegistrationStatusCommand,
29
+ # )
30
+ # from mcp_proxy_adapter.commands.queue_commands import (
31
+ # QueueAddJobCommand,
32
+ # QueueStartJobCommand,
33
+ # QueueStopJobCommand,
34
+ # QueueDeleteJobCommand,
35
+ # QueueGetJobStatusCommand,
36
+ # QueueListJobsCommand,
37
+ # QueueHealthCommand,
38
+ # )
39
+
40
+ __all__ = [
41
+ "Command",
42
+ "CommandResult",
43
+ "SuccessResult",
44
+ "ErrorResult",
45
+ "registry",
46
+ "CommandRegistry",
47
+ "container",
48
+ "DependencyContainer",
49
+ # "AuthValidationCommand",
50
+ # "SSLSetupCommand",
51
+ # "CertificateManagementCommand",
52
+ # "KeyManagementCommand",
53
+ # "CertMonitorCommand",
54
+ # "TransportManagementCommand",
55
+ # "RoleTestCommand",
56
+ # "EchoCommand",
57
+ # "ProxyRegistrationCommand",
58
+ # "RegistrationStatusCommand",
59
+ # "QueueAddJobCommand",
60
+ # "QueueStartJobCommand",
61
+ # "QueueStopJobCommand",
62
+ # "QueueDeleteJobCommand",
63
+ # "QueueGetJobStatusCommand",
64
+ # "QueueListJobsCommand",
65
+ # "QueueHealthCommand",
66
+ ]
@@ -0,0 +1,69 @@
1
+ """
2
+ Authentication Validation Commands
3
+
4
+ This module provides commands for validating different types of authentication:
5
+ - Universal authentication validation
6
+ - Certificate validation
7
+ - Token validation
8
+ - mTLS validation
9
+ - SSL validation
10
+
11
+ Author: MCP Proxy Adapter Team
12
+ Version: 1.0.0
13
+ """
14
+
15
+ import logging
16
+ from typing import Union, Dict, Any
17
+
18
+ from ..commands.base import Command
19
+ from ..commands.result import SuccessResult, ErrorResult
20
+
21
+
22
+ from mcp_proxy_adapter.core.logging import get_global_logger
23
+ class AuthValidationCommand(Command):
24
+ """
25
+ Authentication validation commands.
26
+
27
+ Provides commands for validating different types of authentication
28
+ using the universal AuthValidator.
29
+ """
30
+
31
+ def __init__(self):
32
+ """Initialize authentication validation command."""
33
+ super().__init__()
34
+ self.validator = AuthValidator()
35
+ self.logger = logging.getLogger(__name__)
36
+
37
+
38
+
39
+
40
+
41
+
42
+ async def execute(self, **kwargs) -> Union[SuccessResult, ErrorResult]:
43
+ """
44
+ Execute authentication validation command.
45
+
46
+ This is a placeholder method to satisfy the abstract base class.
47
+ Individual validation methods should be called directly.
48
+
49
+ Args:
50
+ **kwargs: Command parameters
51
+
52
+ Returns:
53
+ Command result
54
+ """
55
+ return ErrorResult(
56
+ message="Method not found. Use specific validation methods instead.",
57
+ code=-32601,
58
+ )
59
+
60
+ @classmethod
61
+ def get_schema(cls) -> Dict[str, Any]:
62
+ """Get JSON schema for auth validation command."""
63
+ return {
64
+ "type": "object",
65
+ "properties": {
66
+ "method": {"type": "string", "enum": ["validate_token", "validate_certificate", "validate_mtls"]},
67
+ "params": {"type": "object"}
68
+ }
69
+ }