mcp-proxy-adapter 2.0.1__py3-none-any.whl → 6.9.50__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.
Potentially problematic release.
This version of mcp-proxy-adapter might be problematic. Click here for more details.
- mcp_proxy_adapter/__init__.py +47 -0
- mcp_proxy_adapter/__main__.py +13 -0
- mcp_proxy_adapter/api/__init__.py +0 -0
- mcp_proxy_adapter/api/app.py +66 -0
- mcp_proxy_adapter/api/core/__init__.py +18 -0
- mcp_proxy_adapter/api/core/app_factory.py +400 -0
- mcp_proxy_adapter/api/core/lifespan_manager.py +55 -0
- mcp_proxy_adapter/api/core/registration_context.py +356 -0
- mcp_proxy_adapter/api/core/registration_manager.py +307 -0
- mcp_proxy_adapter/api/core/registration_tasks.py +84 -0
- mcp_proxy_adapter/api/core/ssl_context_factory.py +88 -0
- mcp_proxy_adapter/api/handlers.py +181 -0
- mcp_proxy_adapter/api/middleware/__init__.py +21 -0
- mcp_proxy_adapter/api/middleware/base.py +54 -0
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +73 -0
- mcp_proxy_adapter/api/middleware/error_handling.py +76 -0
- mcp_proxy_adapter/api/middleware/factory.py +147 -0
- mcp_proxy_adapter/api/middleware/logging.py +31 -0
- mcp_proxy_adapter/api/middleware/performance.py +51 -0
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +140 -0
- mcp_proxy_adapter/api/middleware/transport_middleware.py +87 -0
- mcp_proxy_adapter/api/middleware/unified_security.py +223 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +132 -0
- mcp_proxy_adapter/api/openapi/__init__.py +21 -0
- mcp_proxy_adapter/api/openapi/command_integration.py +105 -0
- mcp_proxy_adapter/api/openapi/openapi_generator.py +40 -0
- mcp_proxy_adapter/api/openapi/openapi_registry.py +62 -0
- mcp_proxy_adapter/api/openapi/schema_loader.py +116 -0
- mcp_proxy_adapter/api/schemas.py +270 -0
- mcp_proxy_adapter/api/tool_integration.py +131 -0
- mcp_proxy_adapter/api/tools.py +163 -0
- mcp_proxy_adapter/cli/__init__.py +12 -0
- mcp_proxy_adapter/cli/commands/__init__.py +15 -0
- mcp_proxy_adapter/cli/commands/client.py +100 -0
- mcp_proxy_adapter/cli/commands/config_generate.py +105 -0
- mcp_proxy_adapter/cli/commands/config_validate.py +94 -0
- mcp_proxy_adapter/cli/commands/generate.py +259 -0
- mcp_proxy_adapter/cli/commands/server.py +174 -0
- mcp_proxy_adapter/cli/commands/sets.py +132 -0
- mcp_proxy_adapter/cli/commands/testconfig.py +177 -0
- mcp_proxy_adapter/cli/examples/__init__.py +8 -0
- mcp_proxy_adapter/cli/examples/http_basic.py +82 -0
- mcp_proxy_adapter/cli/examples/https_token.py +96 -0
- mcp_proxy_adapter/cli/examples/mtls_roles.py +103 -0
- mcp_proxy_adapter/cli/main.py +63 -0
- mcp_proxy_adapter/cli/parser.py +338 -0
- mcp_proxy_adapter/cli/validators.py +231 -0
- mcp_proxy_adapter/client/jsonrpc_client/__init__.py +9 -0
- mcp_proxy_adapter/client/jsonrpc_client/client.py +42 -0
- mcp_proxy_adapter/client/jsonrpc_client/command_api.py +45 -0
- mcp_proxy_adapter/client/jsonrpc_client/proxy_api.py +224 -0
- mcp_proxy_adapter/client/jsonrpc_client/queue_api.py +60 -0
- mcp_proxy_adapter/client/jsonrpc_client/transport.py +108 -0
- mcp_proxy_adapter/client/proxy.py +123 -0
- mcp_proxy_adapter/commands/__init__.py +66 -0
- mcp_proxy_adapter/commands/auth_validation_command.py +69 -0
- mcp_proxy_adapter/commands/base.py +389 -0
- mcp_proxy_adapter/commands/builtin_commands.py +30 -0
- mcp_proxy_adapter/commands/catalog/__init__.py +20 -0
- mcp_proxy_adapter/commands/catalog/catalog_loader.py +34 -0
- mcp_proxy_adapter/commands/catalog/catalog_manager.py +122 -0
- mcp_proxy_adapter/commands/catalog/catalog_syncer.py +149 -0
- mcp_proxy_adapter/commands/catalog/command_catalog.py +43 -0
- mcp_proxy_adapter/commands/catalog/dependency_manager.py +37 -0
- mcp_proxy_adapter/commands/catalog_manager.py +97 -0
- mcp_proxy_adapter/commands/cert_monitor_command.py +552 -0
- mcp_proxy_adapter/commands/certificate_management_command.py +562 -0
- mcp_proxy_adapter/commands/command_registry.py +298 -0
- mcp_proxy_adapter/commands/config_command.py +102 -0
- mcp_proxy_adapter/commands/dependency_container.py +40 -0
- mcp_proxy_adapter/commands/dependency_manager.py +143 -0
- mcp_proxy_adapter/commands/echo_command.py +48 -0
- mcp_proxy_adapter/commands/health_command.py +142 -0
- mcp_proxy_adapter/commands/help_command.py +175 -0
- mcp_proxy_adapter/commands/hooks.py +172 -0
- mcp_proxy_adapter/commands/key_management_command.py +484 -0
- mcp_proxy_adapter/commands/load_command.py +123 -0
- mcp_proxy_adapter/commands/plugins_command.py +246 -0
- mcp_proxy_adapter/commands/protocol_management_command.py +216 -0
- mcp_proxy_adapter/commands/proxy_registration_command.py +319 -0
- mcp_proxy_adapter/commands/queue_commands.py +750 -0
- mcp_proxy_adapter/commands/registration_status_command.py +76 -0
- mcp_proxy_adapter/commands/registry/__init__.py +18 -0
- mcp_proxy_adapter/commands/registry/command_info.py +103 -0
- mcp_proxy_adapter/commands/registry/command_loader.py +207 -0
- mcp_proxy_adapter/commands/registry/command_manager.py +119 -0
- mcp_proxy_adapter/commands/registry/command_registry.py +217 -0
- mcp_proxy_adapter/commands/reload_command.py +136 -0
- mcp_proxy_adapter/commands/result.py +157 -0
- mcp_proxy_adapter/commands/role_test_command.py +99 -0
- mcp_proxy_adapter/commands/roles_management_command.py +502 -0
- mcp_proxy_adapter/commands/security_command.py +472 -0
- mcp_proxy_adapter/commands/settings_command.py +113 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +306 -0
- mcp_proxy_adapter/commands/token_management_command.py +500 -0
- mcp_proxy_adapter/commands/transport_management_command.py +129 -0
- mcp_proxy_adapter/commands/unload_command.py +92 -0
- mcp_proxy_adapter/config.py +32 -0
- mcp_proxy_adapter/core/__init__.py +8 -0
- mcp_proxy_adapter/core/app_factory.py +560 -0
- mcp_proxy_adapter/core/app_runner.py +318 -0
- mcp_proxy_adapter/core/auth_validator.py +508 -0
- mcp_proxy_adapter/core/certificate/__init__.py +20 -0
- mcp_proxy_adapter/core/certificate/certificate_creator.py +372 -0
- mcp_proxy_adapter/core/certificate/certificate_extractor.py +185 -0
- mcp_proxy_adapter/core/certificate/certificate_utils.py +249 -0
- mcp_proxy_adapter/core/certificate/certificate_validator.py +481 -0
- mcp_proxy_adapter/core/certificate/ssl_context_manager.py +65 -0
- mcp_proxy_adapter/core/certificate_utils.py +249 -0
- mcp_proxy_adapter/core/client.py +608 -0
- mcp_proxy_adapter/core/client_manager.py +271 -0
- mcp_proxy_adapter/core/client_security.py +411 -0
- mcp_proxy_adapter/core/config/__init__.py +18 -0
- mcp_proxy_adapter/core/config/config.py +237 -0
- mcp_proxy_adapter/core/config/config_factory.py +22 -0
- mcp_proxy_adapter/core/config/config_loader.py +66 -0
- mcp_proxy_adapter/core/config/feature_manager.py +31 -0
- mcp_proxy_adapter/core/config/simple_config.py +204 -0
- mcp_proxy_adapter/core/config/simple_config_generator.py +131 -0
- mcp_proxy_adapter/core/config/simple_config_validator.py +476 -0
- mcp_proxy_adapter/core/config_converter.py +252 -0
- mcp_proxy_adapter/core/config_validator.py +211 -0
- mcp_proxy_adapter/core/crl_utils.py +362 -0
- mcp_proxy_adapter/core/errors.py +276 -0
- mcp_proxy_adapter/core/job_manager.py +54 -0
- mcp_proxy_adapter/core/logging.py +250 -0
- mcp_proxy_adapter/core/mtls_asgi.py +140 -0
- mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
- mcp_proxy_adapter/core/mtls_proxy.py +229 -0
- mcp_proxy_adapter/core/mtls_server.py +154 -0
- mcp_proxy_adapter/core/protocol_manager.py +232 -0
- mcp_proxy_adapter/core/proxy/__init__.py +19 -0
- mcp_proxy_adapter/core/proxy/auth_manager.py +26 -0
- mcp_proxy_adapter/core/proxy/proxy_registration_manager.py +160 -0
- mcp_proxy_adapter/core/proxy/registration_client.py +186 -0
- mcp_proxy_adapter/core/proxy/ssl_manager.py +101 -0
- mcp_proxy_adapter/core/proxy_client.py +184 -0
- mcp_proxy_adapter/core/proxy_registration.py +80 -0
- mcp_proxy_adapter/core/role_utils.py +103 -0
- mcp_proxy_adapter/core/security_adapter.py +343 -0
- mcp_proxy_adapter/core/security_factory.py +96 -0
- mcp_proxy_adapter/core/security_integration.py +342 -0
- mcp_proxy_adapter/core/server_adapter.py +251 -0
- mcp_proxy_adapter/core/server_engine.py +217 -0
- mcp_proxy_adapter/core/settings.py +260 -0
- mcp_proxy_adapter/core/signal_handler.py +107 -0
- mcp_proxy_adapter/core/ssl_utils.py +161 -0
- mcp_proxy_adapter/core/transport_manager.py +153 -0
- mcp_proxy_adapter/core/unified_config_adapter.py +471 -0
- mcp_proxy_adapter/core/utils.py +101 -0
- mcp_proxy_adapter/core/validation/__init__.py +21 -0
- mcp_proxy_adapter/core/validation/config_validator.py +219 -0
- mcp_proxy_adapter/core/validation/file_validator.py +131 -0
- mcp_proxy_adapter/core/validation/protocol_validator.py +205 -0
- mcp_proxy_adapter/core/validation/security_validator.py +140 -0
- mcp_proxy_adapter/core/validation/validation_result.py +27 -0
- mcp_proxy_adapter/custom_openapi.py +58 -0
- mcp_proxy_adapter/examples/__init__.py +16 -0
- 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 +52 -0
- mcp_proxy_adapter/examples/bugfix_certificate_config.py +261 -0
- mcp_proxy_adapter/examples/cert_manager_bugfix.py +203 -0
- mcp_proxy_adapter/examples/check_config.py +413 -0
- mcp_proxy_adapter/examples/client_usage_example.py +164 -0
- mcp_proxy_adapter/examples/commands/__init__.py +5 -0
- mcp_proxy_adapter/examples/config_builder.py +234 -0
- mcp_proxy_adapter/examples/config_cli.py +282 -0
- mcp_proxy_adapter/examples/create_test_configs.py +174 -0
- mcp_proxy_adapter/examples/debug_request_state.py +130 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +191 -0
- mcp_proxy_adapter/examples/demo_client.py +287 -0
- mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/full_application/commands/__init__.py +8 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +45 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +52 -0
- mcp_proxy_adapter/examples/full_application/commands/echo_command.py +32 -0
- mcp_proxy_adapter/examples/full_application/commands/help_command.py +54 -0
- mcp_proxy_adapter/examples/full_application/commands/list_command.py +57 -0
- mcp_proxy_adapter/examples/full_application/hooks/__init__.py +5 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +29 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +27 -0
- mcp_proxy_adapter/examples/full_application/main.py +311 -0
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +161 -0
- mcp_proxy_adapter/examples/full_application/run_mtls.py +252 -0
- mcp_proxy_adapter/examples/full_application/run_simple.py +152 -0
- mcp_proxy_adapter/examples/full_application/test_minimal_server.py +45 -0
- mcp_proxy_adapter/examples/full_application/test_server.py +163 -0
- mcp_proxy_adapter/examples/full_application/test_simple_server.py +62 -0
- mcp_proxy_adapter/examples/generate_config.py +502 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +335 -0
- mcp_proxy_adapter/examples/queue_demo_simple.py +632 -0
- mcp_proxy_adapter/examples/queue_integration_example.py +578 -0
- mcp_proxy_adapter/examples/queue_server_demo.py +82 -0
- mcp_proxy_adapter/examples/queue_server_example.py +85 -0
- mcp_proxy_adapter/examples/queue_server_simple.py +173 -0
- mcp_proxy_adapter/examples/required_certificates.py +208 -0
- mcp_proxy_adapter/examples/run_example.py +77 -0
- mcp_proxy_adapter/examples/run_full_test_suite.py +619 -0
- mcp_proxy_adapter/examples/run_proxy_server.py +153 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +435 -0
- mcp_proxy_adapter/examples/security_test/__init__.py +18 -0
- mcp_proxy_adapter/examples/security_test/auth_manager.py +14 -0
- mcp_proxy_adapter/examples/security_test/ssl_context_manager.py +28 -0
- mcp_proxy_adapter/examples/security_test/test_client.py +159 -0
- mcp_proxy_adapter/examples/security_test/test_result.py +22 -0
- mcp_proxy_adapter/examples/security_test_client.py +72 -0
- mcp_proxy_adapter/examples/setup/__init__.py +24 -0
- mcp_proxy_adapter/examples/setup/certificate_manager.py +215 -0
- mcp_proxy_adapter/examples/setup/config_generator.py +12 -0
- mcp_proxy_adapter/examples/setup/config_validator.py +118 -0
- mcp_proxy_adapter/examples/setup/environment_setup.py +62 -0
- mcp_proxy_adapter/examples/setup/test_files_generator.py +10 -0
- mcp_proxy_adapter/examples/setup/test_runner.py +89 -0
- mcp_proxy_adapter/examples/setup_test_environment.py +235 -0
- mcp_proxy_adapter/examples/simple_protocol_test.py +125 -0
- mcp_proxy_adapter/examples/test_chk_hostname_automated.py +211 -0
- mcp_proxy_adapter/examples/test_config.py +205 -0
- mcp_proxy_adapter/examples/test_config_builder.py +110 -0
- mcp_proxy_adapter/examples/test_examples.py +308 -0
- mcp_proxy_adapter/examples/test_framework_complete.py +267 -0
- mcp_proxy_adapter/examples/test_mcp_server.py +187 -0
- mcp_proxy_adapter/examples/test_protocol_examples.py +337 -0
- mcp_proxy_adapter/examples/universal_client.py +674 -0
- mcp_proxy_adapter/examples/update_config_certificates.py +135 -0
- mcp_proxy_adapter/examples/validate_generator_compatibility.py +385 -0
- mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +61 -0
- mcp_proxy_adapter/integrations/__init__.py +25 -0
- mcp_proxy_adapter/integrations/queuemgr_integration.py +462 -0
- mcp_proxy_adapter/main.py +311 -0
- mcp_proxy_adapter/openapi.py +375 -0
- mcp_proxy_adapter/schemas/base_schema.json +114 -0
- mcp_proxy_adapter/schemas/openapi_schema.json +314 -0
- mcp_proxy_adapter/schemas/roles.json +37 -0
- mcp_proxy_adapter/schemas/roles_schema.json +162 -0
- mcp_proxy_adapter/version.py +5 -0
- mcp_proxy_adapter-6.9.50.dist-info/METADATA +1088 -0
- mcp_proxy_adapter-6.9.50.dist-info/RECORD +242 -0
- {mcp_proxy_adapter-2.0.1.dist-info → mcp_proxy_adapter-6.9.50.dist-info}/WHEEL +1 -1
- mcp_proxy_adapter-6.9.50.dist-info/entry_points.txt +14 -0
- mcp_proxy_adapter-6.9.50.dist-info/top_level.txt +1 -0
- adapters/__init__.py +0 -16
- analyzers/__init__.py +0 -14
- analyzers/docstring_analyzer.py +0 -199
- analyzers/type_analyzer.py +0 -151
- cli/__init__.py +0 -12
- cli/__main__.py +0 -79
- cli/command_runner.py +0 -233
- dispatchers/__init__.py +0 -14
- dispatchers/base_dispatcher.py +0 -85
- dispatchers/json_rpc_dispatcher.py +0 -198
- generators/__init__.py +0 -14
- generators/endpoint_generator.py +0 -172
- generators/openapi_generator.py +0 -254
- generators/rest_api_generator.py +0 -207
- mcp_proxy_adapter-2.0.1.dist-info/METADATA +0 -272
- mcp_proxy_adapter-2.0.1.dist-info/RECORD +0 -28
- mcp_proxy_adapter-2.0.1.dist-info/licenses/LICENSE +0 -21
- mcp_proxy_adapter-2.0.1.dist-info/top_level.txt +0 -7
- openapi_schema/__init__.py +0 -38
- openapi_schema/command_registry.py +0 -312
- openapi_schema/rest_schema.py +0 -510
- openapi_schema/rpc_generator.py +0 -307
- openapi_schema/rpc_schema.py +0 -416
- validators/__init__.py +0 -14
- validators/base_validator.py +0 -23
- validators/docstring_validator.py +0 -75
- validators/metadata_validator.py +0 -76
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Main proxy registration manager for MCP Proxy Adapter.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import time
|
|
9
|
+
from typing import Dict, Any
|
|
10
|
+
|
|
11
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
|
12
|
+
from mcp_proxy_adapter.core.client_security import create_client_security_manager
|
|
13
|
+
from .registration_client import RegistrationClient
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ProxyRegistrationError(Exception):
|
|
17
|
+
"""Exception raised when proxy registration fails."""
|
|
18
|
+
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ProxyRegistrationManager:
|
|
23
|
+
"""
|
|
24
|
+
Manager for proxy registration functionality with security framework integration.
|
|
25
|
+
|
|
26
|
+
Handles automatic registration and unregistration of the server
|
|
27
|
+
with the MCP proxy server using secure authentication methods.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, config: Dict[str, Any]):
|
|
31
|
+
"""
|
|
32
|
+
Initialize the proxy registration manager.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
config: Application configuration
|
|
36
|
+
"""
|
|
37
|
+
self.config = config
|
|
38
|
+
self.logger = get_global_logger()
|
|
39
|
+
|
|
40
|
+
# Get registration configuration
|
|
41
|
+
self.registration_config = config.get("proxy_registration", {})
|
|
42
|
+
|
|
43
|
+
# Initialize client security
|
|
44
|
+
self.client_security = create_client_security_manager(config)
|
|
45
|
+
|
|
46
|
+
# Registration state
|
|
47
|
+
self.proxy_url = self.registration_config.get("proxy_url")
|
|
48
|
+
self.server_url = None
|
|
49
|
+
self.registered = False
|
|
50
|
+
self.registration_time = None
|
|
51
|
+
|
|
52
|
+
# Initialize registration client
|
|
53
|
+
self.registration_client = RegistrationClient(
|
|
54
|
+
self.client_security, self.registration_config, config, self.proxy_url
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def is_enabled(self) -> bool:
|
|
58
|
+
"""
|
|
59
|
+
Check if proxy registration is enabled.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
True if enabled, False otherwise
|
|
63
|
+
"""
|
|
64
|
+
return self.registration_config.get("enabled", False)
|
|
65
|
+
|
|
66
|
+
async def register(self) -> bool:
|
|
67
|
+
"""
|
|
68
|
+
Register server with proxy.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
True if registration successful, False otherwise
|
|
72
|
+
"""
|
|
73
|
+
if not self.is_enabled():
|
|
74
|
+
self.logger.info("Proxy registration is disabled")
|
|
75
|
+
return True
|
|
76
|
+
|
|
77
|
+
if not self.server_url:
|
|
78
|
+
self.logger.error("Server URL not set for registration")
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
if not self.proxy_url:
|
|
82
|
+
self.logger.error("Proxy URL not configured")
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
self.logger.info(f"Registering with proxy: {self.proxy_url}")
|
|
87
|
+
|
|
88
|
+
success = await self.registration_client.register(self.server_url)
|
|
89
|
+
|
|
90
|
+
if success:
|
|
91
|
+
self.registered = True
|
|
92
|
+
self.registration_time = time.time()
|
|
93
|
+
self.logger.info("✅ Proxy registration completed successfully")
|
|
94
|
+
else:
|
|
95
|
+
self.logger.error("❌ Proxy registration failed")
|
|
96
|
+
|
|
97
|
+
return success
|
|
98
|
+
|
|
99
|
+
except Exception as e:
|
|
100
|
+
self.logger.error(f"Registration error: {e}")
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
async def unregister(self) -> bool:
|
|
104
|
+
"""
|
|
105
|
+
Unregister server from proxy.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
True if unregistration successful, False otherwise
|
|
109
|
+
"""
|
|
110
|
+
if not self.is_enabled():
|
|
111
|
+
self.logger.info("Proxy registration is disabled")
|
|
112
|
+
return True
|
|
113
|
+
|
|
114
|
+
if not self.registered:
|
|
115
|
+
self.logger.info("Server not registered, skipping unregistration")
|
|
116
|
+
return True
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
self.logger.info("Unregistering from proxy")
|
|
120
|
+
|
|
121
|
+
success = await self.registration_client.unregister()
|
|
122
|
+
|
|
123
|
+
if success:
|
|
124
|
+
self.registered = False
|
|
125
|
+
self.registration_time = None
|
|
126
|
+
self.logger.info("✅ Proxy unregistration completed successfully")
|
|
127
|
+
else:
|
|
128
|
+
self.logger.warning("⚠️ Proxy unregistration failed")
|
|
129
|
+
|
|
130
|
+
return success
|
|
131
|
+
|
|
132
|
+
except Exception as e:
|
|
133
|
+
self.logger.error(f"Unregistration error: {e}")
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
def set_server_url(self, server_url: str) -> None:
|
|
137
|
+
"""
|
|
138
|
+
Set server URL for registration.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
server_url: Server URL to register
|
|
142
|
+
"""
|
|
143
|
+
self.server_url = server_url
|
|
144
|
+
self.logger.info(f"Server URL set: {server_url}")
|
|
145
|
+
|
|
146
|
+
def get_registration_status(self) -> Dict[str, Any]:
|
|
147
|
+
"""
|
|
148
|
+
Get current registration status.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
Dictionary with registration status information
|
|
152
|
+
"""
|
|
153
|
+
return {
|
|
154
|
+
"enabled": self.is_enabled(),
|
|
155
|
+
"registered": self.registered,
|
|
156
|
+
"proxy_url": self.proxy_url,
|
|
157
|
+
"server_url": self.server_url,
|
|
158
|
+
"registration_time": self.registration_time,
|
|
159
|
+
"client_security_available": self.client_security is not None,
|
|
160
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Registration client for proxy registration.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Dict, Any
|
|
9
|
+
import aiohttp
|
|
10
|
+
|
|
11
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
|
12
|
+
from .auth_manager import AuthManager
|
|
13
|
+
from .ssl_manager import SSLManager
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class RegistrationClient:
|
|
17
|
+
"""Client for proxy registration operations."""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
client_security,
|
|
22
|
+
registration_config: Dict[str, Any],
|
|
23
|
+
config: Dict[str, Any],
|
|
24
|
+
proxy_url: str,
|
|
25
|
+
):
|
|
26
|
+
"""
|
|
27
|
+
Initialize registration client.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
client_security: Client security manager instance
|
|
31
|
+
registration_config: Registration configuration
|
|
32
|
+
config: Application configuration
|
|
33
|
+
proxy_url: Proxy server URL
|
|
34
|
+
"""
|
|
35
|
+
self.client_security = client_security
|
|
36
|
+
self.registration_config = registration_config
|
|
37
|
+
self.config = config
|
|
38
|
+
self.proxy_url = proxy_url
|
|
39
|
+
self.logger = get_global_logger()
|
|
40
|
+
|
|
41
|
+
# Initialize managers
|
|
42
|
+
self.auth_manager = AuthManager(client_security, registration_config)
|
|
43
|
+
self.ssl_manager = SSLManager(
|
|
44
|
+
client_security, registration_config, config, proxy_url
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
def _prepare_registration_data(self, server_url: str) -> Dict[str, Any]:
|
|
48
|
+
"""
|
|
49
|
+
Prepare registration data.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
server_url: Server URL to register
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Registration data dictionary
|
|
56
|
+
"""
|
|
57
|
+
# Proxy expects "name" field, use server_id or server_name
|
|
58
|
+
server_name = (
|
|
59
|
+
self.registration_config.get("server_id")
|
|
60
|
+
or self.registration_config.get("server_name")
|
|
61
|
+
or "mcp_proxy_adapter"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
"name": server_name,
|
|
66
|
+
"url": server_url,
|
|
67
|
+
"capabilities": self.registration_config.get("capabilities", ["jsonrpc"]),
|
|
68
|
+
"metadata": {
|
|
69
|
+
"server_id": self.registration_config.get("server_id"),
|
|
70
|
+
"server_name": self.registration_config.get("server_name"),
|
|
71
|
+
"description": self.registration_config.get("description", ""),
|
|
72
|
+
"version": self.registration_config.get("version", "1.0.0"),
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async def register(self, server_url: str) -> bool:
|
|
77
|
+
"""
|
|
78
|
+
Register server with proxy.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
server_url: Server URL to register
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
True if registration successful, False otherwise
|
|
85
|
+
"""
|
|
86
|
+
try:
|
|
87
|
+
registration_data = self._prepare_registration_data(server_url)
|
|
88
|
+
|
|
89
|
+
# Get SSL context if needed
|
|
90
|
+
ssl_context = self.ssl_manager.get_ssl_context()
|
|
91
|
+
|
|
92
|
+
# Get headers with authentication if needed
|
|
93
|
+
headers = self.auth_manager.get_headers()
|
|
94
|
+
|
|
95
|
+
# Prepare request configuration
|
|
96
|
+
connector = None
|
|
97
|
+
if ssl_context:
|
|
98
|
+
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
|
99
|
+
|
|
100
|
+
# Send registration request
|
|
101
|
+
async with aiohttp.ClientSession(connector=connector) as session:
|
|
102
|
+
register_url = f"{self.proxy_url}/register"
|
|
103
|
+
self.logger.info(f"Attempting to register server with proxy at {register_url}")
|
|
104
|
+
self.logger.debug(f"Registration data: {registration_data}")
|
|
105
|
+
self.logger.debug(f"Headers: {headers}")
|
|
106
|
+
|
|
107
|
+
# Ensure Content-Type header is set
|
|
108
|
+
if "Content-Type" not in headers:
|
|
109
|
+
headers["Content-Type"] = "application/json"
|
|
110
|
+
|
|
111
|
+
async with session.post(
|
|
112
|
+
register_url,
|
|
113
|
+
json=registration_data,
|
|
114
|
+
headers=headers,
|
|
115
|
+
timeout=aiohttp.ClientTimeout(total=30)
|
|
116
|
+
) as response:
|
|
117
|
+
if response.status == 200:
|
|
118
|
+
result = await response.json()
|
|
119
|
+
self.logger.info(f"✅ Successfully registered with proxy. Server key: {result.get('key')}")
|
|
120
|
+
return True
|
|
121
|
+
else:
|
|
122
|
+
error_text = await response.text()
|
|
123
|
+
self.logger.error(
|
|
124
|
+
f"❌ Failed to register with proxy: {response.status} {response.reason}: {error_text}"
|
|
125
|
+
)
|
|
126
|
+
return False
|
|
127
|
+
|
|
128
|
+
except Exception as e:
|
|
129
|
+
self.logger.error(f"Registration error: {e}", exc_info=True)
|
|
130
|
+
return False
|
|
131
|
+
|
|
132
|
+
async def unregister(self) -> bool:
|
|
133
|
+
"""
|
|
134
|
+
Unregister server from proxy.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
True if unregistration successful, False otherwise
|
|
138
|
+
"""
|
|
139
|
+
try:
|
|
140
|
+
server_name = (
|
|
141
|
+
self.registration_config.get("server_id")
|
|
142
|
+
or self.registration_config.get("server_name")
|
|
143
|
+
or "mcp_proxy_adapter"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
unregister_data = {
|
|
147
|
+
"name": server_name,
|
|
148
|
+
"url": "", # Not needed for unregister
|
|
149
|
+
"capabilities": [],
|
|
150
|
+
"metadata": {},
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# Get SSL context if needed
|
|
154
|
+
ssl_context = self.ssl_manager.get_ssl_context()
|
|
155
|
+
|
|
156
|
+
# Get headers with authentication if needed
|
|
157
|
+
headers = self.auth_manager.get_headers()
|
|
158
|
+
|
|
159
|
+
# Prepare request configuration
|
|
160
|
+
connector = None
|
|
161
|
+
if ssl_context:
|
|
162
|
+
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
|
163
|
+
|
|
164
|
+
# Send unregistration request
|
|
165
|
+
async with aiohttp.ClientSession(connector=connector) as session:
|
|
166
|
+
unregister_url = f"{self.proxy_url}/unregister"
|
|
167
|
+
|
|
168
|
+
async with session.post(
|
|
169
|
+
unregister_url,
|
|
170
|
+
json=unregister_data,
|
|
171
|
+
headers=headers,
|
|
172
|
+
timeout=aiohttp.ClientTimeout(total=10)
|
|
173
|
+
) as response:
|
|
174
|
+
if response.status == 200:
|
|
175
|
+
self.logger.info("✅ Successfully unregistered from proxy")
|
|
176
|
+
return True
|
|
177
|
+
else:
|
|
178
|
+
error_text = await response.text()
|
|
179
|
+
self.logger.warning(
|
|
180
|
+
f"⚠️ Failed to unregister from proxy: {response.status} {response.reason}: {error_text}"
|
|
181
|
+
)
|
|
182
|
+
return False
|
|
183
|
+
|
|
184
|
+
except Exception as e:
|
|
185
|
+
self.logger.error(f"Unregistration error: {e}", exc_info=True)
|
|
186
|
+
return False
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
SSL management for proxy registration.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import ssl
|
|
9
|
+
from typing import Dict, Any, Optional
|
|
10
|
+
from urllib.parse import urlparse
|
|
11
|
+
|
|
12
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SSLManager:
|
|
16
|
+
"""Manager for SSL connections in proxy registration."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, client_security, registration_config: Dict[str, Any], config: Dict[str, Any], proxy_url: str):
|
|
19
|
+
"""
|
|
20
|
+
Initialize SSL manager.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
client_security: Client security manager instance
|
|
24
|
+
registration_config: Registration configuration
|
|
25
|
+
config: Application configuration
|
|
26
|
+
proxy_url: Proxy server URL
|
|
27
|
+
"""
|
|
28
|
+
self.client_security = client_security
|
|
29
|
+
self.registration_config = registration_config
|
|
30
|
+
self.config = config
|
|
31
|
+
self.proxy_url = proxy_url
|
|
32
|
+
self.logger = get_global_logger()
|
|
33
|
+
|
|
34
|
+
def create_ssl_context(self) -> Optional[ssl.SSLContext]:
|
|
35
|
+
"""
|
|
36
|
+
Create SSL context for secure connections using registration SSL configuration.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
SSL context or None if SSL not needed
|
|
40
|
+
"""
|
|
41
|
+
self.logger.debug("_create_ssl_context called")
|
|
42
|
+
|
|
43
|
+
# Decide SSL strictly by proxy URL scheme: use SSL only for https proxy URLs
|
|
44
|
+
try:
|
|
45
|
+
scheme = urlparse(self.proxy_url).scheme if self.proxy_url else "http"
|
|
46
|
+
if scheme.lower() != "https":
|
|
47
|
+
self.logger.debug("Proxy URL is HTTP, skipping SSL context creation for registration")
|
|
48
|
+
return None
|
|
49
|
+
except Exception:
|
|
50
|
+
self.logger.debug("Failed to parse proxy_url, assuming HTTP and skipping SSL context")
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
if not self.client_security:
|
|
54
|
+
self.logger.debug("SSL context creation failed: client_security is None")
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
# Check if SSL is enabled for registration
|
|
59
|
+
cert_config = self.registration_config.get("certificate", {})
|
|
60
|
+
ssl_config = self.registration_config.get("ssl", {})
|
|
61
|
+
|
|
62
|
+
# FALLBACK: if no explicit registration SSL/certs provided, reuse global SSL config
|
|
63
|
+
if not cert_config and not ssl_config:
|
|
64
|
+
global_ssl = self.config.get("security", {}).get("ssl", {}) or self.config.get("ssl", {})
|
|
65
|
+
if global_ssl:
|
|
66
|
+
# Map global ssl to registration-style configs
|
|
67
|
+
mapped_cert = {}
|
|
68
|
+
if global_ssl.get("cert_file") and global_ssl.get("key_file"):
|
|
69
|
+
mapped_cert = {
|
|
70
|
+
"cert_file": global_ssl.get("cert_file"),
|
|
71
|
+
"key_file": global_ssl.get("key_file"),
|
|
72
|
+
}
|
|
73
|
+
mapped_ssl = {}
|
|
74
|
+
if global_ssl.get("ca_cert"):
|
|
75
|
+
mapped_ssl["ca_cert"] = global_ssl.get("ca_cert")
|
|
76
|
+
if global_ssl.get("verify_client") is not None:
|
|
77
|
+
mapped_ssl["verify_mode"] = (
|
|
78
|
+
"CERT_REQUIRED" if global_ssl.get("verify_client") else "CERT_NONE"
|
|
79
|
+
)
|
|
80
|
+
cert_config = mapped_cert
|
|
81
|
+
ssl_config = mapped_ssl
|
|
82
|
+
|
|
83
|
+
# Use client security manager to create SSL context
|
|
84
|
+
if cert_config or ssl_config:
|
|
85
|
+
ssl_context = self.client_security.create_ssl_context(
|
|
86
|
+
cert_config=cert_config,
|
|
87
|
+
ssl_config=ssl_config
|
|
88
|
+
)
|
|
89
|
+
if ssl_context:
|
|
90
|
+
self.logger.debug("SSL context created successfully for registration")
|
|
91
|
+
return ssl_context
|
|
92
|
+
else:
|
|
93
|
+
self.logger.warning("Failed to create SSL context for registration")
|
|
94
|
+
return None
|
|
95
|
+
else:
|
|
96
|
+
self.logger.debug("No SSL configuration found for registration")
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
except Exception as e:
|
|
100
|
+
self.logger.error(f"Error creating SSL context for registration: {e}")
|
|
101
|
+
return None
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Core mTLS Proxy Client.
|
|
6
|
+
|
|
7
|
+
Provides an asynchronous client for communicating with a proxy-like server over
|
|
8
|
+
mutual TLS. Designed to be used by services built on this framework to:
|
|
9
|
+
- perform health/heartbeat checks
|
|
10
|
+
- register themselves with the proxy
|
|
11
|
+
|
|
12
|
+
This client intentionally avoids framework-specific configuration objects and
|
|
13
|
+
accepts explicit parameters for clarity and portability.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import ssl
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from typing import Any, Dict, Optional, Tuple
|
|
20
|
+
from urllib.parse import urljoin
|
|
21
|
+
|
|
22
|
+
import aiohttp # type: ignore[import]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class RegistrationRequest:
|
|
27
|
+
"""Data payload for registration calls to the proxy server."""
|
|
28
|
+
|
|
29
|
+
server_id: str
|
|
30
|
+
server_name: str
|
|
31
|
+
description: Optional[str] = None
|
|
32
|
+
extra: Optional[Dict[str, Any]] = None
|
|
33
|
+
|
|
34
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
35
|
+
payload: Dict[str, Any] = {
|
|
36
|
+
"server_id": self.server_id,
|
|
37
|
+
"server_name": self.server_name,
|
|
38
|
+
}
|
|
39
|
+
if self.description is not None:
|
|
40
|
+
payload["description"] = self.description
|
|
41
|
+
if self.extra:
|
|
42
|
+
payload.update(self.extra)
|
|
43
|
+
return payload
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ProxyClient:
|
|
47
|
+
"""Asynchronous mTLS HTTP client for communicating with a proxy server.
|
|
48
|
+
|
|
49
|
+
Usage:
|
|
50
|
+
async with ProxyClient(
|
|
51
|
+
base_url="https://your-proxy-host:3004",
|
|
52
|
+
ca_cert_path="/path/to/ca.crt",
|
|
53
|
+
client_cert_path="/path/to/client.crt",
|
|
54
|
+
client_key_path="/path/to/client.key",
|
|
55
|
+
) as client:
|
|
56
|
+
status, health = await client.health()
|
|
57
|
+
status, hb = await client.heartbeat()
|
|
58
|
+
status, reg = await client.register(
|
|
59
|
+
RegistrationRequest(...)
|
|
60
|
+
)
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(
|
|
64
|
+
self,
|
|
65
|
+
base_url: str,
|
|
66
|
+
*,
|
|
67
|
+
ca_cert_path: str,
|
|
68
|
+
client_cert_path: str,
|
|
69
|
+
client_key_path: str,
|
|
70
|
+
request_timeout_s: float = 5.0,
|
|
71
|
+
min_tls_version: ssl.TLSVersion = ssl.TLSVersion.TLSv1_2,
|
|
72
|
+
verify_mode: ssl.VerifyMode = ssl.CERT_REQUIRED,
|
|
73
|
+
) -> None:
|
|
74
|
+
if not base_url.startswith("http"):
|
|
75
|
+
raise ValueError("base_url must start with http/https")
|
|
76
|
+
self._base_url: str = base_url.rstrip("/")
|
|
77
|
+
self._ca_cert_path: str = ca_cert_path
|
|
78
|
+
self._client_cert_path: str = client_cert_path
|
|
79
|
+
self._client_key_path: str = client_key_path
|
|
80
|
+
self._request_timeout_s: float = request_timeout_s
|
|
81
|
+
self._min_tls_version: ssl.TLSVersion = min_tls_version
|
|
82
|
+
self._verify_mode: ssl.VerifyMode = verify_mode
|
|
83
|
+
|
|
84
|
+
self._ssl_context: Optional[ssl.SSLContext] = None
|
|
85
|
+
self._session: Optional[aiohttp.ClientSession] = None
|
|
86
|
+
|
|
87
|
+
async def __aenter__(self) -> "ProxyClient":
|
|
88
|
+
await self._ensure_session()
|
|
89
|
+
return self
|
|
90
|
+
|
|
91
|
+
async def __aexit__(self, exc_type, exc, tb) -> None:
|
|
92
|
+
await self.close()
|
|
93
|
+
|
|
94
|
+
async def _ensure_session(self) -> None:
|
|
95
|
+
if self._session is not None:
|
|
96
|
+
return
|
|
97
|
+
ssl_context = self._build_ssl_context()
|
|
98
|
+
timeout = aiohttp.ClientTimeout(total=self._request_timeout_s)
|
|
99
|
+
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
|
100
|
+
self._session = aiohttp.ClientSession(
|
|
101
|
+
timeout=timeout,
|
|
102
|
+
connector=connector,
|
|
103
|
+
)
|
|
104
|
+
self._ssl_context = ssl_context
|
|
105
|
+
|
|
106
|
+
async def close(self) -> None:
|
|
107
|
+
if self._session is not None:
|
|
108
|
+
await self._session.close()
|
|
109
|
+
self._session = None
|
|
110
|
+
self._ssl_context = None
|
|
111
|
+
|
|
112
|
+
def _build_ssl_context(self) -> ssl.SSLContext:
|
|
113
|
+
ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
|
|
114
|
+
ctx.minimum_version = self._min_tls_version
|
|
115
|
+
ctx.verify_mode = self._verify_mode
|
|
116
|
+
ctx.load_verify_locations(self._ca_cert_path)
|
|
117
|
+
ctx.load_cert_chain(self._client_cert_path, self._client_key_path)
|
|
118
|
+
return ctx
|
|
119
|
+
|
|
120
|
+
async def _get_json(self, path: str) -> Tuple[int, Dict[str, Any]]:
|
|
121
|
+
await self._ensure_session()
|
|
122
|
+
assert self._session is not None
|
|
123
|
+
url = urljoin(self._base_url + "/", path.lstrip("/"))
|
|
124
|
+
async with self._session.get(url) as resp:
|
|
125
|
+
status = resp.status
|
|
126
|
+
body_text = await resp.text()
|
|
127
|
+
try:
|
|
128
|
+
data: Dict[str, Any] = (
|
|
129
|
+
json.loads(body_text) if body_text else {}
|
|
130
|
+
)
|
|
131
|
+
except json.JSONDecodeError:
|
|
132
|
+
data = {"raw": body_text}
|
|
133
|
+
return status, data
|
|
134
|
+
|
|
135
|
+
async def _post_json(
|
|
136
|
+
self,
|
|
137
|
+
path: str,
|
|
138
|
+
payload: Dict[str, Any],
|
|
139
|
+
) -> Tuple[int, Dict[str, Any]]:
|
|
140
|
+
await self._ensure_session()
|
|
141
|
+
assert self._session is not None
|
|
142
|
+
url = urljoin(self._base_url + "/", path.lstrip("/"))
|
|
143
|
+
headers = {"Content-Type": "application/json"}
|
|
144
|
+
async with self._session.post(
|
|
145
|
+
url,
|
|
146
|
+
headers=headers,
|
|
147
|
+
json=payload,
|
|
148
|
+
) as resp:
|
|
149
|
+
status = resp.status
|
|
150
|
+
body_text = await resp.text()
|
|
151
|
+
try:
|
|
152
|
+
data: Dict[str, Any] = (
|
|
153
|
+
json.loads(body_text) if body_text else {}
|
|
154
|
+
)
|
|
155
|
+
except json.JSONDecodeError:
|
|
156
|
+
data = {"raw": body_text}
|
|
157
|
+
return status, data
|
|
158
|
+
|
|
159
|
+
async def health(
|
|
160
|
+
self,
|
|
161
|
+
path: str = "/health",
|
|
162
|
+
) -> Tuple[int, Dict[str, Any]]:
|
|
163
|
+
"""Perform a health check against the proxy."""
|
|
164
|
+
return await self._get_json(path)
|
|
165
|
+
|
|
166
|
+
async def heartbeat(
|
|
167
|
+
self,
|
|
168
|
+
path: str = "/heartbeat",
|
|
169
|
+
) -> Tuple[int, Dict[str, Any]]:
|
|
170
|
+
"""Perform a heartbeat (liveness) check against the proxy."""
|
|
171
|
+
return await self._get_json(path)
|
|
172
|
+
|
|
173
|
+
async def register(
|
|
174
|
+
self,
|
|
175
|
+
request: RegistrationRequest,
|
|
176
|
+
*,
|
|
177
|
+
path: str = "/register",
|
|
178
|
+
) -> Tuple[int, Dict[str, Any]]:
|
|
179
|
+
"""Register the current service on the proxy server."""
|
|
180
|
+
payload = request.to_dict()
|
|
181
|
+
return await self._post_json(
|
|
182
|
+
path,
|
|
183
|
+
payload,
|
|
184
|
+
)
|