mcp-proxy-adapter 6.0.0__py3-none-any.whl → 6.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. mcp_proxy_adapter/api/app.py +174 -80
  2. mcp_proxy_adapter/api/handlers.py +16 -5
  3. mcp_proxy_adapter/api/middleware/__init__.py +7 -2
  4. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  5. mcp_proxy_adapter/api/middleware/factory.py +36 -12
  6. mcp_proxy_adapter/api/middleware/unified_security.py +152 -0
  7. mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
  8. mcp_proxy_adapter/commands/__init__.py +7 -1
  9. mcp_proxy_adapter/commands/base.py +7 -4
  10. mcp_proxy_adapter/commands/builtin_commands.py +8 -2
  11. mcp_proxy_adapter/commands/command_registry.py +8 -0
  12. mcp_proxy_adapter/commands/echo_command.py +81 -0
  13. mcp_proxy_adapter/commands/help_command.py +21 -14
  14. mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
  15. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  16. mcp_proxy_adapter/commands/security_command.py +488 -0
  17. mcp_proxy_adapter/commands/ssl_setup_command.py +2 -2
  18. mcp_proxy_adapter/commands/token_management_command.py +1 -1
  19. mcp_proxy_adapter/config.py +81 -21
  20. mcp_proxy_adapter/core/app_factory.py +326 -0
  21. mcp_proxy_adapter/core/client_security.py +384 -0
  22. mcp_proxy_adapter/core/logging.py +8 -3
  23. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  24. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  25. mcp_proxy_adapter/core/protocol_manager.py +9 -0
  26. mcp_proxy_adapter/core/proxy_client.py +602 -0
  27. mcp_proxy_adapter/core/proxy_registration.py +299 -47
  28. mcp_proxy_adapter/core/security_adapter.py +12 -15
  29. mcp_proxy_adapter/core/security_integration.py +277 -0
  30. mcp_proxy_adapter/core/server_adapter.py +345 -0
  31. mcp_proxy_adapter/core/server_engine.py +364 -0
  32. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  33. mcp_proxy_adapter/examples/README.md +230 -97
  34. mcp_proxy_adapter/examples/README_EN.md +258 -0
  35. mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
  36. mcp_proxy_adapter/examples/__pycache__/security_configurations.cpython-312.pyc +0 -0
  37. mcp_proxy_adapter/examples/__pycache__/security_test_client.cpython-312.pyc +0 -0
  38. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
  39. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
  40. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +39 -0
  41. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +25 -0
  42. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
  43. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
  44. mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
  45. mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
  46. mcp_proxy_adapter/examples/cert_config.json +9 -0
  47. mcp_proxy_adapter/examples/certs/admin.crt +32 -0
  48. mcp_proxy_adapter/examples/certs/admin.key +52 -0
  49. mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
  50. mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
  51. mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
  52. mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
  53. mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
  54. mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
  55. mcp_proxy_adapter/examples/certs/client.crt +32 -0
  56. mcp_proxy_adapter/examples/certs/client.key +52 -0
  57. mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
  58. mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
  59. mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
  60. mcp_proxy_adapter/examples/certs/client_user.key +52 -0
  61. mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
  62. mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
  63. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
  64. mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
  65. mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
  66. mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
  67. mcp_proxy_adapter/examples/certs/readonly.key +52 -0
  68. mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
  69. mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
  70. mcp_proxy_adapter/examples/certs/server.crt +32 -0
  71. mcp_proxy_adapter/examples/certs/server.key +52 -0
  72. mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
  73. mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
  74. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
  75. mcp_proxy_adapter/examples/certs/user.crt +32 -0
  76. mcp_proxy_adapter/examples/certs/user.key +52 -0
  77. mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
  78. mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
  79. mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
  80. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
  81. mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
  82. mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
  83. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
  84. mcp_proxy_adapter/examples/commands/__init__.py +1 -0
  85. mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
  86. mcp_proxy_adapter/examples/debug_request_state.py +144 -0
  87. mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
  88. mcp_proxy_adapter/examples/demo_client.py +341 -0
  89. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
  90. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
  91. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
  92. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
  93. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
  94. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
  95. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
  96. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
  97. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
  98. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
  99. mcp_proxy_adapter/examples/full_application/main.py +138 -0
  100. mcp_proxy_adapter/examples/full_application/roles.json +21 -0
  101. mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
  102. mcp_proxy_adapter/examples/generate_certificates.py +121 -0
  103. mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
  104. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
  105. mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
  106. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
  107. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
  108. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
  109. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
  110. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
  111. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
  112. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
  113. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
  114. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
  115. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
  116. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
  117. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
  118. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
  119. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
  120. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
  121. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
  122. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
  123. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
  124. mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
  125. mcp_proxy_adapter/examples/roles.json +38 -0
  126. mcp_proxy_adapter/examples/run_example.py +81 -0
  127. mcp_proxy_adapter/examples/run_security_tests.py +326 -0
  128. mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
  129. mcp_proxy_adapter/examples/security_test_client.py +743 -0
  130. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
  131. mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
  132. mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
  133. mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
  134. mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
  135. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
  136. mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
  137. mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
  138. mcp_proxy_adapter/examples/test_examples.py +344 -0
  139. mcp_proxy_adapter/examples/universal_client.py +628 -0
  140. mcp_proxy_adapter/main.py +21 -10
  141. mcp_proxy_adapter/utils/config_generator.py +639 -0
  142. mcp_proxy_adapter/version.py +2 -1
  143. mcp_proxy_adapter-6.1.0.dist-info/METADATA +205 -0
  144. mcp_proxy_adapter-6.1.0.dist-info/RECORD +193 -0
  145. mcp_proxy_adapter-6.1.0.dist-info/entry_points.txt +2 -0
  146. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/licenses/LICENSE +2 -2
  147. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  148. mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
  149. mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
  150. mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
  151. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  152. mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
  153. mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
  154. mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
  155. mcp_proxy_adapter/api/middleware/security.py +0 -376
  156. mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
  157. mcp_proxy_adapter/examples/__init__.py +0 -7
  158. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  159. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  160. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  161. mcp_proxy_adapter/examples/basic_server/config.json +0 -70
  162. mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
  163. mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
  164. mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
  165. mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
  166. mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
  167. mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
  168. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  169. mcp_proxy_adapter/examples/basic_server/server.py +0 -114
  170. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  171. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  172. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
  173. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  174. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  175. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  176. mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
  177. mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
  178. mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
  179. mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
  180. mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
  181. mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
  182. mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
  183. mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
  184. mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
  185. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  186. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  187. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  188. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  189. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  190. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  191. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  192. mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
  193. mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
  194. mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
  195. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  196. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  197. mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
  198. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  199. mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
  200. mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
  201. mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
  202. mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
  203. mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
  204. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  205. mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
  206. mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
  207. mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
  208. mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
  209. mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
  210. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  211. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  212. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  213. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  214. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  215. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  216. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  217. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  218. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  219. mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
  220. mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
  221. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  222. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  223. mcp_proxy_adapter/schemas/roles_schema.json +0 -162
  224. mcp_proxy_adapter/tests/__init__.py +0 -0
  225. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  226. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  227. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  228. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  229. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  230. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  231. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  232. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  233. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  234. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  235. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  236. mcp_proxy_adapter/tests/conftest.py +0 -131
  237. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  238. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  239. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  240. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  241. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  242. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  243. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  244. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  245. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  246. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  247. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  248. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  249. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  250. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  251. mcp_proxy_adapter/tests/test_config.py +0 -127
  252. mcp_proxy_adapter/tests/test_utils.py +0 -65
  253. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  254. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  255. mcp_proxy_adapter/tests/unit/test_config.py +0 -270
  256. mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
  257. mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
  258. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/WHEEL +0 -0
  259. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,18 @@
1
1
  """
2
- Module for proxy registration functionality.
2
+ Module for proxy registration functionality with security framework integration.
3
3
 
4
4
  This module handles automatic registration and unregistration of the server
5
- with the MCP proxy server during startup and shutdown.
5
+ with the MCP proxy server during startup and shutdown, using mcp_security_framework
6
+ for secure connections and authentication.
7
+
8
+ Author: Vasiliy Zdanovskiy
9
+ email: vasilyvz@gmail.com
6
10
  """
7
11
 
8
12
  import asyncio
9
13
  import json
10
14
  import time
15
+ import ssl
11
16
  from typing import Dict, Any, Optional, Tuple
12
17
  from urllib.parse import urljoin
13
18
 
@@ -15,8 +20,8 @@ import aiohttp
15
20
  import requests
16
21
  from requests.exceptions import RequestException
17
22
 
18
- from mcp_proxy_adapter.config import config
19
23
  from mcp_proxy_adapter.core.logging import logger
24
+ from mcp_proxy_adapter.core.client_security import create_client_security_manager
20
25
 
21
26
 
22
27
  class ProxyRegistrationError(Exception):
@@ -26,28 +31,49 @@ class ProxyRegistrationError(Exception):
26
31
 
27
32
  class ProxyRegistrationManager:
28
33
  """
29
- Manager for proxy registration functionality.
34
+ Manager for proxy registration functionality with security framework integration.
30
35
 
31
36
  Handles automatic registration and unregistration of the server
32
- with the MCP proxy server.
37
+ with the MCP proxy server using secure authentication methods.
33
38
  """
34
39
 
35
- def __init__(self):
36
- """Initialize the proxy registration manager."""
37
- self.registration_config = config.get("proxy_registration", {})
38
- self.proxy_url = self.registration_config.get("proxy_url", "http://localhost:3004")
39
- self.server_id = self.registration_config.get("server_id", "mcp_proxy_adapter")
40
- self.server_name = self.registration_config.get("server_name", "MCP Proxy Adapter")
41
- self.description = self.registration_config.get("description", "JSON-RPC API for interacting with MCP Proxy")
42
- self.timeout = self.registration_config.get("registration_timeout", 30)
43
- self.retry_attempts = self.registration_config.get("retry_attempts", 3)
44
- self.retry_delay = self.registration_config.get("retry_delay", 5)
45
- self.auto_register = self.registration_config.get("auto_register_on_startup", True)
46
- self.auto_unregister = self.registration_config.get("auto_unregister_on_shutdown", True)
40
+ def __init__(self, config: Dict[str, Any]):
41
+ """
42
+ Initialize the proxy registration manager.
43
+
44
+ Args:
45
+ config: Application configuration
46
+ """
47
+ self.config = config
48
+ self.registration_config = config.get("registration", {})
47
49
 
50
+ # Basic registration settings
51
+ self.proxy_url = self.registration_config.get("server_url", "https://proxy-registry.example.com")
52
+ self.server_id = self.registration_config.get("proxy_info", {}).get("name", "mcp_proxy_adapter")
53
+ self.server_name = self.registration_config.get("proxy_info", {}).get("name", "MCP Proxy Adapter")
54
+ self.description = self.registration_config.get("proxy_info", {}).get("description", "JSON-RPC API for interacting with MCP Proxy")
55
+
56
+ # Heartbeat settings
57
+ heartbeat_config = self.registration_config.get("heartbeat", {})
58
+ self.timeout = heartbeat_config.get("timeout", 30)
59
+ self.retry_attempts = heartbeat_config.get("retry_attempts", 3)
60
+ self.retry_delay = heartbeat_config.get("retry_delay", 60)
61
+ self.heartbeat_interval = heartbeat_config.get("interval", 300)
62
+
63
+ # Auto registration settings
64
+ self.auto_register = self.registration_config.get("enabled", False)
65
+ self.auto_unregister = True # Always unregister on shutdown
66
+
67
+ # Initialize client security manager
68
+ self.client_security = create_client_security_manager(config)
69
+
70
+ # Registration state
48
71
  self.registered = False
49
72
  self.server_key: Optional[str] = None
50
73
  self.server_url: Optional[str] = None
74
+ self.heartbeat_task: Optional[asyncio.Task] = None
75
+
76
+ logger.info("Proxy registration manager initialized with security framework integration")
51
77
 
52
78
  def is_enabled(self) -> bool:
53
79
  """
@@ -68,9 +94,55 @@ class ProxyRegistrationManager:
68
94
  self.server_url = server_url
69
95
  logger.info(f"Proxy registration server URL set to: {server_url}")
70
96
 
97
+ def _get_auth_headers(self) -> Dict[str, str]:
98
+ """
99
+ Get authentication headers for registration requests.
100
+
101
+ Returns:
102
+ Dictionary of authentication headers
103
+ """
104
+ if not self.client_security:
105
+ return {"Content-Type": "application/json"}
106
+
107
+ auth_method = self.registration_config.get("auth_method", "certificate")
108
+
109
+ if auth_method == "certificate":
110
+ return self.client_security.get_client_auth_headers("certificate")
111
+ elif auth_method == "token":
112
+ token_config = self.registration_config.get("token", {})
113
+ token = token_config.get("token")
114
+ return self.client_security.get_client_auth_headers("jwt", token=token)
115
+ elif auth_method == "api_key":
116
+ api_key_config = self.registration_config.get("api_key", {})
117
+ api_key = api_key_config.get("key")
118
+ return self.client_security.get_client_auth_headers("api_key", api_key=api_key)
119
+ else:
120
+ return {"Content-Type": "application/json"}
121
+
122
+ def _create_ssl_context(self) -> Optional[ssl.SSLContext]:
123
+ """
124
+ Create SSL context for secure connections.
125
+
126
+ Returns:
127
+ SSL context or None if SSL not needed
128
+ """
129
+ if not self.client_security:
130
+ return None
131
+
132
+ try:
133
+ # Check if SSL is enabled for registration
134
+ cert_config = self.registration_config.get("certificate", {})
135
+ if cert_config.get("enabled", False):
136
+ return self.client_security.create_client_ssl_context()
137
+
138
+ return None
139
+ except Exception as e:
140
+ logger.error(f"Failed to create SSL context: {e}")
141
+ return None
142
+
71
143
  async def register_server(self) -> bool:
72
144
  """
73
- Register the server with the proxy.
145
+ Register the server with the proxy using secure authentication.
74
146
 
75
147
  Returns:
76
148
  True if registration was successful, False otherwise.
@@ -83,11 +155,20 @@ class ProxyRegistrationManager:
83
155
  logger.error("Server URL not set, cannot register with proxy")
84
156
  return False
85
157
 
158
+ # Prepare registration data with proxy info
159
+ proxy_info = self.registration_config.get("proxy_info", {})
86
160
  registration_data = {
87
161
  "server_id": self.server_id,
88
162
  "server_url": self.server_url,
89
163
  "server_name": self.server_name,
90
- "description": self.description
164
+ "description": self.description,
165
+ "version": proxy_info.get("version", "1.0.0"),
166
+ "capabilities": proxy_info.get("capabilities", ["jsonrpc", "rest"]),
167
+ "endpoints": proxy_info.get("endpoints", {
168
+ "jsonrpc": "/api/jsonrpc",
169
+ "rest": "/cmd",
170
+ "health": "/health"
171
+ })
91
172
  }
92
173
 
93
174
  logger.info(f"Attempting to register server with proxy at {self.proxy_url}")
@@ -95,12 +176,17 @@ class ProxyRegistrationManager:
95
176
 
96
177
  for attempt in range(self.retry_attempts):
97
178
  try:
98
- success, result = await self._make_registration_request(registration_data)
179
+ success, result = await self._make_secure_registration_request(registration_data)
99
180
 
100
181
  if success:
101
182
  self.registered = True
102
183
  self.server_key = result.get("server_key")
103
184
  logger.info(f"✅ Successfully registered with proxy. Server key: {self.server_key}")
185
+
186
+ # Start heartbeat if enabled
187
+ if self.registration_config.get("heartbeat", {}).get("enabled", True):
188
+ await self._start_heartbeat()
189
+
104
190
  return True
105
191
  else:
106
192
  error_msg = result.get("error", {}).get("message", "Unknown error")
@@ -135,6 +221,9 @@ class ProxyRegistrationManager:
135
221
  logger.info("Server not registered with proxy, skipping unregistration")
136
222
  return True
137
223
 
224
+ # Stop heartbeat
225
+ await self._stop_heartbeat()
226
+
138
227
  # Extract copy_number from server_key (format: server_id_copy_number)
139
228
  try:
140
229
  copy_number = int(self.server_key.split("_")[-1])
@@ -150,7 +239,7 @@ class ProxyRegistrationManager:
150
239
  logger.debug(f"Unregistration data: {unregistration_data}")
151
240
 
152
241
  try:
153
- success, result = await self._make_unregistration_request(unregistration_data)
242
+ success, result = await self._make_secure_unregistration_request(unregistration_data)
154
243
 
155
244
  if success:
156
245
  unregistered = result.get("unregistered", False)
@@ -171,9 +260,9 @@ class ProxyRegistrationManager:
171
260
  logger.error(f"❌ Unregistration failed with exception: {e}")
172
261
  return False
173
262
 
174
- async def _make_registration_request(self, data: Dict[str, Any]) -> Tuple[bool, Dict[str, Any]]:
263
+ async def _make_secure_registration_request(self, data: Dict[str, Any]) -> Tuple[bool, Dict[str, Any]]:
175
264
  """
176
- Make registration request to proxy.
265
+ Make secure registration request to proxy using security framework.
177
266
 
178
267
  Args:
179
268
  data: Registration data.
@@ -183,19 +272,40 @@ class ProxyRegistrationManager:
183
272
  """
184
273
  url = urljoin(self.proxy_url, "/register")
185
274
 
186
- async with aiohttp.ClientSession() as session:
187
- async with session.post(
188
- url,
189
- json=data,
190
- headers={"Content-Type": "application/json"},
191
- timeout=aiohttp.ClientTimeout(total=self.timeout)
192
- ) as response:
193
- result = await response.json()
194
- return response.status == 200, result
275
+ # Get authentication headers
276
+ headers = self._get_auth_headers()
277
+ headers["Content-Type"] = "application/json"
278
+
279
+ # Create SSL context if needed
280
+ ssl_context = self._create_ssl_context()
281
+
282
+ # Create connector with SSL context
283
+ connector = None
284
+ if ssl_context:
285
+ connector = aiohttp.TCPConnector(ssl=ssl_context)
286
+
287
+ try:
288
+ async with aiohttp.ClientSession(connector=connector) as session:
289
+ async with session.post(
290
+ url,
291
+ json=data,
292
+ headers=headers,
293
+ timeout=aiohttp.ClientTimeout(total=self.timeout)
294
+ ) as response:
295
+ result = await response.json()
296
+
297
+ # Validate response headers if security framework available
298
+ if self.client_security:
299
+ self.client_security.validate_server_response(dict(response.headers))
300
+
301
+ return response.status == 200, result
302
+ finally:
303
+ if connector:
304
+ await connector.close()
195
305
 
196
- async def _make_unregistration_request(self, data: Dict[str, Any]) -> Tuple[bool, Dict[str, Any]]:
306
+ async def _make_secure_unregistration_request(self, data: Dict[str, Any]) -> Tuple[bool, Dict[str, Any]]:
197
307
  """
198
- Make unregistration request to proxy.
308
+ Make secure unregistration request to proxy using security framework.
199
309
 
200
310
  Args:
201
311
  data: Unregistration data.
@@ -205,15 +315,120 @@ class ProxyRegistrationManager:
205
315
  """
206
316
  url = urljoin(self.proxy_url, "/unregister")
207
317
 
208
- async with aiohttp.ClientSession() as session:
209
- async with session.post(
210
- url,
211
- json=data,
212
- headers={"Content-Type": "application/json"},
213
- timeout=aiohttp.ClientTimeout(total=self.timeout)
214
- ) as response:
215
- result = await response.json()
216
- return response.status == 200, result
318
+ # Get authentication headers
319
+ headers = self._get_auth_headers()
320
+ headers["Content-Type"] = "application/json"
321
+
322
+ # Create SSL context if needed
323
+ ssl_context = self._create_ssl_context()
324
+
325
+ # Create connector with SSL context
326
+ connector = None
327
+ if ssl_context:
328
+ connector = aiohttp.TCPConnector(ssl=ssl_context)
329
+
330
+ try:
331
+ async with aiohttp.ClientSession(connector=connector) as session:
332
+ async with session.post(
333
+ url,
334
+ json=data,
335
+ headers=headers,
336
+ timeout=aiohttp.ClientTimeout(total=self.timeout)
337
+ ) as response:
338
+ result = await response.json()
339
+
340
+ # Validate response headers if security framework available
341
+ if self.client_security:
342
+ self.client_security.validate_server_response(dict(response.headers))
343
+
344
+ return response.status == 200, result
345
+ finally:
346
+ if connector:
347
+ await connector.close()
348
+
349
+ async def _start_heartbeat(self) -> None:
350
+ """Start heartbeat task for keeping registration alive."""
351
+ if self.heartbeat_task and not self.heartbeat_task.done():
352
+ return
353
+
354
+ self.heartbeat_task = asyncio.create_task(self._heartbeat_loop())
355
+ logger.info("Heartbeat task started")
356
+
357
+ async def _stop_heartbeat(self) -> None:
358
+ """Stop heartbeat task."""
359
+ if self.heartbeat_task and not self.heartbeat_task.done():
360
+ self.heartbeat_task.cancel()
361
+ try:
362
+ await self.heartbeat_task
363
+ except asyncio.CancelledError:
364
+ pass
365
+ logger.info("Heartbeat task stopped")
366
+
367
+ async def _heartbeat_loop(self) -> None:
368
+ """Heartbeat loop to keep registration alive."""
369
+ while self.registered:
370
+ try:
371
+ await asyncio.sleep(self.heartbeat_interval)
372
+
373
+ if not self.registered:
374
+ break
375
+
376
+ # Send heartbeat
377
+ success = await self._send_heartbeat()
378
+ if not success:
379
+ logger.warning("Heartbeat failed, attempting to re-register")
380
+ await self.register_server()
381
+
382
+ except asyncio.CancelledError:
383
+ break
384
+ except Exception as e:
385
+ logger.error(f"Heartbeat error: {e}")
386
+
387
+ async def _send_heartbeat(self) -> bool:
388
+ """Send heartbeat to proxy server."""
389
+ if not self.server_key:
390
+ return False
391
+
392
+ heartbeat_data = {
393
+ "server_id": self.server_id,
394
+ "server_key": self.server_key,
395
+ "timestamp": int(time.time())
396
+ }
397
+
398
+ url = urljoin(self.proxy_url, "/heartbeat")
399
+
400
+ # Get authentication headers
401
+ headers = self._get_auth_headers()
402
+ headers["Content-Type"] = "application/json"
403
+
404
+ # Create SSL context if needed
405
+ ssl_context = self._create_ssl_context()
406
+
407
+ # Create connector with SSL context
408
+ connector = None
409
+ if ssl_context:
410
+ connector = aiohttp.TCPConnector(ssl=ssl_context)
411
+
412
+ try:
413
+ async with aiohttp.ClientSession(connector=connector) as session:
414
+ async with session.post(
415
+ url,
416
+ json=heartbeat_data,
417
+ headers=headers,
418
+ timeout=aiohttp.ClientTimeout(total=self.timeout)
419
+ ) as response:
420
+ if response.status == 200:
421
+ logger.debug("Heartbeat sent successfully")
422
+ return True
423
+ else:
424
+ logger.warning(f"Heartbeat failed with status: {response.status}")
425
+ return False
426
+ except Exception as e:
427
+ logger.error(f"Heartbeat error: {e}")
428
+ return False
429
+ finally:
430
+ if connector:
431
+ await connector.close()
217
432
 
218
433
  def get_registration_status(self) -> Dict[str, Any]:
219
434
  """
@@ -222,18 +437,44 @@ class ProxyRegistrationManager:
222
437
  Returns:
223
438
  Dictionary with registration status information.
224
439
  """
225
- return {
440
+ status = {
226
441
  "enabled": self.is_enabled(),
227
442
  "registered": self.registered,
228
443
  "server_key": self.server_key,
229
444
  "server_url": self.server_url,
230
445
  "proxy_url": self.proxy_url,
231
- "server_id": self.server_id
446
+ "server_id": self.server_id,
447
+ "heartbeat_active": self.heartbeat_task is not None and not self.heartbeat_task.done()
232
448
  }
449
+
450
+ # Add security information if available
451
+ if self.client_security:
452
+ status["security_enabled"] = True
453
+ status["ssl_enabled"] = self.client_security.is_ssl_enabled()
454
+ status["auth_methods"] = self.client_security.get_supported_auth_methods()
455
+
456
+ cert_info = self.client_security.get_client_certificate_info()
457
+ if cert_info:
458
+ status["client_certificate"] = cert_info
459
+ else:
460
+ status["security_enabled"] = False
461
+
462
+ return status
463
+
233
464
 
465
+ # Global proxy registration manager instance (will be initialized with config)
466
+ proxy_registration_manager: Optional[ProxyRegistrationManager] = None
234
467
 
235
- # Global proxy registration manager instance
236
- proxy_registration_manager = ProxyRegistrationManager()
468
+
469
+ def initialize_proxy_registration(config: Dict[str, Any]) -> None:
470
+ """
471
+ Initialize global proxy registration manager.
472
+
473
+ Args:
474
+ config: Application configuration
475
+ """
476
+ global proxy_registration_manager
477
+ proxy_registration_manager = ProxyRegistrationManager(config)
237
478
 
238
479
 
239
480
  async def register_with_proxy(server_url: str) -> bool:
@@ -246,6 +487,10 @@ async def register_with_proxy(server_url: str) -> bool:
246
487
  Returns:
247
488
  True if registration was successful, False otherwise.
248
489
  """
490
+ if not proxy_registration_manager:
491
+ logger.error("Proxy registration manager not initialized")
492
+ return False
493
+
249
494
  proxy_registration_manager.set_server_url(server_url)
250
495
  return await proxy_registration_manager.register_server()
251
496
 
@@ -257,6 +502,10 @@ async def unregister_from_proxy() -> bool:
257
502
  Returns:
258
503
  True if unregistration was successful, False otherwise.
259
504
  """
505
+ if not proxy_registration_manager:
506
+ logger.error("Proxy registration manager not initialized")
507
+ return False
508
+
260
509
  return await proxy_registration_manager.unregister_server()
261
510
 
262
511
 
@@ -267,4 +516,7 @@ def get_proxy_registration_status() -> Dict[str, Any]:
267
516
  Returns:
268
517
  Dictionary with registration status information.
269
518
  """
519
+ if not proxy_registration_manager:
520
+ return {"error": "Proxy registration manager not initialized"}
521
+
270
522
  return proxy_registration_manager.get_registration_status()
@@ -16,8 +16,7 @@ try:
16
16
  from mcp_security_framework.schemas.config import (
17
17
  AuthConfig, SSLConfig, PermissionConfig, RateLimitConfig
18
18
  )
19
- from mcp_security_framework.schemas.request import SecurityRequest
20
- from mcp_security_framework.schemas.response import SecurityResult
19
+ # Note: SecurityRequest and SecurityResult are not available in current version
21
20
  SECURITY_FRAMEWORK_AVAILABLE = True
22
21
  except ImportError:
23
22
  # Fallback for when mcp_security_framework is not available
@@ -28,8 +27,6 @@ except ImportError:
28
27
  SSLConfig = None
29
28
  PermissionConfig = None
30
29
  RateLimitConfig = None
31
- SecurityRequest = None
32
- SecurityResult = None
33
30
 
34
31
  from mcp_proxy_adapter.core.logging import logger
35
32
 
@@ -245,24 +242,24 @@ class SecurityAdapter:
245
242
  "user_id": None
246
243
  }
247
244
 
248
- def _create_security_request(self, request_data: Dict[str, Any]) -> SecurityRequest:
245
+ def _create_security_request(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
249
246
  """
250
- Create SecurityRequest from request data.
247
+ Create request data for security validation.
251
248
 
252
249
  Args:
253
250
  request_data: Request data dictionary
254
251
 
255
252
  Returns:
256
- SecurityRequest instance
253
+ Request data dictionary for security validation
257
254
  """
258
- return SecurityRequest(
259
- method=request_data.get("method", "GET"),
260
- path=request_data.get("path", "/"),
261
- headers=request_data.get("headers", {}),
262
- query_params=request_data.get("query_params", {}),
263
- client_ip=request_data.get("client_ip", "unknown"),
264
- body=request_data.get("body", {})
265
- )
255
+ return {
256
+ "method": request_data.get("method", "GET"),
257
+ "path": request_data.get("path", "/"),
258
+ "headers": request_data.get("headers", {}),
259
+ "query_params": request_data.get("query_params", {}),
260
+ "client_ip": request_data.get("client_ip", "unknown"),
261
+ "body": request_data.get("body", {})
262
+ }
266
263
 
267
264
  def _fallback_validation(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
268
265
  """