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,84 @@
1
+ """Async helper routines for proxy heartbeat and unregister flows.
2
+
3
+ Author: Vasiliy Zdanovskiy
4
+ email: vasilyvz@gmail.com
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import asyncio
10
+ from typing import Any, Dict, List
11
+
12
+ from mcp_proxy_adapter.client.jsonrpc_client import JsonRpcClient
13
+
14
+ from .registration_context import HeartbeatSettings, ProxyCredentials
15
+
16
+
17
+ def create_heartbeat_task(
18
+ proxy_url: str,
19
+ server_name: str,
20
+ server_url: str,
21
+ capabilities: List[str],
22
+ metadata: Dict[str, Any],
23
+ settings: HeartbeatSettings,
24
+ credentials: ProxyCredentials,
25
+ logger: Any,
26
+ ) -> asyncio.Task:
27
+ """Create and return an asyncio Task that sends heartbeats."""
28
+
29
+ interval = max(2, settings.interval)
30
+
31
+ async def heartbeat_loop() -> None:
32
+ client = JsonRpcClient(protocol="http", host="127.0.0.1", port=8080)
33
+ try:
34
+
35
+ async def _send() -> None:
36
+ await client.heartbeat_to_proxy(
37
+ proxy_url=proxy_url,
38
+ server_name=server_name,
39
+ server_url=server_url,
40
+ capabilities=list(capabilities),
41
+ metadata=metadata,
42
+ cert=credentials.cert,
43
+ verify=credentials.verify,
44
+ )
45
+
46
+ while True:
47
+ try:
48
+ await asyncio.sleep(interval)
49
+ await _send()
50
+ logger.debug("\ud83d\udc93 Heartbeat sent successfully")
51
+ except asyncio.CancelledError:
52
+ raise
53
+ except Exception as exc: # noqa: BLE001
54
+ logger.error(f"Heartbeat error: {exc}")
55
+ except asyncio.CancelledError:
56
+ logger.debug("Heartbeat loop cancelled")
57
+ raise
58
+ finally:
59
+ await client.close()
60
+
61
+ return asyncio.create_task(heartbeat_loop())
62
+
63
+
64
+ async def unregister_from_proxy(
65
+ proxy_url: str,
66
+ server_name: str,
67
+ endpoint: str,
68
+ credentials: ProxyCredentials,
69
+ logger: Any,
70
+ ) -> None:
71
+ """Unregister adapter from proxy using provided credentials."""
72
+
73
+ client = JsonRpcClient(protocol="http", host="127.0.0.1", port=8080)
74
+ try:
75
+ full_url = f"{proxy_url}{endpoint}"
76
+ await client.unregister_from_proxy(
77
+ proxy_url=full_url,
78
+ server_name=server_name,
79
+ cert=credentials.cert,
80
+ verify=credentials.verify,
81
+ )
82
+ logger.info(f"\ud83d\udd1a Unregistered from proxy: {server_name}")
83
+ finally:
84
+ await client.close()
@@ -0,0 +1,88 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ SSL context factory for MCP Proxy Adapter API.
6
+ """
7
+
8
+ import ssl
9
+ from pathlib import Path
10
+ from typing import Any, Dict, Optional
11
+
12
+ from mcp_proxy_adapter.core.logging import get_global_logger
13
+ from mcp_proxy_adapter.core.ssl_utils import SSLUtils
14
+
15
+
16
+ class SSLContextFactory:
17
+ """Factory for creating SSL contexts."""
18
+
19
+ def __init__(self):
20
+ """Initialize SSL context factory."""
21
+ self.logger = get_global_logger()
22
+
23
+ def create_ssl_context(
24
+ self,
25
+ app_config: Optional[Dict[str, Any]] = None
26
+ ) -> Optional[ssl.SSLContext]:
27
+ """
28
+ Create SSL context based on configuration.
29
+
30
+ Args:
31
+ app_config: Application configuration dictionary (optional)
32
+
33
+ Returns:
34
+ SSL context if SSL is enabled and properly configured, None otherwise
35
+ """
36
+ from mcp_proxy_adapter.config import config
37
+
38
+ current_config = app_config if app_config is not None else config.get_all()
39
+
40
+ # Check SSL configuration from new structure
41
+ protocol = current_config.get("server", {}).get("protocol", "http")
42
+ verify_client = current_config.get("transport", {}).get("verify_client", False)
43
+ ssl_enabled = protocol in ["https", "mtls"] or verify_client
44
+
45
+ if not ssl_enabled:
46
+ self.logger.info("SSL is disabled in configuration")
47
+ return None
48
+
49
+ # Get certificate paths from configuration
50
+ cert_file = current_config.get("transport", {}).get("cert_file")
51
+ key_file = current_config.get("transport", {}).get("key_file")
52
+ ca_cert = current_config.get("transport", {}).get("ca_cert")
53
+
54
+ # Convert relative paths to absolute paths
55
+ if cert_file and not Path(cert_file).is_absolute():
56
+ project_root = Path(__file__).parent.parent.parent.parent
57
+ cert_file = str(project_root / cert_file)
58
+ if key_file and not Path(key_file).is_absolute():
59
+ project_root = Path(__file__).parent.parent.parent.parent
60
+ key_file = str(project_root / key_file)
61
+ if ca_cert and not Path(ca_cert).is_absolute():
62
+ project_root = Path(__file__).parent.parent.parent.parent
63
+ ca_cert = str(project_root / ca_cert)
64
+
65
+ if not cert_file or not key_file:
66
+ self.logger.warning("SSL enabled but certificate or key file not specified")
67
+ return None
68
+
69
+ try:
70
+ # Create SSL context using SSLUtils
71
+ ssl_context = SSLUtils.create_ssl_context(
72
+ cert_file=cert_file,
73
+ key_file=key_file,
74
+ ca_cert=ca_cert,
75
+ verify_client=current_config.get("transport", {}).get("verify_client", False),
76
+ cipher_suites=[],
77
+ min_tls_version="1.2",
78
+ max_tls_version="1.3",
79
+ )
80
+
81
+ self.logger.info(
82
+ f"SSL context created successfully for mode: https_only"
83
+ )
84
+ return ssl_context
85
+
86
+ except Exception as e:
87
+ self.logger.error(f"Failed to create SSL context: {e}")
88
+ return None
@@ -0,0 +1,181 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ HTTP handlers for the MCP Proxy Adapter API.
6
+ Provides JSON-RPC handling and health/commands endpoints.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import asyncio
12
+ import time
13
+ from typing import Any, Dict, List, Optional
14
+
15
+ from fastapi import Request
16
+
17
+ from mcp_proxy_adapter.commands.command_registry import registry
18
+ from mcp_proxy_adapter.core.errors import (
19
+ MicroserviceError,
20
+ NotFoundError,
21
+ InvalidRequestError,
22
+ MethodNotFoundError,
23
+ InvalidParamsError,
24
+ InternalError,
25
+ )
26
+ from mcp_proxy_adapter.core.logging import get_global_logger, RequestLogger
27
+
28
+
29
+ async def execute_command(
30
+ command_name: str,
31
+ params: Dict[str, Any],
32
+ request_id: Optional[str] = None,
33
+ request: Optional[Request] = None,
34
+ ) -> Dict[str, Any]:
35
+ """Execute a registered command by name with parameters.
36
+
37
+ Raises MethodNotFoundError if command is not found.
38
+ Wraps unexpected exceptions into InternalError.
39
+ """
40
+ logger = RequestLogger(__name__, request_id) if request_id else get_global_logger()
41
+
42
+ try:
43
+ logger.info(f"Executing command: {command_name}")
44
+
45
+ # Resolve command
46
+ try:
47
+ command_class = registry.get_command(command_name)
48
+ except Exception:
49
+ raise MethodNotFoundError(f"Method not found: {command_name}")
50
+
51
+ # Build context (user info if middleware set state)
52
+ context: Dict[str, Any] = {}
53
+ if request is not None and hasattr(request, "state"):
54
+ user_id = getattr(request.state, "user_id", None)
55
+ user_role = getattr(request.state, "user_role", None)
56
+ user_roles = getattr(request.state, "user_roles", None)
57
+ if user_id or user_role or user_roles:
58
+ context["user"] = {
59
+ "id": user_id,
60
+ "role": user_role,
61
+ "roles": user_roles or [],
62
+ }
63
+
64
+ # Execute with timeout
65
+ started_at = time.time()
66
+ try:
67
+ result_obj = await asyncio.wait_for(
68
+ command_class.run(**params, context=context), timeout=30.0
69
+ )
70
+ except asyncio.TimeoutError:
71
+ elapsed = time.time() - started_at
72
+ raise InternalError(f"Command timed out after {elapsed:.2f}s")
73
+
74
+ elapsed = time.time() - started_at
75
+ logger.info(f"Command '{command_name}' executed in {elapsed:.3f}s")
76
+ return result_obj.to_dict()
77
+
78
+ except MicroserviceError:
79
+ raise
80
+ except Exception as exc:
81
+ logger.exception(f"Unhandled error in command '{command_name}': {exc}")
82
+ raise InternalError("Internal error", data={"error": str(exc)})
83
+
84
+
85
+ async def handle_batch_json_rpc(
86
+ batch_requests: List[Dict[str, Any]], request: Optional[Request] = None
87
+ ) -> List[Dict[str, Any]]:
88
+ """Handle batch JSON-RPC requests."""
89
+ responses: List[Dict[str, Any]] = []
90
+ request_id = getattr(request.state, "request_id", None) if request else None
91
+ for item in batch_requests:
92
+ responses.append(await handle_json_rpc(item, request_id, request))
93
+ return responses
94
+
95
+
96
+ async def handle_json_rpc(
97
+ request_data: Dict[str, Any],
98
+ request_id: Optional[str] = None,
99
+ request: Optional[Request] = None,
100
+ ) -> Dict[str, Any]:
101
+ """Handle a single JSON-RPC request with strict 2.0 compatibility.
102
+
103
+ Also supports simplified form: {"command": "echo", "params": {...}}.
104
+ """
105
+ logger = RequestLogger(__name__, request_id) if request_id else get_global_logger()
106
+
107
+ method: Optional[str]
108
+ params: Dict[str, Any]
109
+ json_rpc_id: Any
110
+
111
+ if "jsonrpc" in request_data:
112
+ if request_data.get("jsonrpc") != "2.0":
113
+ return _error_response(InvalidRequestError("Invalid Request: jsonrpc must be '2.0'"), request_data.get("id"))
114
+ method = request_data.get("method")
115
+ params = request_data.get("params", {})
116
+ json_rpc_id = request_data.get("id")
117
+ if not method:
118
+ return _error_response(InvalidRequestError("Invalid Request: method is required"), json_rpc_id)
119
+ else:
120
+ # Simplified
121
+ method = request_data.get("command")
122
+ params = request_data.get("params", {})
123
+ json_rpc_id = request_data.get("id", 1)
124
+ if not method:
125
+ return _error_response(InvalidRequestError("Invalid Request: command is required"), json_rpc_id)
126
+
127
+ try:
128
+ result = await execute_command(method, params, request_id, request)
129
+ return {"jsonrpc": "2.0", "result": result, "id": json_rpc_id}
130
+ except MicroserviceError as err:
131
+ return _error_response(err, json_rpc_id)
132
+ except Exception as exc:
133
+ logger.exception(f"Unhandled error in JSON-RPC handler: {exc}")
134
+ return _error_response(InternalError("Internal error", data={"error": str(exc)}), json_rpc_id)
135
+
136
+
137
+ def _error_response(error: MicroserviceError, request_id: Any) -> Dict[str, Any]:
138
+ return {"jsonrpc": "2.0", "error": error.to_dict(), "id": request_id}
139
+
140
+
141
+ async def get_server_health() -> Dict[str, Any]:
142
+ """Return server health info."""
143
+ import os
144
+ import platform
145
+ import sys
146
+ import psutil
147
+ from datetime import datetime
148
+
149
+ process = psutil.Process(os.getpid())
150
+ start_time = datetime.fromtimestamp(process.create_time())
151
+ uptime_seconds = (datetime.now() - start_time).total_seconds()
152
+ mem = process.memory_info().rss / (1024 * 1024)
153
+
154
+ return {
155
+ "status": "ok",
156
+ "model": "mcp-proxy-adapter",
157
+ "version": "1.0.0",
158
+ "uptime": uptime_seconds,
159
+ "components": {
160
+ "system": {
161
+ "python_version": sys.version,
162
+ "platform": platform.platform(),
163
+ "cpu_count": os.cpu_count(),
164
+ },
165
+ "process": {
166
+ "pid": os.getpid(),
167
+ "memory_usage_mb": mem,
168
+ "start_time": start_time.isoformat(),
169
+ },
170
+ "commands": {"registered_count": len(registry.get_all_commands())},
171
+ },
172
+ }
173
+
174
+
175
+ async def get_commands_list() -> Dict[str, Dict[str, Any]]:
176
+ """Return list of registered commands with schemas."""
177
+ result: Dict[str, Dict[str, Any]] = {}
178
+ for name, cls in registry.get_all_commands().items():
179
+ schema = cls.get_schema()
180
+ result[name] = {"name": name, "schema": schema, "description": schema.get("description", "")}
181
+ return result
@@ -0,0 +1,21 @@
1
+ """
2
+ Middleware package for API.
3
+ This package contains middleware components for request processing.
4
+ """
5
+
6
+ from typing import Dict, Any, Optional
7
+ from fastapi import FastAPI
8
+
9
+ from mcp_proxy_adapter.core.logging import get_global_logger
10
+ from mcp_proxy_adapter.config import config
11
+ from .factory import MiddlewareFactory
12
+ # from .protocol_middleware import setup_protocol_middleware
13
+
14
+ # Export mcp_security_framework availability
15
+ try:
16
+ from .user_info_middleware import _MCP_SECURITY_AVAILABLE
17
+ mcp_security_framework = _MCP_SECURITY_AVAILABLE
18
+ except ImportError:
19
+ mcp_security_framework = False
20
+
21
+
@@ -0,0 +1,54 @@
1
+ """
2
+ Base middleware module.
3
+ """
4
+
5
+ from typing import Callable, Awaitable
6
+ import logging
7
+
8
+ from starlette.middleware.base import BaseHTTPMiddleware
9
+ from fastapi import Request, Response
10
+
11
+ from mcp_proxy_adapter.core.logging import get_global_logger
12
+
13
+
14
+ class BaseMiddleware(BaseHTTPMiddleware):
15
+ """
16
+ Base class for all middleware.
17
+ """
18
+
19
+
20
+ async def before_request(self, request: Request) -> None:
21
+ """
22
+ Method for processing request before calling the main handler.
23
+
24
+ Args:
25
+ request: Request.
26
+ """
27
+ pass
28
+
29
+ async def after_response(self, request: Request, response: Response) -> Response:
30
+ """
31
+ Method for processing response after calling the main handler.
32
+
33
+ Args:
34
+ request: Request.
35
+ response: Response.
36
+
37
+ Returns:
38
+ Processed response.
39
+ """
40
+ return response
41
+
42
+ async def handle_error(self, request: Request, exception: Exception) -> Response:
43
+ """
44
+ Method for handling errors that occurred in middleware.
45
+
46
+ Args:
47
+ request: Request.
48
+ exception: Exception.
49
+
50
+ Returns:
51
+ Error response.
52
+ """
53
+ # By default, just pass the error further
54
+ raise exception
@@ -0,0 +1,73 @@
1
+ """
2
+ Command Permission Middleware
3
+
4
+ This middleware checks permissions for specific commands based on user roles.
5
+
6
+ Author: Vasiliy Zdanovskiy
7
+ email: vasilyvz@gmail.com
8
+ """
9
+
10
+ import json
11
+ import logging
12
+ from fastapi import Request, Response
13
+ from starlette.middleware.base import BaseHTTPMiddleware
14
+
15
+ from mcp_proxy_adapter.core.logging import get_global_logger
16
+
17
+
18
+ class CommandPermissionMiddleware(BaseHTTPMiddleware):
19
+ """
20
+ Middleware for checking command permissions.
21
+
22
+ This middleware checks if the authenticated user has the required
23
+ permissions to execute specific commands.
24
+ """
25
+
26
+ def __init__(self, app, config: Dict[str, Any]):
27
+ """
28
+ Initialize command permission middleware.
29
+
30
+ Args:
31
+ app: FastAPI application
32
+ config: Configuration dictionary
33
+ """
34
+ super().__init__(app)
35
+ self.config = config
36
+
37
+ # Define command permissions
38
+ self.command_permissions = {
39
+ "echo": ["read"],
40
+ "health": ["read"],
41
+ "role_test": ["read"],
42
+ "config": ["read"],
43
+ "help": ["read"],
44
+ # Add more commands as needed
45
+ }
46
+
47
+ get_global_logger().info("Command permission middleware initialized")
48
+
49
+
50
+ def _check_permissions(
51
+ self, user_roles: list, user_permissions: list, required_permissions: list
52
+ ) -> bool:
53
+ """
54
+ Check if user has required permissions.
55
+
56
+ Args:
57
+ user_roles: User roles
58
+ user_permissions: User permissions
59
+ required_permissions: Required permissions
60
+
61
+ Returns:
62
+ True if user has required permissions
63
+ """
64
+ # Admin has all permissions
65
+ if "admin" in user_roles or "*" in user_permissions:
66
+ return True
67
+
68
+ # Check if user has all required permissions
69
+ for required in required_permissions:
70
+ if required not in user_permissions:
71
+ return False
72
+
73
+ return True
@@ -0,0 +1,76 @@
1
+ """
2
+ Middleware for error handling.
3
+ """
4
+
5
+ import json
6
+ from typing import Optional, Any
7
+
8
+ from fastapi import Request, Response
9
+ from starlette.responses import JSONResponse
10
+
11
+ from mcp_proxy_adapter.core.logging import get_global_logger
12
+ from mcp_proxy_adapter.core.errors import (
13
+ MicroserviceError,
14
+ CommandError,
15
+ ValidationError,
16
+ )
17
+ from .base import BaseMiddleware
18
+
19
+
20
+ class ErrorHandlingMiddleware(BaseMiddleware):
21
+ """
22
+ Middleware for handling and formatting errors.
23
+ """
24
+
25
+ def __init__(self, app):
26
+ """
27
+ Initialize error handling middleware.
28
+
29
+ Args:
30
+ app: FastAPI application
31
+ """
32
+ super().__init__(app)
33
+
34
+
35
+ def _is_json_rpc_request(self, request: Request) -> bool:
36
+ """
37
+ Checks if request is a JSON-RPC request.
38
+
39
+ Args:
40
+ request: Request.
41
+
42
+ Returns:
43
+ True if request is JSON-RPC, False otherwise.
44
+ """
45
+ # Only requests to /api/jsonrpc are JSON-RPC requests
46
+ return request.url.path == "/api/jsonrpc"
47
+
48
+ async def _get_json_rpc_id(self, request: Request) -> Optional[Any]:
49
+ """
50
+ Gets JSON-RPC request ID.
51
+
52
+ Args:
53
+ request: Request.
54
+
55
+ Returns:
56
+ JSON-RPC request ID if available, None otherwise.
57
+ """
58
+ try:
59
+ # Use request state to avoid body parsing if already done
60
+ if hasattr(request.state, "json_rpc_id"):
61
+ return request.state.json_rpc_id
62
+
63
+ # Parse request body
64
+ body = await request.body()
65
+ if body:
66
+ body_text = body.decode("utf-8")
67
+ body_json = json.loads(body_text)
68
+ request_id = body_json.get("id")
69
+
70
+ # Save ID in request state to avoid reparsing
71
+ request.state.json_rpc_id = request_id
72
+ return request_id
73
+ except Exception as e:
74
+ get_global_logger().warning(f"Error parsing JSON-RPC ID: {str(e)}")
75
+
76
+ return None