mcp-proxy-adapter 6.0.0__py3-none-any.whl → 6.0.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.
- mcp_proxy_adapter/__main__.py +27 -7
- mcp_proxy_adapter/api/app.py +209 -79
- mcp_proxy_adapter/api/handlers.py +16 -5
- mcp_proxy_adapter/api/middleware/__init__.py +14 -9
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
- mcp_proxy_adapter/api/middleware/factory.py +36 -12
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +84 -18
- mcp_proxy_adapter/api/middleware/unified_security.py +197 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +158 -0
- mcp_proxy_adapter/commands/__init__.py +7 -1
- mcp_proxy_adapter/commands/base.py +7 -4
- mcp_proxy_adapter/commands/builtin_commands.py +8 -2
- mcp_proxy_adapter/commands/command_registry.py +8 -0
- mcp_proxy_adapter/commands/echo_command.py +81 -0
- mcp_proxy_adapter/commands/health_command.py +1 -1
- mcp_proxy_adapter/commands/help_command.py +21 -14
- mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
- mcp_proxy_adapter/commands/role_test_command.py +141 -0
- mcp_proxy_adapter/commands/security_command.py +488 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +234 -351
- mcp_proxy_adapter/commands/token_management_command.py +1 -1
- mcp_proxy_adapter/config.py +323 -40
- mcp_proxy_adapter/core/app_factory.py +410 -0
- mcp_proxy_adapter/core/app_runner.py +272 -0
- mcp_proxy_adapter/core/certificate_utils.py +291 -73
- mcp_proxy_adapter/core/client.py +574 -0
- mcp_proxy_adapter/core/client_manager.py +284 -0
- mcp_proxy_adapter/core/client_security.py +384 -0
- mcp_proxy_adapter/core/logging.py +8 -3
- mcp_proxy_adapter/core/mtls_asgi.py +156 -0
- mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
- mcp_proxy_adapter/core/protocol_manager.py +169 -10
- mcp_proxy_adapter/core/proxy_client.py +602 -0
- mcp_proxy_adapter/core/proxy_registration.py +299 -47
- mcp_proxy_adapter/core/security_adapter.py +12 -15
- mcp_proxy_adapter/core/security_integration.py +286 -0
- mcp_proxy_adapter/core/server_adapter.py +282 -0
- mcp_proxy_adapter/core/server_engine.py +270 -0
- mcp_proxy_adapter/core/ssl_utils.py +13 -12
- mcp_proxy_adapter/core/transport_manager.py +5 -5
- mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
- mcp_proxy_adapter/examples/__init__.py +13 -4
- mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
- mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +44 -0
- mcp_proxy_adapter/examples/commands/__init__.py +5 -0
- mcp_proxy_adapter/examples/create_certificates_simple.py +550 -0
- mcp_proxy_adapter/examples/debug_request_state.py +112 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +158 -0
- mcp_proxy_adapter/examples/demo_client.py +275 -0
- mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
- mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
- mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
- mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
- mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
- mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +80 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +90 -0
- mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +75 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +71 -0
- mcp_proxy_adapter/examples/full_application/main.py +173 -0
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
- mcp_proxy_adapter/examples/generate_all_certificates.py +362 -0
- mcp_proxy_adapter/examples/generate_certificates.py +177 -0
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
- mcp_proxy_adapter/examples/generate_test_configs.py +331 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +334 -0
- mcp_proxy_adapter/examples/run_example.py +59 -0
- mcp_proxy_adapter/examples/run_full_test_suite.py +318 -0
- mcp_proxy_adapter/examples/run_proxy_server.py +146 -0
- mcp_proxy_adapter/examples/run_security_tests.py +544 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +247 -0
- mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
- mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
- mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
- mcp_proxy_adapter/examples/security_test_client.py +782 -0
- mcp_proxy_adapter/examples/setup_test_environment.py +328 -0
- mcp_proxy_adapter/examples/test_config.py +148 -0
- mcp_proxy_adapter/examples/test_config_generator.py +86 -0
- mcp_proxy_adapter/examples/test_examples.py +281 -0
- mcp_proxy_adapter/examples/universal_client.py +620 -0
- mcp_proxy_adapter/main.py +66 -148
- mcp_proxy_adapter/utils/config_generator.py +1008 -0
- mcp_proxy_adapter/version.py +5 -2
- mcp_proxy_adapter-6.0.1.dist-info/METADATA +679 -0
- mcp_proxy_adapter-6.0.1.dist-info/RECORD +140 -0
- mcp_proxy_adapter-6.0.1.dist-info/entry_points.txt +2 -0
- {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/licenses/LICENSE +2 -2
- mcp_proxy_adapter/api/middleware/auth.py +0 -146
- mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
- mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
- mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
- mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
- mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
- mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
- mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
- mcp_proxy_adapter/api/middleware/security.py +0 -376
- mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
- mcp_proxy_adapter/examples/README.md +0 -124
- mcp_proxy_adapter/examples/basic_server/README.md +0 -60
- mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
- mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
- mcp_proxy_adapter/examples/basic_server/config.json +0 -70
- mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
- mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
- mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
- mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
- mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
- mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
- mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
- mcp_proxy_adapter/examples/basic_server/server.py +0 -114
- mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
- mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
- mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
- mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
- mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
- mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
- mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
- mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
- mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
- mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
- mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
- mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
- mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
- mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
- mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
- mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
- mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
- mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
- mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
- mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
- mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
- mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
- mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
- mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
- mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
- mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
- mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
- mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
- mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
- mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
- mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
- mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
- mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
- mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
- mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
- mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
- mcp_proxy_adapter/examples/deployment/README.md +0 -49
- mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
- mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
- mcp_proxy_adapter/examples/deployment/config.json +0 -29
- mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
- mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
- mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
- mcp_proxy_adapter/examples/deployment/run.sh +0 -43
- mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
- mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
- mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
- mcp_proxy_adapter/schemas/base_schema.json +0 -114
- mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
- mcp_proxy_adapter/schemas/roles_schema.json +0 -162
- mcp_proxy_adapter/tests/__init__.py +0 -0
- mcp_proxy_adapter/tests/api/__init__.py +0 -3
- mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
- mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
- mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
- mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
- mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
- mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
- mcp_proxy_adapter/tests/commands/__init__.py +0 -3
- mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
- mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
- mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
- mcp_proxy_adapter/tests/conftest.py +0 -131
- mcp_proxy_adapter/tests/functional/__init__.py +0 -3
- mcp_proxy_adapter/tests/functional/test_api.py +0 -253
- mcp_proxy_adapter/tests/integration/__init__.py +0 -3
- mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
- mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
- mcp_proxy_adapter/tests/performance/__init__.py +0 -3
- mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
- mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
- mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
- mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
- mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
- mcp_proxy_adapter/tests/test_base_command.py +0 -123
- mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
- mcp_proxy_adapter/tests/test_command_registry.py +0 -281
- mcp_proxy_adapter/tests/test_config.py +0 -127
- mcp_proxy_adapter/tests/test_utils.py +0 -65
- mcp_proxy_adapter/tests/unit/__init__.py +0 -3
- mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
- mcp_proxy_adapter/tests/unit/test_config.py +0 -270
- mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
- mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
- {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/top_level.txt +0 -0
@@ -1,268 +1,409 @@
|
|
1
1
|
"""
|
2
|
-
|
2
|
+
Proxy Registration Command
|
3
3
|
|
4
|
-
This command
|
5
|
-
|
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
|
9
|
-
|
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
|
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
|
-
|
23
|
-
|
22
|
+
@dataclass
|
23
|
+
class ProxyRegistrationCommandResult(SuccessResult):
|
24
|
+
"""Result of proxy registration command."""
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
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
|
-
"
|
70
|
+
"details": {
|
58
71
|
"type": "object",
|
59
|
-
"description": "Additional
|
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
|
-
|
92
|
-
|
93
|
-
|
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) ->
|
92
|
+
async def execute(self, **kwargs) -> ProxyRegistrationCommandResult:
|
96
93
|
"""
|
97
94
|
Execute proxy registration command.
|
98
95
|
|
99
96
|
Args:
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
113
|
+
ProxyRegistrationCommandResult
|
106
114
|
"""
|
107
|
-
|
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
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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"
|
156
|
+
message=f"Unknown operation: {operation}"
|
127
157
|
)
|
128
158
|
|
129
|
-
async def _handle_register(self,
|
130
|
-
"""
|
131
|
-
|
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
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
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
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
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
|
-
|
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
|
-
|
227
|
+
server_key = f"{server_id}_{copy_number}"
|
165
228
|
|
166
|
-
if
|
167
|
-
|
168
|
-
|
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="
|
171
|
-
|
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
|
179
|
-
|
180
|
-
|
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
|
184
|
-
"""
|
185
|
-
|
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
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
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
|
198
|
-
|
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="
|
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
|
278
|
+
return ProxyRegistrationCommandResult(
|
279
|
+
operation="heartbeat",
|
204
280
|
success=False,
|
205
|
-
message="
|
281
|
+
message=f"Server not found: {server_key}"
|
206
282
|
)
|
207
283
|
|
208
|
-
async def
|
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
|
-
|
321
|
+
Check if user has required permissions.
|
211
322
|
|
212
323
|
Args:
|
213
|
-
|
324
|
+
user_permissions: User's permissions
|
325
|
+
required_permissions: Required permissions
|
214
326
|
|
215
327
|
Returns:
|
216
|
-
|
328
|
+
True if user has required permissions
|
217
329
|
"""
|
218
|
-
|
330
|
+
# Admin has all permissions
|
331
|
+
if "*" in user_permissions:
|
332
|
+
return True
|
219
333
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
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
|
-
"
|
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
|
-
"
|
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
|
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": ["
|
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
|
}
|