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,471 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unified Configuration Adapter for mcp_security_framework integration.
|
|
3
|
+
|
|
4
|
+
Author: Vasiliy Zdanovskiy
|
|
5
|
+
email: vasilyvz@gmail.com
|
|
6
|
+
|
|
7
|
+
This module provides a unified adapter for converting mcp_proxy_adapter configuration
|
|
8
|
+
to SecurityConfig format used by mcp_security_framework.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import logging
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from typing import List, Dict, Any
|
|
16
|
+
|
|
17
|
+
# Import mcp_security_framework components
|
|
18
|
+
try:
|
|
19
|
+
from mcp_security_framework import SecurityConfig
|
|
20
|
+
from mcp_security_framework.schemas.config import (
|
|
21
|
+
AuthConfig,
|
|
22
|
+
SSLConfig,
|
|
23
|
+
PermissionConfig,
|
|
24
|
+
RateLimitConfig,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
SECURITY_FRAMEWORK_AVAILABLE = True
|
|
28
|
+
except ImportError as e:
|
|
29
|
+
# NO FALLBACK! mcp_security_framework is REQUIRED
|
|
30
|
+
raise RuntimeError(
|
|
31
|
+
f"CRITICAL: mcp_security_framework is required but not available: {e}. "
|
|
32
|
+
"Install it with: pip install mcp_security_framework>=1.2.8"
|
|
33
|
+
) from e
|
|
34
|
+
|
|
35
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class ValidationResult:
|
|
40
|
+
"""Result of configuration validation."""
|
|
41
|
+
|
|
42
|
+
is_valid: bool
|
|
43
|
+
errors: List[str]
|
|
44
|
+
warnings: List[str]
|
|
45
|
+
details: Dict[str, Any]
|
|
46
|
+
|
|
47
|
+
def __post_init__(self):
|
|
48
|
+
"""Initialize with empty lists if None."""
|
|
49
|
+
if self.errors is None:
|
|
50
|
+
self.errors = []
|
|
51
|
+
if self.warnings is None:
|
|
52
|
+
self.warnings = []
|
|
53
|
+
if self.details is None:
|
|
54
|
+
self.details = {}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class UnifiedConfigAdapter:
|
|
58
|
+
"""
|
|
59
|
+
Unified adapter for converting mcp_proxy_adapter configuration to SecurityConfig.
|
|
60
|
+
|
|
61
|
+
This adapter handles:
|
|
62
|
+
- Legacy configuration format compatibility
|
|
63
|
+
- Configuration validation
|
|
64
|
+
- Conversion to mcp_security_framework format
|
|
65
|
+
- Default value management
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
def __init__(self):
|
|
69
|
+
"""Initialize the unified configuration adapter."""
|
|
70
|
+
self.validation_errors = []
|
|
71
|
+
self.validation_warnings = []
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def validate_configuration(self, config: Dict[str, Any]) -> ValidationResult:
|
|
75
|
+
"""
|
|
76
|
+
Validate configuration before conversion.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
config: Configuration dictionary to validate
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
ValidationResult with validation details
|
|
83
|
+
"""
|
|
84
|
+
self.validation_errors = []
|
|
85
|
+
self.validation_warnings = []
|
|
86
|
+
|
|
87
|
+
# Debug: Check SSL config at start of validation
|
|
88
|
+
if "security" in config:
|
|
89
|
+
ssl_config = config["security"].get("ssl", {})
|
|
90
|
+
print(
|
|
91
|
+
f"🔍 Debug: SSL config at start of validation: enabled={ssl_config.get('enabled', False)}"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Debug: Check if root ssl section exists
|
|
95
|
+
if "ssl" in config:
|
|
96
|
+
print(
|
|
97
|
+
f"🔍 Debug: Root SSL section found: enabled={config['ssl'].get('enabled', False)}"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Check if config is a dictionary
|
|
101
|
+
if not isinstance(config, dict):
|
|
102
|
+
return ValidationResult(
|
|
103
|
+
is_valid=False,
|
|
104
|
+
errors=["Configuration must be a dictionary"],
|
|
105
|
+
warnings=[],
|
|
106
|
+
details={},
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Validate security section
|
|
110
|
+
self._validate_security_section(config)
|
|
111
|
+
|
|
112
|
+
# Validate legacy sections for compatibility
|
|
113
|
+
self._validate_legacy_sections(config)
|
|
114
|
+
|
|
115
|
+
# Check for conflicts
|
|
116
|
+
self._check_configuration_conflicts(config)
|
|
117
|
+
|
|
118
|
+
# Validate individual sections
|
|
119
|
+
self._validate_auth_section(config)
|
|
120
|
+
self._validate_ssl_section(config)
|
|
121
|
+
self._validate_permissions_section(config)
|
|
122
|
+
self._validate_rate_limit_section(config)
|
|
123
|
+
|
|
124
|
+
return ValidationResult(
|
|
125
|
+
is_valid=len(self.validation_errors) == 0,
|
|
126
|
+
errors=self.validation_errors.copy(),
|
|
127
|
+
warnings=self.validation_warnings.copy(),
|
|
128
|
+
details={
|
|
129
|
+
"has_security_section": "security" in config,
|
|
130
|
+
"has_legacy_sections": any(
|
|
131
|
+
key in config for key in ["ssl", "roles", "auth_enabled"]
|
|
132
|
+
),
|
|
133
|
+
"total_errors": len(self.validation_errors),
|
|
134
|
+
"total_warnings": len(self.validation_warnings),
|
|
135
|
+
},
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
def _validate_security_section(self, config: Dict[str, Any]):
|
|
139
|
+
"""Validate security section."""
|
|
140
|
+
security_config = config.get("security", {})
|
|
141
|
+
|
|
142
|
+
if not isinstance(security_config, dict):
|
|
143
|
+
self.validation_errors.append("Security section must be a dictionary")
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
# Check for unknown keys in security section
|
|
147
|
+
known_keys = {
|
|
148
|
+
"enabled",
|
|
149
|
+
"auth",
|
|
150
|
+
"ssl",
|
|
151
|
+
"permissions",
|
|
152
|
+
"rate_limit",
|
|
153
|
+
"public_paths",
|
|
154
|
+
}
|
|
155
|
+
unknown_keys = set(security_config.keys()) - known_keys
|
|
156
|
+
|
|
157
|
+
if unknown_keys:
|
|
158
|
+
self.validation_warnings.append(
|
|
159
|
+
f"Unknown keys in security section: {unknown_keys}"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
def _validate_legacy_sections(self, config: Dict[str, Any]):
|
|
163
|
+
"""Validate legacy configuration sections."""
|
|
164
|
+
legacy_sections = ["ssl", "roles", "auth_enabled", "rate_limit_enabled"]
|
|
165
|
+
|
|
166
|
+
for section in legacy_sections:
|
|
167
|
+
if section in config:
|
|
168
|
+
self.validation_warnings.append(
|
|
169
|
+
f"Legacy section '{section}' found, consider migrating to security section"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
def _check_configuration_conflicts(self, config: Dict[str, Any]):
|
|
173
|
+
"""Check for configuration conflicts."""
|
|
174
|
+
security_config = config.get("security", {})
|
|
175
|
+
|
|
176
|
+
# Check for SSL configuration conflicts
|
|
177
|
+
if "ssl" in config and "ssl" in security_config:
|
|
178
|
+
self.validation_warnings.append(
|
|
179
|
+
"SSL configuration found in both root and security sections"
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# Check for auth configuration conflicts
|
|
183
|
+
if "auth_enabled" in config and "auth" in security_config:
|
|
184
|
+
self.validation_warnings.append(
|
|
185
|
+
"Auth configuration found in both root and security sections"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
def _validate_auth_section(self, config: Dict[str, Any]):
|
|
189
|
+
"""Validate authentication section."""
|
|
190
|
+
auth_config = self._get_auth_config(config)
|
|
191
|
+
|
|
192
|
+
if not isinstance(auth_config, dict):
|
|
193
|
+
self.validation_errors.append("Auth configuration must be a dictionary")
|
|
194
|
+
return
|
|
195
|
+
|
|
196
|
+
# Validate auth methods
|
|
197
|
+
methods = auth_config.get("methods", [])
|
|
198
|
+
if not isinstance(methods, list):
|
|
199
|
+
self.validation_errors.append("Auth methods must be a list")
|
|
200
|
+
else:
|
|
201
|
+
valid_methods = {"api_key", "jwt", "certificate"}
|
|
202
|
+
invalid_methods = set(methods) - valid_methods
|
|
203
|
+
if invalid_methods:
|
|
204
|
+
self.validation_errors.append(
|
|
205
|
+
f"Invalid auth methods: {invalid_methods}"
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# Validate API keys
|
|
209
|
+
api_keys = auth_config.get("api_keys", {})
|
|
210
|
+
if not isinstance(api_keys, dict):
|
|
211
|
+
self.validation_errors.append("API keys must be a dictionary")
|
|
212
|
+
|
|
213
|
+
# Validate JWT configuration
|
|
214
|
+
if "jwt" in methods:
|
|
215
|
+
jwt_secret = auth_config.get("jwt_secret", "")
|
|
216
|
+
if not jwt_secret:
|
|
217
|
+
self.validation_warnings.append("JWT secret is empty or not set")
|
|
218
|
+
|
|
219
|
+
def _validate_ssl_section(self, config: Dict[str, Any]):
|
|
220
|
+
"""Validate SSL section."""
|
|
221
|
+
ssl_config = self._get_ssl_config(config)
|
|
222
|
+
|
|
223
|
+
if not isinstance(ssl_config, dict):
|
|
224
|
+
self.validation_errors.append("SSL configuration must be a dictionary")
|
|
225
|
+
return
|
|
226
|
+
|
|
227
|
+
# Validate certificate files
|
|
228
|
+
if ssl_config.get("enabled", False):
|
|
229
|
+
cert_file = ssl_config.get("cert_file")
|
|
230
|
+
key_file = ssl_config.get("key_file")
|
|
231
|
+
|
|
232
|
+
print(f"🔍 Debug: _validate_ssl_section: cert_file={cert_file}")
|
|
233
|
+
print(f"🔍 Debug: _validate_ssl_section: key_file={key_file}")
|
|
234
|
+
print(
|
|
235
|
+
f"🔍 Debug: _validate_ssl_section: cert_file exists={Path(cert_file).exists() if cert_file else 'None'}"
|
|
236
|
+
)
|
|
237
|
+
print(
|
|
238
|
+
f"🔍 Debug: _validate_ssl_section: key_file exists={Path(key_file).exists() if key_file else 'None'}"
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
if cert_file and not Path(cert_file).exists():
|
|
242
|
+
self.validation_warnings.append(
|
|
243
|
+
f"SSL certificate file not found: {cert_file}"
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
if key_file and not Path(key_file).exists():
|
|
247
|
+
self.validation_warnings.append(f"SSL key file not found: {key_file}")
|
|
248
|
+
|
|
249
|
+
def _validate_permissions_section(self, config: Dict[str, Any]):
|
|
250
|
+
"""Validate permissions section."""
|
|
251
|
+
permissions_config = self._get_permissions_config(config)
|
|
252
|
+
|
|
253
|
+
if not isinstance(permissions_config, dict):
|
|
254
|
+
self.validation_errors.append(
|
|
255
|
+
"Permissions configuration must be a dictionary"
|
|
256
|
+
)
|
|
257
|
+
return
|
|
258
|
+
|
|
259
|
+
# Validate roles file
|
|
260
|
+
if permissions_config.get("enabled", False):
|
|
261
|
+
roles_file = permissions_config.get("roles_file")
|
|
262
|
+
if roles_file and not Path(roles_file).exists():
|
|
263
|
+
self.validation_warnings.append(f"Roles file not found: {roles_file}")
|
|
264
|
+
|
|
265
|
+
def _validate_rate_limit_section(self, config: Dict[str, Any]):
|
|
266
|
+
"""Validate rate limit section."""
|
|
267
|
+
rate_limit_config = self._get_rate_limit_config(config)
|
|
268
|
+
|
|
269
|
+
if not isinstance(rate_limit_config, dict):
|
|
270
|
+
self.validation_errors.append(
|
|
271
|
+
"Rate limit configuration must be a dictionary"
|
|
272
|
+
)
|
|
273
|
+
return
|
|
274
|
+
|
|
275
|
+
# Validate rate limit values only if enabled
|
|
276
|
+
if rate_limit_config.get("enabled", False):
|
|
277
|
+
requests_per_minute = rate_limit_config.get(
|
|
278
|
+
"requests_per_minute",
|
|
279
|
+
rate_limit_config.get("default_requests_per_minute", 0),
|
|
280
|
+
)
|
|
281
|
+
if requests_per_minute <= 0:
|
|
282
|
+
self.validation_errors.append(
|
|
283
|
+
"requests_per_minute must be greater than 0 when rate limiting is enabled"
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
def _convert_auth_config(self, config: Dict[str, Any]) -> AuthConfig:
|
|
287
|
+
"""Convert authentication configuration."""
|
|
288
|
+
auth_config = self._get_auth_config(config)
|
|
289
|
+
|
|
290
|
+
# Get authentication methods
|
|
291
|
+
methods = auth_config.get("methods", ["api_key"])
|
|
292
|
+
|
|
293
|
+
# Get API keys from multiple sources
|
|
294
|
+
api_keys = auth_config.get("api_keys", {})
|
|
295
|
+
if not api_keys:
|
|
296
|
+
# Try legacy SSL config
|
|
297
|
+
legacy_ssl = config.get("ssl", {})
|
|
298
|
+
api_keys = legacy_ssl.get("api_keys", {})
|
|
299
|
+
|
|
300
|
+
return AuthConfig(
|
|
301
|
+
enabled=auth_config.get("enabled", True),
|
|
302
|
+
methods=methods,
|
|
303
|
+
api_keys=api_keys,
|
|
304
|
+
jwt_secret=auth_config.get("jwt_secret", ""),
|
|
305
|
+
jwt_algorithm=auth_config.get("jwt_algorithm", "HS256"),
|
|
306
|
+
jwt_expiration=auth_config.get("jwt_expiration", 3600),
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
def _convert_ssl_config(self, config: Dict[str, Any]) -> SSLConfig:
|
|
310
|
+
"""Convert SSL configuration."""
|
|
311
|
+
ssl_config = self._get_ssl_config(config)
|
|
312
|
+
|
|
313
|
+
return SSLConfig(
|
|
314
|
+
enabled=ssl_config.get("enabled", False),
|
|
315
|
+
cert_file=ssl_config.get("cert_file"),
|
|
316
|
+
key_file=ssl_config.get("key_file"),
|
|
317
|
+
ca_cert=ssl_config.get("ca_cert"),
|
|
318
|
+
min_tls_version=ssl_config.get("min_tls_version", "TLSv1.2"),
|
|
319
|
+
verify_client=ssl_config.get("verify_client", False),
|
|
320
|
+
client_cert_required=ssl_config.get("client_cert_required", False),
|
|
321
|
+
cipher_suites=ssl_config.get("cipher_suites", []),
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
def _convert_permission_config(self, config: Dict[str, Any]) -> PermissionConfig:
|
|
325
|
+
"""Convert permissions configuration."""
|
|
326
|
+
permissions_config = self._get_permissions_config(config)
|
|
327
|
+
|
|
328
|
+
return PermissionConfig(
|
|
329
|
+
enabled=permissions_config.get("enabled", True),
|
|
330
|
+
roles_file=permissions_config.get("roles_file", "roles.json"),
|
|
331
|
+
default_role=permissions_config.get("default_role", "user"),
|
|
332
|
+
deny_by_default=permissions_config.get("deny_by_default", True),
|
|
333
|
+
role_mappings=permissions_config.get("role_mappings", {}),
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
def _convert_rate_limit_config(self, config: Dict[str, Any]) -> RateLimitConfig:
|
|
337
|
+
"""Convert rate limit configuration."""
|
|
338
|
+
rate_limit_config = self._get_rate_limit_config(config)
|
|
339
|
+
|
|
340
|
+
return RateLimitConfig(
|
|
341
|
+
enabled=rate_limit_config.get("enabled", False),
|
|
342
|
+
requests_per_minute=rate_limit_config.get("requests_per_minute", 60),
|
|
343
|
+
requests_per_hour=rate_limit_config.get("requests_per_hour", 1000),
|
|
344
|
+
requests_per_day=rate_limit_config.get("requests_per_day", 10000),
|
|
345
|
+
burst_limit=rate_limit_config.get("burst_limit", 10),
|
|
346
|
+
by_ip=rate_limit_config.get("by_ip", True),
|
|
347
|
+
by_user=rate_limit_config.get("by_user", True),
|
|
348
|
+
by_endpoint=rate_limit_config.get("by_endpoint", False),
|
|
349
|
+
exempt_roles=rate_limit_config.get("exempt_roles", []),
|
|
350
|
+
exempt_endpoints=rate_limit_config.get("exempt_endpoints", []),
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
def _get_auth_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
|
|
354
|
+
"""Get authentication configuration from config."""
|
|
355
|
+
security_config = config.get("security", {})
|
|
356
|
+
|
|
357
|
+
# Ensure security_config is a dictionary
|
|
358
|
+
if not isinstance(security_config, dict):
|
|
359
|
+
return {}
|
|
360
|
+
|
|
361
|
+
auth_config = security_config.get("auth", {})
|
|
362
|
+
|
|
363
|
+
# Handle legacy auth_enabled flag
|
|
364
|
+
if config.get("auth_enabled") is not None:
|
|
365
|
+
auth_config["enabled"] = config["auth_enabled"]
|
|
366
|
+
|
|
367
|
+
return auth_config
|
|
368
|
+
|
|
369
|
+
def _get_ssl_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
|
|
370
|
+
"""Get SSL configuration from config."""
|
|
371
|
+
security_config = config.get("security", {})
|
|
372
|
+
|
|
373
|
+
# Ensure security_config is a dictionary
|
|
374
|
+
if not isinstance(security_config, dict):
|
|
375
|
+
return {}
|
|
376
|
+
|
|
377
|
+
ssl_config = security_config.get("ssl", {})
|
|
378
|
+
|
|
379
|
+
# Debug: Check SSL config before merging
|
|
380
|
+
print(
|
|
381
|
+
f"🔍 Debug: _get_ssl_config: security.ssl key_file={ssl_config.get('key_file')}"
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
# Merge with legacy SSL config, but prioritize security.ssl over legacy ssl
|
|
385
|
+
legacy_ssl = config.get("ssl", {})
|
|
386
|
+
if legacy_ssl:
|
|
387
|
+
print(
|
|
388
|
+
f"🔍 Debug: _get_ssl_config: legacy ssl key_file={legacy_ssl.get('key_file')}"
|
|
389
|
+
)
|
|
390
|
+
# Only merge legacy config if security.ssl is not enabled or missing
|
|
391
|
+
if not ssl_config.get("enabled", False):
|
|
392
|
+
ssl_config.update(legacy_ssl)
|
|
393
|
+
else:
|
|
394
|
+
# If security.ssl is enabled, only merge non-conflicting fields
|
|
395
|
+
for key, value in legacy_ssl.items():
|
|
396
|
+
if key not in ssl_config or ssl_config[key] is None:
|
|
397
|
+
ssl_config[key] = value
|
|
398
|
+
|
|
399
|
+
print(f"🔍 Debug: _get_ssl_config: final key_file={ssl_config.get('key_file')}")
|
|
400
|
+
return ssl_config
|
|
401
|
+
|
|
402
|
+
def _get_permissions_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
|
|
403
|
+
"""Get permissions configuration from config."""
|
|
404
|
+
security_config = config.get("security", {})
|
|
405
|
+
|
|
406
|
+
# Ensure security_config is a dictionary
|
|
407
|
+
if not isinstance(security_config, dict):
|
|
408
|
+
return {}
|
|
409
|
+
|
|
410
|
+
permissions_config = security_config.get("permissions", {})
|
|
411
|
+
|
|
412
|
+
# Handle security.roles - convert to role_mappings format
|
|
413
|
+
security_roles = security_config.get("roles", {})
|
|
414
|
+
if security_roles:
|
|
415
|
+
# Convert roles format to role_mappings
|
|
416
|
+
# If roles is a dict where values are lists (token -> roles mapping)
|
|
417
|
+
# or dict where values are objects with permissions field
|
|
418
|
+
role_mappings = {}
|
|
419
|
+
for role_name, role_value in security_roles.items():
|
|
420
|
+
if isinstance(role_value, list):
|
|
421
|
+
# Format: "role_name": ["permission1", "permission2"]
|
|
422
|
+
role_mappings[role_name] = role_value
|
|
423
|
+
elif isinstance(role_value, dict):
|
|
424
|
+
# Format: "role_name": {"permissions": [...], "description": "..."}
|
|
425
|
+
# Extract permissions field
|
|
426
|
+
if "permissions" in role_value:
|
|
427
|
+
role_mappings[role_name] = role_value["permissions"]
|
|
428
|
+
else:
|
|
429
|
+
# If no permissions field, use empty list
|
|
430
|
+
role_mappings[role_name] = []
|
|
431
|
+
else:
|
|
432
|
+
# Single string permission
|
|
433
|
+
role_mappings[role_name] = [role_value] if isinstance(role_value, str) else []
|
|
434
|
+
|
|
435
|
+
# Set role_mappings in permissions_config
|
|
436
|
+
if role_mappings:
|
|
437
|
+
permissions_config["role_mappings"] = role_mappings
|
|
438
|
+
|
|
439
|
+
# Merge with legacy roles config (from root level)
|
|
440
|
+
legacy_roles = config.get("roles", {})
|
|
441
|
+
if legacy_roles and isinstance(legacy_roles, dict):
|
|
442
|
+
# Convert legacy roles format if needed
|
|
443
|
+
if "role_mappings" not in permissions_config:
|
|
444
|
+
permissions_config["role_mappings"] = {}
|
|
445
|
+
for role_name, role_value in legacy_roles.items():
|
|
446
|
+
if isinstance(role_value, list):
|
|
447
|
+
permissions_config["role_mappings"][role_name] = role_value
|
|
448
|
+
elif isinstance(role_value, dict) and "permissions" in role_value:
|
|
449
|
+
permissions_config["role_mappings"][role_name] = role_value["permissions"]
|
|
450
|
+
|
|
451
|
+
return permissions_config
|
|
452
|
+
|
|
453
|
+
def _get_rate_limit_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
|
|
454
|
+
"""Get rate limit configuration from config."""
|
|
455
|
+
security_config = config.get("security", {})
|
|
456
|
+
|
|
457
|
+
# Ensure security_config is a dictionary
|
|
458
|
+
if not isinstance(security_config, dict):
|
|
459
|
+
return {}
|
|
460
|
+
|
|
461
|
+
rate_limit_config = security_config.get("rate_limit", {})
|
|
462
|
+
|
|
463
|
+
# Handle legacy rate_limit_enabled flag
|
|
464
|
+
if config.get("rate_limit_enabled") is not None:
|
|
465
|
+
rate_limit_config["enabled"] = config["rate_limit_enabled"]
|
|
466
|
+
|
|
467
|
+
return rate_limit_config
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module with utility functions for the microservice.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import socket
|
|
9
|
+
import sys
|
|
10
|
+
import time
|
|
11
|
+
import uuid
|
|
12
|
+
from datetime import datetime, timezone
|
|
13
|
+
from typing import Optional, Dict, Any, List
|
|
14
|
+
|
|
15
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def check_port_availability(host: str, port: int, timeout: float = 1.0) -> bool:
|
|
35
|
+
"""
|
|
36
|
+
Checks if a port is available for binding.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
host: Host address to check
|
|
40
|
+
port: Port number to check
|
|
41
|
+
timeout: Connection timeout in seconds
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
True if port is available, False if port is in use
|
|
45
|
+
"""
|
|
46
|
+
try:
|
|
47
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
|
48
|
+
sock.settimeout(timeout)
|
|
49
|
+
result = sock.connect_ex((host, port))
|
|
50
|
+
return result != 0 # True if connection failed (port is free)
|
|
51
|
+
except Exception as e:
|
|
52
|
+
get_global_logger().warning(f"Error checking port {port} on {host}: {e}")
|
|
53
|
+
return True # Assume port is available if check fails
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def find_available_port(host: str, start_port: int, max_attempts: int = 100) -> Optional[int]:
|
|
57
|
+
"""
|
|
58
|
+
Finds an available port starting from the specified port.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
host: Host address to check
|
|
62
|
+
start_port: Starting port number
|
|
63
|
+
max_attempts: Maximum number of ports to check
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Available port number or None if no port found
|
|
67
|
+
"""
|
|
68
|
+
for port in range(start_port, start_port + max_attempts):
|
|
69
|
+
if check_port_availability(host, port):
|
|
70
|
+
return port
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def get_port_usage_info(port: int) -> str:
|
|
75
|
+
"""
|
|
76
|
+
Gets information about what process is using a port.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
port: Port number to check
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
String with port usage information
|
|
83
|
+
"""
|
|
84
|
+
try:
|
|
85
|
+
import subprocess
|
|
86
|
+
result = subprocess.run(
|
|
87
|
+
["lsof", "-i", f":{port}"],
|
|
88
|
+
capture_output=True,
|
|
89
|
+
text=True,
|
|
90
|
+
timeout=5
|
|
91
|
+
)
|
|
92
|
+
if result.returncode == 0 and result.stdout.strip():
|
|
93
|
+
return f"Port {port} is used by:\n{result.stdout.strip()}"
|
|
94
|
+
else:
|
|
95
|
+
return f"Port {port} appears to be in use but process info unavailable"
|
|
96
|
+
except Exception as e:
|
|
97
|
+
return f"Port {port} is in use (unable to get process info: {e})"
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Configuration validation package for MCP Proxy Adapter.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .config_validator import ConfigValidator
|
|
9
|
+
from .validation_result import ValidationResult, ValidationLevel
|
|
10
|
+
from .file_validator import FileValidator
|
|
11
|
+
from .security_validator import SecurityValidator
|
|
12
|
+
from .protocol_validator import ProtocolValidator
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"ConfigValidator",
|
|
16
|
+
"ValidationResult",
|
|
17
|
+
"ValidationLevel",
|
|
18
|
+
"FileValidator",
|
|
19
|
+
"SecurityValidator",
|
|
20
|
+
"ProtocolValidator",
|
|
21
|
+
]
|