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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. mcp_proxy_adapter/api/app.py +174 -80
  2. mcp_proxy_adapter/api/handlers.py +16 -5
  3. mcp_proxy_adapter/api/middleware/__init__.py +9 -4
  4. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  5. mcp_proxy_adapter/api/middleware/factory.py +36 -12
  6. mcp_proxy_adapter/api/middleware/protocol_middleware.py +32 -13
  7. mcp_proxy_adapter/api/middleware/unified_security.py +160 -0
  8. mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
  9. mcp_proxy_adapter/commands/__init__.py +7 -1
  10. mcp_proxy_adapter/commands/base.py +7 -4
  11. mcp_proxy_adapter/commands/builtin_commands.py +8 -2
  12. mcp_proxy_adapter/commands/command_registry.py +8 -0
  13. mcp_proxy_adapter/commands/echo_command.py +81 -0
  14. mcp_proxy_adapter/commands/help_command.py +21 -14
  15. mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
  16. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  17. mcp_proxy_adapter/commands/security_command.py +488 -0
  18. mcp_proxy_adapter/commands/ssl_setup_command.py +2 -2
  19. mcp_proxy_adapter/commands/token_management_command.py +1 -1
  20. mcp_proxy_adapter/config.py +81 -21
  21. mcp_proxy_adapter/core/app_factory.py +326 -0
  22. mcp_proxy_adapter/core/client_security.py +384 -0
  23. mcp_proxy_adapter/core/logging.py +8 -3
  24. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  25. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  26. mcp_proxy_adapter/core/protocol_manager.py +139 -8
  27. mcp_proxy_adapter/core/proxy_client.py +602 -0
  28. mcp_proxy_adapter/core/proxy_registration.py +299 -47
  29. mcp_proxy_adapter/core/security_adapter.py +12 -15
  30. mcp_proxy_adapter/core/security_integration.py +285 -0
  31. mcp_proxy_adapter/core/server_adapter.py +345 -0
  32. mcp_proxy_adapter/core/server_engine.py +364 -0
  33. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  34. mcp_proxy_adapter/docs/EN/TROUBLESHOOTING.md +285 -0
  35. mcp_proxy_adapter/docs/RU/TROUBLESHOOTING.md +285 -0
  36. mcp_proxy_adapter/examples/README.md +230 -97
  37. mcp_proxy_adapter/examples/README_EN.md +258 -0
  38. mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
  39. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
  40. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
  41. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +43 -0
  42. mcp_proxy_adapter/examples/basic_framework/configs/https_no_protocol_middleware.json +36 -0
  43. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +29 -0
  44. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_protocol_middleware.json +34 -0
  45. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
  46. mcp_proxy_adapter/examples/basic_framework/configs/mtls_simple.json +35 -0
  47. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
  48. mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
  49. mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
  50. mcp_proxy_adapter/examples/cert_config.json +9 -0
  51. mcp_proxy_adapter/examples/certs/admin.crt +32 -0
  52. mcp_proxy_adapter/examples/certs/admin.key +52 -0
  53. mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
  54. mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
  55. mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
  56. mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
  57. mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
  58. mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
  59. mcp_proxy_adapter/examples/certs/client.crt +32 -0
  60. mcp_proxy_adapter/examples/certs/client.key +52 -0
  61. mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
  62. mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
  63. mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
  64. mcp_proxy_adapter/examples/certs/client_user.key +52 -0
  65. mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
  66. mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
  67. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
  68. mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
  69. mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
  70. mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
  71. mcp_proxy_adapter/examples/certs/readonly.key +52 -0
  72. mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
  73. mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
  74. mcp_proxy_adapter/examples/certs/server.crt +32 -0
  75. mcp_proxy_adapter/examples/certs/server.key +52 -0
  76. mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
  77. mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
  78. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
  79. mcp_proxy_adapter/examples/certs/user.crt +32 -0
  80. mcp_proxy_adapter/examples/certs/user.key +52 -0
  81. mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
  82. mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
  83. mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
  84. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
  85. mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
  86. mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
  87. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
  88. mcp_proxy_adapter/examples/commands/__init__.py +1 -0
  89. mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
  90. mcp_proxy_adapter/examples/debug_request_state.py +144 -0
  91. mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
  92. mcp_proxy_adapter/examples/demo_client.py +341 -0
  93. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
  94. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
  95. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
  96. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
  97. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
  98. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
  99. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
  100. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
  101. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
  102. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
  103. mcp_proxy_adapter/examples/full_application/main.py +138 -0
  104. mcp_proxy_adapter/examples/full_application/roles.json +21 -0
  105. mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
  106. mcp_proxy_adapter/examples/generate_certificates.py +121 -0
  107. mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
  108. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
  109. mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
  110. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
  111. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
  112. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
  113. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
  114. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
  115. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
  116. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
  117. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
  118. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
  119. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
  120. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
  121. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
  122. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
  123. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
  124. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
  125. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
  126. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
  127. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
  128. mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
  129. mcp_proxy_adapter/examples/roles.json +38 -0
  130. mcp_proxy_adapter/examples/run_example.py +81 -0
  131. mcp_proxy_adapter/examples/run_security_tests.py +326 -0
  132. mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
  133. mcp_proxy_adapter/examples/security_test_client.py +743 -0
  134. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
  135. mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
  136. mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
  137. mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
  138. mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
  139. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
  140. mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
  141. mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
  142. mcp_proxy_adapter/examples/test_config_generator.py +110 -0
  143. mcp_proxy_adapter/examples/test_examples.py +344 -0
  144. mcp_proxy_adapter/examples/universal_client.py +628 -0
  145. mcp_proxy_adapter/main.py +21 -10
  146. mcp_proxy_adapter/utils/config_generator.py +727 -0
  147. mcp_proxy_adapter/version.py +5 -2
  148. mcp_proxy_adapter-6.1.1.dist-info/METADATA +205 -0
  149. mcp_proxy_adapter-6.1.1.dist-info/RECORD +197 -0
  150. mcp_proxy_adapter-6.1.1.dist-info/entry_points.txt +2 -0
  151. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/licenses/LICENSE +2 -2
  152. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  153. mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
  154. mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
  155. mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
  156. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  157. mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
  158. mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
  159. mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
  160. mcp_proxy_adapter/api/middleware/security.py +0 -376
  161. mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
  162. mcp_proxy_adapter/examples/__init__.py +0 -7
  163. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  164. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  165. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  166. mcp_proxy_adapter/examples/basic_server/config.json +0 -70
  167. mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
  168. mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
  169. mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
  170. mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
  171. mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
  172. mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
  173. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  174. mcp_proxy_adapter/examples/basic_server/server.py +0 -114
  175. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  176. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  177. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
  178. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  179. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  180. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  181. mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
  182. mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
  183. mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
  184. mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
  185. mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
  186. mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
  187. mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
  188. mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
  189. mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
  190. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  191. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  192. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  193. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  194. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  195. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  196. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  197. mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
  198. mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
  199. mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
  200. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  201. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  202. mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
  203. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  204. mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
  205. mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
  206. mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
  207. mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
  208. mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
  209. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  210. mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
  211. mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
  212. mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
  213. mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
  214. mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
  215. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  216. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  217. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  218. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  219. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  220. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  221. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  222. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  223. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  224. mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
  225. mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
  226. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  227. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  228. mcp_proxy_adapter/schemas/roles_schema.json +0 -162
  229. mcp_proxy_adapter/tests/__init__.py +0 -0
  230. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  231. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  232. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  233. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  234. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  235. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  236. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  237. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  238. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  239. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  240. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  241. mcp_proxy_adapter/tests/conftest.py +0 -131
  242. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  243. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  244. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  245. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  246. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  247. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  248. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  249. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  250. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  251. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  252. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  253. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  254. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  255. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  256. mcp_proxy_adapter/tests/test_config.py +0 -127
  257. mcp_proxy_adapter/tests/test_utils.py +0 -65
  258. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  259. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  260. mcp_proxy_adapter/tests/unit/test_config.py +0 -270
  261. mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
  262. mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
  263. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/WHEEL +0 -0
  264. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/top_level.txt +0 -0
@@ -1,268 +1,409 @@
1
1
  """
2
- Command for managing proxy registration.
2
+ Proxy Registration Command
3
3
 
4
- This command allows manual registration, unregistration, and status checking
5
- for the proxy registration functionality.
4
+ This command handles proxy registration functionality with security framework integration.
5
+ It provides endpoints for registration, unregistration, heartbeat, and discovery.
6
+
7
+ Author: Vasiliy Zdanovskiy
8
+ email: vasilyvz@gmail.com
6
9
  """
7
10
 
8
- import asyncio
9
- from typing import Dict, Any, Optional
11
+ import json
12
+ import time
13
+ import uuid
14
+ from typing import Dict, Any, List, Optional
15
+ from dataclasses import dataclass
10
16
 
11
17
  from mcp_proxy_adapter.commands.base import Command
12
- from mcp_proxy_adapter.commands.result import CommandResult
13
- from mcp_proxy_adapter.core.proxy_registration import (
14
- register_with_proxy,
15
- unregister_from_proxy,
16
- get_proxy_registration_status,
17
- proxy_registration_manager
18
- )
18
+ from mcp_proxy_adapter.commands.result import SuccessResult
19
19
  from mcp_proxy_adapter.core.logging import logger
20
20
 
21
21
 
22
- class ProxyRegistrationResult(CommandResult):
23
- """Result class for proxy registration commands."""
22
+ @dataclass
23
+ class ProxyRegistrationCommandResult(SuccessResult):
24
+ """Result of proxy registration command."""
24
25
 
25
- def __init__(self, success: bool, message: str, data: Optional[Dict[str, Any]] = None):
26
- """
27
- Initialize proxy registration result.
26
+ operation: str
27
+ success: bool
28
+ server_key: Optional[str] = None
29
+ message: str = ""
30
+ details: Optional[Dict[str, Any]] = None
31
+
32
+ def to_dict(self) -> Dict[str, Any]:
33
+ """Convert result to dictionary."""
34
+ result = {
35
+ "operation": self.operation,
36
+ "success": self.success,
37
+ "message": self.message
38
+ }
28
39
 
29
- Args:
30
- success: Whether the operation was successful.
31
- message: Result message.
32
- data: Additional data.
33
- """
34
- self.success = success
35
- self.message = message
36
- self.data = data
40
+ if self.server_key:
41
+ result["server_key"] = self.server_key
42
+
43
+ if self.details:
44
+ result["details"] = self.details
45
+
46
+ return result
37
47
 
38
48
  @classmethod
39
49
  def get_schema(cls) -> Dict[str, Any]:
40
- """
41
- Get JSON schema for this result.
42
-
43
- Returns:
44
- JSON schema dictionary.
45
- """
50
+ """Get JSON schema for result."""
46
51
  return {
47
52
  "type": "object",
48
53
  "properties": {
54
+ "operation": {
55
+ "type": "string",
56
+ "description": "Operation performed"
57
+ },
49
58
  "success": {
50
59
  "type": "boolean",
51
- "description": "Whether the operation was successful"
60
+ "description": "Whether operation was successful"
61
+ },
62
+ "server_key": {
63
+ "type": "string",
64
+ "description": "Server key for registered server"
52
65
  },
53
66
  "message": {
54
67
  "type": "string",
55
68
  "description": "Result message"
56
69
  },
57
- "data": {
70
+ "details": {
58
71
  "type": "object",
59
- "description": "Additional data",
60
- "additionalProperties": True
72
+ "description": "Additional details"
61
73
  }
62
74
  },
63
- "required": ["success", "message"]
64
- }
65
-
66
- def to_dict(self) -> Dict[str, Any]:
67
- """
68
- Convert result to dictionary.
69
-
70
- Returns:
71
- Dictionary representation of the result.
72
- """
73
- result = {
74
- "success": self.success,
75
- "message": self.message
75
+ "required": ["operation", "success", "message"]
76
76
  }
77
- if self.data is not None:
78
- result["data"] = self.data
79
- return result
80
77
 
81
78
 
82
79
  class ProxyRegistrationCommand(Command):
83
- """
84
- Command for managing proxy registration.
85
-
86
- Supports registration, unregistration, and status checking.
87
- """
80
+ """Proxy registration command with security framework integration."""
88
81
 
89
82
  name = "proxy_registration"
83
+ descr = "Proxy registration operations (register, unregister, heartbeat, discover)"
84
+ category = "proxy"
85
+ author = "Vasiliy Zdanovskiy"
86
+ email = "vasilyvz@gmail.com"
90
87
 
91
- def __init__(self):
92
- """Initialize the proxy registration command."""
93
- super().__init__()
88
+ # In-memory registry for testing
89
+ _registry: Dict[str, Dict[str, Any]] = {}
90
+ _server_counter = 1
94
91
 
95
- async def execute(self, **kwargs) -> ProxyRegistrationResult:
92
+ async def execute(self, **kwargs) -> ProxyRegistrationCommandResult:
96
93
  """
97
94
  Execute proxy registration command.
98
95
 
99
96
  Args:
100
- **kwargs: Command parameters containing:
101
- - action: "register", "unregister", or "status"
102
- - server_url: (optional) Server URL for registration
103
-
97
+ operation: Operation to perform (register, unregister, heartbeat, discover)
98
+ server_id: Server ID for registration
99
+ server_url: Server URL for registration
100
+ server_name: Server name
101
+ description: Server description
102
+ version: Server version
103
+ capabilities: Server capabilities
104
+ endpoints: Server endpoints
105
+ auth_method: Authentication method
106
+ security_enabled: Whether security is enabled
107
+ server_key: Server key for unregistration/heartbeat
108
+ copy_number: Copy number for unregistration
109
+ timestamp: Timestamp for heartbeat
110
+ status: Status for heartbeat
111
+
104
112
  Returns:
105
- ProxyRegistrationResult with operation result.
113
+ ProxyRegistrationCommandResult
106
114
  """
107
- action = kwargs.get("action", "status")
115
+ operation = kwargs.get("operation", "register")
116
+
117
+ # Check user permissions
118
+ context = kwargs.get("context", {})
119
+ user_info = context.get("user", {})
120
+ user_permissions = user_info.get("permissions", [])
121
+
122
+ # Define required permissions for each operation
123
+ operation_permissions = {
124
+ "register": ["register"],
125
+ "unregister": ["unregister"],
126
+ "heartbeat": ["heartbeat"],
127
+ "discover": ["discover"]
128
+ }
129
+
130
+ required_permissions = operation_permissions.get(operation, ["read"])
131
+
132
+ # Check if user has required permissions
133
+ logger.info(f"Checking permissions: user_permissions={user_permissions}, required={required_permissions}")
134
+ if not self._check_permissions(user_permissions, required_permissions):
135
+ return ProxyRegistrationCommandResult(
136
+ operation=operation,
137
+ success=False,
138
+ message=f"Permission denied: {operation} requires {required_permissions}"
139
+ )
108
140
 
109
- try:
110
- if action == "register":
111
- return await self._handle_register(kwargs)
112
- elif action == "unregister":
113
- return await self._handle_unregister(kwargs)
114
- elif action == "status":
115
- return await self._handle_status(kwargs)
116
- else:
117
- return ProxyRegistrationResult(
118
- success=False,
119
- message=f"Unknown action: {action}. Supported actions: register, unregister, status"
120
- )
121
-
122
- except Exception as e:
123
- logger.error(f"Proxy registration command failed: {e}")
124
- return ProxyRegistrationResult(
141
+ logger.info(f"Executing proxy registration operation: {operation}")
142
+ logger.debug(f"User permissions: {user_permissions}, required: {required_permissions}")
143
+
144
+ if operation == "register":
145
+ return await self._handle_register(kwargs)
146
+ elif operation == "unregister":
147
+ return await self._handle_unregister(kwargs)
148
+ elif operation == "heartbeat":
149
+ return await self._handle_heartbeat(kwargs)
150
+ elif operation == "discover":
151
+ return await self._handle_discover(kwargs)
152
+ else:
153
+ return ProxyRegistrationCommandResult(
154
+ operation=operation,
125
155
  success=False,
126
- message=f"Command execution failed: {str(e)}"
156
+ message=f"Unknown operation: {operation}"
127
157
  )
128
158
 
129
- async def _handle_register(self, params: Dict[str, Any]) -> ProxyRegistrationResult:
130
- """
131
- Handle registration action.
159
+ async def _handle_register(self, kwargs: Dict[str, Any]) -> ProxyRegistrationCommandResult:
160
+ """Handle registration operation."""
161
+ server_id = kwargs.get("server_id")
162
+ server_url = kwargs.get("server_url")
163
+ server_name = kwargs.get("server_name", "Unknown Server")
164
+ description = kwargs.get("description", "")
165
+ version = kwargs.get("version", "1.0.0")
166
+ capabilities = kwargs.get("capabilities", ["jsonrpc", "rest"])
167
+ endpoints = kwargs.get("endpoints", {})
168
+ auth_method = kwargs.get("auth_method", "none")
169
+ security_enabled = kwargs.get("security_enabled", False)
132
170
 
133
- Args:
134
- params: Command parameters.
135
-
136
- Returns:
137
- ProxyRegistrationResult.
138
- """
139
- server_url = params.get("server_url")
171
+ if not server_id or not server_url:
172
+ return ProxyRegistrationCommandResult(
173
+ operation="register",
174
+ success=False,
175
+ message="Missing required parameters: server_id and server_url"
176
+ )
140
177
 
141
- if not server_url:
142
- # Use current server configuration
143
- from mcp_proxy_adapter.config import config
144
-
145
- server_config = config.get("server", {})
146
- server_host = server_config.get("host", "0.0.0.0")
147
- server_port = server_config.get("port", 8000)
148
-
149
- # Determine server URL based on SSL configuration
150
- ssl_config = config.get("ssl", {})
151
- if ssl_config.get("enabled", False):
152
- protocol = "https"
153
- else:
154
- protocol = "http"
155
-
156
- # Use localhost for external access if host is 0.0.0.0
157
- if server_host == "0.0.0.0":
158
- server_host = "localhost"
159
-
160
- server_url = f"{protocol}://{server_host}:{server_port}"
178
+ # Check if server already exists
179
+ existing_servers = [key for key in self._registry.keys() if key.startswith(server_id)]
180
+ copy_number = len(existing_servers) + 1
181
+ server_key = f"{server_id}_{copy_number}"
182
+
183
+ # Create server record
184
+ server_record = {
185
+ "server_id": server_id,
186
+ "server_url": server_url,
187
+ "server_name": server_name,
188
+ "description": description,
189
+ "version": version,
190
+ "capabilities": capabilities,
191
+ "endpoints": endpoints,
192
+ "auth_method": auth_method,
193
+ "security_enabled": security_enabled,
194
+ "registered_at": int(time.time()),
195
+ "last_heartbeat": int(time.time()),
196
+ "status": "active"
197
+ }
198
+
199
+ self._registry[server_key] = server_record
200
+
201
+ logger.info(f"Registered server: {server_key} at {server_url}")
202
+
203
+ return ProxyRegistrationCommandResult(
204
+ operation="register",
205
+ success=True,
206
+ server_key=server_key,
207
+ message=f"Server registered successfully with key: {server_key}",
208
+ details={
209
+ "server_id": server_id,
210
+ "copy_number": copy_number,
211
+ "registered_at": server_record["registered_at"]
212
+ }
213
+ )
214
+
215
+ async def _handle_unregister(self, kwargs: Dict[str, Any]) -> ProxyRegistrationCommandResult:
216
+ """Handle unregistration operation."""
217
+ server_id = kwargs.get("server_id")
218
+ copy_number = kwargs.get("copy_number", 1)
161
219
 
162
- logger.info(f"Attempting to register with proxy using URL: {server_url}")
220
+ if not server_id:
221
+ return ProxyRegistrationCommandResult(
222
+ operation="unregister",
223
+ success=False,
224
+ message="Missing required parameter: server_id"
225
+ )
163
226
 
164
- success = await register_with_proxy(server_url)
227
+ server_key = f"{server_id}_{copy_number}"
165
228
 
166
- if success:
167
- status = get_proxy_registration_status()
168
- return ProxyRegistrationResult(
229
+ if server_key in self._registry:
230
+ del self._registry[server_key]
231
+ logger.info(f"Unregistered server: {server_key}")
232
+
233
+ return ProxyRegistrationCommandResult(
234
+ operation="unregister",
169
235
  success=True,
170
- message=" Successfully registered with proxy",
171
- data={
172
- "server_url": server_url,
173
- "server_key": status.get("server_key"),
174
- "registration_status": status
175
- }
236
+ message=f"Server unregistered successfully: {server_key}",
237
+ details={"unregistered": True}
176
238
  )
177
239
  else:
178
- return ProxyRegistrationResult(
179
- success=False,
180
- message="❌ Failed to register with proxy"
240
+ return ProxyRegistrationCommandResult(
241
+ operation="unregister",
242
+ success=True,
243
+ message=f"Server not found in registry: {server_key}",
244
+ details={"unregistered": False}
181
245
  )
182
246
 
183
- async def _handle_unregister(self, params: Dict[str, Any]) -> ProxyRegistrationResult:
184
- """
185
- Handle unregistration action.
247
+ async def _handle_heartbeat(self, kwargs: Dict[str, Any]) -> ProxyRegistrationCommandResult:
248
+ """Handle heartbeat operation."""
249
+ server_id = kwargs.get("server_id")
250
+ server_key = kwargs.get("server_key")
251
+ timestamp = kwargs.get("timestamp", int(time.time()))
252
+ status = kwargs.get("status", "healthy")
186
253
 
187
- Args:
188
- params: Command parameters.
189
-
190
- Returns:
191
- ProxyRegistrationResult.
192
- """
193
- logger.info("Attempting to unregister from proxy")
194
-
195
- success = await unregister_from_proxy()
254
+ if not server_key:
255
+ return ProxyRegistrationCommandResult(
256
+ operation="heartbeat",
257
+ success=False,
258
+ message="Missing required parameter: server_key"
259
+ )
196
260
 
197
- if success:
198
- return ProxyRegistrationResult(
261
+ if server_key in self._registry:
262
+ self._registry[server_key]["last_heartbeat"] = timestamp
263
+ self._registry[server_key]["status"] = status
264
+
265
+ logger.debug(f"Heartbeat received for server: {server_key}")
266
+
267
+ return ProxyRegistrationCommandResult(
268
+ operation="heartbeat",
199
269
  success=True,
200
- message=" Successfully unregistered from proxy"
270
+ message="Heartbeat processed successfully",
271
+ details={
272
+ "server_key": server_key,
273
+ "timestamp": timestamp,
274
+ "status": status
275
+ }
201
276
  )
202
277
  else:
203
- return ProxyRegistrationResult(
278
+ return ProxyRegistrationCommandResult(
279
+ operation="heartbeat",
204
280
  success=False,
205
- message=" Failed to unregister from proxy"
281
+ message=f"Server not found: {server_key}"
206
282
  )
207
283
 
208
- async def _handle_status(self, params: Dict[str, Any]) -> ProxyRegistrationResult:
284
+ async def _handle_discover(self, kwargs: Dict[str, Any]) -> ProxyRegistrationCommandResult:
285
+ """Handle discovery operation."""
286
+ # Return all registered servers
287
+ proxies = []
288
+
289
+ for server_key, server_record in self._registry.items():
290
+ # Check if server is active (heartbeat within last 5 minutes)
291
+ last_heartbeat = server_record.get("last_heartbeat", 0)
292
+ if time.time() - last_heartbeat < 300: # 5 minutes
293
+ proxy_info = {
294
+ "server_key": server_key,
295
+ "server_id": server_record["server_id"],
296
+ "server_url": server_record["server_url"],
297
+ "server_name": server_record["server_name"],
298
+ "description": server_record["description"],
299
+ "version": server_record["version"],
300
+ "capabilities": server_record["capabilities"],
301
+ "endpoints": server_record["endpoints"],
302
+ "auth_method": server_record["auth_method"],
303
+ "security_enabled": server_record["security_enabled"],
304
+ "registered_at": server_record["registered_at"],
305
+ "last_heartbeat": server_record["last_heartbeat"],
306
+ "status": server_record["status"]
307
+ }
308
+ proxies.append(proxy_info)
309
+
310
+ logger.info(f"Discovery request returned {len(proxies)} active servers")
311
+
312
+ return ProxyRegistrationCommandResult(
313
+ operation="discover",
314
+ success=True,
315
+ message=f"Found {len(proxies)} active proxy servers",
316
+ details={"proxies": proxies}
317
+ )
318
+
319
+ def _check_permissions(self, user_permissions: List[str], required_permissions: List[str]) -> bool:
209
320
  """
210
- Handle status action.
321
+ Check if user has required permissions.
211
322
 
212
323
  Args:
213
- params: Command parameters.
324
+ user_permissions: User's permissions
325
+ required_permissions: Required permissions
214
326
 
215
327
  Returns:
216
- ProxyRegistrationResult.
328
+ True if user has required permissions
217
329
  """
218
- status = get_proxy_registration_status()
330
+ # Admin has all permissions
331
+ if "*" in user_permissions:
332
+ return True
219
333
 
220
- return ProxyRegistrationResult(
221
- success=True,
222
- message="Proxy registration status retrieved successfully",
223
- data={
224
- "registration_status": status,
225
- "enabled": status.get("enabled", False),
226
- "registered": status.get("registered", False),
227
- "server_key": status.get("server_key"),
228
- "server_url": status.get("server_url"),
229
- "proxy_url": status.get("proxy_url")
230
- }
231
- )
334
+ # Check if user has all required permissions
335
+ for required in required_permissions:
336
+ if required not in user_permissions:
337
+ return False
338
+
339
+ return True
232
340
 
233
341
  @classmethod
234
342
  def get_schema(cls) -> Dict[str, Any]:
235
- """
236
- Get command schema.
237
-
238
- Returns:
239
- Command schema.
240
- """
343
+ """Get JSON schema for command parameters."""
241
344
  return {
242
345
  "type": "object",
243
346
  "properties": {
244
- "action": {
347
+ "operation": {
348
+ "type": "string",
349
+ "enum": ["register", "unregister", "heartbeat", "discover"],
350
+ "description": "Operation to perform",
351
+ "default": "register"
352
+ },
353
+ "server_id": {
245
354
  "type": "string",
246
- "enum": ["register", "unregister", "status"],
247
- "description": "Action to perform: register, unregister, or status"
355
+ "description": "Server ID for registration"
248
356
  },
249
357
  "server_url": {
250
358
  "type": "string",
251
- "description": "Server URL for registration (optional, uses current config if not provided)"
359
+ "description": "Server URL for registration"
360
+ },
361
+ "server_name": {
362
+ "type": "string",
363
+ "description": "Server name"
364
+ },
365
+ "description": {
366
+ "type": "string",
367
+ "description": "Server description"
368
+ },
369
+ "version": {
370
+ "type": "string",
371
+ "description": "Server version"
372
+ },
373
+ "capabilities": {
374
+ "type": "array",
375
+ "items": {"type": "string"},
376
+ "description": "Server capabilities"
377
+ },
378
+ "endpoints": {
379
+ "type": "object",
380
+ "description": "Server endpoints"
381
+ },
382
+ "auth_method": {
383
+ "type": "string",
384
+ "description": "Authentication method"
385
+ },
386
+ "security_enabled": {
387
+ "type": "boolean",
388
+ "description": "Whether security is enabled"
389
+ },
390
+ "server_key": {
391
+ "type": "string",
392
+ "description": "Server key for unregistration/heartbeat"
393
+ },
394
+ "copy_number": {
395
+ "type": "integer",
396
+ "description": "Copy number for unregistration"
397
+ },
398
+ "timestamp": {
399
+ "type": "integer",
400
+ "description": "Timestamp for heartbeat"
401
+ },
402
+ "status": {
403
+ "type": "string",
404
+ "description": "Status for heartbeat"
252
405
  }
253
406
  },
254
- "required": ["action"]
255
- }
256
-
257
- def to_dict(self) -> Dict[str, Any]:
258
- """
259
- Convert command to dictionary.
260
-
261
- Returns:
262
- Command dictionary representation.
263
- """
264
- return {
265
- "name": self.name,
266
- "description": "Manage proxy registration (register, unregister, status)",
267
- "schema": self.__class__.get_schema()
407
+ "required": ["operation"],
408
+ "additionalProperties": False
268
409
  }