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,217 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Server Engine Abstraction
|
|
3
|
+
|
|
4
|
+
This module provides an abstraction layer for the hypercorn ASGI server engine,
|
|
5
|
+
providing full mTLS support and SSL capabilities.
|
|
6
|
+
|
|
7
|
+
Author: Vasiliy Zdanovskiy
|
|
8
|
+
email: vasilyvz@gmail.com
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
from abc import ABC, abstractmethod
|
|
13
|
+
from typing import Dict, Optional, Any
|
|
14
|
+
|
|
15
|
+
from .logging import get_global_logger
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ServerEngine(ABC):
|
|
21
|
+
"""
|
|
22
|
+
Abstract base class for server engines.
|
|
23
|
+
|
|
24
|
+
This class defines the interface that all server engines must implement,
|
|
25
|
+
allowing the framework to work with different ASGI servers transparently.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def get_name(self) -> str:
|
|
30
|
+
"""Get the name of the server engine."""
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def get_supported_features(self) -> Dict[str, bool]:
|
|
35
|
+
"""
|
|
36
|
+
Get supported features of this server engine.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Dictionary mapping feature names to boolean support status
|
|
40
|
+
"""
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
def validate_config(self, config: Dict[str, Any]) -> bool:
|
|
44
|
+
"""
|
|
45
|
+
Validate configuration for this engine.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
config: Configuration dictionary to validate
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
True if configuration is valid, False otherwise
|
|
52
|
+
"""
|
|
53
|
+
# Default implementation: always valid
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
def get_config_schema(self) -> Dict[str, Any]:
|
|
57
|
+
"""
|
|
58
|
+
Get configuration schema for this engine.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Dictionary describing the configuration schema
|
|
62
|
+
"""
|
|
63
|
+
# Default implementation: empty schema
|
|
64
|
+
return {}
|
|
65
|
+
|
|
66
|
+
def run_server(self, app: Any, config: Dict[str, Any]) -> None:
|
|
67
|
+
"""
|
|
68
|
+
Run the server with the given application and configuration.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
app: ASGI application
|
|
72
|
+
config: Server configuration
|
|
73
|
+
|
|
74
|
+
Raises:
|
|
75
|
+
NotImplementedError: This method must be implemented by subclasses
|
|
76
|
+
"""
|
|
77
|
+
raise NotImplementedError("run_server must be implemented by subclasses")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class HypercornEngine(ServerEngine):
|
|
81
|
+
"""
|
|
82
|
+
Hypercorn server engine implementation.
|
|
83
|
+
|
|
84
|
+
Provides full mTLS support and better SSL capabilities.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
def get_name(self) -> str:
|
|
88
|
+
return "hypercorn"
|
|
89
|
+
|
|
90
|
+
def get_supported_features(self) -> Dict[str, bool]:
|
|
91
|
+
return {
|
|
92
|
+
"ssl_tls": True,
|
|
93
|
+
"mtls_client_certs": True, # Full support
|
|
94
|
+
"ssl_scope_info": True, # SSL info in request scope
|
|
95
|
+
"client_cert_verification": True,
|
|
96
|
+
"websockets": True,
|
|
97
|
+
"http2": True,
|
|
98
|
+
"reload": True,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
def run_server(self, app: Any, config: Dict[str, Any]) -> None:
|
|
102
|
+
"""
|
|
103
|
+
Run the server with the given application and configuration.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
app: ASGI application
|
|
107
|
+
config: Server configuration dictionary containing:
|
|
108
|
+
- host: Server host (default: "127.0.0.1")
|
|
109
|
+
- port: Server port (default: 8000)
|
|
110
|
+
- certfile: SSL certificate file (optional)
|
|
111
|
+
- keyfile: SSL key file (optional)
|
|
112
|
+
- ca_certs: CA certificate file (optional)
|
|
113
|
+
- verify_mode: SSL verification mode (optional)
|
|
114
|
+
- check_hostname: Enable hostname checking (optional)
|
|
115
|
+
- log_level: Logging level (optional)
|
|
116
|
+
- reload: Enable auto-reload (optional)
|
|
117
|
+
"""
|
|
118
|
+
import asyncio
|
|
119
|
+
import hypercorn.asyncio
|
|
120
|
+
from hypercorn.config import Config as HypercornConfig
|
|
121
|
+
|
|
122
|
+
# Create hypercorn configuration
|
|
123
|
+
hypercorn_config = HypercornConfig()
|
|
124
|
+
hypercorn_config.bind = [f"{config.get('host', '127.0.0.1')}:{config.get('port', 8000)}"]
|
|
125
|
+
|
|
126
|
+
# Apply SSL configuration if present
|
|
127
|
+
if "certfile" in config:
|
|
128
|
+
hypercorn_config.certfile = config["certfile"]
|
|
129
|
+
if "keyfile" in config:
|
|
130
|
+
hypercorn_config.keyfile = config["keyfile"]
|
|
131
|
+
if "ca_certs" in config:
|
|
132
|
+
hypercorn_config.ca_certs = config["ca_certs"]
|
|
133
|
+
if "verify_mode" in config:
|
|
134
|
+
hypercorn_config.verify_mode = config["verify_mode"]
|
|
135
|
+
if "check_hostname" in config:
|
|
136
|
+
hypercorn_config.check_hostname = config["check_hostname"]
|
|
137
|
+
|
|
138
|
+
# Apply other settings
|
|
139
|
+
if "log_level" in config:
|
|
140
|
+
hypercorn_config.loglevel = config["log_level"].upper()
|
|
141
|
+
if "reload" in config:
|
|
142
|
+
hypercorn_config.reload = config["reload"]
|
|
143
|
+
|
|
144
|
+
# Try to set ALPN protocols for HTTP/2 support
|
|
145
|
+
try:
|
|
146
|
+
hypercorn_config.alpn_protocols = ["h2", "http/1.1"]
|
|
147
|
+
except Exception:
|
|
148
|
+
pass
|
|
149
|
+
|
|
150
|
+
# Run the server (blocking call)
|
|
151
|
+
# Note: This uses asyncio.run internally, so it will block until server stops
|
|
152
|
+
asyncio.run(hypercorn.asyncio.serve(app, hypercorn_config))
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class ServerEngineFactory:
|
|
156
|
+
"""
|
|
157
|
+
Factory for creating server engines.
|
|
158
|
+
|
|
159
|
+
This class manages the creation and configuration of different server engines.
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
_engines: Dict[str, ServerEngine] = {}
|
|
163
|
+
|
|
164
|
+
@classmethod
|
|
165
|
+
def register_engine(cls, engine: ServerEngine) -> None:
|
|
166
|
+
"""
|
|
167
|
+
Register a server engine.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
engine: Server engine instance to register
|
|
171
|
+
"""
|
|
172
|
+
cls._engines[engine.get_name()] = engine
|
|
173
|
+
get_global_logger().info(f"Registered server engine: {engine.get_name()}")
|
|
174
|
+
|
|
175
|
+
@classmethod
|
|
176
|
+
def get_engine(cls, engine_name: str) -> Optional[ServerEngine]:
|
|
177
|
+
"""
|
|
178
|
+
Get a registered server engine by name.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
engine_name: Name of the engine to retrieve
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
ServerEngine instance if found, None otherwise
|
|
185
|
+
"""
|
|
186
|
+
return cls._engines.get(engine_name)
|
|
187
|
+
|
|
188
|
+
@classmethod
|
|
189
|
+
def get_available_engines(cls) -> Dict[str, ServerEngine]:
|
|
190
|
+
"""
|
|
191
|
+
Get all registered server engines.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
Dictionary mapping engine names to ServerEngine instances
|
|
195
|
+
"""
|
|
196
|
+
return cls._engines.copy()
|
|
197
|
+
|
|
198
|
+
@classmethod
|
|
199
|
+
def initialize_default_engines(cls) -> None:
|
|
200
|
+
"""Initialize default server engines."""
|
|
201
|
+
# Register hypercorn engine (only supported engine)
|
|
202
|
+
try:
|
|
203
|
+
import hypercorn # noqa: F401
|
|
204
|
+
|
|
205
|
+
cls.register_engine(HypercornEngine())
|
|
206
|
+
get_global_logger().info(
|
|
207
|
+
"Hypercorn engine registered (full mTLS support available)"
|
|
208
|
+
)
|
|
209
|
+
except ImportError:
|
|
210
|
+
get_global_logger().error(
|
|
211
|
+
"Hypercorn not available - this is required for the framework"
|
|
212
|
+
)
|
|
213
|
+
raise
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# Initialize default engines
|
|
217
|
+
ServerEngineFactory.initialize_default_engines()
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Settings management for the MCP Proxy Adapter framework.
|
|
3
|
+
Provides utilities for reading and managing framework settings from configuration.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from mcp_proxy_adapter.config import config
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Settings:
|
|
10
|
+
"""
|
|
11
|
+
Settings management class for the framework.
|
|
12
|
+
Provides easy access to configuration values with type conversion and validation.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
# Store custom settings as a class variable
|
|
16
|
+
_custom_settings: Dict[str, Any] = {}
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def add_custom_settings(cls, settings: Dict[str, Any]) -> None:
|
|
20
|
+
"""
|
|
21
|
+
Add custom settings to the settings manager.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
settings: Dictionary with custom settings
|
|
25
|
+
"""
|
|
26
|
+
cls._custom_settings.update(settings)
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def get_custom_settings(cls) -> Dict[str, Any]:
|
|
30
|
+
"""
|
|
31
|
+
Get all custom settings.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Dictionary with all custom settings
|
|
35
|
+
"""
|
|
36
|
+
return cls._custom_settings.copy()
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def get_custom_setting_value(cls, key: str, default: Any = None) -> Any:
|
|
40
|
+
"""
|
|
41
|
+
Get custom setting value.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
key: Setting key
|
|
45
|
+
default: Default value if key not found
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Setting value
|
|
49
|
+
"""
|
|
50
|
+
return cls._custom_settings.get(key, default)
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def set_custom_setting_value(cls, key: str, value: Any) -> None:
|
|
54
|
+
"""
|
|
55
|
+
Set custom setting value.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
key: Setting key
|
|
59
|
+
value: Value to set
|
|
60
|
+
"""
|
|
61
|
+
cls._custom_settings[key] = value
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def clear_custom_settings(cls) -> None:
|
|
65
|
+
"""
|
|
66
|
+
Clear all custom settings.
|
|
67
|
+
"""
|
|
68
|
+
cls._custom_settings.clear()
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
|
|
74
|
+
@staticmethod
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def get_custom_setting(key: str, default: Any = None) -> Any:
|
|
78
|
+
"""
|
|
79
|
+
Get custom setting from configuration.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
key: Configuration key in dot notation (e.g., "custom.feature_enabled")
|
|
83
|
+
default: Default value if key not found
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Configuration value
|
|
87
|
+
"""
|
|
88
|
+
return config.get(key, default)
|
|
89
|
+
|
|
90
|
+
@staticmethod
|
|
91
|
+
|
|
92
|
+
@staticmethod
|
|
93
|
+
def set_custom_setting(key: str, value: Any) -> None:
|
|
94
|
+
"""
|
|
95
|
+
Set custom setting in configuration.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
key: Configuration key in dot notation
|
|
99
|
+
value: Value to set
|
|
100
|
+
"""
|
|
101
|
+
config.set(key, value)
|
|
102
|
+
|
|
103
|
+
@staticmethod
|
|
104
|
+
def reload_config() -> None:
|
|
105
|
+
"""
|
|
106
|
+
Reload configuration from file and environment variables.
|
|
107
|
+
"""
|
|
108
|
+
config.load_config()
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class ServerSettings:
|
|
112
|
+
"""
|
|
113
|
+
Server-specific settings helper.
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
@staticmethod
|
|
117
|
+
def get_host() -> str:
|
|
118
|
+
"""Get server host."""
|
|
119
|
+
return config.get("server.host", "0.0.0.0")
|
|
120
|
+
|
|
121
|
+
@staticmethod
|
|
122
|
+
def get_port() -> int:
|
|
123
|
+
"""Get server port."""
|
|
124
|
+
return config.get("server.port", 8000)
|
|
125
|
+
|
|
126
|
+
@staticmethod
|
|
127
|
+
def get_debug() -> bool:
|
|
128
|
+
"""Get debug mode."""
|
|
129
|
+
return config.get("server.debug", False)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class LoggingSettings:
|
|
133
|
+
"""
|
|
134
|
+
Logging-specific settings helper.
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
@staticmethod
|
|
138
|
+
def get_level() -> str:
|
|
139
|
+
"""Get logging level."""
|
|
140
|
+
return config.get("logging.level", "INFO")
|
|
141
|
+
|
|
142
|
+
@staticmethod
|
|
143
|
+
def get_log_dir() -> str:
|
|
144
|
+
"""Get log directory."""
|
|
145
|
+
return config.get("logging.log_dir", "./logs")
|
|
146
|
+
|
|
147
|
+
@staticmethod
|
|
148
|
+
|
|
149
|
+
@staticmethod
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
|
|
153
|
+
@staticmethod
|
|
154
|
+
|
|
155
|
+
@staticmethod
|
|
156
|
+
|
|
157
|
+
@staticmethod
|
|
158
|
+
|
|
159
|
+
@staticmethod
|
|
160
|
+
|
|
161
|
+
@staticmethod
|
|
162
|
+
|
|
163
|
+
@staticmethod
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class CommandsSettings:
|
|
167
|
+
"""
|
|
168
|
+
Commands-specific settings helper.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
@staticmethod
|
|
172
|
+
def get_auto_discovery() -> bool:
|
|
173
|
+
"""Get auto discovery setting."""
|
|
174
|
+
return config.get("commands.auto_discovery", True)
|
|
175
|
+
|
|
176
|
+
@staticmethod
|
|
177
|
+
def get_discovery_path() -> str:
|
|
178
|
+
"""Get discovery path."""
|
|
179
|
+
return config.get("commands.discovery_path", "mcp_proxy_adapter.commands")
|
|
180
|
+
|
|
181
|
+
@staticmethod
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# Convenience functions for easy access
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def get_auto_discovery() -> bool:
|
|
196
|
+
"""Get auto discovery setting."""
|
|
197
|
+
return CommandsSettings.get_auto_discovery()
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def get_discovery_path() -> str:
|
|
201
|
+
"""Get discovery path."""
|
|
202
|
+
return CommandsSettings.get_discovery_path()
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def add_custom_settings(settings: Dict[str, Any]) -> None:
|
|
212
|
+
"""
|
|
213
|
+
Add custom settings to the settings manager.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
settings: Dictionary with custom settings
|
|
217
|
+
"""
|
|
218
|
+
Settings.add_custom_settings(settings)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def get_custom_settings() -> Dict[str, Any]:
|
|
222
|
+
"""
|
|
223
|
+
Get all custom settings.
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
Dictionary with all custom settings
|
|
227
|
+
"""
|
|
228
|
+
return Settings.get_custom_settings()
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def get_custom_setting_value(key: str, default: Any = None) -> Any:
|
|
232
|
+
"""
|
|
233
|
+
Get custom setting value.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
key: Setting key
|
|
237
|
+
default: Default value if key not found
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
Setting value
|
|
241
|
+
"""
|
|
242
|
+
return Settings.get_custom_setting_value(key, default)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def set_custom_setting_value(key: str, value: Any) -> None:
|
|
246
|
+
"""
|
|
247
|
+
Set custom setting value.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
key: Setting key
|
|
251
|
+
value: Value to set
|
|
252
|
+
"""
|
|
253
|
+
Settings.set_custom_setting_value(key, value)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def clear_custom_settings() -> None:
|
|
257
|
+
"""
|
|
258
|
+
Clear all custom settings.
|
|
259
|
+
"""
|
|
260
|
+
Settings.clear_custom_settings()
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Signal handler for graceful shutdown with proxy unregistration.
|
|
3
|
+
|
|
4
|
+
This module provides signal handling for SIGTERM, SIGINT, and SIGHUP
|
|
5
|
+
to ensure proper proxy unregistration before server shutdown.
|
|
6
|
+
|
|
7
|
+
Author: Vasiliy Zdanovskiy
|
|
8
|
+
email: vasilyvz@gmail.com
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import signal
|
|
12
|
+
import threading
|
|
13
|
+
from typing import Optional, Callable, Any
|
|
14
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SignalHandler:
|
|
18
|
+
"""
|
|
19
|
+
Signal handler for graceful shutdown with proxy unregistration.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self):
|
|
23
|
+
"""Initialize signal handler."""
|
|
24
|
+
self._shutdown_callback: Optional[Callable] = None
|
|
25
|
+
self._shutdown_event = threading.Event()
|
|
26
|
+
self._original_handlers = {}
|
|
27
|
+
self._setup_signal_handlers()
|
|
28
|
+
|
|
29
|
+
def set_shutdown_callback(self, callback: Callable):
|
|
30
|
+
"""
|
|
31
|
+
Set callback function to be called during shutdown.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
callback: Function to call during shutdown
|
|
35
|
+
"""
|
|
36
|
+
self._shutdown_callback = callback
|
|
37
|
+
get_global_logger().info("Shutdown callback set for signal handler")
|
|
38
|
+
|
|
39
|
+
def _setup_signal_handlers(self):
|
|
40
|
+
"""Setup signal handlers for graceful shutdown."""
|
|
41
|
+
# Handle SIGTERM (termination signal)
|
|
42
|
+
self._original_handlers[signal.SIGTERM] = signal.signal(
|
|
43
|
+
signal.SIGTERM, self._handle_shutdown_signal
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Handle SIGINT (Ctrl+C)
|
|
47
|
+
self._original_handlers[signal.SIGINT] = signal.signal(
|
|
48
|
+
signal.SIGINT, self._handle_shutdown_signal
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Handle SIGHUP (hangup signal)
|
|
52
|
+
self._original_handlers[signal.SIGHUP] = signal.signal(
|
|
53
|
+
signal.SIGHUP, self._handle_shutdown_signal
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
get_global_logger().info("Signal handlers installed for SIGTERM, SIGINT, SIGHUP")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def wait_for_shutdown(self, timeout: Optional[float] = None) -> bool:
|
|
61
|
+
"""
|
|
62
|
+
Wait for shutdown signal.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
timeout: Maximum time to wait in seconds
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
True if shutdown signal received, False if timeout
|
|
69
|
+
"""
|
|
70
|
+
return self._shutdown_event.wait(timeout)
|
|
71
|
+
|
|
72
|
+
def is_shutdown_requested(self) -> bool:
|
|
73
|
+
"""
|
|
74
|
+
Check if shutdown has been requested.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
True if shutdown signal received
|
|
78
|
+
"""
|
|
79
|
+
return self._shutdown_event.is_set()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# Global signal handler instance
|
|
84
|
+
_signal_handler: Optional[SignalHandler] = None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def get_signal_handler() -> SignalHandler:
|
|
88
|
+
"""Get the global signal handler instance."""
|
|
89
|
+
global _signal_handler
|
|
90
|
+
if _signal_handler is None:
|
|
91
|
+
_signal_handler = SignalHandler()
|
|
92
|
+
return _signal_handler
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def is_shutdown_requested() -> bool:
|
|
100
|
+
"""
|
|
101
|
+
Check if shutdown has been requested.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
True if shutdown signal received
|
|
105
|
+
"""
|
|
106
|
+
handler = get_signal_handler()
|
|
107
|
+
return handler.is_shutdown_requested()
|