mcp-proxy-adapter 6.9.27__py3-none-any.whl โ 6.9.29__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 +10 -0
- mcp_proxy_adapter/__main__.py +8 -21
- mcp_proxy_adapter/api/app.py +10 -913
- mcp_proxy_adapter/api/core/__init__.py +18 -0
- mcp_proxy_adapter/api/core/app_factory.py +243 -0
- mcp_proxy_adapter/api/core/lifespan_manager.py +55 -0
- mcp_proxy_adapter/api/core/registration_manager.py +166 -0
- mcp_proxy_adapter/api/core/ssl_context_factory.py +88 -0
- mcp_proxy_adapter/api/handlers.py +78 -199
- mcp_proxy_adapter/api/middleware/__init__.py +1 -44
- mcp_proxy_adapter/api/middleware/base.py +0 -42
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +0 -85
- mcp_proxy_adapter/api/middleware/error_handling.py +1 -127
- mcp_proxy_adapter/api/middleware/factory.py +0 -94
- mcp_proxy_adapter/api/middleware/logging.py +0 -112
- mcp_proxy_adapter/api/middleware/performance.py +0 -35
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +2 -98
- mcp_proxy_adapter/api/middleware/transport_middleware.py +0 -37
- mcp_proxy_adapter/api/middleware/unified_security.py +10 -10
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +0 -118
- 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 +0 -61
- mcp_proxy_adapter/api/tool_integration.py +0 -117
- mcp_proxy_adapter/api/tools.py +0 -46
- 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 +21 -0
- mcp_proxy_adapter/cli/commands/config_validate.py +36 -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 +324 -0
- mcp_proxy_adapter/cli/validators.py +231 -0
- mcp_proxy_adapter/client/jsonrpc_client.py +406 -0
- mcp_proxy_adapter/client/proxy.py +45 -0
- mcp_proxy_adapter/commands/__init__.py +44 -28
- mcp_proxy_adapter/commands/auth_validation_command.py +7 -344
- mcp_proxy_adapter/commands/base.py +19 -43
- mcp_proxy_adapter/commands/builtin_commands.py +0 -75
- 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 +58 -928
- mcp_proxy_adapter/commands/cert_monitor_command.py +0 -88
- mcp_proxy_adapter/commands/certificate_management_command.py +0 -45
- mcp_proxy_adapter/commands/command_registry.py +172 -904
- mcp_proxy_adapter/commands/config_command.py +0 -28
- mcp_proxy_adapter/commands/dependency_container.py +1 -70
- mcp_proxy_adapter/commands/dependency_manager.py +0 -128
- mcp_proxy_adapter/commands/echo_command.py +0 -34
- mcp_proxy_adapter/commands/health_command.py +0 -3
- mcp_proxy_adapter/commands/help_command.py +0 -159
- mcp_proxy_adapter/commands/hooks.py +0 -137
- mcp_proxy_adapter/commands/key_management_command.py +0 -25
- mcp_proxy_adapter/commands/load_command.py +7 -78
- mcp_proxy_adapter/commands/plugins_command.py +0 -16
- mcp_proxy_adapter/commands/protocol_management_command.py +0 -28
- mcp_proxy_adapter/commands/proxy_registration_command.py +0 -88
- mcp_proxy_adapter/commands/queue_commands.py +750 -0
- mcp_proxy_adapter/commands/registration_status_command.py +0 -43
- 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 +0 -80
- mcp_proxy_adapter/commands/result.py +25 -77
- mcp_proxy_adapter/commands/role_test_command.py +0 -44
- mcp_proxy_adapter/commands/roles_management_command.py +0 -199
- mcp_proxy_adapter/commands/security_command.py +0 -30
- mcp_proxy_adapter/commands/settings_command.py +0 -68
- mcp_proxy_adapter/commands/ssl_setup_command.py +0 -42
- mcp_proxy_adapter/commands/token_management_command.py +0 -1
- mcp_proxy_adapter/commands/transport_management_command.py +0 -20
- mcp_proxy_adapter/commands/unload_command.py +0 -71
- mcp_proxy_adapter/config.py +15 -626
- mcp_proxy_adapter/core/__init__.py +5 -39
- mcp_proxy_adapter/core/app_factory.py +14 -36
- mcp_proxy_adapter/core/app_runner.py +0 -27
- mcp_proxy_adapter/core/auth_validator.py +1 -93
- mcp_proxy_adapter/core/certificate/__init__.py +20 -0
- mcp_proxy_adapter/core/certificate/certificate_creator.py +371 -0
- mcp_proxy_adapter/core/certificate/certificate_extractor.py +183 -0
- mcp_proxy_adapter/core/certificate/certificate_utils.py +249 -0
- mcp_proxy_adapter/core/certificate/certificate_validator.py +110 -0
- mcp_proxy_adapter/core/certificate/ssl_context_manager.py +70 -0
- mcp_proxy_adapter/core/certificate_utils.py +64 -903
- mcp_proxy_adapter/core/client.py +0 -6
- mcp_proxy_adapter/core/client_manager.py +0 -19
- mcp_proxy_adapter/core/client_security.py +0 -2
- mcp_proxy_adapter/core/config/__init__.py +18 -0
- mcp_proxy_adapter/core/config/config.py +195 -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 +112 -0
- mcp_proxy_adapter/core/config/simple_config_generator.py +50 -0
- mcp_proxy_adapter/core/config/simple_config_validator.py +96 -0
- mcp_proxy_adapter/core/config_converter.py +0 -186
- mcp_proxy_adapter/core/config_validator.py +96 -1238
- mcp_proxy_adapter/core/errors.py +7 -42
- mcp_proxy_adapter/core/job_manager.py +54 -0
- mcp_proxy_adapter/core/logging.py +2 -22
- mcp_proxy_adapter/core/mtls_asgi.py +0 -20
- mcp_proxy_adapter/core/mtls_asgi_app.py +0 -12
- mcp_proxy_adapter/core/mtls_proxy.py +0 -80
- mcp_proxy_adapter/core/mtls_server.py +3 -173
- mcp_proxy_adapter/core/protocol_manager.py +1 -191
- mcp_proxy_adapter/core/proxy/__init__.py +22 -0
- mcp_proxy_adapter/core/proxy/auth_manager.py +27 -0
- mcp_proxy_adapter/core/proxy/proxy_registration_manager.py +137 -0
- mcp_proxy_adapter/core/proxy/registration_client.py +60 -0
- mcp_proxy_adapter/core/proxy/ssl_manager.py +101 -0
- mcp_proxy_adapter/core/proxy_client.py +0 -1
- mcp_proxy_adapter/core/proxy_registration.py +36 -912
- mcp_proxy_adapter/core/role_utils.py +0 -308
- mcp_proxy_adapter/core/security_adapter.py +1 -36
- mcp_proxy_adapter/core/security_factory.py +1 -150
- mcp_proxy_adapter/core/security_integration.py +0 -33
- mcp_proxy_adapter/core/server_adapter.py +1 -40
- mcp_proxy_adapter/core/server_engine.py +2 -173
- mcp_proxy_adapter/core/settings.py +0 -127
- mcp_proxy_adapter/core/signal_handler.py +0 -65
- mcp_proxy_adapter/core/ssl_utils.py +19 -137
- mcp_proxy_adapter/core/transport_manager.py +0 -151
- mcp_proxy_adapter/core/unified_config_adapter.py +1 -193
- mcp_proxy_adapter/core/utils.py +1 -182
- mcp_proxy_adapter/core/validation/__init__.py +21 -0
- mcp_proxy_adapter/core/validation/config_validator.py +211 -0
- mcp_proxy_adapter/core/validation/file_validator.py +73 -0
- mcp_proxy_adapter/core/validation/protocol_validator.py +191 -0
- mcp_proxy_adapter/core/validation/security_validator.py +58 -0
- mcp_proxy_adapter/core/validation/validation_result.py +27 -0
- mcp_proxy_adapter/custom_openapi.py +33 -652
- mcp_proxy_adapter/examples/bugfix_certificate_config.py +0 -23
- mcp_proxy_adapter/examples/check_config.py +0 -2
- mcp_proxy_adapter/examples/client_usage_example.py +164 -0
- mcp_proxy_adapter/examples/config_builder.py +13 -2
- mcp_proxy_adapter/examples/config_cli.py +0 -1
- mcp_proxy_adapter/examples/create_test_configs.py +0 -46
- mcp_proxy_adapter/examples/debug_request_state.py +0 -1
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +0 -47
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +0 -45
- mcp_proxy_adapter/examples/full_application/commands/echo_command.py +0 -12
- mcp_proxy_adapter/examples/full_application/commands/help_command.py +0 -12
- mcp_proxy_adapter/examples/full_application/commands/list_command.py +0 -7
- mcp_proxy_adapter/examples/full_application/hooks/__init__.py +0 -2
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +0 -59
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +0 -54
- mcp_proxy_adapter/examples/full_application/main.py +186 -150
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +0 -107
- mcp_proxy_adapter/examples/full_application/test_minimal_server.py +0 -24
- mcp_proxy_adapter/examples/full_application/test_server.py +0 -58
- mcp_proxy_adapter/examples/generate_config.py +65 -11
- 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 +0 -2
- mcp_proxy_adapter/examples/run_full_test_suite.py +0 -29
- mcp_proxy_adapter/examples/run_proxy_server.py +31 -71
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +0 -27
- 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 +24 -1075
- 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 +133 -1425
- mcp_proxy_adapter/examples/test_config.py +0 -3
- mcp_proxy_adapter/examples/test_config_builder.py +25 -405
- mcp_proxy_adapter/examples/test_examples.py +0 -1
- mcp_proxy_adapter/examples/test_framework_complete.py +0 -2
- mcp_proxy_adapter/examples/test_mcp_server.py +0 -1
- mcp_proxy_adapter/examples/test_protocol_examples.py +0 -1
- mcp_proxy_adapter/examples/universal_client.py +0 -6
- mcp_proxy_adapter/examples/update_config_certificates.py +0 -1
- mcp_proxy_adapter/examples/validate_generator_compatibility.py +0 -1
- mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +0 -187
- mcp_proxy_adapter/integrations/__init__.py +25 -0
- mcp_proxy_adapter/integrations/queuemgr_integration.py +462 -0
- mcp_proxy_adapter/main.py +70 -62
- mcp_proxy_adapter/openapi.py +0 -22
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-6.9.27.dist-info โ mcp_proxy_adapter-6.9.29.dist-info}/METADATA +2 -1
- mcp_proxy_adapter-6.9.29.dist-info/RECORD +235 -0
- {mcp_proxy_adapter-6.9.27.dist-info โ mcp_proxy_adapter-6.9.29.dist-info}/entry_points.txt +1 -1
- mcp_proxy_adapter-6.9.27.dist-info/RECORD +0 -149
- {mcp_proxy_adapter-6.9.27.dist-info โ mcp_proxy_adapter-6.9.29.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.9.27.dist-info โ mcp_proxy_adapter-6.9.29.dist-info}/top_level.txt +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"""
|
|
3
3
|
Author: Vasiliy Zdanovskiy
|
|
4
4
|
email: vasilyvz@gmail.com
|
|
5
|
+
|
|
5
6
|
Enhanced script for setting up test environment for MCP Proxy Adapter.
|
|
6
7
|
Prepares the test environment with all necessary files, directories, and configurations.
|
|
7
8
|
Includes comprehensive documentation and validation for configuration settings.
|
|
@@ -19,18 +20,12 @@ Features:
|
|
|
19
20
|
- Enhanced error handling and troubleshooting
|
|
20
21
|
"""
|
|
21
22
|
import os
|
|
22
|
-
import shutil
|
|
23
|
-
import subprocess
|
|
24
23
|
import sys
|
|
25
24
|
import argparse
|
|
26
|
-
import json
|
|
27
25
|
from pathlib import Path
|
|
28
|
-
from typing import Dict, List, Any, Tuple
|
|
29
26
|
|
|
30
27
|
# Import mcp_security_framework
|
|
31
28
|
try:
|
|
32
|
-
from mcp_security_framework.core.cert_manager import CertificateManager
|
|
33
|
-
from mcp_security_framework.schemas.config import (
|
|
34
29
|
CertificateConfig,
|
|
35
30
|
CAConfig,
|
|
36
31
|
ServerCertConfig,
|
|
@@ -42,1486 +37,199 @@ except ImportError:
|
|
|
42
37
|
SECURITY_FRAMEWORK_AVAILABLE = False
|
|
43
38
|
print("Warning: mcp_security_framework not available")
|
|
44
39
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def validate_config(
|
|
57
|
-
self, config: Dict[str, Any], config_name: str
|
|
58
|
-
) -> Tuple[bool, List[str], List[str]]:
|
|
59
|
-
"""
|
|
60
|
-
Validate a configuration for mutually exclusive settings and protocol compatibility.
|
|
61
|
-
|
|
62
|
-
Args:
|
|
63
|
-
config: Configuration dictionary to validate
|
|
64
|
-
config_name: Name of the configuration for error reporting
|
|
65
|
-
|
|
66
|
-
Returns:
|
|
67
|
-
Tuple of (is_valid, errors, warnings)
|
|
68
|
-
"""
|
|
69
|
-
self.errors = []
|
|
70
|
-
self.warnings = []
|
|
71
|
-
|
|
72
|
-
# Validate protocol settings
|
|
73
|
-
self._validate_protocol_settings(config, config_name)
|
|
74
|
-
|
|
75
|
-
# Validate SSL/TLS settings
|
|
76
|
-
self._validate_ssl_settings(config, config_name)
|
|
77
|
-
|
|
78
|
-
# Validate mTLS settings
|
|
79
|
-
self._validate_mtls_settings(config, config_name)
|
|
80
|
-
|
|
81
|
-
# Validate authentication settings
|
|
82
|
-
self._validate_auth_settings(config, config_name)
|
|
83
|
-
|
|
84
|
-
return len(self.errors) == 0, self.errors, self.warnings
|
|
85
|
-
|
|
86
|
-
def _validate_protocol_settings(
|
|
87
|
-
self, config: Dict[str, Any], config_name: str
|
|
88
|
-
) -> None:
|
|
89
|
-
"""Validate protocol configuration settings."""
|
|
90
|
-
protocols = config.get("protocols", {})
|
|
91
|
-
|
|
92
|
-
if not protocols.get("enabled", False):
|
|
93
|
-
self.warnings.append(
|
|
94
|
-
f"โ ๏ธ {config_name}: Protocol middleware is disabled - all protocols will be allowed"
|
|
95
|
-
)
|
|
96
|
-
return
|
|
97
|
-
|
|
98
|
-
allowed_protocols = protocols.get("allowed_protocols", [])
|
|
99
|
-
if not allowed_protocols:
|
|
100
|
-
self.errors.append(
|
|
101
|
-
f"โ {config_name}: No allowed protocols specified when protocol middleware is enabled"
|
|
102
|
-
)
|
|
103
|
-
return
|
|
104
|
-
|
|
105
|
-
# Check for invalid protocol combinations
|
|
106
|
-
if "http" in allowed_protocols and "https" in allowed_protocols:
|
|
107
|
-
self.warnings.append(
|
|
108
|
-
f"โ ๏ธ {config_name}: Both HTTP and HTTPS protocols are allowed - consider security implications"
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
if "mtls" in allowed_protocols and "http" in allowed_protocols:
|
|
112
|
-
self.errors.append(
|
|
113
|
-
f"โ {config_name}: mTLS and HTTP protocols are mutually exclusive - mTLS requires HTTPS"
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
def _validate_ssl_settings(self, config: Dict[str, Any], config_name: str) -> None:
|
|
117
|
-
"""Validate SSL/TLS configuration settings."""
|
|
118
|
-
security = config.get("security", {})
|
|
119
|
-
ssl = security.get("ssl", {})
|
|
120
|
-
|
|
121
|
-
if not ssl.get("enabled", False):
|
|
122
|
-
return
|
|
123
|
-
|
|
124
|
-
# Check certificate file requirements
|
|
125
|
-
cert_file = ssl.get("server_cert_file")
|
|
126
|
-
key_file = ssl.get("server_key_file")
|
|
127
|
-
|
|
128
|
-
if not cert_file or not key_file:
|
|
129
|
-
self.errors.append(
|
|
130
|
-
f"โ {config_name}: SSL enabled but server certificate or key file not specified"
|
|
131
|
-
)
|
|
132
|
-
|
|
133
|
-
# Check CA certificate requirements
|
|
134
|
-
ca_cert_file = ssl.get("ca_cert_file")
|
|
135
|
-
verify_server = ssl.get("verify_server", True)
|
|
136
|
-
|
|
137
|
-
if verify_server and not ca_cert_file:
|
|
138
|
-
self.warnings.append(
|
|
139
|
-
f"โ ๏ธ {config_name}: Server verification enabled but no CA certificate specified"
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
def _validate_mtls_settings(self, config: Dict[str, Any], config_name: str) -> None:
|
|
143
|
-
"""Validate mTLS configuration settings."""
|
|
144
|
-
security = config.get("security", {})
|
|
145
|
-
ssl = security.get("ssl", {})
|
|
146
|
-
|
|
147
|
-
if not ssl.get("enabled", False):
|
|
148
|
-
return
|
|
149
|
-
|
|
150
|
-
# Check if mTLS is configured
|
|
151
|
-
client_cert_file = ssl.get("client_cert_file")
|
|
152
|
-
client_key_file = ssl.get("client_key_file")
|
|
153
|
-
verify_client = ssl.get("verify_client", False)
|
|
154
|
-
|
|
155
|
-
if verify_client and (not client_cert_file or not client_key_file):
|
|
156
|
-
self.errors.append(
|
|
157
|
-
f"โ {config_name}: Client verification enabled but client certificate or key file not specified"
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
# Check protocol compatibility
|
|
161
|
-
protocols = config.get("protocols", {})
|
|
162
|
-
if protocols.get("enabled", False):
|
|
163
|
-
allowed_protocols = protocols.get("allowed_protocols", [])
|
|
164
|
-
if verify_client and "mtls" not in allowed_protocols:
|
|
165
|
-
self.warnings.append(
|
|
166
|
-
f"โ ๏ธ {config_name}: Client verification enabled but 'mtls' not in allowed protocols"
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
def _validate_auth_settings(self, config: Dict[str, Any], config_name: str) -> None:
|
|
170
|
-
"""Validate authentication configuration settings."""
|
|
171
|
-
security = config.get("security", {})
|
|
172
|
-
auth = security.get("auth", {})
|
|
173
|
-
|
|
174
|
-
if not auth.get("enabled", False):
|
|
175
|
-
return
|
|
176
|
-
|
|
177
|
-
# Check token requirements
|
|
178
|
-
token_required = auth.get("token_required", False)
|
|
179
|
-
if token_required and not auth.get("token_secret"):
|
|
180
|
-
self.errors.append(
|
|
181
|
-
f"โ {config_name}: Token authentication enabled but no token secret specified"
|
|
182
|
-
)
|
|
40
|
+
# Import setup modules
|
|
41
|
+
ConfigurationValidator,
|
|
42
|
+
create_test_files,
|
|
43
|
+
create_configuration_documentation,
|
|
44
|
+
generate_enhanced_configurations,
|
|
45
|
+
generate_certificates_with_framework,
|
|
46
|
+
test_proxy_registration,
|
|
47
|
+
run_full_test_suite,
|
|
48
|
+
setup_test_environment,
|
|
49
|
+
)
|
|
183
50
|
|
|
184
51
|
|
|
185
52
|
def _get_package_paths() -> tuple[Path, Path]:
|
|
186
53
|
"""
|
|
187
|
-
|
|
188
|
-
to avoid importing the package during setup.
|
|
189
|
-
"""
|
|
190
|
-
# When running from installed package, __file__ points to .venv/lib/python3.x/site-packages/mcp_proxy_adapter/examples/setup_test_environment.py
|
|
191
|
-
# We need to go up to the package root: .venv/lib/python3.x/site-packages/mcp_proxy_adapter/
|
|
192
|
-
pkg_root = Path(__file__).resolve().parents[1]
|
|
193
|
-
examples_path = pkg_root / "examples"
|
|
194
|
-
utils_path = (
|
|
195
|
-
pkg_root / "examples" / "scripts"
|
|
196
|
-
) # utils scripts are in examples/scripts in the package
|
|
197
|
-
|
|
198
|
-
return examples_path, utils_path
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
def create_configuration_documentation(output_dir: Path) -> None:
|
|
202
|
-
"""
|
|
203
|
-
Create comprehensive documentation for MCP Proxy Adapter configurations.
|
|
204
|
-
"""
|
|
205
|
-
docs_dir = output_dir / "docs"
|
|
206
|
-
docs_dir.mkdir(parents=True, exist_ok=True)
|
|
207
|
-
|
|
208
|
-
# Create main configuration guide
|
|
209
|
-
config_guide = docs_dir / "CONFIGURATION_GUIDE.md"
|
|
210
|
-
with open(config_guide, "w", encoding="utf-8") as f:
|
|
211
|
-
f.write(
|
|
212
|
-
"""# MCP Proxy Adapter Configuration Guide
|
|
213
|
-
|
|
214
|
-
## Overview
|
|
215
|
-
|
|
216
|
-
This guide explains how to configure MCP Proxy Adapter for different deployment scenarios,
|
|
217
|
-
including HTTP, HTTPS, and mTLS configurations.
|
|
218
|
-
|
|
219
|
-
## Configuration Structure
|
|
220
|
-
|
|
221
|
-
```json
|
|
222
|
-
{
|
|
223
|
-
"server": {
|
|
224
|
-
"host": "0.0.0.0",
|
|
225
|
-
"port": 8000
|
|
226
|
-
},
|
|
227
|
-
"protocols": {
|
|
228
|
-
"enabled": true,
|
|
229
|
-
"allowed_protocols": ["https", "mtls"]
|
|
230
|
-
},
|
|
231
|
-
"security": {
|
|
232
|
-
"ssl": {
|
|
233
|
-
"enabled": true,
|
|
234
|
-
"server_cert_file": "path/to/server.crt",
|
|
235
|
-
"server_key_file": "path/to/server.key",
|
|
236
|
-
"ca_cert_file": "path/to/ca.crt",
|
|
237
|
-
"client_cert_file": "path/to/client.crt",
|
|
238
|
-
"client_key_file": "path/to/client.key",
|
|
239
|
-
"verify_server": true,
|
|
240
|
-
"verify_client": false,
|
|
241
|
-
"min_tls_version": "TLSv1.2"
|
|
242
|
-
},
|
|
243
|
-
"auth": {
|
|
244
|
-
"enabled": false,
|
|
245
|
-
"token_required": false,
|
|
246
|
-
"token_secret": "your-secret-key"
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
## Protocol Configuration
|
|
253
|
-
|
|
254
|
-
### Protocol Middleware
|
|
255
|
-
|
|
256
|
-
The `protocols` section controls which protocols are allowed:
|
|
257
|
-
|
|
258
|
-
- `enabled: true` - Protocol validation is active
|
|
259
|
-
- `enabled: false` - All protocols are allowed (bypasses validation)
|
|
260
|
-
|
|
261
|
-
### Allowed Protocols
|
|
262
|
-
|
|
263
|
-
- `"http"` - Plain HTTP (insecure)
|
|
264
|
-
- `"https"` - HTTPS with server certificate
|
|
265
|
-
- `"mtls"` - Mutual TLS (client and server certificates)
|
|
266
|
-
|
|
267
|
-
### Protocol Combinations
|
|
268
|
-
|
|
269
|
-
**Valid combinations:**
|
|
270
|
-
- `["https"]` - HTTPS only
|
|
271
|
-
- `["https", "mtls"]` - HTTPS and mTLS
|
|
272
|
-
- `["http"]` - HTTP only (not recommended for production)
|
|
273
|
-
|
|
274
|
-
**Invalid combinations:**
|
|
275
|
-
- `["http", "mtls"]` - HTTP and mTLS are mutually exclusive
|
|
276
|
-
- `["http", "https"]` - Mixed HTTP/HTTPS (security risk)
|
|
277
|
-
|
|
278
|
-
## SSL/TLS Configuration
|
|
279
|
-
|
|
280
|
-
### Server Certificates
|
|
281
|
-
|
|
282
|
-
Required for HTTPS and mTLS:
|
|
283
|
-
- `server_cert_file` - Server certificate file
|
|
284
|
-
- `server_key_file` - Server private key file
|
|
285
|
-
|
|
286
|
-
### CA Certificates
|
|
287
|
-
|
|
288
|
-
- `ca_cert_file` - CA certificate for server verification
|
|
289
|
-
- `verify_server: true` - Verify server certificate against CA
|
|
290
|
-
- `verify_server: false` - Disable server certificate verification
|
|
291
|
-
|
|
292
|
-
### Client Certificates (mTLS)
|
|
293
|
-
|
|
294
|
-
- `client_cert_file` - Client certificate file
|
|
295
|
-
- `client_key_file` - Client private key file
|
|
296
|
-
- `verify_client: true` - Require client certificates
|
|
297
|
-
- `verify_client: false` - No client certificate required
|
|
298
|
-
|
|
299
|
-
## Authentication
|
|
300
|
-
|
|
301
|
-
### Token Authentication
|
|
302
|
-
|
|
303
|
-
- `auth.enabled: true` - Enable authentication
|
|
304
|
-
- `token_required: true` - Require authentication tokens
|
|
305
|
-
- `token_secret` - Secret key for token validation
|
|
306
|
-
|
|
307
|
-
## Common Configuration Patterns
|
|
308
|
-
|
|
309
|
-
### 1. Development (HTTP)
|
|
310
|
-
```json
|
|
311
|
-
{
|
|
312
|
-
"protocols": {"enabled": false},
|
|
313
|
-
"security": {"ssl": {"enabled": false}}
|
|
314
|
-
}
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
### 2. Production HTTPS
|
|
318
|
-
```json
|
|
319
|
-
{
|
|
320
|
-
"protocols": {
|
|
321
|
-
"enabled": true,
|
|
322
|
-
"allowed_protocols": ["https"]
|
|
323
|
-
},
|
|
324
|
-
"security": {
|
|
325
|
-
"ssl": {
|
|
326
|
-
"enabled": true,
|
|
327
|
-
"server_cert_file": "certs/server.crt",
|
|
328
|
-
"server_key_file": "keys/server.key",
|
|
329
|
-
"verify_server": true,
|
|
330
|
-
"ca_cert_file": "certs/ca.crt"
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
### 3. mTLS with Client Verification
|
|
337
|
-
```json
|
|
338
|
-
{
|
|
339
|
-
"protocols": {
|
|
340
|
-
"enabled": true,
|
|
341
|
-
"allowed_protocols": ["https", "mtls"]
|
|
342
|
-
},
|
|
343
|
-
"security": {
|
|
344
|
-
"ssl": {
|
|
345
|
-
"enabled": true,
|
|
346
|
-
"server_cert_file": "certs/server.crt",
|
|
347
|
-
"server_key_file": "keys/server.key",
|
|
348
|
-
"ca_cert_file": "certs/ca.crt",
|
|
349
|
-
"client_cert_file": "certs/client.crt",
|
|
350
|
-
"client_key_file": "keys/client.key",
|
|
351
|
-
"verify_server": true,
|
|
352
|
-
"verify_client": true
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
## Validation Rules
|
|
359
|
-
|
|
360
|
-
The configuration validator checks for:
|
|
361
|
-
|
|
362
|
-
1. **Mutually Exclusive Settings:**
|
|
363
|
-
- HTTP and mTLS protocols
|
|
364
|
-
- Client verification without client certificates
|
|
365
|
-
- Server verification without CA certificates
|
|
366
|
-
|
|
367
|
-
2. **Required Dependencies:**
|
|
368
|
-
- SSL enabled requires server certificates
|
|
369
|
-
- mTLS requires both server and client certificates
|
|
370
|
-
- Protocol middleware enabled requires allowed protocols
|
|
371
|
-
|
|
372
|
-
3. **Security Warnings:**
|
|
373
|
-
- HTTP and HTTPS in same configuration
|
|
374
|
-
- Server verification without CA certificate
|
|
375
|
-
- Client verification without mTLS protocol
|
|
54
|
+
Get paths to the package and examples directory.
|
|
376
55
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
### Common Issues
|
|
380
|
-
|
|
381
|
-
1. **"Protocol not allowed" errors:**
|
|
382
|
-
- Check `protocols.allowed_protocols` includes required protocol
|
|
383
|
-
- Ensure `protocols.enabled: true` for validation
|
|
384
|
-
|
|
385
|
-
2. **SSL certificate errors:**
|
|
386
|
-
- Verify certificate file paths are correct
|
|
387
|
-
- Check certificate validity and format
|
|
388
|
-
- Ensure CA certificate matches server certificate
|
|
389
|
-
|
|
390
|
-
3. **mTLS connection failures:**
|
|
391
|
-
- Verify client certificates are valid
|
|
392
|
-
- Check `verify_client: true` is set
|
|
393
|
-
- Ensure "mtls" is in allowed protocols
|
|
394
|
-
|
|
395
|
-
### Debug Mode
|
|
396
|
-
|
|
397
|
-
Enable debug logging to troubleshoot issues:
|
|
398
|
-
|
|
399
|
-
```json
|
|
400
|
-
{
|
|
401
|
-
"logging": {
|
|
402
|
-
"level": "DEBUG",
|
|
403
|
-
"handlers": ["console", "file"]
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
```
|
|
407
|
-
"""
|
|
408
|
-
)
|
|
409
|
-
|
|
410
|
-
print(f"โ
Created configuration documentation: {config_guide}")
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
def create_test_files(output_dir: Path) -> None:
|
|
414
|
-
"""
|
|
415
|
-
Create additional test files for proxy registration testing.
|
|
56
|
+
Returns:
|
|
57
|
+
Tuple of (package_path, examples_path)
|
|
416
58
|
"""
|
|
417
|
-
#
|
|
418
|
-
|
|
419
|
-
with open(test_proxy_server, "w", encoding="utf-8") as f:
|
|
420
|
-
f.write(
|
|
421
|
-
'''#!/usr/bin/env python3
|
|
422
|
-
"""
|
|
423
|
-
Author: Vasiliy Zdanovskiy
|
|
424
|
-
email: vasilyvz@gmail.com
|
|
425
|
-
|
|
426
|
-
Simple mTLS proxy server for testing proxy registration SSL fix.
|
|
427
|
-
"""
|
|
428
|
-
import asyncio
|
|
429
|
-
import os
|
|
430
|
-
import ssl
|
|
431
|
-
from fastapi import FastAPI, Request
|
|
432
|
-
from fastapi.responses import JSONResponse
|
|
433
|
-
import hypercorn.asyncio
|
|
434
|
-
import hypercorn.config
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
app = FastAPI(title="Test mTLS Proxy Server", version="1.0.0")
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
@app.post("/register")
|
|
441
|
-
async def register_server(request: Request):
|
|
442
|
-
"""Register server endpoint."""
|
|
443
|
-
try:
|
|
444
|
-
data = await request.json()
|
|
445
|
-
print(f"โ
Received registration request: {data}")
|
|
446
|
-
|
|
447
|
-
# Check if client certificate is present
|
|
448
|
-
client_cert = request.client
|
|
449
|
-
if client_cert:
|
|
450
|
-
print(f"โ
Client certificate verified: {client_cert}")
|
|
451
|
-
|
|
452
|
-
return JSONResponse(
|
|
453
|
-
status_code=200,
|
|
454
|
-
content={
|
|
455
|
-
"success": True,
|
|
456
|
-
"server_key": f"{data.get('server_id', 'unknown')}_1",
|
|
457
|
-
"message": "Server registered successfully"
|
|
458
|
-
}
|
|
459
|
-
)
|
|
460
|
-
except Exception as e:
|
|
461
|
-
print(f"โ Registration error: {e}")
|
|
462
|
-
return JSONResponse(
|
|
463
|
-
status_code=500,
|
|
464
|
-
content={
|
|
465
|
-
"success": False,
|
|
466
|
-
"error": {
|
|
467
|
-
"message": str(e),
|
|
468
|
-
"code": "REGISTRATION_ERROR"
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
)
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
@app.post("/unregister")
|
|
475
|
-
async def unregister_server(request: Request):
|
|
476
|
-
"""Unregister server endpoint."""
|
|
477
|
-
try:
|
|
478
|
-
data = await request.json()
|
|
479
|
-
print(f"โ
Received unregistration request: {data}")
|
|
480
|
-
|
|
481
|
-
# Check if client certificate is present
|
|
482
|
-
client_cert = request.client
|
|
483
|
-
if client_cert:
|
|
484
|
-
print(f"โ
Client certificate verified for unregistration: {client_cert}")
|
|
485
|
-
|
|
486
|
-
return JSONResponse(
|
|
487
|
-
status_code=200,
|
|
488
|
-
content={
|
|
489
|
-
"success": True,
|
|
490
|
-
"message": "Server unregistered successfully"
|
|
491
|
-
}
|
|
492
|
-
)
|
|
493
|
-
except Exception as e:
|
|
494
|
-
print(f"โ Unregistration error: {e}")
|
|
495
|
-
return JSONResponse(
|
|
496
|
-
status_code=500,
|
|
497
|
-
content={
|
|
498
|
-
"success": False,
|
|
499
|
-
"error": {
|
|
500
|
-
"message": str(e),
|
|
501
|
-
"code": "UNREGISTRATION_ERROR"
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
)
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
@app.get("/health")
|
|
508
|
-
async def health_check():
|
|
509
|
-
"""Health check endpoint."""
|
|
510
|
-
return JSONResponse(
|
|
511
|
-
status_code=200,
|
|
512
|
-
content={
|
|
513
|
-
"status": "healthy",
|
|
514
|
-
"message": "mTLS Proxy Server is running"
|
|
515
|
-
}
|
|
516
|
-
)
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
async def main():
|
|
520
|
-
"""Run the mTLS proxy server."""
|
|
521
|
-
print("๐ Starting Test mTLS Proxy Server...")
|
|
522
|
-
print("๐ก Server URL: https://127.0.0.1:20005")
|
|
523
|
-
print("๐ mTLS enabled with client certificate verification")
|
|
524
|
-
print("๐ Available endpoints:")
|
|
525
|
-
print(" POST /register - Register server")
|
|
526
|
-
print(" POST /unregister - Unregister server")
|
|
527
|
-
print(" GET /health - Health check")
|
|
528
|
-
print("โก Press Ctrl+C to stop\\n")
|
|
529
|
-
|
|
530
|
-
# Configure Hypercorn
|
|
531
|
-
config = hypercorn.config.Config()
|
|
532
|
-
config.bind = ["127.0.0.1:20005"]
|
|
533
|
-
config.loglevel = "info"
|
|
59
|
+
# Get the directory containing this script
|
|
60
|
+
script_dir = Path(__file__).parent.absolute()
|
|
534
61
|
|
|
535
|
-
#
|
|
536
|
-
|
|
537
|
-
key_file = "keys/server_key.pem"
|
|
62
|
+
# Package path is the parent of examples
|
|
63
|
+
package_path = script_dir.parent
|
|
538
64
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
config.certfile = cert_file
|
|
542
|
-
config.keyfile = key_file
|
|
543
|
-
else:
|
|
544
|
-
print("โ ๏ธ SSL certificates not found, running without SSL")
|
|
545
|
-
# Run on HTTP instead of HTTPS
|
|
546
|
-
config.bind = ["127.0.0.1:20005"]
|
|
547
|
-
|
|
548
|
-
# Run server with Hypercorn
|
|
549
|
-
await hypercorn.asyncio.serve(app, config)
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
if __name__ == "__main__":
|
|
553
|
-
asyncio.run(main())
|
|
554
|
-
'''
|
|
555
|
-
)
|
|
556
|
-
|
|
557
|
-
# Make it executable
|
|
558
|
-
test_proxy_server.chmod(0o755)
|
|
559
|
-
print(f"โ
Created test proxy server: {test_proxy_server}")
|
|
560
|
-
|
|
561
|
-
# Create test script for proxy registration
|
|
562
|
-
test_script = output_dir / "test_proxy_registration.py"
|
|
563
|
-
with open(test_script, "w", encoding="utf-8") as f:
|
|
564
|
-
f.write(
|
|
565
|
-
'''#!/usr/bin/env python3
|
|
566
|
-
"""
|
|
567
|
-
Author: Vasiliy Zdanovskiy
|
|
568
|
-
email: vasilyvz@gmail.com
|
|
569
|
-
|
|
570
|
-
Test script to verify proxy registration SSL configuration fix.
|
|
571
|
-
"""
|
|
572
|
-
import sys
|
|
573
|
-
import subprocess
|
|
574
|
-
import time
|
|
575
|
-
import requests
|
|
576
|
-
from pathlib import Path
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
def test_proxy_registration():
|
|
580
|
-
"""Test proxy registration with SSL configuration."""
|
|
581
|
-
print("๐งช Testing Proxy Registration SSL Configuration Fix")
|
|
582
|
-
print("=" * 60)
|
|
65
|
+
# Examples path
|
|
66
|
+
examples_path = script_dir
|
|
583
67
|
|
|
584
|
-
|
|
585
|
-
error_count = 0
|
|
586
|
-
errors = []
|
|
587
|
-
|
|
588
|
-
# Kill any existing process on port 20005
|
|
589
|
-
print("๐งน Killing any existing process on port 20005...")
|
|
590
|
-
try:
|
|
591
|
-
subprocess.run(["fuser", "-k", "20005/tcp"], check=False, capture_output=True)
|
|
592
|
-
except FileNotFoundError:
|
|
593
|
-
# fuser not available, try with lsof
|
|
594
|
-
try:
|
|
595
|
-
# Get PIDs using lsof
|
|
596
|
-
result = subprocess.run(["lsof", "-ti:20005"], capture_output=True, text=True, check=False)
|
|
597
|
-
if result.returncode == 0 and result.stdout.strip():
|
|
598
|
-
pids = result.stdout.strip().split('\\n')
|
|
599
|
-
for pid in pids:
|
|
600
|
-
if pid.strip():
|
|
601
|
-
subprocess.run(["kill", "-9", pid.strip()], check=False, capture_output=True)
|
|
602
|
-
except:
|
|
603
|
-
pass
|
|
604
|
-
|
|
605
|
-
# Start proxy server
|
|
606
|
-
print("๐ Starting test proxy server...")
|
|
607
|
-
proxy_process = subprocess.Popen([
|
|
608
|
-
sys.executable, "test_proxy_server.py"
|
|
609
|
-
], cwd=Path(__file__).parent)
|
|
610
|
-
|
|
611
|
-
try:
|
|
612
|
-
# Wait for server to start
|
|
613
|
-
print("โณ Waiting for proxy server to start...")
|
|
614
|
-
time.sleep(5)
|
|
615
|
-
|
|
616
|
-
# Test proxy server health - try both HTTP and HTTPS
|
|
617
|
-
print("๐ Testing proxy server health...")
|
|
618
|
-
proxy_working = False
|
|
619
|
-
|
|
620
|
-
# Try HTTP first
|
|
621
|
-
try:
|
|
622
|
-
print("๐ Trying HTTP connection...")
|
|
623
|
-
response = requests.get(
|
|
624
|
-
"http://127.0.0.1:20005/health",
|
|
625
|
-
timeout=10
|
|
626
|
-
)
|
|
627
|
-
if response.status_code == 200:
|
|
628
|
-
print("โ
Proxy server is running on HTTP")
|
|
629
|
-
proxy_working = True
|
|
630
|
-
else:
|
|
631
|
-
print(f"โ ๏ธ HTTP health check failed: {response.status_code}")
|
|
632
|
-
except Exception as e:
|
|
633
|
-
print(f"โ ๏ธ HTTP connection failed: {e}")
|
|
634
|
-
|
|
635
|
-
# Try HTTPS if HTTP failed
|
|
636
|
-
if not proxy_working:
|
|
637
|
-
try:
|
|
638
|
-
print("๐ Trying HTTPS connection...")
|
|
639
|
-
response = requests.get(
|
|
640
|
-
"https://127.0.0.1:20005/health",
|
|
641
|
-
verify=False,
|
|
642
|
-
timeout=10
|
|
643
|
-
)
|
|
644
|
-
if response.status_code == 200:
|
|
645
|
-
print("โ
Proxy server is running on HTTPS")
|
|
646
|
-
proxy_working = True
|
|
647
|
-
else:
|
|
648
|
-
print(f"โ ๏ธ HTTPS health check failed: {response.status_code}")
|
|
649
|
-
except Exception as e:
|
|
650
|
-
print(f"โ ๏ธ HTTPS connection failed: {e}")
|
|
651
|
-
|
|
652
|
-
if not proxy_working:
|
|
653
|
-
error_count += 1
|
|
654
|
-
errors.append("Failed to connect to proxy server on both HTTP and HTTPS")
|
|
655
|
-
print("โ Failed to connect to proxy server on both HTTP and HTTPS")
|
|
656
|
-
return False
|
|
657
|
-
|
|
658
|
-
# Test mTLS server with registration
|
|
659
|
-
print("๐ Starting mTLS server with proxy registration...")
|
|
660
|
-
server_process = subprocess.Popen([
|
|
661
|
-
sys.executable, "-m", "mcp_proxy_adapter",
|
|
662
|
-
"--config", "configs/test_proxy_registration.json"
|
|
663
|
-
], cwd=Path(__file__).parent)
|
|
664
|
-
|
|
665
|
-
try:
|
|
666
|
-
# Wait for server to start and attempt registration
|
|
667
|
-
print("โณ Waiting for server to start and register...")
|
|
668
|
-
time.sleep(10)
|
|
669
|
-
|
|
670
|
-
# Check if server is running
|
|
671
|
-
if server_process.poll() is None:
|
|
672
|
-
print("โ
mTLS server started successfully")
|
|
673
|
-
|
|
674
|
-
# Check if registration was successful by querying the proxy server
|
|
675
|
-
print("โณ Checking registration status...")
|
|
676
|
-
time.sleep(5) # Give more time for registration attempt
|
|
677
|
-
|
|
678
|
-
if server_process.poll() is None:
|
|
679
|
-
print("โ
Server is running - checking registration status...")
|
|
680
|
-
|
|
681
|
-
# Try to check if server is registered by querying proxy server
|
|
682
|
-
try:
|
|
683
|
-
# Check if we can get server list from proxy (if it has such endpoint)
|
|
684
|
-
# For now, we'll assume registration is successful if server is still running
|
|
685
|
-
# and no error messages were shown in the logs
|
|
686
|
-
print("โ
Server is running and appears to be registered successfully")
|
|
687
|
-
print("โ
Proxy registration test PASSED")
|
|
688
|
-
return True
|
|
689
|
-
except Exception as e:
|
|
690
|
-
error_count += 1
|
|
691
|
-
errors.append(f"Failed to verify registration: {e}")
|
|
692
|
-
print(f"โ Failed to verify registration: {e}")
|
|
693
|
-
print(f"๐ Error count: {error_count}")
|
|
694
|
-
print(f"๐ Errors: {errors}")
|
|
695
|
-
return False
|
|
696
|
-
else:
|
|
697
|
-
error_count += 1
|
|
698
|
-
errors.append("Server stopped unexpectedly")
|
|
699
|
-
print("โ Server stopped unexpectedly")
|
|
700
|
-
print(f"๐ Error count: {error_count}")
|
|
701
|
-
print(f"๐ Errors: {errors}")
|
|
702
|
-
return False
|
|
703
|
-
else:
|
|
704
|
-
error_count += 1
|
|
705
|
-
errors.append("mTLS server failed to start")
|
|
706
|
-
print("โ mTLS server failed to start")
|
|
707
|
-
print(f"๐ Error count: {error_count}")
|
|
708
|
-
print(f"๐ Errors: {errors}")
|
|
709
|
-
return False
|
|
710
|
-
|
|
711
|
-
finally:
|
|
712
|
-
# Clean up server process
|
|
713
|
-
if server_process.poll() is None:
|
|
714
|
-
server_process.terminate()
|
|
715
|
-
server_process.wait()
|
|
716
|
-
|
|
717
|
-
finally:
|
|
718
|
-
# Clean up proxy process
|
|
719
|
-
if proxy_process.poll() is None:
|
|
720
|
-
proxy_process.terminate()
|
|
721
|
-
proxy_process.wait()
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
def main():
|
|
725
|
-
"""Run the test."""
|
|
726
|
-
success = test_proxy_registration()
|
|
727
|
-
|
|
728
|
-
if success:
|
|
729
|
-
print("\\n๐ All tests passed! Proxy registration SSL fix is working correctly.")
|
|
730
|
-
return 0
|
|
731
|
-
else:
|
|
732
|
-
print("\\nโ Tests failed. Check the logs above for details.")
|
|
733
|
-
return 1
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
if __name__ == "__main__":
|
|
737
|
-
sys.exit(main())
|
|
738
|
-
'''
|
|
739
|
-
)
|
|
740
|
-
|
|
741
|
-
# Make it executable
|
|
742
|
-
test_script.chmod(0o755)
|
|
743
|
-
print(f"โ
Created test script: {test_script}")
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
def generate_enhanced_configurations(output_dir: Path) -> None:
|
|
747
|
-
"""
|
|
748
|
-
Generate enhanced configurations with proper protocol settings and validation.
|
|
749
|
-
"""
|
|
750
|
-
configs_dir = output_dir / "configs"
|
|
751
|
-
configs_dir.mkdir(parents=True, exist_ok=True)
|
|
752
|
-
|
|
753
|
-
validator = ConfigurationValidator()
|
|
754
|
-
|
|
755
|
-
# Single comprehensive HTTP configuration with all features
|
|
756
|
-
configs = {
|
|
757
|
-
"comprehensive_http.json": {
|
|
758
|
-
"uuid": "550e8400-e29b-41d4-a716-446655440000",
|
|
759
|
-
"server": {
|
|
760
|
-
"host": "127.0.0.1",
|
|
761
|
-
"port": 20001,
|
|
762
|
-
"debug": False,
|
|
763
|
-
"log_level": "INFO",
|
|
764
|
-
"workers": 1,
|
|
765
|
-
"reload": False,
|
|
766
|
-
},
|
|
767
|
-
"ssl": {"enabled": False},
|
|
768
|
-
"security": {
|
|
769
|
-
"enabled": True,
|
|
770
|
-
"auth": {
|
|
771
|
-
"enabled": True,
|
|
772
|
-
"methods": ["api_key"],
|
|
773
|
-
"api_keys": {
|
|
774
|
-
"admin-token-123": "admin",
|
|
775
|
-
"user-token-456": "user",
|
|
776
|
-
"readonly-token-789": "readonly",
|
|
777
|
-
"guest-token-abc": "guest",
|
|
778
|
-
"proxy-token-def": "proxy",
|
|
779
|
-
},
|
|
780
|
-
},
|
|
781
|
-
"permissions": {"enabled": True, "roles_file": "configs/roles.json"},
|
|
782
|
-
},
|
|
783
|
-
"registration": {
|
|
784
|
-
"enabled": True,
|
|
785
|
-
"url": "http://127.0.0.1:3004/proxy",
|
|
786
|
-
"name": "comprehensive_http_adapter",
|
|
787
|
-
"capabilities": [
|
|
788
|
-
"http",
|
|
789
|
-
"token_auth",
|
|
790
|
-
"roles",
|
|
791
|
-
"registration",
|
|
792
|
-
"heartbeat",
|
|
793
|
-
],
|
|
794
|
-
"retry_count": 3,
|
|
795
|
-
"retry_delay": 5,
|
|
796
|
-
"heartbeat": {"enabled": True, "interval": 30},
|
|
797
|
-
},
|
|
798
|
-
"protocols": {"enabled": True, "allowed_protocols": ["http"]},
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
# Generate and validate configurations
|
|
803
|
-
for config_name, config_data in configs.items():
|
|
804
|
-
config_path = configs_dir / config_name
|
|
805
|
-
|
|
806
|
-
# Validate configuration
|
|
807
|
-
is_valid, errors, warnings = validator.validate_config(config_data, config_name)
|
|
808
|
-
|
|
809
|
-
if not is_valid:
|
|
810
|
-
print(f"โ Configuration {config_name} has errors:")
|
|
811
|
-
for error in errors:
|
|
812
|
-
print(f" {error}")
|
|
813
|
-
continue
|
|
814
|
-
|
|
815
|
-
if warnings:
|
|
816
|
-
print(f"โ ๏ธ Configuration {config_name} has warnings:")
|
|
817
|
-
for warning in warnings:
|
|
818
|
-
print(f" {warning}")
|
|
819
|
-
|
|
820
|
-
# Write configuration file
|
|
821
|
-
with open(config_path, "w", encoding="utf-8") as f:
|
|
822
|
-
json.dump(config_data, f, indent=2)
|
|
823
|
-
|
|
824
|
-
print(f"โ
Generated configuration: {config_path}")
|
|
825
|
-
|
|
826
|
-
# Create configuration index
|
|
827
|
-
index_path = configs_dir / "README.md"
|
|
828
|
-
with open(index_path, "w", encoding="utf-8") as f:
|
|
829
|
-
f.write(
|
|
830
|
-
"""# Comprehensive HTTP Configuration
|
|
831
|
-
|
|
832
|
-
This directory contains a single comprehensive HTTP configuration with all features enabled.
|
|
833
|
-
|
|
834
|
-
## Available Configuration
|
|
835
|
-
|
|
836
|
-
### Comprehensive HTTP
|
|
837
|
-
- `comprehensive_http.json` - Complete HTTP server with all features
|
|
838
|
-
|
|
839
|
-
## Features
|
|
840
|
-
|
|
841
|
-
### Security
|
|
842
|
-
- **Token Authentication**: API key-based access control with 5 predefined tokens
|
|
843
|
-
- **Role-based Permissions**: Granular access control with 5 roles
|
|
844
|
-
- **Roles**: admin, user, readonly, guest, proxy
|
|
845
|
-
|
|
846
|
-
### Authentication Tokens
|
|
847
|
-
- `admin-token-123` โ admin role (full access)
|
|
848
|
-
- `user-token-456` โ user role (read, write, execute)
|
|
849
|
-
- `readonly-token-789` โ readonly role (read only)
|
|
850
|
-
- `guest-token-abc` โ guest role (limited access)
|
|
851
|
-
- `proxy-token-def` โ proxy role (registration, heartbeat)
|
|
852
|
-
|
|
853
|
-
### Registration & Monitoring
|
|
854
|
-
- **Proxy Registration**: Automatic service discovery
|
|
855
|
-
- **Heartbeat**: 30-second health monitoring
|
|
856
|
-
- **Retry Logic**: 3 attempts with 5-second delay
|
|
857
|
-
|
|
858
|
-
### Protocols
|
|
859
|
-
- **HTTP**: Standard web protocol (port 20001)
|
|
860
|
-
|
|
861
|
-
## Usage
|
|
862
|
-
|
|
863
|
-
### Starting the Server
|
|
864
|
-
```bash
|
|
865
|
-
# Start comprehensive HTTP server
|
|
866
|
-
python -m mcp_proxy_adapter --config configs/comprehensive_http.json
|
|
867
|
-
```
|
|
868
|
-
|
|
869
|
-
### Testing Authentication
|
|
870
|
-
```bash
|
|
871
|
-
# Test with admin token
|
|
872
|
-
curl -H "Authorization: Bearer admin-token-123" http://localhost:20001/health
|
|
873
|
-
|
|
874
|
-
# Test with user token
|
|
875
|
-
curl -H "Authorization: Bearer user-token-456" http://localhost:20001/health
|
|
876
|
-
|
|
877
|
-
# Test with readonly token
|
|
878
|
-
curl -H "Authorization: Bearer readonly-token-789" http://localhost:20001/health
|
|
879
|
-
```
|
|
880
|
-
|
|
881
|
-
## Configuration Details
|
|
882
|
-
|
|
883
|
-
- **Server**: 127.0.0.1:20001
|
|
884
|
-
- **SSL**: Disabled (HTTP only)
|
|
885
|
-
- **Authentication**: Token-based with roles
|
|
886
|
-
- **Registration**: Enabled with proxy server
|
|
887
|
-
- **Heartbeat**: 30-second interval
|
|
888
|
-
- **Protocols**: HTTP only
|
|
889
|
-
|
|
890
|
-
## Customization
|
|
891
|
-
|
|
892
|
-
Edit `comprehensive_http.json` to:
|
|
893
|
-
- Change server host/port
|
|
894
|
-
- Add/modify authentication tokens
|
|
895
|
-
- Update role permissions
|
|
896
|
-
- Modify registration settings
|
|
897
|
-
- Adjust heartbeat interval
|
|
898
|
-
|
|
899
|
-
## Troubleshooting
|
|
900
|
-
|
|
901
|
-
1. **Port Conflicts**: Ensure port 20001 is not in use
|
|
902
|
-
2. **Token Issues**: Verify token format and role mapping
|
|
903
|
-
3. **Registration Issues**: Check proxy server availability
|
|
904
|
-
4. **Permission Errors**: Verify roles.json file exists
|
|
905
|
-
"""
|
|
906
|
-
)
|
|
907
|
-
|
|
908
|
-
print(f"โ
Created configuration index: {index_path}")
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
def setup_test_environment(output_dir: Path) -> None:
|
|
912
|
-
"""
|
|
913
|
-
Setup test environment under output_dir with required files
|
|
914
|
-
and directories.
|
|
915
|
-
|
|
916
|
-
All created directories and copied files are rooted at output_dir
|
|
917
|
-
so users can run scripts relative to that directory.
|
|
918
|
-
"""
|
|
919
|
-
print("๐ง Setting up enhanced test environment...")
|
|
920
|
-
output_dir = output_dir.resolve()
|
|
921
|
-
output_dir.mkdir(parents=True, exist_ok=True)
|
|
922
|
-
|
|
923
|
-
# Create test environment directory structure
|
|
924
|
-
directories = [
|
|
925
|
-
"examples/basic_framework",
|
|
926
|
-
"examples/full_application",
|
|
927
|
-
"scripts",
|
|
928
|
-
"configs",
|
|
929
|
-
"certs",
|
|
930
|
-
"keys",
|
|
931
|
-
"tokens",
|
|
932
|
-
"logs",
|
|
933
|
-
"docs",
|
|
934
|
-
]
|
|
935
|
-
for directory in directories:
|
|
936
|
-
target_dir = output_dir / directory
|
|
937
|
-
target_dir.mkdir(parents=True, exist_ok=True)
|
|
938
|
-
print(f"โ
Created directory: {target_dir}")
|
|
939
|
-
|
|
940
|
-
# Resolve package paths
|
|
941
|
-
examples_src_root, utils_src_root = _get_package_paths()
|
|
942
|
-
|
|
943
|
-
# Copy example files
|
|
944
|
-
basic_framework_src = examples_src_root / "basic_framework"
|
|
945
|
-
if basic_framework_src.exists():
|
|
946
|
-
shutil.copytree(
|
|
947
|
-
basic_framework_src,
|
|
948
|
-
output_dir / "examples/basic_framework",
|
|
949
|
-
dirs_exist_ok=True,
|
|
950
|
-
)
|
|
951
|
-
print("โ
Copied basic_framework examples")
|
|
952
|
-
|
|
953
|
-
full_application_src = examples_src_root / "full_application"
|
|
954
|
-
if full_application_src.exists():
|
|
955
|
-
shutil.copytree(
|
|
956
|
-
full_application_src,
|
|
957
|
-
output_dir / "examples/full_application",
|
|
958
|
-
dirs_exist_ok=True,
|
|
959
|
-
)
|
|
960
|
-
print("โ
Copied full_application examples")
|
|
961
|
-
|
|
962
|
-
# Copy utility scripts from examples/scripts
|
|
963
|
-
config_generator_src = utils_src_root / "config_generator.py"
|
|
964
|
-
if config_generator_src.exists():
|
|
965
|
-
shutil.copy2(config_generator_src, output_dir / "scripts/")
|
|
966
|
-
print("โ
Copied config_generator.py")
|
|
967
|
-
|
|
968
|
-
# Copy certificate generation scripts from examples/scripts
|
|
969
|
-
create_certs_src = utils_src_root / "create_certificates_simple.py"
|
|
970
|
-
if create_certs_src.exists():
|
|
971
|
-
shutil.copy2(create_certs_src, output_dir / "scripts/")
|
|
972
|
-
print("โ
Copied create_certificates_simple.py")
|
|
973
|
-
|
|
974
|
-
cert_tokens_src = utils_src_root / "generate_certificates_and_tokens.py"
|
|
975
|
-
if cert_tokens_src.exists():
|
|
976
|
-
shutil.copy2(cert_tokens_src, output_dir / "scripts/")
|
|
977
|
-
print("โ
Copied generate_certificates_and_tokens.py")
|
|
978
|
-
|
|
979
|
-
# Copy test suite runner from examples
|
|
980
|
-
test_suite_src = examples_src_root / "run_full_test_suite.py"
|
|
981
|
-
if test_suite_src.exists():
|
|
982
|
-
shutil.copy2(test_suite_src, output_dir)
|
|
983
|
-
print("โ
Copied run_full_test_suite.py")
|
|
984
|
-
|
|
985
|
-
# Copy other required test files from examples
|
|
986
|
-
test_files = [
|
|
987
|
-
"create_test_configs.py",
|
|
988
|
-
"run_security_tests.py",
|
|
989
|
-
"run_proxy_server.py",
|
|
990
|
-
]
|
|
991
|
-
for test_file in test_files:
|
|
992
|
-
test_file_src = examples_src_root / test_file
|
|
993
|
-
if test_file_src.exists():
|
|
994
|
-
shutil.copy2(test_file_src, output_dir)
|
|
995
|
-
print(f"โ
Copied {test_file}")
|
|
996
|
-
|
|
997
|
-
# Create comprehensive_config.json if it doesn't exist
|
|
998
|
-
comprehensive_config_dst = output_dir / "comprehensive_config.json"
|
|
999
|
-
if not comprehensive_config_dst.exists():
|
|
1000
|
-
# Create a basic comprehensive config
|
|
1001
|
-
comprehensive_config = {
|
|
1002
|
-
"uuid": "79db1fa0-ff4e-4695-8c94-1d5ac470b613",
|
|
1003
|
-
"server": {"host": "127.0.0.1", "port": 20001},
|
|
1004
|
-
"ssl": {
|
|
1005
|
-
"enabled": False,
|
|
1006
|
-
"cert_file": "certs/localhost_server.crt",
|
|
1007
|
-
"key_file": "keys/server_key.pem",
|
|
1008
|
-
"ca_cert": "certs/mcp_proxy_adapter_ca_ca.crt",
|
|
1009
|
-
"verify_client": False,
|
|
1010
|
-
},
|
|
1011
|
-
"security": {
|
|
1012
|
-
"enabled": True,
|
|
1013
|
-
"auth": {
|
|
1014
|
-
"enabled": True,
|
|
1015
|
-
"methods": ["token"],
|
|
1016
|
-
"api_keys": {
|
|
1017
|
-
"admin-token-123": "admin",
|
|
1018
|
-
"user-token-456": "user",
|
|
1019
|
-
"readonly-token-789": "readonly",
|
|
1020
|
-
},
|
|
1021
|
-
},
|
|
1022
|
-
"permissions": {"enabled": True, "roles_file": "roles.json"},
|
|
1023
|
-
},
|
|
1024
|
-
"protocols": {
|
|
1025
|
-
"enabled": True,
|
|
1026
|
-
"default_protocol": "http",
|
|
1027
|
-
"allowed_protocols": ["http"],
|
|
1028
|
-
},
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
with open(comprehensive_config_dst, "w", encoding="utf-8") as f:
|
|
1032
|
-
json.dump(comprehensive_config, f, indent=2)
|
|
1033
|
-
print("โ
Created comprehensive_config.json")
|
|
1034
|
-
else:
|
|
1035
|
-
print("โ
comprehensive_config.json already exists")
|
|
1036
|
-
|
|
1037
|
-
# Copy roles.json to the root directory for compatibility
|
|
1038
|
-
roles_src = examples_src_root / "roles.json"
|
|
1039
|
-
if roles_src.exists():
|
|
1040
|
-
shutil.copy2(roles_src, output_dir)
|
|
1041
|
-
print("โ
Copied roles.json to root directory")
|
|
1042
|
-
else:
|
|
1043
|
-
# Create a basic roles.json if it doesn't exist
|
|
1044
|
-
roles_config = {
|
|
1045
|
-
"roles": {
|
|
1046
|
-
"admin": {
|
|
1047
|
-
"permissions": ["*"],
|
|
1048
|
-
"description": "Full access to all commands",
|
|
1049
|
-
},
|
|
1050
|
-
"user": {
|
|
1051
|
-
"permissions": ["echo", "health", "help"],
|
|
1052
|
-
"description": "Basic user permissions",
|
|
1053
|
-
},
|
|
1054
|
-
"readonly": {
|
|
1055
|
-
"permissions": ["health", "help"],
|
|
1056
|
-
"description": "Read-only access",
|
|
1057
|
-
},
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
roles_dst = output_dir / "roles.json"
|
|
1062
|
-
with open(roles_dst, "w", encoding="utf-8") as f:
|
|
1063
|
-
json.dump(roles_config, f, indent=2)
|
|
1064
|
-
print("โ
Created roles.json in root directory")
|
|
1065
|
-
|
|
1066
|
-
# Also copy from configs directory if it exists
|
|
1067
|
-
roles_configs_src = output_dir / "configs" / "roles.json"
|
|
1068
|
-
if roles_configs_src.exists():
|
|
1069
|
-
shutil.copy2(roles_configs_src, output_dir / "roles.json")
|
|
1070
|
-
print("โ
Updated roles.json from configs directory")
|
|
1071
|
-
|
|
1072
|
-
# Create configuration documentation
|
|
1073
|
-
create_configuration_documentation(output_dir)
|
|
1074
|
-
|
|
1075
|
-
# Generate enhanced configurations
|
|
1076
|
-
generate_enhanced_configurations(output_dir)
|
|
1077
|
-
|
|
1078
|
-
# Create test files
|
|
1079
|
-
create_test_files(output_dir)
|
|
1080
|
-
|
|
1081
|
-
print(
|
|
1082
|
-
"๐ Enhanced test environment setup completed successfully at: {}".format(
|
|
1083
|
-
output_dir
|
|
1084
|
-
)
|
|
1085
|
-
)
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
def generate_certificates_with_framework(output_dir: Path) -> bool:
|
|
1089
|
-
"""
|
|
1090
|
-
Generate certificates using mcp_security_framework.
|
|
1091
|
-
"""
|
|
1092
|
-
if not SECURITY_FRAMEWORK_AVAILABLE:
|
|
1093
|
-
print("โ mcp_security_framework not available for certificate " "generation")
|
|
1094
|
-
return False
|
|
1095
|
-
try:
|
|
1096
|
-
print("๐ Generating certificates using mcp_security_framework...")
|
|
1097
|
-
# Configure certificate manager
|
|
1098
|
-
cert_config = CertificateConfig(
|
|
1099
|
-
cert_storage_path=str((output_dir / "certs").resolve()),
|
|
1100
|
-
key_storage_path=str((output_dir / "keys").resolve()),
|
|
1101
|
-
default_validity_days=365,
|
|
1102
|
-
key_size=2048,
|
|
1103
|
-
hash_algorithm="sha256",
|
|
1104
|
-
)
|
|
1105
|
-
cert_manager = CertificateManager(cert_config)
|
|
1106
|
-
# Generate CA certificate
|
|
1107
|
-
ca_config = CAConfig(
|
|
1108
|
-
common_name="MCP Proxy Adapter Test CA",
|
|
1109
|
-
organization="Test Organization",
|
|
1110
|
-
organizational_unit="Certificate Authority",
|
|
1111
|
-
country="US",
|
|
1112
|
-
state="Test State",
|
|
1113
|
-
locality="Test City",
|
|
1114
|
-
validity_years=10, # Use validity_years instead of validity_days
|
|
1115
|
-
key_size=2048,
|
|
1116
|
-
hash_algorithm="sha256",
|
|
1117
|
-
)
|
|
1118
|
-
cert_pair = cert_manager.create_root_ca(ca_config)
|
|
1119
|
-
if not cert_pair or not cert_pair.certificate_path:
|
|
1120
|
-
print("โ Failed to create CA certificate: Invalid certificate pair")
|
|
1121
|
-
return False
|
|
1122
|
-
print("โ
CA certificate created successfully")
|
|
1123
|
-
# Find CA key file
|
|
1124
|
-
ca_key_path = cert_pair.private_key_path
|
|
1125
|
-
# Generate server certificate (localhost_server.crt)
|
|
1126
|
-
server_config = ServerCertConfig(
|
|
1127
|
-
common_name="localhost",
|
|
1128
|
-
organization="Test Organization",
|
|
1129
|
-
organizational_unit="Server",
|
|
1130
|
-
country="US",
|
|
1131
|
-
state="Test State",
|
|
1132
|
-
locality="Test City",
|
|
1133
|
-
validity_days=365,
|
|
1134
|
-
key_size=2048,
|
|
1135
|
-
hash_algorithm="sha256",
|
|
1136
|
-
subject_alt_names=[
|
|
1137
|
-
"localhost",
|
|
1138
|
-
"127.0.0.1",
|
|
1139
|
-
],
|
|
1140
|
-
ca_cert_path=cert_pair.certificate_path,
|
|
1141
|
-
ca_key_path=ca_key_path,
|
|
1142
|
-
)
|
|
1143
|
-
cert_pair = cert_manager.create_server_certificate(server_config)
|
|
1144
|
-
if not cert_pair or not cert_pair.certificate_path:
|
|
1145
|
-
print("โ Failed to create server certificate: Invalid certificate " "pair")
|
|
1146
|
-
return False
|
|
1147
|
-
print("โ
Server certificate created successfully")
|
|
1148
|
-
|
|
1149
|
-
# Generate additional server certificate (mcp_proxy_adapter_server.crt) for HTTPS configs
|
|
1150
|
-
server_config2 = ServerCertConfig(
|
|
1151
|
-
common_name="mcp_proxy_adapter_server",
|
|
1152
|
-
organization="Test Organization",
|
|
1153
|
-
organizational_unit="Server",
|
|
1154
|
-
country="US",
|
|
1155
|
-
state="Test State",
|
|
1156
|
-
locality="Test City",
|
|
1157
|
-
validity_days=365,
|
|
1158
|
-
key_size=2048,
|
|
1159
|
-
hash_algorithm="sha256",
|
|
1160
|
-
subject_alt_names=[
|
|
1161
|
-
"localhost",
|
|
1162
|
-
"127.0.0.1",
|
|
1163
|
-
"mcp_proxy_adapter_server",
|
|
1164
|
-
],
|
|
1165
|
-
ca_cert_path=cert_pair.certificate_path,
|
|
1166
|
-
ca_key_path=cert_pair.private_key_path,
|
|
1167
|
-
)
|
|
1168
|
-
cert_pair2 = cert_manager.create_server_certificate(server_config2)
|
|
1169
|
-
if not cert_pair2 or not cert_pair2.certificate_path:
|
|
1170
|
-
print("โ Failed to create mcp_proxy_adapter_server certificate: Invalid certificate " "pair")
|
|
1171
|
-
return False
|
|
1172
|
-
print("โ
mcp_proxy_adapter_server certificate created successfully")
|
|
1173
|
-
|
|
1174
|
-
# Create symlinks with the expected names for HTTPS configs
|
|
1175
|
-
import shutil
|
|
1176
|
-
certs_dir = output_dir / "certs"
|
|
1177
|
-
keys_dir = output_dir / "keys"
|
|
1178
|
-
|
|
1179
|
-
# Create symlink for mcp_proxy_adapter_server.crt
|
|
1180
|
-
expected_cert_path = certs_dir / "mcp_proxy_adapter_server.crt"
|
|
1181
|
-
if not expected_cert_path.exists():
|
|
1182
|
-
shutil.copy2(cert_pair2.certificate_path, expected_cert_path)
|
|
1183
|
-
print(f"โ
Created mcp_proxy_adapter_server.crt: {expected_cert_path}")
|
|
1184
|
-
|
|
1185
|
-
# Create symlink for mcp_proxy_adapter_server.key
|
|
1186
|
-
expected_key_path = certs_dir / "mcp_proxy_adapter_server.key"
|
|
1187
|
-
if not expected_key_path.exists():
|
|
1188
|
-
shutil.copy2(cert_pair2.private_key_path, expected_key_path)
|
|
1189
|
-
print(f"โ
Created mcp_proxy_adapter_server.key: {expected_key_path}")
|
|
1190
|
-
# Generate client certificates
|
|
1191
|
-
client_configs = [
|
|
1192
|
-
(
|
|
1193
|
-
"admin",
|
|
1194
|
-
["admin"],
|
|
1195
|
-
[
|
|
1196
|
-
"read",
|
|
1197
|
-
"write",
|
|
1198
|
-
"execute",
|
|
1199
|
-
"delete",
|
|
1200
|
-
"admin",
|
|
1201
|
-
"register",
|
|
1202
|
-
"unregister",
|
|
1203
|
-
"heartbeat",
|
|
1204
|
-
"discover",
|
|
1205
|
-
],
|
|
1206
|
-
),
|
|
1207
|
-
(
|
|
1208
|
-
"user",
|
|
1209
|
-
["user"],
|
|
1210
|
-
[
|
|
1211
|
-
"read",
|
|
1212
|
-
"execute",
|
|
1213
|
-
"register",
|
|
1214
|
-
"unregister",
|
|
1215
|
-
"heartbeat",
|
|
1216
|
-
"discover",
|
|
1217
|
-
],
|
|
1218
|
-
),
|
|
1219
|
-
("readonly", ["readonly"], ["read", "discover"]),
|
|
1220
|
-
("guest", ["guest"], ["read", "discover"]),
|
|
1221
|
-
(
|
|
1222
|
-
"proxy",
|
|
1223
|
-
["proxy"],
|
|
1224
|
-
["register", "unregister", "heartbeat", "discover"],
|
|
1225
|
-
),
|
|
1226
|
-
]
|
|
1227
|
-
for client_name, roles, permissions in client_configs:
|
|
1228
|
-
client_config = ClientCertConfig(
|
|
1229
|
-
common_name=f"{client_name}-client",
|
|
1230
|
-
organization="Test Organization",
|
|
1231
|
-
organizational_unit="Client",
|
|
1232
|
-
country="US",
|
|
1233
|
-
state="Test State",
|
|
1234
|
-
locality="Test City",
|
|
1235
|
-
validity_days=730,
|
|
1236
|
-
key_size=2048,
|
|
1237
|
-
hash_algorithm="sha256",
|
|
1238
|
-
roles=roles,
|
|
1239
|
-
permissions=permissions,
|
|
1240
|
-
ca_cert_path=cert_pair.certificate_path,
|
|
1241
|
-
ca_key_path=cert_pair.private_key_path,
|
|
1242
|
-
)
|
|
1243
|
-
client_cert_pair = cert_manager.create_client_certificate(client_config)
|
|
1244
|
-
if not client_cert_pair or not client_cert_pair.certificate_path:
|
|
1245
|
-
print(
|
|
1246
|
-
(
|
|
1247
|
-
"โ Failed to create client certificate {}: "
|
|
1248
|
-
"Invalid certificate pair"
|
|
1249
|
-
).format(client_name)
|
|
1250
|
-
)
|
|
1251
|
-
return False
|
|
1252
|
-
print("โ
Client certificate {} created successfully".format(client_name))
|
|
1253
|
-
print(
|
|
1254
|
-
"๐ All certificates generated successfully using "
|
|
1255
|
-
"mcp_security_framework!"
|
|
1256
|
-
)
|
|
1257
|
-
return True
|
|
1258
|
-
except Exception as e:
|
|
1259
|
-
print("โ Error generating certificates with framework: {}".format(e))
|
|
1260
|
-
print("\n๐ง TROUBLESHOOTING:")
|
|
1261
|
-
print("1. Check if mcp_security_framework is installed:")
|
|
1262
|
-
print(" pip install mcp_security_framework")
|
|
1263
|
-
print("\n2. Verify write permissions in output directory")
|
|
1264
|
-
print("\n3. Check if certs/ and keys/ directories exist")
|
|
1265
|
-
return False
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
def run_full_test_suite(target_root: Path) -> bool:
|
|
1269
|
-
"""Run the full test suite after environment setup."""
|
|
1270
|
-
print("\n" + "=" * 60)
|
|
1271
|
-
print("๐ AUTOMATICALLY RUNNING FULL TEST SUITE")
|
|
1272
|
-
print("=" * 60)
|
|
1273
|
-
|
|
1274
|
-
try:
|
|
1275
|
-
# Change to target directory
|
|
1276
|
-
original_cwd = Path.cwd()
|
|
1277
|
-
os.chdir(target_root)
|
|
1278
|
-
|
|
1279
|
-
# Run the full test suite
|
|
1280
|
-
result = subprocess.run(
|
|
1281
|
-
[sys.executable, "run_full_test_suite.py"],
|
|
1282
|
-
capture_output=True,
|
|
1283
|
-
text=True,
|
|
1284
|
-
timeout=300,
|
|
1285
|
-
) # 5 minute timeout
|
|
1286
|
-
|
|
1287
|
-
# Print output
|
|
1288
|
-
if result.stdout:
|
|
1289
|
-
print("๐ Test Suite Output:")
|
|
1290
|
-
print(result.stdout)
|
|
1291
|
-
|
|
1292
|
-
if result.stderr:
|
|
1293
|
-
print("โ ๏ธ Test Suite Warnings/Errors:")
|
|
1294
|
-
print(result.stderr)
|
|
1295
|
-
|
|
1296
|
-
if result.returncode == 0:
|
|
1297
|
-
print("๐ FULL TEST SUITE COMPLETED SUCCESSFULLY!")
|
|
1298
|
-
return True
|
|
1299
|
-
else:
|
|
1300
|
-
print(f"โ FULL TEST SUITE FAILED (exit code: {result.returncode})")
|
|
1301
|
-
return False
|
|
1302
|
-
|
|
1303
|
-
except subprocess.TimeoutExpired:
|
|
1304
|
-
print("โฐ Test suite timed out after 5 minutes")
|
|
1305
|
-
return False
|
|
1306
|
-
except Exception as e:
|
|
1307
|
-
print(f"โ Error running test suite: {e}")
|
|
1308
|
-
return False
|
|
1309
|
-
finally:
|
|
1310
|
-
# Restore original working directory
|
|
1311
|
-
os.chdir(original_cwd)
|
|
68
|
+
return package_path, examples_path
|
|
1312
69
|
|
|
1313
70
|
|
|
1314
71
|
def validate_output_directory(output_dir: Path) -> bool:
|
|
1315
72
|
"""
|
|
1316
|
-
Validate output directory
|
|
73
|
+
Validate that the output directory is suitable for setup.
|
|
1317
74
|
|
|
1318
75
|
Args:
|
|
1319
|
-
output_dir:
|
|
76
|
+
output_dir: Directory to validate
|
|
1320
77
|
|
|
1321
78
|
Returns:
|
|
1322
|
-
True if
|
|
79
|
+
True if valid, False otherwise
|
|
1323
80
|
"""
|
|
1324
|
-
|
|
81
|
+
try:
|
|
82
|
+
# Check if directory exists
|
|
83
|
+
if not output_dir.exists():
|
|
84
|
+
print(f"๐ Creating output directory: {output_dir}")
|
|
85
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
86
|
+
return True
|
|
1325
87
|
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
print(f"โ Path exists but is not a directory: {output_dir}")
|
|
88
|
+
# Check if directory is writable
|
|
89
|
+
if not os.access(output_dir, os.W_OK):
|
|
90
|
+
print(f"โ Error: Directory {output_dir} is not writable")
|
|
1330
91
|
return False
|
|
1331
92
|
|
|
1332
|
-
# Check if directory is empty
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
if len(contents) > 5:
|
|
1341
|
-
print(f" ... and {len(contents) - 5} more items")
|
|
1342
|
-
print("\n๐ก Please use an empty directory or specify a different path.")
|
|
93
|
+
# Check if directory is empty (optional warning)
|
|
94
|
+
contents = list(output_dir.iterdir())
|
|
95
|
+
if contents:
|
|
96
|
+
print(f"โ ๏ธ Warning: Directory {output_dir} is not empty")
|
|
97
|
+
print(f" Found {len(contents)} items")
|
|
98
|
+
response = input(" Continue anyway? (y/N): ").strip().lower()
|
|
99
|
+
if response not in ['y', 'yes']:
|
|
100
|
+
print(" Setup cancelled")
|
|
1343
101
|
return False
|
|
1344
|
-
except PermissionError:
|
|
1345
|
-
print(f"โ Permission denied accessing directory: {output_dir}")
|
|
1346
|
-
return False
|
|
1347
|
-
else:
|
|
1348
|
-
# Directory doesn't exist, try to create it
|
|
1349
|
-
try:
|
|
1350
|
-
output_dir.mkdir(parents=True, exist_ok=True)
|
|
1351
|
-
print(f"โ
Created directory: {output_dir}")
|
|
1352
|
-
except PermissionError:
|
|
1353
|
-
print(f"โ Permission denied creating directory: {output_dir}")
|
|
1354
|
-
return False
|
|
1355
|
-
except Exception as e:
|
|
1356
|
-
print(f"โ Failed to create directory {output_dir}: {e}")
|
|
1357
|
-
return False
|
|
1358
102
|
|
|
1359
|
-
|
|
103
|
+
return True
|
|
104
|
+
|
|
105
|
+
except Exception as e:
|
|
106
|
+
print(f"โ Error validating output directory: {e}")
|
|
107
|
+
return False
|
|
1360
108
|
|
|
1361
109
|
|
|
1362
110
|
def check_ports_available() -> bool:
|
|
1363
111
|
"""
|
|
1364
|
-
Check if
|
|
1365
|
-
|
|
112
|
+
Check if required ports are available.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
True if ports are available, False otherwise
|
|
1366
116
|
"""
|
|
1367
117
|
import socket
|
|
1368
118
|
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
20010, # proxy_port
|
|
1372
|
-
20020, # basic_http (http_simple)
|
|
1373
|
-
20021, # http_token
|
|
1374
|
-
20022, # https_simple
|
|
1375
|
-
20023, # https_token
|
|
1376
|
-
20024, # mtls_no_roles
|
|
1377
|
-
20025, # mtls_simple
|
|
1378
|
-
20026, # mtls_with_roles
|
|
1379
|
-
20005, # test_proxy_server
|
|
1380
|
-
3006, # proxy registration
|
|
1381
|
-
]
|
|
119
|
+
ports_to_check = [8080, 8443, 20005, 3005]
|
|
120
|
+
unavailable_ports = []
|
|
1382
121
|
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
print("๐ Checking port availability...")
|
|
1386
|
-
for port in test_ports:
|
|
122
|
+
for port in ports_to_check:
|
|
1387
123
|
try:
|
|
1388
|
-
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as
|
|
1389
|
-
|
|
1390
|
-
result =
|
|
124
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
125
|
+
s.settimeout(1)
|
|
126
|
+
result = s.connect_ex(('localhost', port))
|
|
1391
127
|
if result == 0:
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
except Exception as e:
|
|
1397
|
-
print(f" โ ๏ธ Could not check port {port}: {e}")
|
|
1398
|
-
occupied_ports.append(port)
|
|
128
|
+
unavailable_ports.append(port)
|
|
129
|
+
except Exception:
|
|
130
|
+
# If we can't check, assume it's available
|
|
131
|
+
pass
|
|
1399
132
|
|
|
1400
|
-
if
|
|
1401
|
-
print(
|
|
1402
|
-
|
|
1403
|
-
)
|
|
1404
|
-
|
|
1405
|
-
for port in occupied_ports:
|
|
1406
|
-
print(f" - Port {port}: kill processes using this port")
|
|
1407
|
-
print("\n๐ง You can use these commands to free ports:")
|
|
1408
|
-
for port in occupied_ports:
|
|
1409
|
-
print(f" fuser -k {port}/tcp")
|
|
1410
|
-
return False
|
|
133
|
+
if unavailable_ports:
|
|
134
|
+
print(f"โ ๏ธ Warning: The following ports are in use: {unavailable_ports}")
|
|
135
|
+
print(" This may cause issues when running the examples")
|
|
136
|
+
response = input(" Continue anyway? (y/N): ").strip().lower()
|
|
137
|
+
return response in ['y', 'yes']
|
|
1411
138
|
|
|
1412
|
-
print(f"โ
All {len(test_ports)} required ports are available")
|
|
1413
139
|
return True
|
|
1414
140
|
|
|
1415
141
|
|
|
1416
142
|
def main() -> int:
|
|
1417
|
-
"""
|
|
143
|
+
"""
|
|
144
|
+
Main function to set up the test environment.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Exit code (0 for success, 1 for failure)
|
|
148
|
+
"""
|
|
1418
149
|
parser = argparse.ArgumentParser(
|
|
1419
|
-
description="
|
|
150
|
+
description="Set up test environment for MCP Proxy Adapter",
|
|
151
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
152
|
+
epilog="""
|
|
153
|
+
Examples:
|
|
154
|
+
python setup_test_environment.py # Setup in current directory
|
|
155
|
+
python setup_test_environment.py -o /path/to/test # Setup in specific directory
|
|
156
|
+
python setup_test_environment.py --no-certs # Skip certificate generation
|
|
157
|
+
python setup_test_environment.py --run-tests # Run tests after setup
|
|
158
|
+
"""
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
parser.add_argument(
|
|
162
|
+
"-o", "--output-dir",
|
|
163
|
+
type=Path,
|
|
164
|
+
default=Path.cwd(),
|
|
165
|
+
help="Output directory for test environment (default: current directory)"
|
|
1420
166
|
)
|
|
167
|
+
|
|
1421
168
|
parser.add_argument(
|
|
1422
|
-
"
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
default=None,
|
|
1426
|
-
help=(
|
|
1427
|
-
"Target directory to create the test environment "
|
|
1428
|
-
"(default: auto-generated directory in /tmp)"
|
|
1429
|
-
),
|
|
169
|
+
"--no-certs",
|
|
170
|
+
action="store_true",
|
|
171
|
+
help="Skip certificate generation"
|
|
1430
172
|
)
|
|
173
|
+
|
|
1431
174
|
parser.add_argument(
|
|
1432
|
-
"--
|
|
175
|
+
"--run-tests",
|
|
176
|
+
action="store_true",
|
|
177
|
+
help="Run tests after setup"
|
|
1433
178
|
)
|
|
179
|
+
|
|
1434
180
|
parser.add_argument(
|
|
1435
|
-
"--
|
|
181
|
+
"--verbose",
|
|
182
|
+
action="store_true",
|
|
183
|
+
help="Enable verbose output"
|
|
1436
184
|
)
|
|
185
|
+
|
|
1437
186
|
args = parser.parse_args()
|
|
1438
187
|
|
|
1439
|
-
|
|
1440
|
-
print("
|
|
188
|
+
print("๐ MCP Proxy Adapter Test Environment Setup")
|
|
189
|
+
print("=" * 50)
|
|
190
|
+
|
|
191
|
+
# Validate output directory
|
|
192
|
+
if not validate_output_directory(args.output_dir):
|
|
193
|
+
print("โ Setup failed: Invalid output directory")
|
|
194
|
+
return 1
|
|
195
|
+
|
|
196
|
+
# Check ports
|
|
1441
197
|
if not check_ports_available():
|
|
1442
|
-
print("
|
|
1443
|
-
print("๐ก Please free the occupied ports and try again.")
|
|
198
|
+
print("โ Setup cancelled: Port conflicts detected")
|
|
1444
199
|
return 1
|
|
1445
200
|
|
|
1446
201
|
try:
|
|
1447
|
-
#
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
timestamp = int(time.time())
|
|
1454
|
-
target_root = Path(tempfile.gettempdir()) / f"mcp_test_env_{timestamp}"
|
|
1455
|
-
print(f"๐ง Auto-generating test environment directory: {target_root}")
|
|
1456
|
-
else:
|
|
1457
|
-
target_root = Path(args.output_dir)
|
|
1458
|
-
|
|
1459
|
-
# Validate output directory
|
|
1460
|
-
print(f"๐ Validating output directory: {target_root}")
|
|
1461
|
-
if not validate_output_directory(target_root):
|
|
1462
|
-
print("\nโ Directory validation failed. Exiting.")
|
|
202
|
+
# Run setup
|
|
203
|
+
success = setup_test_environment(args.output_dir)
|
|
204
|
+
|
|
205
|
+
if not success:
|
|
206
|
+
print("โ Setup failed")
|
|
1463
207
|
return 1
|
|
1464
208
|
|
|
1465
|
-
print(
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
# Generate certificates if framework is available and not skipped
|
|
1469
|
-
if not args.skip_certs and SECURITY_FRAMEWORK_AVAILABLE:
|
|
1470
|
-
generate_certificates_with_framework(target_root)
|
|
1471
|
-
elif args.skip_certs:
|
|
1472
|
-
print("โ ๏ธ Skipping certificate generation (--skip-certs specified)")
|
|
1473
|
-
else:
|
|
1474
|
-
print(
|
|
1475
|
-
"โ ๏ธ Skipping certificate generation (mcp_security_framework "
|
|
1476
|
-
"not available)"
|
|
1477
|
-
)
|
|
209
|
+
print("โ
Test environment setup completed successfully!")
|
|
210
|
+
print(f"๐ Output directory: {args.output_dir.absolute()}")
|
|
1478
211
|
|
|
1479
|
-
# Run
|
|
1480
|
-
if
|
|
1481
|
-
|
|
212
|
+
# Run tests if requested
|
|
213
|
+
if args.run_tests:
|
|
214
|
+
print("\\n๐งช Running tests...")
|
|
215
|
+
test_success = run_full_test_suite(args.output_dir)
|
|
1482
216
|
if not test_success:
|
|
1483
|
-
print("
|
|
217
|
+
print("โ ๏ธ Some tests failed, but setup completed")
|
|
1484
218
|
return 1
|
|
1485
|
-
else:
|
|
1486
|
-
print("โ ๏ธ Skipping test suite execution (--skip-tests specified)")
|
|
1487
219
|
|
|
220
|
+
print("\\n๐ Setup complete! You can now run the examples.")
|
|
221
|
+
return 0
|
|
222
|
+
|
|
223
|
+
except KeyboardInterrupt:
|
|
224
|
+
print("\\nโ ๏ธ Setup interrupted by user")
|
|
225
|
+
return 1
|
|
1488
226
|
except Exception as e:
|
|
1489
|
-
print(
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
print("\n๐ง TROUBLESHOOTING:")
|
|
1494
|
-
print("1. Check if output directory is writable")
|
|
1495
|
-
print("2. Verify mcp_security_framework installation")
|
|
1496
|
-
print("3. Check available disk space")
|
|
227
|
+
print(f"\\nโ Setup failed with error: {e}")
|
|
228
|
+
if args.verbose:
|
|
229
|
+
import traceback
|
|
230
|
+
traceback.print_exc()
|
|
1497
231
|
return 1
|
|
1498
232
|
|
|
1499
|
-
print("\n" + "=" * 60)
|
|
1500
|
-
print("โ
ENHANCED TEST ENVIRONMENT SETUP COMPLETED SUCCESSFULLY")
|
|
1501
|
-
print("=" * 60)
|
|
1502
|
-
|
|
1503
|
-
if not args.skip_tests:
|
|
1504
|
-
print("\n๐ ALL TESTS PASSED - Environment is ready for use!")
|
|
1505
|
-
else:
|
|
1506
|
-
print("\n๐ NEXT STEPS:")
|
|
1507
|
-
print("1. Review configuration documentation:")
|
|
1508
|
-
print(" cat docs/CONFIGURATION_GUIDE.md")
|
|
1509
|
-
print("\n2. Check available configurations:")
|
|
1510
|
-
print(" ls -la configs/")
|
|
1511
|
-
print("\n3. Run the full test suite:")
|
|
1512
|
-
print(" python run_full_test_suite.py")
|
|
1513
|
-
print("\n4. Test proxy registration SSL fix:")
|
|
1514
|
-
print(" python test_proxy_registration.py")
|
|
1515
|
-
print("\n5. Start server with a specific configuration:")
|
|
1516
|
-
print(" python -m mcp_proxy_adapter --config configs/production_https.json")
|
|
1517
|
-
print("\n6. Run security tests:")
|
|
1518
|
-
print(" python -m mcp_proxy_adapter.examples.run_security_tests")
|
|
1519
|
-
print("\n7. Generate additional certificates (if needed):")
|
|
1520
|
-
print(" python scripts/create_certificates_simple.py")
|
|
1521
|
-
|
|
1522
|
-
print("=" * 60)
|
|
1523
|
-
return 0
|
|
1524
|
-
|
|
1525
233
|
|
|
1526
234
|
if __name__ == "__main__":
|
|
1527
|
-
exit(main())
|
|
235
|
+
sys.exit(main())
|