mcp-proxy-adapter 4.1.1__py3-none-any.whl → 6.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_proxy_adapter/__main__.py +32 -0
- mcp_proxy_adapter/api/app.py +290 -33
- mcp_proxy_adapter/api/handlers.py +32 -6
- mcp_proxy_adapter/api/middleware/__init__.py +38 -32
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
- mcp_proxy_adapter/api/middleware/error_handling.py +9 -0
- mcp_proxy_adapter/api/middleware/factory.py +243 -0
- mcp_proxy_adapter/api/middleware/logging.py +32 -6
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +201 -0
- mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
- mcp_proxy_adapter/api/middleware/unified_security.py +197 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +158 -0
- mcp_proxy_adapter/commands/__init__.py +19 -4
- mcp_proxy_adapter/commands/auth_validation_command.py +408 -0
- mcp_proxy_adapter/commands/base.py +66 -32
- mcp_proxy_adapter/commands/builtin_commands.py +95 -0
- mcp_proxy_adapter/commands/catalog_manager.py +838 -0
- mcp_proxy_adapter/commands/cert_monitor_command.py +620 -0
- mcp_proxy_adapter/commands/certificate_management_command.py +608 -0
- mcp_proxy_adapter/commands/command_registry.py +711 -354
- mcp_proxy_adapter/commands/dependency_manager.py +245 -0
- mcp_proxy_adapter/commands/echo_command.py +81 -0
- mcp_proxy_adapter/commands/health_command.py +8 -1
- mcp_proxy_adapter/commands/help_command.py +21 -14
- mcp_proxy_adapter/commands/hooks.py +200 -167
- mcp_proxy_adapter/commands/key_management_command.py +506 -0
- mcp_proxy_adapter/commands/load_command.py +176 -0
- mcp_proxy_adapter/commands/plugins_command.py +235 -0
- mcp_proxy_adapter/commands/protocol_management_command.py +232 -0
- mcp_proxy_adapter/commands/proxy_registration_command.py +409 -0
- mcp_proxy_adapter/commands/reload_command.py +48 -50
- mcp_proxy_adapter/commands/result.py +1 -0
- mcp_proxy_adapter/commands/role_test_command.py +141 -0
- mcp_proxy_adapter/commands/roles_management_command.py +697 -0
- mcp_proxy_adapter/commands/security_command.py +488 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +366 -0
- mcp_proxy_adapter/commands/token_management_command.py +529 -0
- mcp_proxy_adapter/commands/transport_management_command.py +144 -0
- mcp_proxy_adapter/commands/unload_command.py +158 -0
- mcp_proxy_adapter/config.py +394 -14
- mcp_proxy_adapter/core/app_factory.py +410 -0
- mcp_proxy_adapter/core/app_runner.py +272 -0
- mcp_proxy_adapter/core/auth_validator.py +606 -0
- mcp_proxy_adapter/core/certificate_utils.py +1045 -0
- mcp_proxy_adapter/core/client.py +574 -0
- mcp_proxy_adapter/core/client_manager.py +284 -0
- mcp_proxy_adapter/core/client_security.py +384 -0
- mcp_proxy_adapter/core/config_converter.py +405 -0
- mcp_proxy_adapter/core/config_validator.py +218 -0
- mcp_proxy_adapter/core/logging.py +19 -3
- mcp_proxy_adapter/core/mtls_asgi.py +156 -0
- mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
- mcp_proxy_adapter/core/protocol_manager.py +385 -0
- mcp_proxy_adapter/core/proxy_client.py +602 -0
- mcp_proxy_adapter/core/proxy_registration.py +522 -0
- mcp_proxy_adapter/core/role_utils.py +426 -0
- mcp_proxy_adapter/core/security_adapter.py +370 -0
- mcp_proxy_adapter/core/security_factory.py +239 -0
- mcp_proxy_adapter/core/security_integration.py +286 -0
- mcp_proxy_adapter/core/server_adapter.py +282 -0
- mcp_proxy_adapter/core/server_engine.py +270 -0
- mcp_proxy_adapter/core/settings.py +1 -0
- mcp_proxy_adapter/core/ssl_utils.py +234 -0
- mcp_proxy_adapter/core/transport_manager.py +292 -0
- mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
- mcp_proxy_adapter/custom_openapi.py +22 -11
- mcp_proxy_adapter/examples/__init__.py +13 -4
- 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 +44 -0
- mcp_proxy_adapter/examples/commands/__init__.py +5 -0
- mcp_proxy_adapter/examples/create_certificates_simple.py +550 -0
- mcp_proxy_adapter/examples/debug_request_state.py +112 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +158 -0
- mcp_proxy_adapter/examples/demo_client.py +275 -0
- mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
- mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
- mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
- mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
- mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
- mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +80 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +90 -0
- mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +75 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +71 -0
- mcp_proxy_adapter/examples/full_application/main.py +173 -0
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
- mcp_proxy_adapter/examples/generate_all_certificates.py +362 -0
- mcp_proxy_adapter/examples/generate_certificates.py +177 -0
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
- mcp_proxy_adapter/examples/generate_test_configs.py +331 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +334 -0
- mcp_proxy_adapter/examples/run_example.py +59 -0
- mcp_proxy_adapter/examples/run_full_test_suite.py +318 -0
- mcp_proxy_adapter/examples/run_proxy_server.py +146 -0
- mcp_proxy_adapter/examples/run_security_tests.py +544 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +247 -0
- mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
- mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
- mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
- mcp_proxy_adapter/examples/security_test_client.py +782 -0
- mcp_proxy_adapter/examples/setup_test_environment.py +328 -0
- mcp_proxy_adapter/examples/test_config.py +148 -0
- mcp_proxy_adapter/examples/test_config_generator.py +86 -0
- mcp_proxy_adapter/examples/test_examples.py +281 -0
- mcp_proxy_adapter/examples/universal_client.py +620 -0
- mcp_proxy_adapter/main.py +93 -0
- mcp_proxy_adapter/utils/config_generator.py +1008 -0
- mcp_proxy_adapter/version.py +5 -2
- mcp_proxy_adapter-6.0.1.dist-info/METADATA +679 -0
- mcp_proxy_adapter-6.0.1.dist-info/RECORD +140 -0
- mcp_proxy_adapter-6.0.1.dist-info/entry_points.txt +2 -0
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/licenses/LICENSE +2 -2
- mcp_proxy_adapter/api/middleware/auth.py +0 -146
- mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
- mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
- mcp_proxy_adapter/examples/README.md +0 -124
- mcp_proxy_adapter/examples/basic_server/README.md +0 -60
- mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
- mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
- mcp_proxy_adapter/examples/basic_server/config.json +0 -35
- mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
- mcp_proxy_adapter/examples/basic_server/server.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
- mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
- mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -250
- mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
- mcp_proxy_adapter/examples/custom_commands/config.json +0 -35
- mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
- mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
- mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
- mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
- mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
- mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
- mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
- mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
- mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
- mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/server.py +0 -228
- mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
- mcp_proxy_adapter/examples/deployment/README.md +0 -49
- mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
- mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
- mcp_proxy_adapter/examples/deployment/config.json +0 -29
- mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
- mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
- mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
- mcp_proxy_adapter/examples/deployment/run.sh +0 -43
- mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
- mcp_proxy_adapter/schemas/base_schema.json +0 -114
- mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
- mcp_proxy_adapter/tests/__init__.py +0 -0
- mcp_proxy_adapter/tests/api/__init__.py +0 -3
- mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
- mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
- mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
- mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
- mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
- mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
- mcp_proxy_adapter/tests/commands/__init__.py +0 -3
- mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
- mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
- mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
- mcp_proxy_adapter/tests/conftest.py +0 -131
- mcp_proxy_adapter/tests/functional/__init__.py +0 -3
- mcp_proxy_adapter/tests/functional/test_api.py +0 -253
- mcp_proxy_adapter/tests/integration/__init__.py +0 -3
- mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
- mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
- mcp_proxy_adapter/tests/performance/__init__.py +0 -3
- mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
- mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
- mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
- mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
- mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
- mcp_proxy_adapter/tests/test_base_command.py +0 -123
- mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
- mcp_proxy_adapter/tests/test_command_registry.py +0 -281
- mcp_proxy_adapter/tests/test_config.py +0 -127
- mcp_proxy_adapter/tests/test_utils.py +0 -65
- mcp_proxy_adapter/tests/unit/__init__.py +0 -3
- mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
- mcp_proxy_adapter/tests/unit/test_config.py +0 -217
- mcp_proxy_adapter-4.1.1.dist-info/METADATA +0 -200
- mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Example Runner Script
|
4
|
+
This script provides a simple way to run the examples.
|
5
|
+
Author: Vasiliy Zdanovskiy
|
6
|
+
email: vasilyvz@gmail.com
|
7
|
+
"""
|
8
|
+
import sys
|
9
|
+
import subprocess
|
10
|
+
import argparse
|
11
|
+
from pathlib import Path
|
12
|
+
def run_basic_example(config_name: str, port: int = None):
|
13
|
+
"""Run basic framework example."""
|
14
|
+
config_path = Path(__file__).parent / "basic_framework" / "configs" / f"{config_name}.json"
|
15
|
+
main_script = Path(__file__).parent / "basic_framework" / "main.py"
|
16
|
+
if not config_path.exists():
|
17
|
+
print(f"❌ Configuration file not found: {config_path}")
|
18
|
+
return False
|
19
|
+
cmd = [sys.executable, str(main_script), "--config", str(config_path)]
|
20
|
+
if port:
|
21
|
+
cmd.extend(["--port", str(port)])
|
22
|
+
print(f"🚀 Running basic framework example with {config_name} configuration...")
|
23
|
+
return subprocess.run(cmd).returncode == 0
|
24
|
+
def run_full_example(config_name: str, port: int = None):
|
25
|
+
"""Run full application example."""
|
26
|
+
config_path = Path(__file__).parent / "full_application" / "configs" / f"{config_name}.json"
|
27
|
+
main_script = Path(__file__).parent / "full_application" / "main.py"
|
28
|
+
if not config_path.exists():
|
29
|
+
print(f"❌ Configuration file not found: {config_path}")
|
30
|
+
return False
|
31
|
+
cmd = [sys.executable, str(main_script), "--config", str(config_path)]
|
32
|
+
if port:
|
33
|
+
cmd.extend(["--port", str(port)])
|
34
|
+
print(f"🚀 Running full application example with {config_name} configuration...")
|
35
|
+
return subprocess.run(cmd).returncode == 0
|
36
|
+
def main():
|
37
|
+
"""Main function."""
|
38
|
+
parser = argparse.ArgumentParser(description="Run MCP Proxy Adapter Examples")
|
39
|
+
parser.add_argument("example", choices=["basic", "full"], help="Example type")
|
40
|
+
parser.add_argument("config", help="Configuration name (e.g., http_simple, https_auth)")
|
41
|
+
parser.add_argument("--port", type=int, help="Override port")
|
42
|
+
args = parser.parse_args()
|
43
|
+
# Available configurations
|
44
|
+
configs = [
|
45
|
+
"http_simple", "https_simple", "http_auth",
|
46
|
+
"https_auth", "mtls_no_roles", "mtls_with_roles"
|
47
|
+
]
|
48
|
+
if args.config not in configs:
|
49
|
+
print(f"❌ Unknown configuration: {args.config}")
|
50
|
+
print(f"Available configurations: {', '.join(configs)}")
|
51
|
+
return 1
|
52
|
+
# Run the appropriate example
|
53
|
+
if args.example == "basic":
|
54
|
+
success = run_basic_example(args.config, args.port)
|
55
|
+
else:
|
56
|
+
success = run_full_example(args.config, args.port)
|
57
|
+
return 0 if success else 1
|
58
|
+
if __name__ == "__main__":
|
59
|
+
sys.exit(main())
|
@@ -0,0 +1,318 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Author: Vasiliy Zdanovskiy
|
4
|
+
email: vasilyvz@gmail.com
|
5
|
+
Full test suite runner for MCP Proxy Adapter.
|
6
|
+
Automates the complete testing workflow.
|
7
|
+
"""
|
8
|
+
import os
|
9
|
+
import sys
|
10
|
+
import subprocess
|
11
|
+
import time
|
12
|
+
from pathlib import Path
|
13
|
+
from typing import List, Dict, Optional
|
14
|
+
|
15
|
+
class FullTestSuiteRunner:
|
16
|
+
"""Comprehensive test suite runner that automates the entire testing process."""
|
17
|
+
|
18
|
+
def __init__(self):
|
19
|
+
"""Initialize the test suite runner."""
|
20
|
+
self.working_dir = Path.cwd()
|
21
|
+
self.configs_dir = self.working_dir / "configs"
|
22
|
+
self.certs_dir = self.working_dir / "certs"
|
23
|
+
self.keys_dir = self.working_dir / "keys"
|
24
|
+
self.roles_file = self.working_dir / "configs" / "roles.json"
|
25
|
+
|
26
|
+
def print_step(self, step: str, description: str):
|
27
|
+
"""Print a formatted step header."""
|
28
|
+
print(f"\n{'='*60}")
|
29
|
+
print(f"🔧 STEP {step}: {description}")
|
30
|
+
print(f"{'='*60}")
|
31
|
+
|
32
|
+
def print_success(self, message: str):
|
33
|
+
"""Print a success message."""
|
34
|
+
print(f"✅ {message}")
|
35
|
+
|
36
|
+
def print_error(self, message: str):
|
37
|
+
"""Print an error message."""
|
38
|
+
print(f"❌ {message}")
|
39
|
+
|
40
|
+
def print_info(self, message: str):
|
41
|
+
"""Print an info message."""
|
42
|
+
print(f"ℹ️ {message}")
|
43
|
+
|
44
|
+
def check_environment(self) -> bool:
|
45
|
+
"""Check if the environment is properly set up."""
|
46
|
+
self.print_step("1", "Environment Validation")
|
47
|
+
|
48
|
+
# Check if we're in a virtual environment
|
49
|
+
if not hasattr(sys, 'real_prefix') and not (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix):
|
50
|
+
self.print_error("Not running in a virtual environment!")
|
51
|
+
self.print_info("Please activate your virtual environment first:")
|
52
|
+
self.print_info(" source venv/bin/activate # or .venv/bin/activate")
|
53
|
+
return False
|
54
|
+
|
55
|
+
self.print_success("Virtual environment is active")
|
56
|
+
|
57
|
+
# Check if mcp_proxy_adapter is installed
|
58
|
+
try:
|
59
|
+
import mcp_proxy_adapter
|
60
|
+
self.print_success(f"mcp_proxy_adapter is installed (version: {mcp_proxy_adapter.__version__})")
|
61
|
+
except ImportError:
|
62
|
+
self.print_error("mcp_proxy_adapter is not installed!")
|
63
|
+
self.print_info("Please install it first:")
|
64
|
+
self.print_info(" pip install mcp_proxy_adapter")
|
65
|
+
return False
|
66
|
+
|
67
|
+
# Check Python version
|
68
|
+
python_version = sys.version_info
|
69
|
+
if python_version.major >= 3 and python_version.minor >= 8:
|
70
|
+
self.print_success(f"Python version: {python_version.major}.{python_version.minor}.{python_version.micro}")
|
71
|
+
else:
|
72
|
+
self.print_error(f"Python {python_version.major}.{python_version.minor} is not supported. Need Python 3.8+")
|
73
|
+
return False
|
74
|
+
|
75
|
+
return True
|
76
|
+
|
77
|
+
def create_directories(self) -> bool:
|
78
|
+
"""Create necessary directories for testing."""
|
79
|
+
self.print_step("2", "Directory Creation")
|
80
|
+
|
81
|
+
try:
|
82
|
+
# Create configs directory
|
83
|
+
self.configs_dir.mkdir(exist_ok=True)
|
84
|
+
self.print_success(f"Created/verified configs directory: {self.configs_dir}")
|
85
|
+
|
86
|
+
# Create certs directory
|
87
|
+
self.certs_dir.mkdir(exist_ok=True)
|
88
|
+
self.print_success(f"Created/verified certs directory: {self.certs_dir}")
|
89
|
+
|
90
|
+
# Create keys directory
|
91
|
+
self.keys_dir.mkdir(exist_ok=True)
|
92
|
+
self.print_success(f"Created/verified keys directory: {self.keys_dir}")
|
93
|
+
|
94
|
+
return True
|
95
|
+
|
96
|
+
except Exception as e:
|
97
|
+
self.print_error(f"Failed to create directories: {e}")
|
98
|
+
return False
|
99
|
+
|
100
|
+
def generate_certificates(self) -> bool:
|
101
|
+
"""Generate SSL certificates for testing."""
|
102
|
+
self.print_step("3", "Certificate Generation")
|
103
|
+
|
104
|
+
try:
|
105
|
+
# Run certificate generation script
|
106
|
+
cmd = [sys.executable, "-m", "mcp_proxy_adapter.examples.create_certificates_simple"]
|
107
|
+
self.print_info("Running certificate generation script...")
|
108
|
+
|
109
|
+
result = subprocess.run(cmd, capture_output=True, text=True, cwd=self.working_dir)
|
110
|
+
|
111
|
+
if result.returncode == 0:
|
112
|
+
self.print_success("Certificates generated successfully")
|
113
|
+
if result.stdout:
|
114
|
+
print(result.stdout)
|
115
|
+
return True
|
116
|
+
else:
|
117
|
+
self.print_error("Certificate generation failed!")
|
118
|
+
if result.stderr:
|
119
|
+
print("Error output:")
|
120
|
+
print(result.stderr)
|
121
|
+
return False
|
122
|
+
|
123
|
+
except Exception as e:
|
124
|
+
self.print_error(f"Failed to generate certificates: {e}")
|
125
|
+
return False
|
126
|
+
|
127
|
+
def generate_configurations(self) -> bool:
|
128
|
+
"""Generate test configurations."""
|
129
|
+
self.print_step("4", "Configuration Generation")
|
130
|
+
|
131
|
+
try:
|
132
|
+
# Run configuration generation script
|
133
|
+
cmd = [sys.executable, "-m", "mcp_proxy_adapter.examples.generate_test_configs"]
|
134
|
+
self.print_info("Running configuration generation script...")
|
135
|
+
|
136
|
+
result = subprocess.run(cmd, capture_output=True, text=True, cwd=self.working_dir)
|
137
|
+
|
138
|
+
if result.returncode == 0:
|
139
|
+
self.print_success("Configurations generated successfully")
|
140
|
+
if result.stdout:
|
141
|
+
print(result.stdout)
|
142
|
+
return True
|
143
|
+
else:
|
144
|
+
self.print_error("Configuration generation failed!")
|
145
|
+
if result.stderr:
|
146
|
+
print("Error output:")
|
147
|
+
print(result.stderr)
|
148
|
+
return False
|
149
|
+
|
150
|
+
except Exception as e:
|
151
|
+
self.print_error(f"Failed to generate configurations: {e}")
|
152
|
+
return False
|
153
|
+
|
154
|
+
def run_security_tests(self) -> bool:
|
155
|
+
"""Run the security test suite."""
|
156
|
+
self.print_step("5", "Security Testing")
|
157
|
+
|
158
|
+
try:
|
159
|
+
# Run security tests
|
160
|
+
cmd = [sys.executable, "-m", "mcp_proxy_adapter.examples.run_security_tests", "--verbose"]
|
161
|
+
self.print_info("Running security tests...")
|
162
|
+
|
163
|
+
# Debug: show current working directory and check files
|
164
|
+
self.print_info(f"DEBUG: Current working directory: {os.getcwd()}")
|
165
|
+
self.print_info(f"DEBUG: Working directory from class: {self.working_dir}")
|
166
|
+
|
167
|
+
# Check if certificates exist before running tests
|
168
|
+
localhost_cert = self.certs_dir / "localhost_server.crt"
|
169
|
+
self.print_info(f"DEBUG: localhost_server.crt exists: {localhost_cert.exists()}")
|
170
|
+
if localhost_cert.exists():
|
171
|
+
self.print_info(f"DEBUG: localhost_server.crt is symlink: {localhost_cert.is_symlink()}")
|
172
|
+
if localhost_cert.is_symlink():
|
173
|
+
self.print_info(f"DEBUG: localhost_server.crt symlink target: {localhost_cert.readlink()}")
|
174
|
+
|
175
|
+
# List all files in certs directory
|
176
|
+
self.print_info("DEBUG: Files in certs directory:")
|
177
|
+
for file in self.certs_dir.iterdir():
|
178
|
+
self.print_info(f"DEBUG: {file.name} -> {file}")
|
179
|
+
|
180
|
+
result = subprocess.run(cmd, capture_output=True, text=True, cwd=self.working_dir)
|
181
|
+
|
182
|
+
if result.returncode == 0:
|
183
|
+
self.print_success("Security tests completed successfully!")
|
184
|
+
if result.stdout:
|
185
|
+
print(result.stdout)
|
186
|
+
return True
|
187
|
+
else:
|
188
|
+
self.print_error("Security tests failed!")
|
189
|
+
if result.stdout:
|
190
|
+
print("Test output:")
|
191
|
+
print(result.stdout)
|
192
|
+
if result.stderr:
|
193
|
+
print("Error output:")
|
194
|
+
print(result.stderr)
|
195
|
+
return False
|
196
|
+
|
197
|
+
except Exception as e:
|
198
|
+
self.print_error(f"Failed to run security tests: {e}")
|
199
|
+
return False
|
200
|
+
|
201
|
+
def cleanup(self):
|
202
|
+
"""Clean up temporary files and processes."""
|
203
|
+
self.print_info("Cleaning up...")
|
204
|
+
|
205
|
+
# Simple cleanup - just print success message
|
206
|
+
# Process cleanup is handled by the test scripts themselves
|
207
|
+
print("✅ Cleanup completed")
|
208
|
+
|
209
|
+
|
210
|
+
|
211
|
+
def cleanup_directories(self) -> bool:
|
212
|
+
"""Clean up existing test directories before starting."""
|
213
|
+
self.print_info("Cleaning up existing test directories...")
|
214
|
+
|
215
|
+
try:
|
216
|
+
import shutil
|
217
|
+
|
218
|
+
# Directories to clean
|
219
|
+
dirs_to_clean = [self.configs_dir, self.certs_dir, self.keys_dir]
|
220
|
+
files_to_clean = [self.working_dir / "roles.json"]
|
221
|
+
|
222
|
+
# Remove directories
|
223
|
+
for dir_path in dirs_to_clean:
|
224
|
+
if dir_path.exists():
|
225
|
+
shutil.rmtree(dir_path)
|
226
|
+
print(f"🗑️ Removed directory: {dir_path}")
|
227
|
+
|
228
|
+
# Remove files
|
229
|
+
for file_path in files_to_clean:
|
230
|
+
if file_path.exists():
|
231
|
+
file_path.unlink()
|
232
|
+
print(f"🗑️ Removed file: {file_path}")
|
233
|
+
|
234
|
+
self.print_success("Directory cleanup completed")
|
235
|
+
return True
|
236
|
+
|
237
|
+
except Exception as e:
|
238
|
+
self.print_error(f"Failed to cleanup directories: {e}")
|
239
|
+
return False
|
240
|
+
|
241
|
+
def run_full_suite(self) -> bool:
|
242
|
+
"""Run the complete test suite."""
|
243
|
+
print("🚀 MCP Proxy Adapter - Full Test Suite")
|
244
|
+
print("=" * 60)
|
245
|
+
print(f"Working directory: {self.working_dir}")
|
246
|
+
print(f"Python executable: {sys.executable}")
|
247
|
+
|
248
|
+
try:
|
249
|
+
# Step 0: Clean up existing directories
|
250
|
+
if not self.cleanup_directories():
|
251
|
+
return False
|
252
|
+
|
253
|
+
# Step 1: Environment validation
|
254
|
+
if not self.check_environment():
|
255
|
+
return False
|
256
|
+
|
257
|
+
# Step 2: Directory creation
|
258
|
+
if not self.create_directories():
|
259
|
+
return False
|
260
|
+
|
261
|
+
# Step 3: Certificate generation
|
262
|
+
if not self.generate_certificates():
|
263
|
+
return False
|
264
|
+
|
265
|
+
# Step 4: Configuration generation
|
266
|
+
if not self.generate_configurations():
|
267
|
+
return False
|
268
|
+
|
269
|
+
# Step 5: Security testing
|
270
|
+
if not self.run_security_tests():
|
271
|
+
return False
|
272
|
+
|
273
|
+
# All steps completed successfully
|
274
|
+
print(f"\n{'='*60}")
|
275
|
+
print("🎉 FULL TEST SUITE COMPLETED SUCCESSFULLY!")
|
276
|
+
print("="*60)
|
277
|
+
print("✅ Environment validated")
|
278
|
+
print("✅ Directories cleaned")
|
279
|
+
print("✅ Directories created")
|
280
|
+
print("✅ Certificates generated")
|
281
|
+
print("✅ Configurations generated")
|
282
|
+
print("✅ Security tests passed")
|
283
|
+
print(f"\n📁 Test artifacts created in: {self.working_dir}")
|
284
|
+
print(f"📁 Configurations: {self.configs_dir}")
|
285
|
+
print(f"📁 Certificates: {self.certs_dir}")
|
286
|
+
print(f"📁 Keys: {self.keys_dir}")
|
287
|
+
|
288
|
+
return True
|
289
|
+
|
290
|
+
except KeyboardInterrupt:
|
291
|
+
print("\n\n⚠️ Test suite interrupted by user")
|
292
|
+
return False
|
293
|
+
except Exception as e:
|
294
|
+
self.print_error(f"Unexpected error during test suite execution: {e}")
|
295
|
+
return False
|
296
|
+
finally:
|
297
|
+
try:
|
298
|
+
self.print_info("Starting cleanup in finally block...")
|
299
|
+
self.cleanup()
|
300
|
+
self.print_info("Cleanup in finally block completed")
|
301
|
+
except Exception as e:
|
302
|
+
self.print_error(f"Cleanup failed in finally block: {e}")
|
303
|
+
import traceback
|
304
|
+
traceback.print_exc()
|
305
|
+
|
306
|
+
def main():
|
307
|
+
"""Main entry point."""
|
308
|
+
runner = FullTestSuiteRunner()
|
309
|
+
|
310
|
+
try:
|
311
|
+
success = runner.run_full_suite()
|
312
|
+
sys.exit(0 if success else 1)
|
313
|
+
except Exception as e:
|
314
|
+
print(f"❌ Fatal error: {e}")
|
315
|
+
sys.exit(1)
|
316
|
+
|
317
|
+
if __name__ == "__main__":
|
318
|
+
main()
|
@@ -0,0 +1,146 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Author: Vasiliy Zdanovskiy
|
4
|
+
email: vasilyvz@gmail.com
|
5
|
+
|
6
|
+
Lightweight local proxy server for MCP Proxy Adapter examples.
|
7
|
+
|
8
|
+
This server provides proxy registration endpoints at /proxy for adapter instances
|
9
|
+
to register/unregister/heartbeat and for simple discovery.
|
10
|
+
"""
|
11
|
+
|
12
|
+
import argparse
|
13
|
+
import asyncio
|
14
|
+
import signal
|
15
|
+
import sys
|
16
|
+
from typing import Dict, List, Optional
|
17
|
+
import json
|
18
|
+
from datetime import datetime, timedelta
|
19
|
+
|
20
|
+
from fastapi import FastAPI, HTTPException
|
21
|
+
from pydantic import BaseModel
|
22
|
+
from mcp_proxy_adapter.core.server_adapter import UnifiedServerRunner
|
23
|
+
|
24
|
+
|
25
|
+
# Simple in-memory storage for registered adapters
|
26
|
+
registered_adapters: Dict[str, Dict] = {}
|
27
|
+
|
28
|
+
|
29
|
+
class AdapterRegistration(BaseModel):
|
30
|
+
name: str
|
31
|
+
url: str
|
32
|
+
capabilities: List[str]
|
33
|
+
metadata: Optional[Dict] = {}
|
34
|
+
|
35
|
+
|
36
|
+
class ProxyRouter:
|
37
|
+
"""Simple proxy router for MCP examples."""
|
38
|
+
|
39
|
+
def __init__(self):
|
40
|
+
self.app = FastAPI(title="MCP Local Proxy", version="1.0.0")
|
41
|
+
self._setup_routes()
|
42
|
+
|
43
|
+
def _setup_routes(self):
|
44
|
+
@self.app.post("/proxy/register")
|
45
|
+
async def register_adapter(registration: AdapterRegistration):
|
46
|
+
"""Register an adapter with the proxy."""
|
47
|
+
adapter_id = registration.name
|
48
|
+
registered_adapters[adapter_id] = {
|
49
|
+
"name": registration.name,
|
50
|
+
"url": registration.url,
|
51
|
+
"capabilities": registration.capabilities,
|
52
|
+
"metadata": registration.metadata,
|
53
|
+
"registered_at": datetime.now().isoformat(),
|
54
|
+
"last_heartbeat": datetime.now().isoformat(),
|
55
|
+
"status": "active"
|
56
|
+
}
|
57
|
+
print(f"✅ Registered adapter: {adapter_id} at {registration.url}")
|
58
|
+
return {"status": "registered", "adapter_id": adapter_id}
|
59
|
+
|
60
|
+
@self.app.post("/proxy/unregister")
|
61
|
+
async def unregister_adapter(adapter_id: str):
|
62
|
+
"""Unregister an adapter from the proxy."""
|
63
|
+
if adapter_id in registered_adapters:
|
64
|
+
del registered_adapters[adapter_id]
|
65
|
+
print(f"✅ Unregistered adapter: {adapter_id}")
|
66
|
+
return {"status": "unregistered", "adapter_id": adapter_id}
|
67
|
+
else:
|
68
|
+
raise HTTPException(status_code=404, detail="Adapter not found")
|
69
|
+
|
70
|
+
@self.app.get("/proxy/list")
|
71
|
+
async def list_adapters():
|
72
|
+
"""List all registered adapters."""
|
73
|
+
return {
|
74
|
+
"adapters": list(registered_adapters.values()),
|
75
|
+
"count": len(registered_adapters)
|
76
|
+
}
|
77
|
+
|
78
|
+
@self.app.get("/proxy/health")
|
79
|
+
async def health_check():
|
80
|
+
"""Health check endpoint."""
|
81
|
+
return {
|
82
|
+
"status": "healthy",
|
83
|
+
"timestamp": datetime.now().isoformat(),
|
84
|
+
"adapters_count": len(registered_adapters)
|
85
|
+
}
|
86
|
+
|
87
|
+
@self.app.post("/proxy/heartbeat")
|
88
|
+
async def heartbeat(adapter_id: str):
|
89
|
+
"""Receive heartbeat from adapter."""
|
90
|
+
if adapter_id in registered_adapters:
|
91
|
+
registered_adapters[adapter_id]["last_heartbeat"] = datetime.now().isoformat()
|
92
|
+
return {"status": "ok", "adapter_id": adapter_id}
|
93
|
+
else:
|
94
|
+
raise HTTPException(status_code=404, detail="Adapter not found")
|
95
|
+
|
96
|
+
|
97
|
+
def create_proxy_app() -> FastAPI:
|
98
|
+
"""Create FastAPI app with proxy endpoints."""
|
99
|
+
router = ProxyRouter()
|
100
|
+
return router.app
|
101
|
+
|
102
|
+
|
103
|
+
def main() -> None:
|
104
|
+
parser = argparse.ArgumentParser(description="Run local proxy server for MCP examples")
|
105
|
+
parser.add_argument("--host", default="127.0.0.1", help="Host to bind to (default: 127.0.0.1)")
|
106
|
+
parser.add_argument("--port", type=int, default=3004, help="Port to bind to (default: 3004)")
|
107
|
+
parser.add_argument("--log-level", default="info", choices=["debug", "info", "warning", "error"], help="Log level")
|
108
|
+
|
109
|
+
args = parser.parse_args()
|
110
|
+
|
111
|
+
# Create FastAPI app
|
112
|
+
app = create_proxy_app()
|
113
|
+
|
114
|
+
# Setup graceful shutdown
|
115
|
+
def signal_handler(signum, frame):
|
116
|
+
print("\n🛑 Shutting down proxy server...")
|
117
|
+
sys.exit(0)
|
118
|
+
|
119
|
+
signal.signal(signal.SIGINT, signal_handler)
|
120
|
+
signal.signal(signal.SIGTERM, signal_handler)
|
121
|
+
|
122
|
+
print("🚀 Starting MCP Local Proxy Server...")
|
123
|
+
print(f"📡 Server URL: http://{args.host}:{args.port}")
|
124
|
+
print(f"🔗 Proxy endpoints available at: http://{args.host}:{args.port}/proxy")
|
125
|
+
print("📋 Supported endpoints:")
|
126
|
+
print(" POST /proxy/register - Register adapter")
|
127
|
+
print(" POST /proxy/unregister - Unregister adapter")
|
128
|
+
print(" GET /proxy/list - List registered adapters")
|
129
|
+
print(" GET /proxy/health - Health check")
|
130
|
+
print(" POST /proxy/heartbeat - Heartbeat from adapter")
|
131
|
+
print("⚡ Press Ctrl+C to stop\n")
|
132
|
+
|
133
|
+
# Run server via unified runner (hypercorn under the hood)
|
134
|
+
runner = UnifiedServerRunner()
|
135
|
+
runner.run_server(
|
136
|
+
app,
|
137
|
+
{
|
138
|
+
"host": args.host,
|
139
|
+
"port": args.port,
|
140
|
+
"log_level": args.log_level,
|
141
|
+
},
|
142
|
+
)
|
143
|
+
|
144
|
+
|
145
|
+
if __name__ == "__main__":
|
146
|
+
main()
|