mcp-proxy-adapter 6.9.43__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/__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 +355 -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 +266 -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 +35 -0
- mcp_proxy_adapter/cli/commands/config_validate.py +74 -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 +128 -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 +388 -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 +116 -0
- mcp_proxy_adapter/core/config/simple_config_generator.py +100 -0
- mcp_proxy_adapter/core/config/simple_config_validator.py +380 -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 +190 -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 +13 -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 +264 -0
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +81 -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 +313 -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.43.dist-info/METADATA +739 -0
- mcp_proxy_adapter-6.9.43.dist-info/RECORD +242 -0
- mcp_proxy_adapter-6.9.43.dist-info/WHEEL +5 -0
- mcp_proxy_adapter-6.9.43.dist-info/entry_points.txt +12 -0
- mcp_proxy_adapter-6.9.43.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"""Registration helper context builders for proxy interactions.
|
|
2
|
+
|
|
3
|
+
Author: Vasiliy Zdanovskiy
|
|
4
|
+
email: vasilyvz@gmail.com
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class ProxyCredentials:
|
|
16
|
+
"""Client certificate and verification settings for proxy communication."""
|
|
17
|
+
|
|
18
|
+
cert: Optional[Tuple[str, str]]
|
|
19
|
+
verify: Union[bool, str]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class RegistrationContext:
|
|
24
|
+
"""Prepared data required to register the adapter with a proxy."""
|
|
25
|
+
|
|
26
|
+
server_name: str
|
|
27
|
+
advertised_url: str
|
|
28
|
+
proxy_url: str
|
|
29
|
+
register_endpoint: str
|
|
30
|
+
capabilities: List[str]
|
|
31
|
+
metadata: Dict[str, Any]
|
|
32
|
+
use_proxy_client: bool
|
|
33
|
+
proxy_client_config: Dict[str, Any]
|
|
34
|
+
proxy_registration_config: Dict[str, Any]
|
|
35
|
+
credentials: ProxyCredentials
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class HeartbeatSettings:
|
|
40
|
+
"""Configuration for heartbeat scheduling."""
|
|
41
|
+
|
|
42
|
+
interval: int
|
|
43
|
+
endpoint: str
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def prepare_registration_context(
|
|
47
|
+
config: Dict[str, Any], logger: Any
|
|
48
|
+
) -> Optional[RegistrationContext]:
|
|
49
|
+
"""Build registration context from configuration.
|
|
50
|
+
|
|
51
|
+
Returns ``None`` when registration should not be performed.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
proxy_client_config = dict(config.get("proxy_client") or {})
|
|
55
|
+
proxy_registration_config = dict(config.get("proxy_registration") or {})
|
|
56
|
+
use_proxy_client = bool(proxy_client_config)
|
|
57
|
+
|
|
58
|
+
if use_proxy_client:
|
|
59
|
+
registration_config = proxy_client_config.get("registration") or {}
|
|
60
|
+
registration_enabled = registration_config.get("auto_on_startup", False)
|
|
61
|
+
else:
|
|
62
|
+
registration_config = proxy_registration_config
|
|
63
|
+
registration_enabled = proxy_registration_config.get("enabled", False)
|
|
64
|
+
|
|
65
|
+
if not registration_enabled:
|
|
66
|
+
logger.info(
|
|
67
|
+
"Proxy registration disabled (auto_on_startup=false or enabled=false)"
|
|
68
|
+
)
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
if use_proxy_client:
|
|
72
|
+
proxy_host = proxy_client_config.get("host", "localhost")
|
|
73
|
+
proxy_port = proxy_client_config.get("port", 3005)
|
|
74
|
+
# Determine proxy scheme based on proxy protocol
|
|
75
|
+
# NOTE: proxy_client.protocol indicates the SERVER's protocol, not the proxy's protocol
|
|
76
|
+
# Determine proxy protocol:
|
|
77
|
+
# 1. Check explicit proxy_protocol field (if exists) - this is the proxy's actual protocol
|
|
78
|
+
# 2. Check if port is 3005 (test proxy) - always use HTTP for test proxy
|
|
79
|
+
# 3. Check if client certificates are provided AND exist on disk (indicates HTTPS/mTLS proxy)
|
|
80
|
+
# 4. Check server protocol as hint (if server uses HTTPS/mTLS, proxy might too)
|
|
81
|
+
# 5. Fallback to HTTP (for test proxy)
|
|
82
|
+
proxy_protocol = proxy_client_config.get("proxy_protocol")
|
|
83
|
+
if not proxy_protocol:
|
|
84
|
+
# Test proxy (port 3005) always uses HTTP
|
|
85
|
+
if proxy_port == 3005:
|
|
86
|
+
proxy_protocol = "http"
|
|
87
|
+
else:
|
|
88
|
+
# Check if client certificates are provided for proxy connection
|
|
89
|
+
cert_file = proxy_client_config.get("cert_file")
|
|
90
|
+
key_file = proxy_client_config.get("key_file")
|
|
91
|
+
ca_cert_file = proxy_client_config.get("ca_cert_file")
|
|
92
|
+
cert_tuple = _build_cert_tuple(cert_file, key_file)
|
|
93
|
+
|
|
94
|
+
if cert_tuple:
|
|
95
|
+
# Certificates exist and are valid - proxy uses HTTPS/mTLS
|
|
96
|
+
# Certificates in proxy_client are for connecting TO the proxy
|
|
97
|
+
# Check if CA cert is provided to determine if it's mTLS
|
|
98
|
+
if ca_cert_file and Path(ca_cert_file).exists():
|
|
99
|
+
# All certificates (cert, key, CA) provided - likely mTLS
|
|
100
|
+
# Use server protocol as hint for mTLS vs HTTPS
|
|
101
|
+
server_protocol = proxy_client_config.get("protocol", "http")
|
|
102
|
+
proxy_protocol = (
|
|
103
|
+
"mtls" if server_protocol == "mtls" else "https"
|
|
104
|
+
)
|
|
105
|
+
else:
|
|
106
|
+
# Only cert and key, no CA - HTTPS
|
|
107
|
+
proxy_protocol = "https"
|
|
108
|
+
else:
|
|
109
|
+
# No valid certificates - check server protocol as hint
|
|
110
|
+
server_protocol = proxy_client_config.get("protocol", "http")
|
|
111
|
+
if server_protocol in ("https", "mtls"):
|
|
112
|
+
# Server uses HTTPS/mTLS - proxy might too, but no certs provided
|
|
113
|
+
# Default to HTTP for safety (test proxy scenario)
|
|
114
|
+
proxy_protocol = "http"
|
|
115
|
+
else:
|
|
116
|
+
# Server uses HTTP - proxy likely uses HTTP too
|
|
117
|
+
proxy_protocol = "http"
|
|
118
|
+
# If proxy uses HTTPS/mTLS, use https scheme
|
|
119
|
+
proxy_scheme = "https" if proxy_protocol in ("https", "mtls") else "http"
|
|
120
|
+
proxy_base_url = f"{proxy_scheme}://{proxy_host}:{proxy_port}"
|
|
121
|
+
register_endpoint = registration_config.get("register_endpoint", "/register")
|
|
122
|
+
if register_endpoint.startswith("/proxy/"):
|
|
123
|
+
proxy_url = f"{proxy_base_url}/proxy"
|
|
124
|
+
else:
|
|
125
|
+
proxy_url = proxy_base_url
|
|
126
|
+
else:
|
|
127
|
+
proxy_url_candidate = (
|
|
128
|
+
proxy_registration_config.get("proxy_url")
|
|
129
|
+
or proxy_registration_config.get("server_url")
|
|
130
|
+
)
|
|
131
|
+
if not proxy_url_candidate:
|
|
132
|
+
logger.warning("No proxy server URL configured")
|
|
133
|
+
return None
|
|
134
|
+
proxy_url = str(proxy_url_candidate)
|
|
135
|
+
register_endpoint = "/register"
|
|
136
|
+
|
|
137
|
+
server_config = dict(config.get("server") or {})
|
|
138
|
+
host = server_config.get("host", "127.0.0.1")
|
|
139
|
+
port = server_config.get("port", 8000)
|
|
140
|
+
protocol = server_config.get("protocol", "http")
|
|
141
|
+
advertised_host = server_config.get("advertised_host") or host
|
|
142
|
+
scheme = "https" if protocol in ("https", "mtls") else "http"
|
|
143
|
+
advertised_url = f"{scheme}://{advertised_host}:{port}"
|
|
144
|
+
|
|
145
|
+
if use_proxy_client:
|
|
146
|
+
server_name = proxy_client_config.get("server_id") or proxy_client_config.get(
|
|
147
|
+
"server_name"
|
|
148
|
+
)
|
|
149
|
+
server_name = server_name or f"mcp-adapter-{host}-{port}"
|
|
150
|
+
capabilities = proxy_client_config.get("capabilities", ["jsonrpc", "health"])
|
|
151
|
+
metadata = {
|
|
152
|
+
"uuid": config.get("uuid"),
|
|
153
|
+
"protocol": protocol,
|
|
154
|
+
"host": host,
|
|
155
|
+
"port": port,
|
|
156
|
+
**(proxy_client_config.get("metadata") or {}),
|
|
157
|
+
}
|
|
158
|
+
else:
|
|
159
|
+
server_name = proxy_registration_config.get(
|
|
160
|
+
"server_id"
|
|
161
|
+
) or proxy_registration_config.get("server_name")
|
|
162
|
+
server_name = server_name or f"mcp-adapter-{host}-{port}"
|
|
163
|
+
capabilities = proxy_registration_config.get(
|
|
164
|
+
"capabilities", ["jsonrpc", "health"]
|
|
165
|
+
)
|
|
166
|
+
metadata = {
|
|
167
|
+
"uuid": config.get("uuid"),
|
|
168
|
+
"protocol": protocol,
|
|
169
|
+
"host": host,
|
|
170
|
+
"port": port,
|
|
171
|
+
**(proxy_registration_config.get("metadata") or {}),
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
credentials = _resolve_registration_credentials(
|
|
175
|
+
use_proxy_client, proxy_client_config, proxy_registration_config
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
return RegistrationContext(
|
|
179
|
+
server_name=server_name,
|
|
180
|
+
advertised_url=advertised_url,
|
|
181
|
+
proxy_url=proxy_url,
|
|
182
|
+
register_endpoint=register_endpoint,
|
|
183
|
+
capabilities=capabilities,
|
|
184
|
+
metadata=metadata,
|
|
185
|
+
use_proxy_client=use_proxy_client,
|
|
186
|
+
proxy_client_config=proxy_client_config,
|
|
187
|
+
proxy_registration_config=proxy_registration_config,
|
|
188
|
+
credentials=credentials,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def resolve_runtime_credentials(
|
|
193
|
+
use_proxy_client: bool,
|
|
194
|
+
proxy_client_config: Dict[str, Any],
|
|
195
|
+
proxy_registration_config: Dict[str, Any],
|
|
196
|
+
) -> ProxyCredentials:
|
|
197
|
+
"""Return credentials for runtime interactions (heartbeat, unregister)."""
|
|
198
|
+
|
|
199
|
+
if use_proxy_client:
|
|
200
|
+
cert_tuple = _build_cert_tuple(
|
|
201
|
+
proxy_client_config.get("cert_file"),
|
|
202
|
+
proxy_client_config.get("key_file"),
|
|
203
|
+
)
|
|
204
|
+
proxy_protocol = proxy_client_config.get("protocol", "http")
|
|
205
|
+
ca_cert = proxy_client_config.get("ca_cert_file")
|
|
206
|
+
|
|
207
|
+
verify: Union[bool, str] = True
|
|
208
|
+
if proxy_protocol == "http":
|
|
209
|
+
verify = False
|
|
210
|
+
elif ca_cert:
|
|
211
|
+
verify = ca_cert
|
|
212
|
+
|
|
213
|
+
return ProxyCredentials(cert=cert_tuple, verify=verify)
|
|
214
|
+
|
|
215
|
+
return _resolve_registration_credentials(False, {}, proxy_registration_config)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def resolve_heartbeat_settings(
|
|
219
|
+
use_proxy_client: bool,
|
|
220
|
+
proxy_client_config: Dict[str, Any],
|
|
221
|
+
proxy_registration_config: Dict[str, Any],
|
|
222
|
+
) -> HeartbeatSettings:
|
|
223
|
+
"""Compute heartbeat interval and endpoint."""
|
|
224
|
+
|
|
225
|
+
if use_proxy_client:
|
|
226
|
+
heartbeat_config = proxy_client_config.get("heartbeat") or {}
|
|
227
|
+
interval = int(heartbeat_config.get("interval", 30))
|
|
228
|
+
endpoint = heartbeat_config.get("endpoint", "/heartbeat")
|
|
229
|
+
else:
|
|
230
|
+
heartbeat_config = proxy_registration_config.get("heartbeat") or {}
|
|
231
|
+
interval = int(
|
|
232
|
+
heartbeat_config.get(
|
|
233
|
+
"interval", proxy_registration_config.get("heartbeat_interval", 30)
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
endpoint = "/heartbeat"
|
|
237
|
+
|
|
238
|
+
return HeartbeatSettings(interval=interval, endpoint=endpoint)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def resolve_unregister_endpoint(
|
|
242
|
+
use_proxy_client: bool,
|
|
243
|
+
proxy_client_config: Dict[str, Any],
|
|
244
|
+
) -> str:
|
|
245
|
+
"""Get unregister endpoint path."""
|
|
246
|
+
|
|
247
|
+
if use_proxy_client:
|
|
248
|
+
registration_config = proxy_client_config.get("registration") or {}
|
|
249
|
+
endpoint = registration_config.get("unregister_endpoint")
|
|
250
|
+
if endpoint:
|
|
251
|
+
return str(endpoint)
|
|
252
|
+
return "/unregister"
|
|
253
|
+
return "/unregister"
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _resolve_registration_credentials(
|
|
257
|
+
use_proxy_client: bool,
|
|
258
|
+
proxy_client_config: Dict[str, Any],
|
|
259
|
+
proxy_registration_config: Dict[str, Any],
|
|
260
|
+
) -> ProxyCredentials:
|
|
261
|
+
if use_proxy_client:
|
|
262
|
+
# Use same logic as resolve_runtime_credentials for consistency
|
|
263
|
+
# Check if proxy itself uses HTTPS/mTLS and requires certificates
|
|
264
|
+
cert_tuple = _build_cert_tuple(
|
|
265
|
+
proxy_client_config.get("cert_file"),
|
|
266
|
+
proxy_client_config.get("key_file"),
|
|
267
|
+
)
|
|
268
|
+
# Determine proxy protocol (same logic as in prepare_registration_context)
|
|
269
|
+
proxy_protocol = proxy_client_config.get("proxy_protocol")
|
|
270
|
+
if not proxy_protocol:
|
|
271
|
+
# Get proxy port for test proxy detection
|
|
272
|
+
proxy_port = proxy_client_config.get("port", 3005)
|
|
273
|
+
# Test proxy (port 3005) always uses HTTP
|
|
274
|
+
if proxy_port == 3005:
|
|
275
|
+
proxy_protocol = "http"
|
|
276
|
+
else:
|
|
277
|
+
# Check if client certificates are provided for proxy connection
|
|
278
|
+
cert_file = proxy_client_config.get("cert_file")
|
|
279
|
+
key_file = proxy_client_config.get("key_file")
|
|
280
|
+
ca_cert_file = proxy_client_config.get("ca_cert_file")
|
|
281
|
+
cert_tuple = _build_cert_tuple(cert_file, key_file)
|
|
282
|
+
|
|
283
|
+
if cert_tuple:
|
|
284
|
+
# Certificates exist and are valid - proxy uses HTTPS/mTLS
|
|
285
|
+
# Check if CA cert is provided to determine if it's mTLS
|
|
286
|
+
if ca_cert_file and Path(ca_cert_file).exists():
|
|
287
|
+
# All certificates (cert, key, CA) provided - likely mTLS
|
|
288
|
+
# Use server protocol as hint for mTLS vs HTTPS
|
|
289
|
+
server_protocol = proxy_client_config.get("protocol", "http")
|
|
290
|
+
proxy_protocol = (
|
|
291
|
+
"mtls" if server_protocol == "mtls" else "https"
|
|
292
|
+
)
|
|
293
|
+
else:
|
|
294
|
+
# Only cert and key, no CA - HTTPS
|
|
295
|
+
proxy_protocol = "https"
|
|
296
|
+
else:
|
|
297
|
+
# No valid certificates - check server protocol as hint
|
|
298
|
+
server_protocol = proxy_client_config.get("protocol", "http")
|
|
299
|
+
if server_protocol in ("https", "mtls"):
|
|
300
|
+
# Server uses HTTPS/mTLS - proxy might too, but no certs provided
|
|
301
|
+
# Default to HTTP for safety (test proxy scenario)
|
|
302
|
+
proxy_protocol = "http"
|
|
303
|
+
else:
|
|
304
|
+
# Server uses HTTP - proxy likely uses HTTP too
|
|
305
|
+
proxy_protocol = "http"
|
|
306
|
+
ca_cert = proxy_client_config.get("ca_cert_file")
|
|
307
|
+
|
|
308
|
+
verify: Union[bool, str] = True
|
|
309
|
+
if proxy_protocol == "http":
|
|
310
|
+
# HTTP proxy doesn't need certificates
|
|
311
|
+
verify = False
|
|
312
|
+
elif proxy_protocol in ("https", "mtls"):
|
|
313
|
+
# HTTPS/mTLS proxy requires certificates
|
|
314
|
+
if ca_cert:
|
|
315
|
+
verify = ca_cert
|
|
316
|
+
elif proxy_protocol == "mtls":
|
|
317
|
+
# mTLS requires CA cert
|
|
318
|
+
verify = True # Will fail if no CA, but that's expected
|
|
319
|
+
else:
|
|
320
|
+
# HTTPS can use system CA store
|
|
321
|
+
verify = True
|
|
322
|
+
|
|
323
|
+
return ProxyCredentials(cert=cert_tuple, verify=verify)
|
|
324
|
+
|
|
325
|
+
cert_config = proxy_registration_config.get("certificate") or {}
|
|
326
|
+
ssl_config = proxy_registration_config.get("ssl") or {}
|
|
327
|
+
|
|
328
|
+
cert_tuple = _build_cert_tuple(
|
|
329
|
+
cert_config.get("cert_file"),
|
|
330
|
+
cert_config.get("key_file"),
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
verify: Union[bool, str] = True
|
|
334
|
+
ca_cert = ssl_config.get("ca_cert")
|
|
335
|
+
verify_mode = ssl_config.get("verify_mode", "CERT_REQUIRED")
|
|
336
|
+
if verify_mode == "CERT_NONE":
|
|
337
|
+
verify = False
|
|
338
|
+
elif ca_cert:
|
|
339
|
+
verify = ca_cert
|
|
340
|
+
|
|
341
|
+
return ProxyCredentials(cert=cert_tuple, verify=verify)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def _build_cert_tuple(
|
|
345
|
+
cert_file: Optional[str],
|
|
346
|
+
key_file: Optional[str],
|
|
347
|
+
) -> Optional[Tuple[str, str]]:
|
|
348
|
+
if not cert_file or not key_file:
|
|
349
|
+
return None
|
|
350
|
+
|
|
351
|
+
cert_path = Path(cert_file)
|
|
352
|
+
key_path = Path(key_file)
|
|
353
|
+
if not cert_path.exists() or not key_path.exists():
|
|
354
|
+
return None
|
|
355
|
+
|
|
356
|
+
return (str(cert_path.absolute()), str(key_path.absolute()))
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Registration management utilities for MCP Proxy Adapter API.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
from typing import Any, Dict, List, Optional
|
|
12
|
+
|
|
13
|
+
from mcp_proxy_adapter.client.jsonrpc_client import JsonRpcClient
|
|
14
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
|
15
|
+
|
|
16
|
+
from mcp_proxy_adapter.api.core.registration_context import (
|
|
17
|
+
HeartbeatSettings,
|
|
18
|
+
ProxyCredentials,
|
|
19
|
+
RegistrationContext,
|
|
20
|
+
prepare_registration_context,
|
|
21
|
+
resolve_heartbeat_settings,
|
|
22
|
+
resolve_runtime_credentials,
|
|
23
|
+
resolve_unregister_endpoint,
|
|
24
|
+
)
|
|
25
|
+
from mcp_proxy_adapter.api.core.registration_tasks import (
|
|
26
|
+
create_heartbeat_task,
|
|
27
|
+
unregister_from_proxy as unregister_task,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class RegistrationManager:
|
|
32
|
+
"""Manager for proxy registration functionality using JsonRpcClient."""
|
|
33
|
+
|
|
34
|
+
def __init__(self) -> None:
|
|
35
|
+
"""Initialize registration manager."""
|
|
36
|
+
self.logger = get_global_logger()
|
|
37
|
+
self.registered = False
|
|
38
|
+
self.registration_task: Optional[asyncio.Task] = None
|
|
39
|
+
self.server_name: Optional[str] = None
|
|
40
|
+
self.server_url: Optional[str] = None
|
|
41
|
+
self.proxy_url: Optional[str] = None
|
|
42
|
+
self.capabilities: List[str] = []
|
|
43
|
+
self.metadata: Dict[str, Any] = {}
|
|
44
|
+
self.config: Optional[Dict[str, Any]] = None
|
|
45
|
+
self._use_proxy_client = False
|
|
46
|
+
self._proxy_client_config: Dict[str, Any] = {}
|
|
47
|
+
self._proxy_registration_config: Dict[str, Any] = {}
|
|
48
|
+
self._registration_credentials: Optional[ProxyCredentials] = None
|
|
49
|
+
self._runtime_credentials: Optional[ProxyCredentials] = None
|
|
50
|
+
self._register_endpoint: str = "/register"
|
|
51
|
+
self._heartbeat_settings: Optional[HeartbeatSettings] = None
|
|
52
|
+
|
|
53
|
+
async def register_with_proxy(self, config: Dict[str, Any]) -> bool:
|
|
54
|
+
"""
|
|
55
|
+
Register this server with the proxy using JsonRpcClient.
|
|
56
|
+
|
|
57
|
+
Supports both ``proxy_client`` (SimpleConfig format) and ``proxy_registration`` (legacy format).
|
|
58
|
+
Registration is controlled by ``registration.auto_on_startup`` rather than ``proxy_client.enabled``.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
context = prepare_registration_context(config, self.logger)
|
|
62
|
+
if context is None:
|
|
63
|
+
return True
|
|
64
|
+
|
|
65
|
+
self._apply_context(context, config)
|
|
66
|
+
|
|
67
|
+
proxy_url = self.proxy_url
|
|
68
|
+
assert proxy_url is not None
|
|
69
|
+
assert self.server_name is not None
|
|
70
|
+
assert self.server_url is not None
|
|
71
|
+
|
|
72
|
+
client = JsonRpcClient(protocol="http", host="127.0.0.1", port=8080)
|
|
73
|
+
|
|
74
|
+
async def _register() -> Dict[str, Any]:
|
|
75
|
+
self._log_credentials("🔐 Registration SSL config", context.credentials)
|
|
76
|
+
self.logger.info(f"📡 Connecting to proxy: {proxy_url}")
|
|
77
|
+
self.logger.debug(
|
|
78
|
+
" Endpoint: %s, Server: %s -> %s",
|
|
79
|
+
self._register_endpoint,
|
|
80
|
+
self.server_name,
|
|
81
|
+
self.server_url,
|
|
82
|
+
)
|
|
83
|
+
return await client.register_with_proxy(
|
|
84
|
+
proxy_url=proxy_url,
|
|
85
|
+
server_name=context.server_name,
|
|
86
|
+
server_url=context.advertised_url,
|
|
87
|
+
capabilities=self.capabilities,
|
|
88
|
+
metadata=self.metadata,
|
|
89
|
+
cert=context.credentials.cert,
|
|
90
|
+
verify=context.credentials.verify,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
max_retries = 5
|
|
94
|
+
retry_delay = 2
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
for attempt in range(max_retries):
|
|
98
|
+
try:
|
|
99
|
+
registration_response = await _register()
|
|
100
|
+
if registration_response is not None:
|
|
101
|
+
self.logger.debug(
|
|
102
|
+
"Proxy registration response payload: %s",
|
|
103
|
+
registration_response,
|
|
104
|
+
)
|
|
105
|
+
self.logger.info(
|
|
106
|
+
"✅ Successfully registered with proxy as %s -> %s",
|
|
107
|
+
self.server_name,
|
|
108
|
+
self.server_url,
|
|
109
|
+
)
|
|
110
|
+
self.registered = True
|
|
111
|
+
return True
|
|
112
|
+
except Exception as exc: # noqa: BLE001
|
|
113
|
+
full_error = self._format_httpx_error(exc)
|
|
114
|
+
if attempt < max_retries - 1:
|
|
115
|
+
self.logger.warning(
|
|
116
|
+
"⚠️ Registration attempt %s/%s failed: %s. Retrying in %ss...",
|
|
117
|
+
attempt + 1,
|
|
118
|
+
max_retries,
|
|
119
|
+
full_error,
|
|
120
|
+
retry_delay,
|
|
121
|
+
)
|
|
122
|
+
await asyncio.sleep(retry_delay)
|
|
123
|
+
retry_delay *= 2
|
|
124
|
+
else:
|
|
125
|
+
self.logger.error(
|
|
126
|
+
"❌ Failed to register with proxy after %s attempts: %s",
|
|
127
|
+
max_retries,
|
|
128
|
+
full_error,
|
|
129
|
+
)
|
|
130
|
+
return False
|
|
131
|
+
except Exception as exc: # noqa: BLE001
|
|
132
|
+
self.logger.error(f"❌ Registration error: {exc}")
|
|
133
|
+
return False
|
|
134
|
+
finally:
|
|
135
|
+
await client.close()
|
|
136
|
+
|
|
137
|
+
async def start_heartbeat(self, _config: Dict[str, Any]) -> None:
|
|
138
|
+
"""Start heartbeat task using JsonRpcClient."""
|
|
139
|
+
|
|
140
|
+
if not self._can_start_tasks() or not self.registered:
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
credentials = resolve_runtime_credentials(
|
|
144
|
+
self._use_proxy_client,
|
|
145
|
+
self._proxy_client_config,
|
|
146
|
+
self._proxy_registration_config,
|
|
147
|
+
)
|
|
148
|
+
settings = resolve_heartbeat_settings(
|
|
149
|
+
self._use_proxy_client,
|
|
150
|
+
self._proxy_client_config,
|
|
151
|
+
self._proxy_registration_config,
|
|
152
|
+
)
|
|
153
|
+
self._runtime_credentials = credentials
|
|
154
|
+
self._heartbeat_settings = settings
|
|
155
|
+
|
|
156
|
+
heartbeat_url = f"{self.proxy_url}{settings.endpoint}"
|
|
157
|
+
self.logger.info(
|
|
158
|
+
"💓 Starting heartbeat task (interval: %ss)", settings.interval
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
assert self.server_name is not None
|
|
162
|
+
assert self.server_url is not None
|
|
163
|
+
|
|
164
|
+
self.registration_task = create_heartbeat_task(
|
|
165
|
+
proxy_url=heartbeat_url,
|
|
166
|
+
server_name=self.server_name,
|
|
167
|
+
server_url=self.server_url,
|
|
168
|
+
capabilities=self.capabilities,
|
|
169
|
+
metadata=self.metadata,
|
|
170
|
+
settings=settings,
|
|
171
|
+
credentials=credentials,
|
|
172
|
+
logger=self.logger,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
async def stop(self) -> None:
|
|
176
|
+
"""Stop registration manager and unregister from proxy."""
|
|
177
|
+
|
|
178
|
+
if self.registration_task:
|
|
179
|
+
self.registration_task.cancel()
|
|
180
|
+
try:
|
|
181
|
+
await self.registration_task
|
|
182
|
+
except asyncio.CancelledError:
|
|
183
|
+
pass
|
|
184
|
+
self.registration_task = None
|
|
185
|
+
|
|
186
|
+
if not (self.registered and self._can_start_tasks() and self.config):
|
|
187
|
+
self.registered = False
|
|
188
|
+
return
|
|
189
|
+
|
|
190
|
+
credentials = self._runtime_credentials or resolve_runtime_credentials(
|
|
191
|
+
self._use_proxy_client,
|
|
192
|
+
self._proxy_client_config,
|
|
193
|
+
self._proxy_registration_config,
|
|
194
|
+
)
|
|
195
|
+
endpoint = resolve_unregister_endpoint(
|
|
196
|
+
self._use_proxy_client,
|
|
197
|
+
self._proxy_client_config,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
assert self.proxy_url is not None
|
|
201
|
+
assert self.server_name is not None
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
await unregister_task(
|
|
205
|
+
proxy_url=self.proxy_url,
|
|
206
|
+
server_name=self.server_name,
|
|
207
|
+
endpoint=endpoint,
|
|
208
|
+
credentials=credentials,
|
|
209
|
+
logger=self.logger,
|
|
210
|
+
)
|
|
211
|
+
except Exception as exc: # noqa: BLE001
|
|
212
|
+
self.logger.error(f"Error unregistering from proxy: {exc}")
|
|
213
|
+
finally:
|
|
214
|
+
self.registered = False
|
|
215
|
+
|
|
216
|
+
def _apply_context(
|
|
217
|
+
self, context: RegistrationContext, config: Dict[str, Any]
|
|
218
|
+
) -> None:
|
|
219
|
+
self.server_name = context.server_name
|
|
220
|
+
self.server_url = context.advertised_url
|
|
221
|
+
self.proxy_url = context.proxy_url
|
|
222
|
+
self.capabilities = list(context.capabilities)
|
|
223
|
+
self.metadata = dict(context.metadata)
|
|
224
|
+
self.config = config
|
|
225
|
+
self._use_proxy_client = context.use_proxy_client
|
|
226
|
+
self._proxy_client_config = context.proxy_client_config
|
|
227
|
+
self._proxy_registration_config = context.proxy_registration_config
|
|
228
|
+
self._registration_credentials = context.credentials
|
|
229
|
+
self._runtime_credentials = None
|
|
230
|
+
self._register_endpoint = context.register_endpoint
|
|
231
|
+
|
|
232
|
+
def _log_credentials(self, prefix: str, credentials: ProxyCredentials) -> None:
|
|
233
|
+
self.logger.info(
|
|
234
|
+
"%s: cert=%s, verify=%s",
|
|
235
|
+
prefix,
|
|
236
|
+
credentials.cert is not None,
|
|
237
|
+
credentials.verify,
|
|
238
|
+
)
|
|
239
|
+
if credentials.cert:
|
|
240
|
+
self.logger.debug(" Client cert: %s, key: %s", *credentials.cert)
|
|
241
|
+
if isinstance(credentials.verify, str):
|
|
242
|
+
self.logger.debug(" CA cert: %s", credentials.verify)
|
|
243
|
+
|
|
244
|
+
def _format_httpx_error(self, exc: Exception) -> str:
|
|
245
|
+
import httpx
|
|
246
|
+
|
|
247
|
+
error_msg = str(exc) or type(exc).__name__
|
|
248
|
+
details: List[str] = [f"type={type(exc).__name__}"]
|
|
249
|
+
|
|
250
|
+
if isinstance(exc, httpx.HTTPStatusError):
|
|
251
|
+
details.append(f"status={exc.response.status_code}")
|
|
252
|
+
try:
|
|
253
|
+
details.append(f"response={exc.response.text[:200]}")
|
|
254
|
+
except Exception: # noqa: BLE001
|
|
255
|
+
pass
|
|
256
|
+
elif isinstance(exc, httpx.ConnectError):
|
|
257
|
+
details.append("connection_failed")
|
|
258
|
+
if hasattr(exc, "request"):
|
|
259
|
+
details.append(f"url={exc.request.url}")
|
|
260
|
+
elif isinstance(exc, httpx.TimeoutException):
|
|
261
|
+
details.append("timeout")
|
|
262
|
+
|
|
263
|
+
return f"{error_msg} ({', '.join(details)})" if details else error_msg
|
|
264
|
+
|
|
265
|
+
def _can_start_tasks(self) -> bool:
|
|
266
|
+
return bool(self.proxy_url and self.server_name and self.server_url)
|