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,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
  }