mcp-proxy-adapter 4.1.1__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.
- mcp_proxy_adapter/__main__.py +12 -0
- mcp_proxy_adapter/api/app.py +254 -33
- mcp_proxy_adapter/api/handlers.py +32 -6
- mcp_proxy_adapter/api/middleware/__init__.py +36 -30
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
- mcp_proxy_adapter/api/middleware/error_handling.py +9 -0
- mcp_proxy_adapter/api/middleware/factory.py +243 -0
- mcp_proxy_adapter/api/middleware/logging.py +32 -6
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +135 -0
- mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
- mcp_proxy_adapter/api/middleware/unified_security.py +152 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
- mcp_proxy_adapter/commands/__init__.py +19 -4
- mcp_proxy_adapter/commands/auth_validation_command.py +408 -0
- mcp_proxy_adapter/commands/base.py +66 -32
- mcp_proxy_adapter/commands/builtin_commands.py +95 -0
- mcp_proxy_adapter/commands/catalog_manager.py +838 -0
- mcp_proxy_adapter/commands/cert_monitor_command.py +620 -0
- mcp_proxy_adapter/commands/certificate_management_command.py +608 -0
- mcp_proxy_adapter/commands/command_registry.py +711 -354
- mcp_proxy_adapter/commands/dependency_manager.py +245 -0
- mcp_proxy_adapter/commands/echo_command.py +81 -0
- mcp_proxy_adapter/commands/health_command.py +7 -0
- mcp_proxy_adapter/commands/help_command.py +21 -14
- mcp_proxy_adapter/commands/hooks.py +200 -167
- mcp_proxy_adapter/commands/key_management_command.py +506 -0
- mcp_proxy_adapter/commands/load_command.py +176 -0
- mcp_proxy_adapter/commands/plugins_command.py +235 -0
- mcp_proxy_adapter/commands/protocol_management_command.py +232 -0
- mcp_proxy_adapter/commands/proxy_registration_command.py +409 -0
- mcp_proxy_adapter/commands/reload_command.py +48 -50
- mcp_proxy_adapter/commands/result.py +1 -0
- mcp_proxy_adapter/commands/role_test_command.py +141 -0
- mcp_proxy_adapter/commands/roles_management_command.py +697 -0
- mcp_proxy_adapter/commands/security_command.py +488 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +483 -0
- mcp_proxy_adapter/commands/token_management_command.py +529 -0
- mcp_proxy_adapter/commands/transport_management_command.py +144 -0
- mcp_proxy_adapter/commands/unload_command.py +158 -0
- mcp_proxy_adapter/config.py +159 -2
- mcp_proxy_adapter/core/app_factory.py +326 -0
- mcp_proxy_adapter/core/auth_validator.py +606 -0
- mcp_proxy_adapter/core/certificate_utils.py +827 -0
- mcp_proxy_adapter/core/client_security.py +384 -0
- mcp_proxy_adapter/core/config_converter.py +405 -0
- mcp_proxy_adapter/core/config_validator.py +218 -0
- mcp_proxy_adapter/core/logging.py +19 -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 +235 -0
- mcp_proxy_adapter/core/proxy_client.py +602 -0
- mcp_proxy_adapter/core/proxy_registration.py +522 -0
- mcp_proxy_adapter/core/role_utils.py +426 -0
- mcp_proxy_adapter/core/security_adapter.py +370 -0
- mcp_proxy_adapter/core/security_factory.py +239 -0
- mcp_proxy_adapter/core/security_integration.py +277 -0
- mcp_proxy_adapter/core/server_adapter.py +345 -0
- mcp_proxy_adapter/core/server_engine.py +364 -0
- mcp_proxy_adapter/core/settings.py +1 -0
- mcp_proxy_adapter/core/ssl_utils.py +233 -0
- mcp_proxy_adapter/core/transport_manager.py +292 -0
- mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
- mcp_proxy_adapter/custom_openapi.py +22 -11
- mcp_proxy_adapter/examples/README.md +230 -97
- mcp_proxy_adapter/examples/README_EN.md +258 -0
- mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
- mcp_proxy_adapter/examples/__pycache__/security_configurations.cpython-312.pyc +0 -0
- mcp_proxy_adapter/examples/__pycache__/security_test_client.cpython-312.pyc +0 -0
- mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
- mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +39 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +25 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
- mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
- mcp_proxy_adapter/examples/cert_config.json +9 -0
- mcp_proxy_adapter/examples/certs/admin.crt +32 -0
- mcp_proxy_adapter/examples/certs/admin.key +52 -0
- mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
- mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
- mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
- mcp_proxy_adapter/examples/certs/client.crt +32 -0
- mcp_proxy_adapter/examples/certs/client.key +52 -0
- mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
- mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
- mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
- mcp_proxy_adapter/examples/certs/client_user.key +52 -0
- mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
- mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
- mcp_proxy_adapter/examples/certs/readonly.key +52 -0
- mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/server.crt +32 -0
- mcp_proxy_adapter/examples/certs/server.key +52 -0
- mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
- mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
- mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
- mcp_proxy_adapter/examples/certs/user.crt +32 -0
- mcp_proxy_adapter/examples/certs/user.key +52 -0
- mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
- mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
- mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
- mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
- mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
- mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
- mcp_proxy_adapter/examples/commands/__init__.py +1 -0
- mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
- mcp_proxy_adapter/examples/debug_request_state.py +144 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
- mcp_proxy_adapter/examples/demo_client.py +341 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
- mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
- mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
- mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
- mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
- mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
- mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
- mcp_proxy_adapter/examples/full_application/main.py +138 -0
- mcp_proxy_adapter/examples/full_application/roles.json +21 -0
- mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
- mcp_proxy_adapter/examples/generate_certificates.py +121 -0
- mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
- mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
- mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
- mcp_proxy_adapter/examples/roles.json +38 -0
- mcp_proxy_adapter/examples/run_example.py +81 -0
- mcp_proxy_adapter/examples/run_security_tests.py +326 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
- mcp_proxy_adapter/examples/security_test_client.py +743 -0
- mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
- mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
- mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
- mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
- mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
- mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
- mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
- mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
- mcp_proxy_adapter/examples/test_examples.py +344 -0
- mcp_proxy_adapter/examples/universal_client.py +628 -0
- mcp_proxy_adapter/main.py +186 -0
- mcp_proxy_adapter/utils/config_generator.py +639 -0
- mcp_proxy_adapter/version.py +2 -1
- mcp_proxy_adapter-6.1.0.dist-info/METADATA +205 -0
- mcp_proxy_adapter-6.1.0.dist-info/RECORD +193 -0
- mcp_proxy_adapter-6.1.0.dist-info/entry_points.txt +2 -0
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/licenses/LICENSE +2 -2
- mcp_proxy_adapter/api/middleware/auth.py +0 -146
- mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
- mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
- mcp_proxy_adapter/examples/__init__.py +0 -7
- 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 -35
- mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
- mcp_proxy_adapter/examples/basic_server/server.py +0 -103
- 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 -250
- 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/config.json +0 -35
- 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/hooks.py +0 -230
- mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
- mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/server.py +0 -228
- mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
- 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/schemas/base_schema.json +0 -114
- mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
- 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 -217
- mcp_proxy_adapter-4.1.1.dist-info/METADATA +0 -200
- mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,409 @@
|
|
1
|
+
"""
|
2
|
+
Proxy Registration Command
|
3
|
+
|
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
|
9
|
+
"""
|
10
|
+
|
11
|
+
import json
|
12
|
+
import time
|
13
|
+
import uuid
|
14
|
+
from typing import Dict, Any, List, Optional
|
15
|
+
from dataclasses import dataclass
|
16
|
+
|
17
|
+
from mcp_proxy_adapter.commands.base import Command
|
18
|
+
from mcp_proxy_adapter.commands.result import SuccessResult
|
19
|
+
from mcp_proxy_adapter.core.logging import logger
|
20
|
+
|
21
|
+
|
22
|
+
@dataclass
|
23
|
+
class ProxyRegistrationCommandResult(SuccessResult):
|
24
|
+
"""Result of proxy registration command."""
|
25
|
+
|
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
|
+
}
|
39
|
+
|
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
|
47
|
+
|
48
|
+
@classmethod
|
49
|
+
def get_schema(cls) -> Dict[str, Any]:
|
50
|
+
"""Get JSON schema for result."""
|
51
|
+
return {
|
52
|
+
"type": "object",
|
53
|
+
"properties": {
|
54
|
+
"operation": {
|
55
|
+
"type": "string",
|
56
|
+
"description": "Operation performed"
|
57
|
+
},
|
58
|
+
"success": {
|
59
|
+
"type": "boolean",
|
60
|
+
"description": "Whether operation was successful"
|
61
|
+
},
|
62
|
+
"server_key": {
|
63
|
+
"type": "string",
|
64
|
+
"description": "Server key for registered server"
|
65
|
+
},
|
66
|
+
"message": {
|
67
|
+
"type": "string",
|
68
|
+
"description": "Result message"
|
69
|
+
},
|
70
|
+
"details": {
|
71
|
+
"type": "object",
|
72
|
+
"description": "Additional details"
|
73
|
+
}
|
74
|
+
},
|
75
|
+
"required": ["operation", "success", "message"]
|
76
|
+
}
|
77
|
+
|
78
|
+
|
79
|
+
class ProxyRegistrationCommand(Command):
|
80
|
+
"""Proxy registration command with security framework integration."""
|
81
|
+
|
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"
|
87
|
+
|
88
|
+
# In-memory registry for testing
|
89
|
+
_registry: Dict[str, Dict[str, Any]] = {}
|
90
|
+
_server_counter = 1
|
91
|
+
|
92
|
+
async def execute(self, **kwargs) -> ProxyRegistrationCommandResult:
|
93
|
+
"""
|
94
|
+
Execute proxy registration command.
|
95
|
+
|
96
|
+
Args:
|
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
|
+
|
112
|
+
Returns:
|
113
|
+
ProxyRegistrationCommandResult
|
114
|
+
"""
|
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
|
+
)
|
140
|
+
|
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,
|
155
|
+
success=False,
|
156
|
+
message=f"Unknown operation: {operation}"
|
157
|
+
)
|
158
|
+
|
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)
|
170
|
+
|
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
|
+
)
|
177
|
+
|
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)
|
219
|
+
|
220
|
+
if not server_id:
|
221
|
+
return ProxyRegistrationCommandResult(
|
222
|
+
operation="unregister",
|
223
|
+
success=False,
|
224
|
+
message="Missing required parameter: server_id"
|
225
|
+
)
|
226
|
+
|
227
|
+
server_key = f"{server_id}_{copy_number}"
|
228
|
+
|
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",
|
235
|
+
success=True,
|
236
|
+
message=f"Server unregistered successfully: {server_key}",
|
237
|
+
details={"unregistered": True}
|
238
|
+
)
|
239
|
+
else:
|
240
|
+
return ProxyRegistrationCommandResult(
|
241
|
+
operation="unregister",
|
242
|
+
success=True,
|
243
|
+
message=f"Server not found in registry: {server_key}",
|
244
|
+
details={"unregistered": False}
|
245
|
+
)
|
246
|
+
|
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")
|
253
|
+
|
254
|
+
if not server_key:
|
255
|
+
return ProxyRegistrationCommandResult(
|
256
|
+
operation="heartbeat",
|
257
|
+
success=False,
|
258
|
+
message="Missing required parameter: server_key"
|
259
|
+
)
|
260
|
+
|
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",
|
269
|
+
success=True,
|
270
|
+
message="Heartbeat processed successfully",
|
271
|
+
details={
|
272
|
+
"server_key": server_key,
|
273
|
+
"timestamp": timestamp,
|
274
|
+
"status": status
|
275
|
+
}
|
276
|
+
)
|
277
|
+
else:
|
278
|
+
return ProxyRegistrationCommandResult(
|
279
|
+
operation="heartbeat",
|
280
|
+
success=False,
|
281
|
+
message=f"Server not found: {server_key}"
|
282
|
+
)
|
283
|
+
|
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:
|
320
|
+
"""
|
321
|
+
Check if user has required permissions.
|
322
|
+
|
323
|
+
Args:
|
324
|
+
user_permissions: User's permissions
|
325
|
+
required_permissions: Required permissions
|
326
|
+
|
327
|
+
Returns:
|
328
|
+
True if user has required permissions
|
329
|
+
"""
|
330
|
+
# Admin has all permissions
|
331
|
+
if "*" in user_permissions:
|
332
|
+
return True
|
333
|
+
|
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
|
340
|
+
|
341
|
+
@classmethod
|
342
|
+
def get_schema(cls) -> Dict[str, Any]:
|
343
|
+
"""Get JSON schema for command parameters."""
|
344
|
+
return {
|
345
|
+
"type": "object",
|
346
|
+
"properties": {
|
347
|
+
"operation": {
|
348
|
+
"type": "string",
|
349
|
+
"enum": ["register", "unregister", "heartbeat", "discover"],
|
350
|
+
"description": "Operation to perform",
|
351
|
+
"default": "register"
|
352
|
+
},
|
353
|
+
"server_id": {
|
354
|
+
"type": "string",
|
355
|
+
"description": "Server ID for registration"
|
356
|
+
},
|
357
|
+
"server_url": {
|
358
|
+
"type": "string",
|
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"
|
405
|
+
}
|
406
|
+
},
|
407
|
+
"required": ["operation"],
|
408
|
+
"additionalProperties": False
|
409
|
+
}
|
@@ -20,11 +20,11 @@ class ReloadResult:
|
|
20
20
|
def __init__(
|
21
21
|
self,
|
22
22
|
config_reloaded: bool,
|
23
|
-
|
24
|
-
custom_commands_preserved: int,
|
25
|
-
total_commands: int,
|
26
|
-
built_in_commands: int,
|
23
|
+
builtin_commands: int,
|
27
24
|
custom_commands: int,
|
25
|
+
loaded_commands: int,
|
26
|
+
remote_commands: int = 0,
|
27
|
+
total_commands: int = 0,
|
28
28
|
server_restart_required: bool = True,
|
29
29
|
success: bool = True,
|
30
30
|
error_message: Optional[str] = None
|
@@ -34,21 +34,20 @@ class ReloadResult:
|
|
34
34
|
|
35
35
|
Args:
|
36
36
|
config_reloaded: Whether configuration was reloaded successfully
|
37
|
-
|
38
|
-
|
37
|
+
builtin_commands: Number of built-in commands registered
|
38
|
+
custom_commands: Number of custom commands registered
|
39
|
+
loaded_commands: Number of commands loaded from directory
|
39
40
|
total_commands: Total number of commands after reload
|
40
|
-
built_in_commands: Number of built-in commands
|
41
|
-
custom_commands: Number of custom commands
|
42
41
|
server_restart_required: Whether server restart is required
|
43
42
|
success: Whether reload was successful
|
44
43
|
error_message: Error message if reload failed
|
45
44
|
"""
|
46
45
|
self.config_reloaded = config_reloaded
|
47
|
-
self.
|
48
|
-
self.custom_commands_preserved = custom_commands_preserved
|
49
|
-
self.total_commands = total_commands
|
50
|
-
self.built_in_commands = built_in_commands
|
46
|
+
self.builtin_commands = builtin_commands
|
51
47
|
self.custom_commands = custom_commands
|
48
|
+
self.loaded_commands = loaded_commands
|
49
|
+
self.remote_commands = remote_commands
|
50
|
+
self.total_commands = total_commands
|
52
51
|
self.server_restart_required = server_restart_required
|
53
52
|
self.success = success
|
54
53
|
self.error_message = error_message
|
@@ -63,11 +62,11 @@ class ReloadResult:
|
|
63
62
|
return {
|
64
63
|
"success": self.success,
|
65
64
|
"config_reloaded": self.config_reloaded,
|
66
|
-
"
|
67
|
-
"custom_commands_preserved": self.custom_commands_preserved,
|
68
|
-
"total_commands": self.total_commands,
|
69
|
-
"built_in_commands": self.built_in_commands,
|
65
|
+
"builtin_commands": self.builtin_commands,
|
70
66
|
"custom_commands": self.custom_commands,
|
67
|
+
"loaded_commands": self.loaded_commands,
|
68
|
+
"remote_commands": self.remote_commands,
|
69
|
+
"total_commands": self.total_commands,
|
71
70
|
"server_restart_required": self.server_restart_required,
|
72
71
|
"message": "Server restart required to apply configuration changes",
|
73
72
|
"error_message": self.error_message
|
@@ -91,25 +90,25 @@ class ReloadResult:
|
|
91
90
|
"type": "boolean",
|
92
91
|
"description": "Whether configuration was reloaded successfully"
|
93
92
|
},
|
94
|
-
"
|
93
|
+
"builtin_commands": {
|
95
94
|
"type": "integer",
|
96
|
-
"description": "Number of commands
|
95
|
+
"description": "Number of built-in commands registered"
|
97
96
|
},
|
98
|
-
"
|
97
|
+
"custom_commands": {
|
99
98
|
"type": "integer",
|
100
|
-
"description": "Number of custom commands
|
99
|
+
"description": "Number of custom commands registered"
|
101
100
|
},
|
102
|
-
"
|
101
|
+
"loaded_commands": {
|
103
102
|
"type": "integer",
|
104
|
-
"description": "
|
103
|
+
"description": "Number of commands loaded from directory"
|
105
104
|
},
|
106
|
-
"
|
105
|
+
"remote_commands": {
|
107
106
|
"type": "integer",
|
108
|
-
"description": "Number of
|
107
|
+
"description": "Number of commands loaded from remote plugins"
|
109
108
|
},
|
110
|
-
"
|
109
|
+
"total_commands": {
|
111
110
|
"type": "integer",
|
112
|
-
"description": "
|
111
|
+
"description": "Total number of commands after reload"
|
113
112
|
},
|
114
113
|
"server_restart_required": {
|
115
114
|
"type": "boolean",
|
@@ -125,9 +124,8 @@ class ReloadResult:
|
|
125
124
|
}
|
126
125
|
},
|
127
126
|
"required": [
|
128
|
-
"success", "config_reloaded", "
|
129
|
-
"
|
130
|
-
"built_in_commands", "custom_commands", "server_restart_required"
|
127
|
+
"success", "config_reloaded", "builtin_commands", "custom_commands",
|
128
|
+
"loaded_commands", "remote_commands", "total_commands", "server_restart_required"
|
131
129
|
]
|
132
130
|
}
|
133
131
|
|
@@ -135,7 +133,7 @@ class ReloadResult:
|
|
135
133
|
class ReloadCommand(Command):
|
136
134
|
"""
|
137
135
|
Command for reloading configuration and rediscovering commands.
|
138
|
-
|
136
|
+
Uses the unified initialization logic.
|
139
137
|
"""
|
140
138
|
|
141
139
|
name = "reload"
|
@@ -145,7 +143,7 @@ class ReloadCommand(Command):
|
|
145
143
|
Execute reload command.
|
146
144
|
|
147
145
|
Args:
|
148
|
-
**params: Command parameters (
|
146
|
+
**params: Command parameters (config_path)
|
149
147
|
|
150
148
|
Returns:
|
151
149
|
ReloadResult with reload information
|
@@ -153,18 +151,23 @@ class ReloadCommand(Command):
|
|
153
151
|
try:
|
154
152
|
logger.info("🔄 Starting configuration and commands reload...")
|
155
153
|
|
156
|
-
#
|
157
|
-
|
154
|
+
# Get config path from parameters
|
155
|
+
config_path = params.get("config_path")
|
156
|
+
if not config_path:
|
157
|
+
logger.warning("No config_path provided, using default configuration")
|
158
|
+
|
159
|
+
# Perform reload using unified initialization
|
160
|
+
reload_info = registry.reload_system(config_path=config_path)
|
158
161
|
|
159
162
|
# Create result
|
160
163
|
result = ReloadResult(
|
161
164
|
config_reloaded=reload_info.get("config_reloaded", False),
|
162
|
-
|
163
|
-
custom_commands_preserved=reload_info.get("custom_commands_preserved", 0),
|
164
|
-
total_commands=reload_info.get("total_commands", 0),
|
165
|
-
built_in_commands=reload_info.get("built_in_commands", 0),
|
165
|
+
builtin_commands=reload_info.get("builtin_commands", 0),
|
166
166
|
custom_commands=reload_info.get("custom_commands", 0),
|
167
|
-
|
167
|
+
loaded_commands=reload_info.get("loaded_commands", 0),
|
168
|
+
remote_commands=reload_info.get("remote_commands", 0),
|
169
|
+
total_commands=reload_info.get("total_commands", 0),
|
170
|
+
server_restart_required=True, # Default to True as per tests
|
168
171
|
success=True
|
169
172
|
)
|
170
173
|
|
@@ -175,11 +178,11 @@ class ReloadCommand(Command):
|
|
175
178
|
logger.error(f"❌ Reload failed: {str(e)}")
|
176
179
|
return ReloadResult(
|
177
180
|
config_reloaded=False,
|
178
|
-
|
179
|
-
custom_commands_preserved=0,
|
180
|
-
total_commands=0,
|
181
|
-
built_in_commands=0,
|
181
|
+
builtin_commands=0,
|
182
182
|
custom_commands=0,
|
183
|
+
loaded_commands=0,
|
184
|
+
remote_commands=0,
|
185
|
+
total_commands=0,
|
183
186
|
server_restart_required=False,
|
184
187
|
success=False,
|
185
188
|
error_message=str(e)
|
@@ -196,15 +199,10 @@ class ReloadCommand(Command):
|
|
196
199
|
return {
|
197
200
|
"type": "object",
|
198
201
|
"properties": {
|
199
|
-
"
|
202
|
+
"config_path": {
|
200
203
|
"type": "string",
|
201
|
-
"description": "Path to
|
202
|
-
"default":
|
203
|
-
},
|
204
|
-
"force_restart": {
|
205
|
-
"type": "boolean",
|
206
|
-
"description": "Force server restart to apply configuration changes",
|
207
|
-
"default": True
|
204
|
+
"description": "Path to configuration file to reload",
|
205
|
+
"default": None
|
208
206
|
}
|
209
207
|
},
|
210
208
|
"additionalProperties": False
|