mcp-proxy-adapter 6.0.0__py3-none-any.whl → 6.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_proxy_adapter/api/app.py +174 -80
- mcp_proxy_adapter/api/handlers.py +16 -5
- mcp_proxy_adapter/api/middleware/__init__.py +9 -4
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
- mcp_proxy_adapter/api/middleware/factory.py +36 -12
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +32 -13
- mcp_proxy_adapter/api/middleware/unified_security.py +160 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
- mcp_proxy_adapter/commands/__init__.py +7 -1
- mcp_proxy_adapter/commands/base.py +7 -4
- mcp_proxy_adapter/commands/builtin_commands.py +8 -2
- mcp_proxy_adapter/commands/command_registry.py +8 -0
- mcp_proxy_adapter/commands/echo_command.py +81 -0
- mcp_proxy_adapter/commands/help_command.py +21 -14
- mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
- mcp_proxy_adapter/commands/role_test_command.py +141 -0
- mcp_proxy_adapter/commands/security_command.py +488 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +2 -2
- mcp_proxy_adapter/commands/token_management_command.py +1 -1
- mcp_proxy_adapter/config.py +81 -21
- mcp_proxy_adapter/core/app_factory.py +326 -0
- mcp_proxy_adapter/core/client_security.py +384 -0
- mcp_proxy_adapter/core/logging.py +8 -3
- mcp_proxy_adapter/core/mtls_asgi.py +156 -0
- mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
- mcp_proxy_adapter/core/protocol_manager.py +139 -8
- mcp_proxy_adapter/core/proxy_client.py +602 -0
- mcp_proxy_adapter/core/proxy_registration.py +299 -47
- mcp_proxy_adapter/core/security_adapter.py +12 -15
- mcp_proxy_adapter/core/security_integration.py +285 -0
- mcp_proxy_adapter/core/server_adapter.py +345 -0
- mcp_proxy_adapter/core/server_engine.py +364 -0
- mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
- mcp_proxy_adapter/docs/EN/TROUBLESHOOTING.md +285 -0
- mcp_proxy_adapter/docs/RU/TROUBLESHOOTING.md +285 -0
- 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/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 +43 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_no_protocol_middleware.json +36 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +29 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_protocol_middleware.json +34 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_simple.json +35 -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_config_generator.py +110 -0
- mcp_proxy_adapter/examples/test_examples.py +344 -0
- mcp_proxy_adapter/examples/universal_client.py +628 -0
- mcp_proxy_adapter/main.py +21 -10
- mcp_proxy_adapter/utils/config_generator.py +727 -0
- mcp_proxy_adapter/version.py +5 -2
- mcp_proxy_adapter-6.1.1.dist-info/METADATA +205 -0
- mcp_proxy_adapter-6.1.1.dist-info/RECORD +197 -0
- mcp_proxy_adapter-6.1.1.dist-info/entry_points.txt +2 -0
- {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/licenses/LICENSE +2 -2
- mcp_proxy_adapter/api/middleware/auth.py +0 -146
- mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
- mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
- mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
- mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
- mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
- mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
- mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
- mcp_proxy_adapter/api/middleware/security.py +0 -376
- mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
- mcp_proxy_adapter/examples/__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 -70
- mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
- mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
- mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
- mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
- mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
- mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
- mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
- mcp_proxy_adapter/examples/basic_server/server.py +0 -114
- mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
- mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
- mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
- mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
- mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
- mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
- mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
- mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
- mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
- mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
- mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
- mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
- mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
- mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
- mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
- mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
- mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
- mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
- mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
- mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
- mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
- mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
- mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
- mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
- mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
- mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
- mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
- mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
- mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
- mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
- mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
- mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
- mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
- mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
- mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
- mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
- mcp_proxy_adapter/examples/deployment/README.md +0 -49
- mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
- mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
- mcp_proxy_adapter/examples/deployment/config.json +0 -29
- mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
- mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
- mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
- mcp_proxy_adapter/examples/deployment/run.sh +0 -43
- mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
- mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
- mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
- mcp_proxy_adapter/schemas/base_schema.json +0 -114
- mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
- mcp_proxy_adapter/schemas/roles_schema.json +0 -162
- mcp_proxy_adapter/tests/__init__.py +0 -0
- mcp_proxy_adapter/tests/api/__init__.py +0 -3
- mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
- mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
- mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
- mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
- mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
- mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
- mcp_proxy_adapter/tests/commands/__init__.py +0 -3
- mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
- mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
- mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
- mcp_proxy_adapter/tests/conftest.py +0 -131
- mcp_proxy_adapter/tests/functional/__init__.py +0 -3
- mcp_proxy_adapter/tests/functional/test_api.py +0 -253
- mcp_proxy_adapter/tests/integration/__init__.py +0 -3
- mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
- mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
- mcp_proxy_adapter/tests/performance/__init__.py +0 -3
- mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
- mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
- mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
- mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
- mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
- mcp_proxy_adapter/tests/test_base_command.py +0 -123
- mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
- mcp_proxy_adapter/tests/test_command_registry.py +0 -281
- mcp_proxy_adapter/tests/test_config.py +0 -127
- mcp_proxy_adapter/tests/test_utils.py +0 -65
- mcp_proxy_adapter/tests/unit/__init__.py +0 -3
- mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
- mcp_proxy_adapter/tests/unit/test_config.py +0 -270
- mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
- mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
- {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,18 @@
|
|
1
1
|
"""
|
2
|
-
Module for proxy registration functionality.
|
2
|
+
Module for proxy registration functionality with security framework integration.
|
3
3
|
|
4
4
|
This module handles automatic registration and unregistration of the server
|
5
|
-
with the MCP proxy server during startup and shutdown
|
5
|
+
with the MCP proxy server during startup and shutdown, using mcp_security_framework
|
6
|
+
for secure connections and authentication.
|
7
|
+
|
8
|
+
Author: Vasiliy Zdanovskiy
|
9
|
+
email: vasilyvz@gmail.com
|
6
10
|
"""
|
7
11
|
|
8
12
|
import asyncio
|
9
13
|
import json
|
10
14
|
import time
|
15
|
+
import ssl
|
11
16
|
from typing import Dict, Any, Optional, Tuple
|
12
17
|
from urllib.parse import urljoin
|
13
18
|
|
@@ -15,8 +20,8 @@ import aiohttp
|
|
15
20
|
import requests
|
16
21
|
from requests.exceptions import RequestException
|
17
22
|
|
18
|
-
from mcp_proxy_adapter.config import config
|
19
23
|
from mcp_proxy_adapter.core.logging import logger
|
24
|
+
from mcp_proxy_adapter.core.client_security import create_client_security_manager
|
20
25
|
|
21
26
|
|
22
27
|
class ProxyRegistrationError(Exception):
|
@@ -26,28 +31,49 @@ class ProxyRegistrationError(Exception):
|
|
26
31
|
|
27
32
|
class ProxyRegistrationManager:
|
28
33
|
"""
|
29
|
-
Manager for proxy registration functionality.
|
34
|
+
Manager for proxy registration functionality with security framework integration.
|
30
35
|
|
31
36
|
Handles automatic registration and unregistration of the server
|
32
|
-
with the MCP proxy server.
|
37
|
+
with the MCP proxy server using secure authentication methods.
|
33
38
|
"""
|
34
39
|
|
35
|
-
def __init__(self):
|
36
|
-
"""
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
self.
|
43
|
-
self.
|
44
|
-
self.retry_delay = self.registration_config.get("retry_delay", 5)
|
45
|
-
self.auto_register = self.registration_config.get("auto_register_on_startup", True)
|
46
|
-
self.auto_unregister = self.registration_config.get("auto_unregister_on_shutdown", True)
|
40
|
+
def __init__(self, config: Dict[str, Any]):
|
41
|
+
"""
|
42
|
+
Initialize the proxy registration manager.
|
43
|
+
|
44
|
+
Args:
|
45
|
+
config: Application configuration
|
46
|
+
"""
|
47
|
+
self.config = config
|
48
|
+
self.registration_config = config.get("registration", {})
|
47
49
|
|
50
|
+
# Basic registration settings
|
51
|
+
self.proxy_url = self.registration_config.get("server_url", "https://proxy-registry.example.com")
|
52
|
+
self.server_id = self.registration_config.get("proxy_info", {}).get("name", "mcp_proxy_adapter")
|
53
|
+
self.server_name = self.registration_config.get("proxy_info", {}).get("name", "MCP Proxy Adapter")
|
54
|
+
self.description = self.registration_config.get("proxy_info", {}).get("description", "JSON-RPC API for interacting with MCP Proxy")
|
55
|
+
|
56
|
+
# Heartbeat settings
|
57
|
+
heartbeat_config = self.registration_config.get("heartbeat", {})
|
58
|
+
self.timeout = heartbeat_config.get("timeout", 30)
|
59
|
+
self.retry_attempts = heartbeat_config.get("retry_attempts", 3)
|
60
|
+
self.retry_delay = heartbeat_config.get("retry_delay", 60)
|
61
|
+
self.heartbeat_interval = heartbeat_config.get("interval", 300)
|
62
|
+
|
63
|
+
# Auto registration settings
|
64
|
+
self.auto_register = self.registration_config.get("enabled", False)
|
65
|
+
self.auto_unregister = True # Always unregister on shutdown
|
66
|
+
|
67
|
+
# Initialize client security manager
|
68
|
+
self.client_security = create_client_security_manager(config)
|
69
|
+
|
70
|
+
# Registration state
|
48
71
|
self.registered = False
|
49
72
|
self.server_key: Optional[str] = None
|
50
73
|
self.server_url: Optional[str] = None
|
74
|
+
self.heartbeat_task: Optional[asyncio.Task] = None
|
75
|
+
|
76
|
+
logger.info("Proxy registration manager initialized with security framework integration")
|
51
77
|
|
52
78
|
def is_enabled(self) -> bool:
|
53
79
|
"""
|
@@ -68,9 +94,55 @@ class ProxyRegistrationManager:
|
|
68
94
|
self.server_url = server_url
|
69
95
|
logger.info(f"Proxy registration server URL set to: {server_url}")
|
70
96
|
|
97
|
+
def _get_auth_headers(self) -> Dict[str, str]:
|
98
|
+
"""
|
99
|
+
Get authentication headers for registration requests.
|
100
|
+
|
101
|
+
Returns:
|
102
|
+
Dictionary of authentication headers
|
103
|
+
"""
|
104
|
+
if not self.client_security:
|
105
|
+
return {"Content-Type": "application/json"}
|
106
|
+
|
107
|
+
auth_method = self.registration_config.get("auth_method", "certificate")
|
108
|
+
|
109
|
+
if auth_method == "certificate":
|
110
|
+
return self.client_security.get_client_auth_headers("certificate")
|
111
|
+
elif auth_method == "token":
|
112
|
+
token_config = self.registration_config.get("token", {})
|
113
|
+
token = token_config.get("token")
|
114
|
+
return self.client_security.get_client_auth_headers("jwt", token=token)
|
115
|
+
elif auth_method == "api_key":
|
116
|
+
api_key_config = self.registration_config.get("api_key", {})
|
117
|
+
api_key = api_key_config.get("key")
|
118
|
+
return self.client_security.get_client_auth_headers("api_key", api_key=api_key)
|
119
|
+
else:
|
120
|
+
return {"Content-Type": "application/json"}
|
121
|
+
|
122
|
+
def _create_ssl_context(self) -> Optional[ssl.SSLContext]:
|
123
|
+
"""
|
124
|
+
Create SSL context for secure connections.
|
125
|
+
|
126
|
+
Returns:
|
127
|
+
SSL context or None if SSL not needed
|
128
|
+
"""
|
129
|
+
if not self.client_security:
|
130
|
+
return None
|
131
|
+
|
132
|
+
try:
|
133
|
+
# Check if SSL is enabled for registration
|
134
|
+
cert_config = self.registration_config.get("certificate", {})
|
135
|
+
if cert_config.get("enabled", False):
|
136
|
+
return self.client_security.create_client_ssl_context()
|
137
|
+
|
138
|
+
return None
|
139
|
+
except Exception as e:
|
140
|
+
logger.error(f"Failed to create SSL context: {e}")
|
141
|
+
return None
|
142
|
+
|
71
143
|
async def register_server(self) -> bool:
|
72
144
|
"""
|
73
|
-
Register the server with the proxy.
|
145
|
+
Register the server with the proxy using secure authentication.
|
74
146
|
|
75
147
|
Returns:
|
76
148
|
True if registration was successful, False otherwise.
|
@@ -83,11 +155,20 @@ class ProxyRegistrationManager:
|
|
83
155
|
logger.error("Server URL not set, cannot register with proxy")
|
84
156
|
return False
|
85
157
|
|
158
|
+
# Prepare registration data with proxy info
|
159
|
+
proxy_info = self.registration_config.get("proxy_info", {})
|
86
160
|
registration_data = {
|
87
161
|
"server_id": self.server_id,
|
88
162
|
"server_url": self.server_url,
|
89
163
|
"server_name": self.server_name,
|
90
|
-
"description": self.description
|
164
|
+
"description": self.description,
|
165
|
+
"version": proxy_info.get("version", "1.0.0"),
|
166
|
+
"capabilities": proxy_info.get("capabilities", ["jsonrpc", "rest"]),
|
167
|
+
"endpoints": proxy_info.get("endpoints", {
|
168
|
+
"jsonrpc": "/api/jsonrpc",
|
169
|
+
"rest": "/cmd",
|
170
|
+
"health": "/health"
|
171
|
+
})
|
91
172
|
}
|
92
173
|
|
93
174
|
logger.info(f"Attempting to register server with proxy at {self.proxy_url}")
|
@@ -95,12 +176,17 @@ class ProxyRegistrationManager:
|
|
95
176
|
|
96
177
|
for attempt in range(self.retry_attempts):
|
97
178
|
try:
|
98
|
-
success, result = await self.
|
179
|
+
success, result = await self._make_secure_registration_request(registration_data)
|
99
180
|
|
100
181
|
if success:
|
101
182
|
self.registered = True
|
102
183
|
self.server_key = result.get("server_key")
|
103
184
|
logger.info(f"✅ Successfully registered with proxy. Server key: {self.server_key}")
|
185
|
+
|
186
|
+
# Start heartbeat if enabled
|
187
|
+
if self.registration_config.get("heartbeat", {}).get("enabled", True):
|
188
|
+
await self._start_heartbeat()
|
189
|
+
|
104
190
|
return True
|
105
191
|
else:
|
106
192
|
error_msg = result.get("error", {}).get("message", "Unknown error")
|
@@ -135,6 +221,9 @@ class ProxyRegistrationManager:
|
|
135
221
|
logger.info("Server not registered with proxy, skipping unregistration")
|
136
222
|
return True
|
137
223
|
|
224
|
+
# Stop heartbeat
|
225
|
+
await self._stop_heartbeat()
|
226
|
+
|
138
227
|
# Extract copy_number from server_key (format: server_id_copy_number)
|
139
228
|
try:
|
140
229
|
copy_number = int(self.server_key.split("_")[-1])
|
@@ -150,7 +239,7 @@ class ProxyRegistrationManager:
|
|
150
239
|
logger.debug(f"Unregistration data: {unregistration_data}")
|
151
240
|
|
152
241
|
try:
|
153
|
-
success, result = await self.
|
242
|
+
success, result = await self._make_secure_unregistration_request(unregistration_data)
|
154
243
|
|
155
244
|
if success:
|
156
245
|
unregistered = result.get("unregistered", False)
|
@@ -171,9 +260,9 @@ class ProxyRegistrationManager:
|
|
171
260
|
logger.error(f"❌ Unregistration failed with exception: {e}")
|
172
261
|
return False
|
173
262
|
|
174
|
-
async def
|
263
|
+
async def _make_secure_registration_request(self, data: Dict[str, Any]) -> Tuple[bool, Dict[str, Any]]:
|
175
264
|
"""
|
176
|
-
Make registration request to proxy.
|
265
|
+
Make secure registration request to proxy using security framework.
|
177
266
|
|
178
267
|
Args:
|
179
268
|
data: Registration data.
|
@@ -183,19 +272,40 @@ class ProxyRegistrationManager:
|
|
183
272
|
"""
|
184
273
|
url = urljoin(self.proxy_url, "/register")
|
185
274
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
275
|
+
# Get authentication headers
|
276
|
+
headers = self._get_auth_headers()
|
277
|
+
headers["Content-Type"] = "application/json"
|
278
|
+
|
279
|
+
# Create SSL context if needed
|
280
|
+
ssl_context = self._create_ssl_context()
|
281
|
+
|
282
|
+
# Create connector with SSL context
|
283
|
+
connector = None
|
284
|
+
if ssl_context:
|
285
|
+
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
286
|
+
|
287
|
+
try:
|
288
|
+
async with aiohttp.ClientSession(connector=connector) as session:
|
289
|
+
async with session.post(
|
290
|
+
url,
|
291
|
+
json=data,
|
292
|
+
headers=headers,
|
293
|
+
timeout=aiohttp.ClientTimeout(total=self.timeout)
|
294
|
+
) as response:
|
295
|
+
result = await response.json()
|
296
|
+
|
297
|
+
# Validate response headers if security framework available
|
298
|
+
if self.client_security:
|
299
|
+
self.client_security.validate_server_response(dict(response.headers))
|
300
|
+
|
301
|
+
return response.status == 200, result
|
302
|
+
finally:
|
303
|
+
if connector:
|
304
|
+
await connector.close()
|
195
305
|
|
196
|
-
async def
|
306
|
+
async def _make_secure_unregistration_request(self, data: Dict[str, Any]) -> Tuple[bool, Dict[str, Any]]:
|
197
307
|
"""
|
198
|
-
Make unregistration request to proxy.
|
308
|
+
Make secure unregistration request to proxy using security framework.
|
199
309
|
|
200
310
|
Args:
|
201
311
|
data: Unregistration data.
|
@@ -205,15 +315,120 @@ class ProxyRegistrationManager:
|
|
205
315
|
"""
|
206
316
|
url = urljoin(self.proxy_url, "/unregister")
|
207
317
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
318
|
+
# Get authentication headers
|
319
|
+
headers = self._get_auth_headers()
|
320
|
+
headers["Content-Type"] = "application/json"
|
321
|
+
|
322
|
+
# Create SSL context if needed
|
323
|
+
ssl_context = self._create_ssl_context()
|
324
|
+
|
325
|
+
# Create connector with SSL context
|
326
|
+
connector = None
|
327
|
+
if ssl_context:
|
328
|
+
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
329
|
+
|
330
|
+
try:
|
331
|
+
async with aiohttp.ClientSession(connector=connector) as session:
|
332
|
+
async with session.post(
|
333
|
+
url,
|
334
|
+
json=data,
|
335
|
+
headers=headers,
|
336
|
+
timeout=aiohttp.ClientTimeout(total=self.timeout)
|
337
|
+
) as response:
|
338
|
+
result = await response.json()
|
339
|
+
|
340
|
+
# Validate response headers if security framework available
|
341
|
+
if self.client_security:
|
342
|
+
self.client_security.validate_server_response(dict(response.headers))
|
343
|
+
|
344
|
+
return response.status == 200, result
|
345
|
+
finally:
|
346
|
+
if connector:
|
347
|
+
await connector.close()
|
348
|
+
|
349
|
+
async def _start_heartbeat(self) -> None:
|
350
|
+
"""Start heartbeat task for keeping registration alive."""
|
351
|
+
if self.heartbeat_task and not self.heartbeat_task.done():
|
352
|
+
return
|
353
|
+
|
354
|
+
self.heartbeat_task = asyncio.create_task(self._heartbeat_loop())
|
355
|
+
logger.info("Heartbeat task started")
|
356
|
+
|
357
|
+
async def _stop_heartbeat(self) -> None:
|
358
|
+
"""Stop heartbeat task."""
|
359
|
+
if self.heartbeat_task and not self.heartbeat_task.done():
|
360
|
+
self.heartbeat_task.cancel()
|
361
|
+
try:
|
362
|
+
await self.heartbeat_task
|
363
|
+
except asyncio.CancelledError:
|
364
|
+
pass
|
365
|
+
logger.info("Heartbeat task stopped")
|
366
|
+
|
367
|
+
async def _heartbeat_loop(self) -> None:
|
368
|
+
"""Heartbeat loop to keep registration alive."""
|
369
|
+
while self.registered:
|
370
|
+
try:
|
371
|
+
await asyncio.sleep(self.heartbeat_interval)
|
372
|
+
|
373
|
+
if not self.registered:
|
374
|
+
break
|
375
|
+
|
376
|
+
# Send heartbeat
|
377
|
+
success = await self._send_heartbeat()
|
378
|
+
if not success:
|
379
|
+
logger.warning("Heartbeat failed, attempting to re-register")
|
380
|
+
await self.register_server()
|
381
|
+
|
382
|
+
except asyncio.CancelledError:
|
383
|
+
break
|
384
|
+
except Exception as e:
|
385
|
+
logger.error(f"Heartbeat error: {e}")
|
386
|
+
|
387
|
+
async def _send_heartbeat(self) -> bool:
|
388
|
+
"""Send heartbeat to proxy server."""
|
389
|
+
if not self.server_key:
|
390
|
+
return False
|
391
|
+
|
392
|
+
heartbeat_data = {
|
393
|
+
"server_id": self.server_id,
|
394
|
+
"server_key": self.server_key,
|
395
|
+
"timestamp": int(time.time())
|
396
|
+
}
|
397
|
+
|
398
|
+
url = urljoin(self.proxy_url, "/heartbeat")
|
399
|
+
|
400
|
+
# Get authentication headers
|
401
|
+
headers = self._get_auth_headers()
|
402
|
+
headers["Content-Type"] = "application/json"
|
403
|
+
|
404
|
+
# Create SSL context if needed
|
405
|
+
ssl_context = self._create_ssl_context()
|
406
|
+
|
407
|
+
# Create connector with SSL context
|
408
|
+
connector = None
|
409
|
+
if ssl_context:
|
410
|
+
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
411
|
+
|
412
|
+
try:
|
413
|
+
async with aiohttp.ClientSession(connector=connector) as session:
|
414
|
+
async with session.post(
|
415
|
+
url,
|
416
|
+
json=heartbeat_data,
|
417
|
+
headers=headers,
|
418
|
+
timeout=aiohttp.ClientTimeout(total=self.timeout)
|
419
|
+
) as response:
|
420
|
+
if response.status == 200:
|
421
|
+
logger.debug("Heartbeat sent successfully")
|
422
|
+
return True
|
423
|
+
else:
|
424
|
+
logger.warning(f"Heartbeat failed with status: {response.status}")
|
425
|
+
return False
|
426
|
+
except Exception as e:
|
427
|
+
logger.error(f"Heartbeat error: {e}")
|
428
|
+
return False
|
429
|
+
finally:
|
430
|
+
if connector:
|
431
|
+
await connector.close()
|
217
432
|
|
218
433
|
def get_registration_status(self) -> Dict[str, Any]:
|
219
434
|
"""
|
@@ -222,18 +437,44 @@ class ProxyRegistrationManager:
|
|
222
437
|
Returns:
|
223
438
|
Dictionary with registration status information.
|
224
439
|
"""
|
225
|
-
|
440
|
+
status = {
|
226
441
|
"enabled": self.is_enabled(),
|
227
442
|
"registered": self.registered,
|
228
443
|
"server_key": self.server_key,
|
229
444
|
"server_url": self.server_url,
|
230
445
|
"proxy_url": self.proxy_url,
|
231
|
-
"server_id": self.server_id
|
446
|
+
"server_id": self.server_id,
|
447
|
+
"heartbeat_active": self.heartbeat_task is not None and not self.heartbeat_task.done()
|
232
448
|
}
|
449
|
+
|
450
|
+
# Add security information if available
|
451
|
+
if self.client_security:
|
452
|
+
status["security_enabled"] = True
|
453
|
+
status["ssl_enabled"] = self.client_security.is_ssl_enabled()
|
454
|
+
status["auth_methods"] = self.client_security.get_supported_auth_methods()
|
455
|
+
|
456
|
+
cert_info = self.client_security.get_client_certificate_info()
|
457
|
+
if cert_info:
|
458
|
+
status["client_certificate"] = cert_info
|
459
|
+
else:
|
460
|
+
status["security_enabled"] = False
|
461
|
+
|
462
|
+
return status
|
463
|
+
|
233
464
|
|
465
|
+
# Global proxy registration manager instance (will be initialized with config)
|
466
|
+
proxy_registration_manager: Optional[ProxyRegistrationManager] = None
|
234
467
|
|
235
|
-
|
236
|
-
|
468
|
+
|
469
|
+
def initialize_proxy_registration(config: Dict[str, Any]) -> None:
|
470
|
+
"""
|
471
|
+
Initialize global proxy registration manager.
|
472
|
+
|
473
|
+
Args:
|
474
|
+
config: Application configuration
|
475
|
+
"""
|
476
|
+
global proxy_registration_manager
|
477
|
+
proxy_registration_manager = ProxyRegistrationManager(config)
|
237
478
|
|
238
479
|
|
239
480
|
async def register_with_proxy(server_url: str) -> bool:
|
@@ -246,6 +487,10 @@ async def register_with_proxy(server_url: str) -> bool:
|
|
246
487
|
Returns:
|
247
488
|
True if registration was successful, False otherwise.
|
248
489
|
"""
|
490
|
+
if not proxy_registration_manager:
|
491
|
+
logger.error("Proxy registration manager not initialized")
|
492
|
+
return False
|
493
|
+
|
249
494
|
proxy_registration_manager.set_server_url(server_url)
|
250
495
|
return await proxy_registration_manager.register_server()
|
251
496
|
|
@@ -257,6 +502,10 @@ async def unregister_from_proxy() -> bool:
|
|
257
502
|
Returns:
|
258
503
|
True if unregistration was successful, False otherwise.
|
259
504
|
"""
|
505
|
+
if not proxy_registration_manager:
|
506
|
+
logger.error("Proxy registration manager not initialized")
|
507
|
+
return False
|
508
|
+
|
260
509
|
return await proxy_registration_manager.unregister_server()
|
261
510
|
|
262
511
|
|
@@ -267,4 +516,7 @@ def get_proxy_registration_status() -> Dict[str, Any]:
|
|
267
516
|
Returns:
|
268
517
|
Dictionary with registration status information.
|
269
518
|
"""
|
519
|
+
if not proxy_registration_manager:
|
520
|
+
return {"error": "Proxy registration manager not initialized"}
|
521
|
+
|
270
522
|
return proxy_registration_manager.get_registration_status()
|
@@ -16,8 +16,7 @@ try:
|
|
16
16
|
from mcp_security_framework.schemas.config import (
|
17
17
|
AuthConfig, SSLConfig, PermissionConfig, RateLimitConfig
|
18
18
|
)
|
19
|
-
|
20
|
-
from mcp_security_framework.schemas.response import SecurityResult
|
19
|
+
# Note: SecurityRequest and SecurityResult are not available in current version
|
21
20
|
SECURITY_FRAMEWORK_AVAILABLE = True
|
22
21
|
except ImportError:
|
23
22
|
# Fallback for when mcp_security_framework is not available
|
@@ -28,8 +27,6 @@ except ImportError:
|
|
28
27
|
SSLConfig = None
|
29
28
|
PermissionConfig = None
|
30
29
|
RateLimitConfig = None
|
31
|
-
SecurityRequest = None
|
32
|
-
SecurityResult = None
|
33
30
|
|
34
31
|
from mcp_proxy_adapter.core.logging import logger
|
35
32
|
|
@@ -245,24 +242,24 @@ class SecurityAdapter:
|
|
245
242
|
"user_id": None
|
246
243
|
}
|
247
244
|
|
248
|
-
def _create_security_request(self, request_data: Dict[str, Any]) ->
|
245
|
+
def _create_security_request(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
|
249
246
|
"""
|
250
|
-
Create
|
247
|
+
Create request data for security validation.
|
251
248
|
|
252
249
|
Args:
|
253
250
|
request_data: Request data dictionary
|
254
251
|
|
255
252
|
Returns:
|
256
|
-
|
253
|
+
Request data dictionary for security validation
|
257
254
|
"""
|
258
|
-
return
|
259
|
-
method
|
260
|
-
path
|
261
|
-
headers
|
262
|
-
query_params
|
263
|
-
client_ip
|
264
|
-
body
|
265
|
-
|
255
|
+
return {
|
256
|
+
"method": request_data.get("method", "GET"),
|
257
|
+
"path": request_data.get("path", "/"),
|
258
|
+
"headers": request_data.get("headers", {}),
|
259
|
+
"query_params": request_data.get("query_params", {}),
|
260
|
+
"client_ip": request_data.get("client_ip", "unknown"),
|
261
|
+
"body": request_data.get("body", {})
|
262
|
+
}
|
266
263
|
|
267
264
|
def _fallback_validation(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
|
268
265
|
"""
|