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,237 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Main configuration class for MCP Proxy Adapter.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, Optional, List
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
|
12
|
+
from .config_loader import ConfigLoader
|
|
13
|
+
from .feature_manager import FeatureManager
|
|
14
|
+
from .config_factory import ConfigFactory
|
|
15
|
+
|
|
16
|
+
# Import validation if available
|
|
17
|
+
try:
|
|
18
|
+
VALIDATION_AVAILABLE = True
|
|
19
|
+
except ImportError:
|
|
20
|
+
VALIDATION_AVAILABLE = False
|
|
21
|
+
|
|
22
|
+
# Import configuration errors
|
|
23
|
+
from ..errors import ConfigError, ValidationResult
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Config:
|
|
27
|
+
"""
|
|
28
|
+
Configuration management class for the microservice.
|
|
29
|
+
Allows loading settings from configuration file and environment variables.
|
|
30
|
+
Supports optional features that can be enabled/disabled.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self, config_path: Optional[str] = None, validate_on_load: bool = False
|
|
35
|
+
):
|
|
36
|
+
"""
|
|
37
|
+
Initialize configuration.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
config_path: Path to configuration file. If not specified,
|
|
41
|
+
"./config.json" is used.
|
|
42
|
+
validate_on_load: Whether to validate configuration on load (default: False)
|
|
43
|
+
|
|
44
|
+
Raises:
|
|
45
|
+
ConfigError: If configuration validation fails
|
|
46
|
+
"""
|
|
47
|
+
self.config_path = config_path or "./config.json"
|
|
48
|
+
self.config_data: Dict[str, Any] = {}
|
|
49
|
+
self.validate_on_load = validate_on_load
|
|
50
|
+
self.validation_results: List[ValidationResult] = []
|
|
51
|
+
self.validator = None
|
|
52
|
+
|
|
53
|
+
# Initialize components
|
|
54
|
+
self.logger = get_global_logger()
|
|
55
|
+
self.loader = ConfigLoader()
|
|
56
|
+
self.feature_manager = FeatureManager(self.config_data)
|
|
57
|
+
self.factory = ConfigFactory()
|
|
58
|
+
|
|
59
|
+
# Load configuration
|
|
60
|
+
self.load_config()
|
|
61
|
+
|
|
62
|
+
def load_config(self) -> None:
|
|
63
|
+
"""Load configuration from file and environment variables."""
|
|
64
|
+
try:
|
|
65
|
+
# Load from file if it exists
|
|
66
|
+
if Path(self.config_path).exists():
|
|
67
|
+
file_config = self.loader.load_from_file(self.config_path)
|
|
68
|
+
self.config_data.update(file_config)
|
|
69
|
+
|
|
70
|
+
# Load from environment variables
|
|
71
|
+
try:
|
|
72
|
+
env_config = self.loader.load_from_env()
|
|
73
|
+
self._merge_config(env_config)
|
|
74
|
+
except AttributeError:
|
|
75
|
+
# load_from_env doesn't exist yet, skip
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
# Validate if required
|
|
79
|
+
if self.validate_on_load and VALIDATION_AVAILABLE:
|
|
80
|
+
self.validate()
|
|
81
|
+
|
|
82
|
+
except Exception as e:
|
|
83
|
+
self.logger.error(f"Failed to load configuration: {e}")
|
|
84
|
+
raise ConfigError(f"Configuration loading failed: {e}")
|
|
85
|
+
|
|
86
|
+
def _merge_config(self, new_config: Dict[str, Any]) -> None:
|
|
87
|
+
"""
|
|
88
|
+
Merge new configuration into existing configuration.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
new_config: New configuration to merge
|
|
92
|
+
"""
|
|
93
|
+
for section, values in new_config.items():
|
|
94
|
+
if section in self.config_data:
|
|
95
|
+
if isinstance(self.config_data[section], dict) and isinstance(
|
|
96
|
+
values, dict
|
|
97
|
+
):
|
|
98
|
+
self.config_data[section].update(values)
|
|
99
|
+
else:
|
|
100
|
+
self.config_data[section] = values
|
|
101
|
+
else:
|
|
102
|
+
self.config_data[section] = values
|
|
103
|
+
|
|
104
|
+
def enable_feature(self, feature: str) -> None:
|
|
105
|
+
"""
|
|
106
|
+
Enable a feature.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
feature: Feature name
|
|
110
|
+
"""
|
|
111
|
+
self.feature_manager.enable_feature(feature)
|
|
112
|
+
|
|
113
|
+
def disable_feature(self, feature: str) -> None:
|
|
114
|
+
"""
|
|
115
|
+
Disable a feature.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
feature: Feature name
|
|
119
|
+
"""
|
|
120
|
+
self.feature_manager.disable_feature(feature)
|
|
121
|
+
|
|
122
|
+
def is_feature_enabled(self, feature: str) -> bool:
|
|
123
|
+
"""
|
|
124
|
+
Check if feature is enabled.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
feature: Feature name
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
True if enabled, False otherwise
|
|
131
|
+
"""
|
|
132
|
+
return self.feature_manager.is_feature_enabled(feature)
|
|
133
|
+
|
|
134
|
+
def get_enabled_features(self) -> List[str]:
|
|
135
|
+
"""
|
|
136
|
+
Get list of enabled features.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
List of enabled feature names
|
|
140
|
+
"""
|
|
141
|
+
return self.feature_manager.get_enabled_features()
|
|
142
|
+
|
|
143
|
+
def validate(self) -> List[ValidationResult]:
|
|
144
|
+
"""
|
|
145
|
+
Validate configuration.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
List of validation results
|
|
149
|
+
"""
|
|
150
|
+
if not VALIDATION_AVAILABLE:
|
|
151
|
+
self.logger.warning("Configuration validation not available")
|
|
152
|
+
return []
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
from ..config_validator import ConfigValidator
|
|
156
|
+
|
|
157
|
+
self.validator = ConfigValidator()
|
|
158
|
+
self.validator.config_data = self.config_data
|
|
159
|
+
self.validation_results = self.validator.validate_config()
|
|
160
|
+
return self.validation_results
|
|
161
|
+
except Exception as e:
|
|
162
|
+
self.logger.error(f"Configuration validation failed: {e}")
|
|
163
|
+
return []
|
|
164
|
+
|
|
165
|
+
def get_validation_errors(self) -> List[ValidationResult]:
|
|
166
|
+
"""Get validation errors."""
|
|
167
|
+
return [result for result in self.validation_results if result.level == "error"]
|
|
168
|
+
|
|
169
|
+
def get_validation_warnings(self) -> List[ValidationResult]:
|
|
170
|
+
"""Get validation warnings."""
|
|
171
|
+
return [
|
|
172
|
+
result for result in self.validation_results if result.level == "warning"
|
|
173
|
+
]
|
|
174
|
+
|
|
175
|
+
def get_validation_summary(self) -> Dict[str, Any]:
|
|
176
|
+
"""Get validation summary."""
|
|
177
|
+
errors = self.get_validation_errors()
|
|
178
|
+
warnings = self.get_validation_warnings()
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
"total": len(self.validation_results),
|
|
182
|
+
"errors": len(errors),
|
|
183
|
+
"warnings": len(warnings),
|
|
184
|
+
"is_valid": len(errors) == 0,
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
def check_feature_requirements(self, feature: str) -> List[ValidationResult]:
|
|
188
|
+
"""
|
|
189
|
+
Check feature requirements.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
feature: Feature name
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
List of validation results
|
|
196
|
+
"""
|
|
197
|
+
return self.feature_manager.check_feature_requirements(feature)
|
|
198
|
+
|
|
199
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
200
|
+
"""
|
|
201
|
+
Get configuration value using dot notation.
|
|
202
|
+
|
|
203
|
+
Supports nested dictionary access using dot notation.
|
|
204
|
+
For example: "transport.cert_file" will access config_data["transport"]["cert_file"]
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
key: Configuration key, can use dot notation (e.g., "transport.cert_file")
|
|
208
|
+
default: Default value if key not found
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
Configuration value or default
|
|
212
|
+
|
|
213
|
+
Examples:
|
|
214
|
+
>>> config.get("server.host", "0.0.0.0")
|
|
215
|
+
>>> config.get("transport.ssl.cert_file")
|
|
216
|
+
"""
|
|
217
|
+
keys = key.split(".")
|
|
218
|
+
value = self.config_data
|
|
219
|
+
|
|
220
|
+
for k in keys:
|
|
221
|
+
if isinstance(value, dict):
|
|
222
|
+
value = value.get(k)
|
|
223
|
+
if value is None:
|
|
224
|
+
return default
|
|
225
|
+
else:
|
|
226
|
+
return default
|
|
227
|
+
|
|
228
|
+
return value if value is not None else default
|
|
229
|
+
|
|
230
|
+
def get_all(self) -> Dict[str, Any]:
|
|
231
|
+
"""
|
|
232
|
+
Get all configuration data.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
Complete configuration dictionary
|
|
236
|
+
"""
|
|
237
|
+
return self.config_data
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Configuration factory for creating predefined configurations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Dict, Any
|
|
9
|
+
|
|
10
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ConfigFactory:
|
|
14
|
+
"""Factory for creating predefined configurations."""
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
"""Initialize config factory."""
|
|
18
|
+
self.logger = get_global_logger()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Configuration loading utilities for MCP Proxy Adapter.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ConfigLoader:
|
|
17
|
+
"""Loader for configuration files and environment variables."""
|
|
18
|
+
|
|
19
|
+
def __init__(self):
|
|
20
|
+
"""Initialize config loader."""
|
|
21
|
+
self.logger = get_global_logger()
|
|
22
|
+
|
|
23
|
+
def load_from_file(self, config_path: str | Path) -> dict:
|
|
24
|
+
"""
|
|
25
|
+
Load configuration from JSON file.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
config_path: Path to configuration file
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Configuration dictionary
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
FileNotFoundError: If config file doesn't exist
|
|
35
|
+
json.JSONDecodeError: If config file is invalid JSON
|
|
36
|
+
"""
|
|
37
|
+
path = Path(config_path)
|
|
38
|
+
if not path.exists():
|
|
39
|
+
raise FileNotFoundError(f"Configuration file not found: {config_path}")
|
|
40
|
+
|
|
41
|
+
with open(path, 'r', encoding='utf-8') as f:
|
|
42
|
+
return json.load(f)
|
|
43
|
+
|
|
44
|
+
def _convert_env_value(self, value: str) -> Any:
|
|
45
|
+
"""
|
|
46
|
+
Convert environment variable value to appropriate type.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
value: Value as string
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Converted value
|
|
53
|
+
"""
|
|
54
|
+
# Try to convert to appropriate type
|
|
55
|
+
if value.lower() == "true":
|
|
56
|
+
return True
|
|
57
|
+
elif value.lower() == "false":
|
|
58
|
+
return False
|
|
59
|
+
elif value.isdigit():
|
|
60
|
+
return int(value)
|
|
61
|
+
else:
|
|
62
|
+
try:
|
|
63
|
+
return float(value)
|
|
64
|
+
except ValueError:
|
|
65
|
+
return value
|
|
66
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Feature management utilities for MCP Proxy Adapter configuration.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, List
|
|
9
|
+
|
|
10
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class FeatureManager:
|
|
14
|
+
"""Manager for configuration features."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, config_data: Dict[str, Any]):
|
|
17
|
+
"""
|
|
18
|
+
Initialize feature manager.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
config_data: Configuration data dictionary
|
|
22
|
+
"""
|
|
23
|
+
self.config_data = config_data
|
|
24
|
+
self.logger = get_global_logger()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Simple configuration data container and IO helpers for MCP Proxy Adapter.
|
|
6
|
+
|
|
7
|
+
This module provides a minimal, explicit configuration model with three
|
|
8
|
+
sections: server, proxy_client and auth.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any, Dict, List, Optional
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class ServerConfig:
|
|
21
|
+
host: str
|
|
22
|
+
port: int
|
|
23
|
+
protocol: str # http | https | mtls
|
|
24
|
+
cert_file: Optional[str] = None
|
|
25
|
+
key_file: Optional[str] = None
|
|
26
|
+
ca_cert_file: Optional[str] = None
|
|
27
|
+
crl_file: Optional[str] = None
|
|
28
|
+
use_system_ca: bool = False # If True, allow system CA store when ca_cert_file is not provided
|
|
29
|
+
log_dir: str = "./logs"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class HeartbeatConfig:
|
|
34
|
+
endpoint: str = "/heartbeat"
|
|
35
|
+
interval: int = 30
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class RegistrationConfig:
|
|
40
|
+
register_endpoint: str = "/register"
|
|
41
|
+
unregister_endpoint: str = "/unregister"
|
|
42
|
+
auto_on_startup: bool = True
|
|
43
|
+
auto_on_shutdown: bool = True
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class ProxyClientConfig:
|
|
48
|
+
enabled: bool = False
|
|
49
|
+
host: str = "localhost"
|
|
50
|
+
port: int = 3005
|
|
51
|
+
protocol: str = "http"
|
|
52
|
+
server_id: Optional[str] = None # Server identifier for registration (preferred)
|
|
53
|
+
server_name: Optional[str] = None # Legacy field, use server_id instead
|
|
54
|
+
cert_file: Optional[str] = None
|
|
55
|
+
key_file: Optional[str] = None
|
|
56
|
+
ca_cert_file: Optional[str] = None
|
|
57
|
+
crl_file: Optional[str] = None
|
|
58
|
+
use_system_ca: bool = False # If True, allow system CA store when ca_cert_file is not provided
|
|
59
|
+
heartbeat: HeartbeatConfig = field(default_factory=HeartbeatConfig)
|
|
60
|
+
registration: RegistrationConfig = field(default_factory=RegistrationConfig)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class AuthConfig:
|
|
65
|
+
use_token: bool = False
|
|
66
|
+
use_roles: bool = False
|
|
67
|
+
tokens: Dict[str, List[str]] = field(default_factory=dict)
|
|
68
|
+
roles: Dict[str, List[str]] = field(default_factory=dict)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class SimpleConfigModel:
|
|
73
|
+
server: ServerConfig
|
|
74
|
+
proxy_client: ProxyClientConfig = field(default_factory=ProxyClientConfig)
|
|
75
|
+
auth: AuthConfig = field(default_factory=AuthConfig)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class SimpleConfig:
|
|
79
|
+
"""High-level loader/saver for SimpleConfigModel."""
|
|
80
|
+
|
|
81
|
+
def __init__(self, config_path: str = "config.json") -> None:
|
|
82
|
+
self.config_path: Path = Path(config_path)
|
|
83
|
+
self.model: Optional[SimpleConfigModel] = None
|
|
84
|
+
|
|
85
|
+
def load(self) -> SimpleConfigModel:
|
|
86
|
+
content = json.loads(self.config_path.read_text(encoding="utf-8"))
|
|
87
|
+
server = ServerConfig(**content["server"]) # type: ignore[arg-type]
|
|
88
|
+
proxy_client = ProxyClientConfig(**content.get("proxy_client", {})) # type: ignore[arg-type]
|
|
89
|
+
# Nested structures for proxy client (heartbeat/registration)
|
|
90
|
+
if isinstance(content.get("proxy_client"), dict):
|
|
91
|
+
pc = content["proxy_client"]
|
|
92
|
+
if isinstance(pc.get("heartbeat"), dict):
|
|
93
|
+
proxy_client.heartbeat = HeartbeatConfig(**pc["heartbeat"]) # type: ignore[arg-type]
|
|
94
|
+
if isinstance(pc.get("registration"), dict):
|
|
95
|
+
proxy_client.registration = RegistrationConfig(**pc["registration"]) # type: ignore[arg-type]
|
|
96
|
+
auth = AuthConfig(**content.get("auth", {})) # type: ignore[arg-type]
|
|
97
|
+
self.model = SimpleConfigModel(server=server, proxy_client=proxy_client, auth=auth)
|
|
98
|
+
return self.model
|
|
99
|
+
|
|
100
|
+
def save(self, out_path: Optional[str] = None) -> None:
|
|
101
|
+
if self.model is None:
|
|
102
|
+
raise ValueError("Configuration model is not loaded")
|
|
103
|
+
path = Path(out_path) if out_path else self.config_path
|
|
104
|
+
data: Dict[str, Any] = {
|
|
105
|
+
"server": vars(self.model.server),
|
|
106
|
+
"proxy_client": {
|
|
107
|
+
**{k: v for k, v in vars(self.model.proxy_client).items() if k not in {"heartbeat", "registration"}},
|
|
108
|
+
"heartbeat": vars(self.model.proxy_client.heartbeat),
|
|
109
|
+
"registration": vars(self.model.proxy_client.registration),
|
|
110
|
+
},
|
|
111
|
+
"auth": vars(self.model.auth),
|
|
112
|
+
}
|
|
113
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
114
|
+
path.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8")
|
|
115
|
+
|
|
116
|
+
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Simple configuration generator for MCP Proxy Adapter.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
from .simple_config import (
|
|
13
|
+
SimpleConfig,
|
|
14
|
+
SimpleConfigModel,
|
|
15
|
+
ServerConfig,
|
|
16
|
+
ProxyClientConfig,
|
|
17
|
+
AuthConfig,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SimpleConfigGenerator:
|
|
22
|
+
"""Generate minimal configuration according to the plan."""
|
|
23
|
+
|
|
24
|
+
def generate(
|
|
25
|
+
self,
|
|
26
|
+
protocol: str,
|
|
27
|
+
with_proxy: bool = False,
|
|
28
|
+
out_path: str = "config.json",
|
|
29
|
+
server_host: Optional[str] = None,
|
|
30
|
+
server_port: Optional[int] = None,
|
|
31
|
+
server_cert_file: Optional[str] = None,
|
|
32
|
+
server_key_file: Optional[str] = None,
|
|
33
|
+
server_ca_cert_file: Optional[str] = None,
|
|
34
|
+
proxy_host: Optional[str] = None,
|
|
35
|
+
proxy_port: Optional[int] = None,
|
|
36
|
+
proxy_cert_file: Optional[str] = None,
|
|
37
|
+
proxy_key_file: Optional[str] = None,
|
|
38
|
+
proxy_ca_cert_file: Optional[str] = None,
|
|
39
|
+
) -> str:
|
|
40
|
+
"""
|
|
41
|
+
Generate configuration with optional custom parameters.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
protocol: Server protocol (http, https, mtls)
|
|
45
|
+
with_proxy: Enable proxy registration
|
|
46
|
+
out_path: Output file path
|
|
47
|
+
server_host: Server host (default: 0.0.0.0)
|
|
48
|
+
server_port: Server port (default: 8080)
|
|
49
|
+
server_cert_file: Server certificate file path
|
|
50
|
+
server_key_file: Server key file path
|
|
51
|
+
server_ca_cert_file: Server CA certificate file path
|
|
52
|
+
proxy_host: Proxy host (default: localhost)
|
|
53
|
+
proxy_port: Proxy port (default: 3005)
|
|
54
|
+
proxy_cert_file: Proxy client certificate file path
|
|
55
|
+
proxy_key_file: Proxy client key file path
|
|
56
|
+
proxy_ca_cert_file: Proxy CA certificate file path
|
|
57
|
+
"""
|
|
58
|
+
# Server configuration
|
|
59
|
+
server = ServerConfig(
|
|
60
|
+
host=server_host or "0.0.0.0", port=server_port or 8080, protocol=protocol
|
|
61
|
+
)
|
|
62
|
+
if protocol in ("https", "mtls"):
|
|
63
|
+
server.cert_file = server_cert_file or "./certs/server.crt"
|
|
64
|
+
server.key_file = server_key_file or "./certs/server.key"
|
|
65
|
+
# For mtls: CA is required if use_system_ca=False (default), optional if use_system_ca=True
|
|
66
|
+
# Only set if explicitly provided
|
|
67
|
+
if protocol == "mtls" and server_ca_cert_file:
|
|
68
|
+
server.ca_cert_file = server_ca_cert_file
|
|
69
|
+
# use_system_ca defaults to False (only CA from config is used by default)
|
|
70
|
+
|
|
71
|
+
# Proxy configuration
|
|
72
|
+
proxy = ProxyClientConfig(enabled=with_proxy)
|
|
73
|
+
if with_proxy:
|
|
74
|
+
# NOTE: proxy.protocol indicates the SERVER's protocol, not the proxy's protocol
|
|
75
|
+
# The proxy itself typically runs on HTTP (for test proxy) or may have its own protocol
|
|
76
|
+
# This field is used to determine if client certificates are needed for proxy connection
|
|
77
|
+
# (if proxy itself uses HTTPS/mTLS, which is not the case for test proxy)
|
|
78
|
+
proxy.protocol = protocol
|
|
79
|
+
proxy.host = proxy_host or "localhost"
|
|
80
|
+
proxy.port = proxy_port or 3005
|
|
81
|
+
# Client certificates for proxy connection (only if proxy itself uses HTTPS/mTLS)
|
|
82
|
+
# For test proxy (HTTP), these are not used but may be set for consistency
|
|
83
|
+
if protocol in ("https", "mtls"):
|
|
84
|
+
proxy.cert_file = proxy_cert_file or "./certs/client.crt"
|
|
85
|
+
proxy.key_file = proxy_key_file or "./certs/client.key"
|
|
86
|
+
# For mtls: CA is required if use_system_ca=False (default), optional if use_system_ca=True
|
|
87
|
+
# Only set if explicitly provided
|
|
88
|
+
if protocol == "mtls" and proxy_ca_cert_file:
|
|
89
|
+
proxy.ca_cert_file = proxy_ca_cert_file
|
|
90
|
+
# use_system_ca defaults to False (only CA from config is used by default)
|
|
91
|
+
# Explicitly set registration.auto_on_startup when proxy is enabled
|
|
92
|
+
# Note: enabled controls client availability, registration.auto_on_startup controls auto-registration
|
|
93
|
+
proxy.registration.auto_on_startup = True
|
|
94
|
+
|
|
95
|
+
auth = AuthConfig(use_token=False, use_roles=False, tokens={}, roles={})
|
|
96
|
+
|
|
97
|
+
cfg = SimpleConfig()
|
|
98
|
+
cfg.model = SimpleConfigModel(server=server, proxy_client=proxy, auth=auth)
|
|
99
|
+
cfg.save(out_path)
|
|
100
|
+
return out_path
|