mcp-proxy-adapter 2.0.1__py3-none-any.whl โ 6.9.50__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mcp-proxy-adapter might be problematic. Click here for more details.
- mcp_proxy_adapter/__init__.py +47 -0
- mcp_proxy_adapter/__main__.py +13 -0
- mcp_proxy_adapter/api/__init__.py +0 -0
- mcp_proxy_adapter/api/app.py +66 -0
- mcp_proxy_adapter/api/core/__init__.py +18 -0
- mcp_proxy_adapter/api/core/app_factory.py +400 -0
- mcp_proxy_adapter/api/core/lifespan_manager.py +55 -0
- mcp_proxy_adapter/api/core/registration_context.py +356 -0
- mcp_proxy_adapter/api/core/registration_manager.py +307 -0
- mcp_proxy_adapter/api/core/registration_tasks.py +84 -0
- mcp_proxy_adapter/api/core/ssl_context_factory.py +88 -0
- mcp_proxy_adapter/api/handlers.py +181 -0
- mcp_proxy_adapter/api/middleware/__init__.py +21 -0
- mcp_proxy_adapter/api/middleware/base.py +54 -0
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +73 -0
- mcp_proxy_adapter/api/middleware/error_handling.py +76 -0
- mcp_proxy_adapter/api/middleware/factory.py +147 -0
- mcp_proxy_adapter/api/middleware/logging.py +31 -0
- mcp_proxy_adapter/api/middleware/performance.py +51 -0
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +140 -0
- mcp_proxy_adapter/api/middleware/transport_middleware.py +87 -0
- mcp_proxy_adapter/api/middleware/unified_security.py +223 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +132 -0
- mcp_proxy_adapter/api/openapi/__init__.py +21 -0
- mcp_proxy_adapter/api/openapi/command_integration.py +105 -0
- mcp_proxy_adapter/api/openapi/openapi_generator.py +40 -0
- mcp_proxy_adapter/api/openapi/openapi_registry.py +62 -0
- mcp_proxy_adapter/api/openapi/schema_loader.py +116 -0
- mcp_proxy_adapter/api/schemas.py +270 -0
- mcp_proxy_adapter/api/tool_integration.py +131 -0
- mcp_proxy_adapter/api/tools.py +163 -0
- mcp_proxy_adapter/cli/__init__.py +12 -0
- mcp_proxy_adapter/cli/commands/__init__.py +15 -0
- mcp_proxy_adapter/cli/commands/client.py +100 -0
- mcp_proxy_adapter/cli/commands/config_generate.py +105 -0
- mcp_proxy_adapter/cli/commands/config_validate.py +94 -0
- mcp_proxy_adapter/cli/commands/generate.py +259 -0
- mcp_proxy_adapter/cli/commands/server.py +174 -0
- mcp_proxy_adapter/cli/commands/sets.py +132 -0
- mcp_proxy_adapter/cli/commands/testconfig.py +177 -0
- mcp_proxy_adapter/cli/examples/__init__.py +8 -0
- mcp_proxy_adapter/cli/examples/http_basic.py +82 -0
- mcp_proxy_adapter/cli/examples/https_token.py +96 -0
- mcp_proxy_adapter/cli/examples/mtls_roles.py +103 -0
- mcp_proxy_adapter/cli/main.py +63 -0
- mcp_proxy_adapter/cli/parser.py +338 -0
- mcp_proxy_adapter/cli/validators.py +231 -0
- mcp_proxy_adapter/client/jsonrpc_client/__init__.py +9 -0
- mcp_proxy_adapter/client/jsonrpc_client/client.py +42 -0
- mcp_proxy_adapter/client/jsonrpc_client/command_api.py +45 -0
- mcp_proxy_adapter/client/jsonrpc_client/proxy_api.py +224 -0
- mcp_proxy_adapter/client/jsonrpc_client/queue_api.py +60 -0
- mcp_proxy_adapter/client/jsonrpc_client/transport.py +108 -0
- mcp_proxy_adapter/client/proxy.py +123 -0
- mcp_proxy_adapter/commands/__init__.py +66 -0
- mcp_proxy_adapter/commands/auth_validation_command.py +69 -0
- mcp_proxy_adapter/commands/base.py +389 -0
- mcp_proxy_adapter/commands/builtin_commands.py +30 -0
- mcp_proxy_adapter/commands/catalog/__init__.py +20 -0
- mcp_proxy_adapter/commands/catalog/catalog_loader.py +34 -0
- mcp_proxy_adapter/commands/catalog/catalog_manager.py +122 -0
- mcp_proxy_adapter/commands/catalog/catalog_syncer.py +149 -0
- mcp_proxy_adapter/commands/catalog/command_catalog.py +43 -0
- mcp_proxy_adapter/commands/catalog/dependency_manager.py +37 -0
- mcp_proxy_adapter/commands/catalog_manager.py +97 -0
- mcp_proxy_adapter/commands/cert_monitor_command.py +552 -0
- mcp_proxy_adapter/commands/certificate_management_command.py +562 -0
- mcp_proxy_adapter/commands/command_registry.py +298 -0
- mcp_proxy_adapter/commands/config_command.py +102 -0
- mcp_proxy_adapter/commands/dependency_container.py +40 -0
- mcp_proxy_adapter/commands/dependency_manager.py +143 -0
- mcp_proxy_adapter/commands/echo_command.py +48 -0
- mcp_proxy_adapter/commands/health_command.py +142 -0
- mcp_proxy_adapter/commands/help_command.py +175 -0
- mcp_proxy_adapter/commands/hooks.py +172 -0
- mcp_proxy_adapter/commands/key_management_command.py +484 -0
- mcp_proxy_adapter/commands/load_command.py +123 -0
- mcp_proxy_adapter/commands/plugins_command.py +246 -0
- mcp_proxy_adapter/commands/protocol_management_command.py +216 -0
- mcp_proxy_adapter/commands/proxy_registration_command.py +319 -0
- mcp_proxy_adapter/commands/queue_commands.py +750 -0
- mcp_proxy_adapter/commands/registration_status_command.py +76 -0
- mcp_proxy_adapter/commands/registry/__init__.py +18 -0
- mcp_proxy_adapter/commands/registry/command_info.py +103 -0
- mcp_proxy_adapter/commands/registry/command_loader.py +207 -0
- mcp_proxy_adapter/commands/registry/command_manager.py +119 -0
- mcp_proxy_adapter/commands/registry/command_registry.py +217 -0
- mcp_proxy_adapter/commands/reload_command.py +136 -0
- mcp_proxy_adapter/commands/result.py +157 -0
- mcp_proxy_adapter/commands/role_test_command.py +99 -0
- mcp_proxy_adapter/commands/roles_management_command.py +502 -0
- mcp_proxy_adapter/commands/security_command.py +472 -0
- mcp_proxy_adapter/commands/settings_command.py +113 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +306 -0
- mcp_proxy_adapter/commands/token_management_command.py +500 -0
- mcp_proxy_adapter/commands/transport_management_command.py +129 -0
- mcp_proxy_adapter/commands/unload_command.py +92 -0
- mcp_proxy_adapter/config.py +32 -0
- mcp_proxy_adapter/core/__init__.py +8 -0
- mcp_proxy_adapter/core/app_factory.py +560 -0
- mcp_proxy_adapter/core/app_runner.py +318 -0
- mcp_proxy_adapter/core/auth_validator.py +508 -0
- mcp_proxy_adapter/core/certificate/__init__.py +20 -0
- mcp_proxy_adapter/core/certificate/certificate_creator.py +372 -0
- mcp_proxy_adapter/core/certificate/certificate_extractor.py +185 -0
- mcp_proxy_adapter/core/certificate/certificate_utils.py +249 -0
- mcp_proxy_adapter/core/certificate/certificate_validator.py +481 -0
- mcp_proxy_adapter/core/certificate/ssl_context_manager.py +65 -0
- mcp_proxy_adapter/core/certificate_utils.py +249 -0
- mcp_proxy_adapter/core/client.py +608 -0
- mcp_proxy_adapter/core/client_manager.py +271 -0
- mcp_proxy_adapter/core/client_security.py +411 -0
- mcp_proxy_adapter/core/config/__init__.py +18 -0
- mcp_proxy_adapter/core/config/config.py +237 -0
- mcp_proxy_adapter/core/config/config_factory.py +22 -0
- mcp_proxy_adapter/core/config/config_loader.py +66 -0
- mcp_proxy_adapter/core/config/feature_manager.py +31 -0
- mcp_proxy_adapter/core/config/simple_config.py +204 -0
- mcp_proxy_adapter/core/config/simple_config_generator.py +131 -0
- mcp_proxy_adapter/core/config/simple_config_validator.py +476 -0
- mcp_proxy_adapter/core/config_converter.py +252 -0
- mcp_proxy_adapter/core/config_validator.py +211 -0
- mcp_proxy_adapter/core/crl_utils.py +362 -0
- mcp_proxy_adapter/core/errors.py +276 -0
- mcp_proxy_adapter/core/job_manager.py +54 -0
- mcp_proxy_adapter/core/logging.py +250 -0
- mcp_proxy_adapter/core/mtls_asgi.py +140 -0
- mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
- mcp_proxy_adapter/core/mtls_proxy.py +229 -0
- mcp_proxy_adapter/core/mtls_server.py +154 -0
- mcp_proxy_adapter/core/protocol_manager.py +232 -0
- mcp_proxy_adapter/core/proxy/__init__.py +19 -0
- mcp_proxy_adapter/core/proxy/auth_manager.py +26 -0
- mcp_proxy_adapter/core/proxy/proxy_registration_manager.py +160 -0
- mcp_proxy_adapter/core/proxy/registration_client.py +186 -0
- mcp_proxy_adapter/core/proxy/ssl_manager.py +101 -0
- mcp_proxy_adapter/core/proxy_client.py +184 -0
- mcp_proxy_adapter/core/proxy_registration.py +80 -0
- mcp_proxy_adapter/core/role_utils.py +103 -0
- mcp_proxy_adapter/core/security_adapter.py +343 -0
- mcp_proxy_adapter/core/security_factory.py +96 -0
- mcp_proxy_adapter/core/security_integration.py +342 -0
- mcp_proxy_adapter/core/server_adapter.py +251 -0
- mcp_proxy_adapter/core/server_engine.py +217 -0
- mcp_proxy_adapter/core/settings.py +260 -0
- mcp_proxy_adapter/core/signal_handler.py +107 -0
- mcp_proxy_adapter/core/ssl_utils.py +161 -0
- mcp_proxy_adapter/core/transport_manager.py +153 -0
- mcp_proxy_adapter/core/unified_config_adapter.py +471 -0
- mcp_proxy_adapter/core/utils.py +101 -0
- mcp_proxy_adapter/core/validation/__init__.py +21 -0
- mcp_proxy_adapter/core/validation/config_validator.py +219 -0
- mcp_proxy_adapter/core/validation/file_validator.py +131 -0
- mcp_proxy_adapter/core/validation/protocol_validator.py +205 -0
- mcp_proxy_adapter/core/validation/security_validator.py +140 -0
- mcp_proxy_adapter/core/validation/validation_result.py +27 -0
- mcp_proxy_adapter/custom_openapi.py +58 -0
- mcp_proxy_adapter/examples/__init__.py +16 -0
- mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
- mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +52 -0
- mcp_proxy_adapter/examples/bugfix_certificate_config.py +261 -0
- mcp_proxy_adapter/examples/cert_manager_bugfix.py +203 -0
- mcp_proxy_adapter/examples/check_config.py +413 -0
- mcp_proxy_adapter/examples/client_usage_example.py +164 -0
- mcp_proxy_adapter/examples/commands/__init__.py +5 -0
- mcp_proxy_adapter/examples/config_builder.py +234 -0
- mcp_proxy_adapter/examples/config_cli.py +282 -0
- mcp_proxy_adapter/examples/create_test_configs.py +174 -0
- mcp_proxy_adapter/examples/debug_request_state.py +130 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +191 -0
- mcp_proxy_adapter/examples/demo_client.py +287 -0
- mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/full_application/commands/__init__.py +8 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +45 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +52 -0
- mcp_proxy_adapter/examples/full_application/commands/echo_command.py +32 -0
- mcp_proxy_adapter/examples/full_application/commands/help_command.py +54 -0
- mcp_proxy_adapter/examples/full_application/commands/list_command.py +57 -0
- mcp_proxy_adapter/examples/full_application/hooks/__init__.py +5 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +29 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +27 -0
- mcp_proxy_adapter/examples/full_application/main.py +311 -0
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +161 -0
- mcp_proxy_adapter/examples/full_application/run_mtls.py +252 -0
- mcp_proxy_adapter/examples/full_application/run_simple.py +152 -0
- mcp_proxy_adapter/examples/full_application/test_minimal_server.py +45 -0
- mcp_proxy_adapter/examples/full_application/test_server.py +163 -0
- mcp_proxy_adapter/examples/full_application/test_simple_server.py +62 -0
- mcp_proxy_adapter/examples/generate_config.py +502 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +335 -0
- mcp_proxy_adapter/examples/queue_demo_simple.py +632 -0
- mcp_proxy_adapter/examples/queue_integration_example.py +578 -0
- mcp_proxy_adapter/examples/queue_server_demo.py +82 -0
- mcp_proxy_adapter/examples/queue_server_example.py +85 -0
- mcp_proxy_adapter/examples/queue_server_simple.py +173 -0
- mcp_proxy_adapter/examples/required_certificates.py +208 -0
- mcp_proxy_adapter/examples/run_example.py +77 -0
- mcp_proxy_adapter/examples/run_full_test_suite.py +619 -0
- mcp_proxy_adapter/examples/run_proxy_server.py +153 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +435 -0
- mcp_proxy_adapter/examples/security_test/__init__.py +18 -0
- mcp_proxy_adapter/examples/security_test/auth_manager.py +14 -0
- mcp_proxy_adapter/examples/security_test/ssl_context_manager.py +28 -0
- mcp_proxy_adapter/examples/security_test/test_client.py +159 -0
- mcp_proxy_adapter/examples/security_test/test_result.py +22 -0
- mcp_proxy_adapter/examples/security_test_client.py +72 -0
- mcp_proxy_adapter/examples/setup/__init__.py +24 -0
- mcp_proxy_adapter/examples/setup/certificate_manager.py +215 -0
- mcp_proxy_adapter/examples/setup/config_generator.py +12 -0
- mcp_proxy_adapter/examples/setup/config_validator.py +118 -0
- mcp_proxy_adapter/examples/setup/environment_setup.py +62 -0
- mcp_proxy_adapter/examples/setup/test_files_generator.py +10 -0
- mcp_proxy_adapter/examples/setup/test_runner.py +89 -0
- mcp_proxy_adapter/examples/setup_test_environment.py +235 -0
- mcp_proxy_adapter/examples/simple_protocol_test.py +125 -0
- mcp_proxy_adapter/examples/test_chk_hostname_automated.py +211 -0
- mcp_proxy_adapter/examples/test_config.py +205 -0
- mcp_proxy_adapter/examples/test_config_builder.py +110 -0
- mcp_proxy_adapter/examples/test_examples.py +308 -0
- mcp_proxy_adapter/examples/test_framework_complete.py +267 -0
- mcp_proxy_adapter/examples/test_mcp_server.py +187 -0
- mcp_proxy_adapter/examples/test_protocol_examples.py +337 -0
- mcp_proxy_adapter/examples/universal_client.py +674 -0
- mcp_proxy_adapter/examples/update_config_certificates.py +135 -0
- mcp_proxy_adapter/examples/validate_generator_compatibility.py +385 -0
- mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +61 -0
- mcp_proxy_adapter/integrations/__init__.py +25 -0
- mcp_proxy_adapter/integrations/queuemgr_integration.py +462 -0
- mcp_proxy_adapter/main.py +311 -0
- mcp_proxy_adapter/openapi.py +375 -0
- mcp_proxy_adapter/schemas/base_schema.json +114 -0
- mcp_proxy_adapter/schemas/openapi_schema.json +314 -0
- mcp_proxy_adapter/schemas/roles.json +37 -0
- mcp_proxy_adapter/schemas/roles_schema.json +162 -0
- mcp_proxy_adapter/version.py +5 -0
- mcp_proxy_adapter-6.9.50.dist-info/METADATA +1088 -0
- mcp_proxy_adapter-6.9.50.dist-info/RECORD +242 -0
- {mcp_proxy_adapter-2.0.1.dist-info โ mcp_proxy_adapter-6.9.50.dist-info}/WHEEL +1 -1
- mcp_proxy_adapter-6.9.50.dist-info/entry_points.txt +14 -0
- mcp_proxy_adapter-6.9.50.dist-info/top_level.txt +1 -0
- adapters/__init__.py +0 -16
- analyzers/__init__.py +0 -14
- analyzers/docstring_analyzer.py +0 -199
- analyzers/type_analyzer.py +0 -151
- cli/__init__.py +0 -12
- cli/__main__.py +0 -79
- cli/command_runner.py +0 -233
- dispatchers/__init__.py +0 -14
- dispatchers/base_dispatcher.py +0 -85
- dispatchers/json_rpc_dispatcher.py +0 -198
- generators/__init__.py +0 -14
- generators/endpoint_generator.py +0 -172
- generators/openapi_generator.py +0 -254
- generators/rest_api_generator.py +0 -207
- mcp_proxy_adapter-2.0.1.dist-info/METADATA +0 -272
- mcp_proxy_adapter-2.0.1.dist-info/RECORD +0 -28
- mcp_proxy_adapter-2.0.1.dist-info/licenses/LICENSE +0 -21
- mcp_proxy_adapter-2.0.1.dist-info/top_level.txt +0 -7
- openapi_schema/__init__.py +0 -38
- openapi_schema/command_registry.py +0 -312
- openapi_schema/rest_schema.py +0 -510
- openapi_schema/rpc_generator.py +0 -307
- openapi_schema/rpc_schema.py +0 -416
- validators/__init__.py +0 -14
- validators/base_validator.py +0 -23
- validators/docstring_validator.py +0 -75
- validators/metadata_validator.py +0 -76
|
@@ -0,0 +1,153 @@
|
|
|
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
|
+
|
|
18
|
+
from fastapi import FastAPI, HTTPException
|
|
19
|
+
from pydantic import BaseModel
|
|
20
|
+
|
|
21
|
+
# Simple in-memory storage for registered adapters
|
|
22
|
+
registered_adapters: Dict[str, Dict] = {}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class AdapterRegistration(BaseModel):
|
|
26
|
+
server_id: Optional[str] = None # Preferred field name
|
|
27
|
+
server_url: Optional[str] = None # Preferred field name
|
|
28
|
+
name: Optional[str] = None # Legacy field name (for backward compatibility)
|
|
29
|
+
url: Optional[str] = None # Legacy field name (for backward compatibility)
|
|
30
|
+
capabilities: List[str] = []
|
|
31
|
+
metadata: Optional[Dict] = {}
|
|
32
|
+
|
|
33
|
+
def get_server_id(self) -> str:
|
|
34
|
+
"""Get server ID from either server_id (preferred) or name (legacy)."""
|
|
35
|
+
return self.server_id or self.name or "unknown"
|
|
36
|
+
|
|
37
|
+
def get_server_url(self) -> str:
|
|
38
|
+
"""Get server URL from either server_url (preferred) or url (legacy)."""
|
|
39
|
+
return self.server_url or self.url or ""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ProxyRouter:
|
|
43
|
+
"""Simple proxy router for MCP examples."""
|
|
44
|
+
|
|
45
|
+
def __init__(self):
|
|
46
|
+
self.app = FastAPI(title="MCP Local Proxy", version="1.0.0")
|
|
47
|
+
self._setup_routes()
|
|
48
|
+
|
|
49
|
+
def _setup_routes(self):
|
|
50
|
+
@self.app.post("/register")
|
|
51
|
+
def register(adapter: AdapterRegistration): # type: ignore[name-defined]
|
|
52
|
+
server_id = adapter.get_server_id()
|
|
53
|
+
server_url = adapter.get_server_url()
|
|
54
|
+
if not server_id or not server_url:
|
|
55
|
+
raise HTTPException(
|
|
56
|
+
status_code=400,
|
|
57
|
+
detail="server_id (or name) and server_url (or url) are required",
|
|
58
|
+
)
|
|
59
|
+
registered_adapters[server_id] = {
|
|
60
|
+
"server_id": server_id,
|
|
61
|
+
"server_url": server_url,
|
|
62
|
+
"capabilities": adapter.capabilities,
|
|
63
|
+
"metadata": adapter.metadata or {},
|
|
64
|
+
}
|
|
65
|
+
return {"status": "ok", "registered": server_id}
|
|
66
|
+
|
|
67
|
+
@self.app.post("/unregister")
|
|
68
|
+
def unregister(adapter: AdapterRegistration): # type: ignore[name-defined]
|
|
69
|
+
server_id = adapter.get_server_id()
|
|
70
|
+
registered_adapters.pop(server_id, None)
|
|
71
|
+
return {"status": "ok", "unregistered": server_id}
|
|
72
|
+
|
|
73
|
+
@self.app.post("/proxy/heartbeat")
|
|
74
|
+
def heartbeat(adapter: AdapterRegistration): # type: ignore[name-defined]
|
|
75
|
+
server_id = adapter.get_server_id()
|
|
76
|
+
if server_id in registered_adapters:
|
|
77
|
+
return {"status": "ok", "heartbeat": server_id}
|
|
78
|
+
raise HTTPException(status_code=404, detail="Adapter not registered")
|
|
79
|
+
|
|
80
|
+
@self.app.get("/proxy/list")
|
|
81
|
+
def list_registered():
|
|
82
|
+
return {"servers": list(registered_adapters.values())}
|
|
83
|
+
|
|
84
|
+
@self.app.get("/proxy/health")
|
|
85
|
+
def proxy_health():
|
|
86
|
+
return {"status": "ok", "model": "mcp-local-proxy", "version": "1.0.0"}
|
|
87
|
+
|
|
88
|
+
# Compatibility endpoint expected by test instructions
|
|
89
|
+
@self.app.get("/servers")
|
|
90
|
+
def servers_plain():
|
|
91
|
+
return list(registered_adapters.values())
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def create_proxy_app() -> FastAPI:
|
|
95
|
+
"""Create FastAPI app with proxy endpoints."""
|
|
96
|
+
router = ProxyRouter()
|
|
97
|
+
return router.app
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def main() -> None:
|
|
101
|
+
parser = argparse.ArgumentParser(
|
|
102
|
+
description="Run local proxy server for MCP examples"
|
|
103
|
+
)
|
|
104
|
+
parser.add_argument(
|
|
105
|
+
"--host", default="127.0.0.1", help="Host to bind to (default: 127.0.0.1)"
|
|
106
|
+
)
|
|
107
|
+
parser.add_argument(
|
|
108
|
+
"--port", type=int, default=3004, help="Port to bind to (default: 3004)"
|
|
109
|
+
)
|
|
110
|
+
parser.add_argument(
|
|
111
|
+
"--log-level",
|
|
112
|
+
default="info",
|
|
113
|
+
choices=["debug", "info", "warning", "error"],
|
|
114
|
+
help="Log level",
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
args = parser.parse_args()
|
|
118
|
+
|
|
119
|
+
# Create FastAPI app
|
|
120
|
+
app = create_proxy_app()
|
|
121
|
+
|
|
122
|
+
# Setup graceful shutdown
|
|
123
|
+
def signal_handler(signum, frame): # type: ignore[no-redef]
|
|
124
|
+
print("\n๐ Proxy server stopping...")
|
|
125
|
+
sys.exit(0)
|
|
126
|
+
|
|
127
|
+
signal.signal(signal.SIGINT, signal_handler)
|
|
128
|
+
signal.signal(signal.SIGTERM, signal_handler)
|
|
129
|
+
|
|
130
|
+
print("๐ Starting MCP Local Proxy Server...")
|
|
131
|
+
print(f"๐ก Server URL: http://{args.host}:{args.port}")
|
|
132
|
+
print(f"๐ Proxy endpoints available at: http://{args.host}:{args.port}/proxy")
|
|
133
|
+
print("๐ Supported endpoints:")
|
|
134
|
+
print(" POST /proxy/register - Register adapter")
|
|
135
|
+
print(" POST /proxy/unregister - Unregister adapter")
|
|
136
|
+
print(" GET /proxy/list - List registered adapters")
|
|
137
|
+
print(" GET /proxy/health - Health check")
|
|
138
|
+
print(" POST /proxy/heartbeat - Heartbeat from adapter")
|
|
139
|
+
print("โก Press Ctrl+C to stop\n")
|
|
140
|
+
|
|
141
|
+
# Run server with Hypercorn
|
|
142
|
+
from hypercorn.asyncio import serve
|
|
143
|
+
from hypercorn.config import Config
|
|
144
|
+
|
|
145
|
+
config = Config()
|
|
146
|
+
config.bind = [f"{args.host}:{args.port}"]
|
|
147
|
+
config.loglevel = args.log_level
|
|
148
|
+
|
|
149
|
+
asyncio.run(serve(app, config))
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
if __name__ == "__main__":
|
|
153
|
+
main()
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Security Testing Script - Fixed Version
|
|
4
|
+
This script runs comprehensive security tests without fallback mode
|
|
5
|
+
and with proper port management.
|
|
6
|
+
Author: Vasiliy Zdanovskiy
|
|
7
|
+
email: vasilyvz@gmail.com
|
|
8
|
+
"""
|
|
9
|
+
import asyncio
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
import time
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
# Add project root to path
|
|
18
|
+
project_root = Path(__file__).parent.parent.parent
|
|
19
|
+
sys.path.insert(0, str(project_root))
|
|
20
|
+
from security_test_client import SecurityTestClient, TestResult
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SecurityTestRunner:
|
|
24
|
+
"""Security test runner with proper port management."""
|
|
25
|
+
|
|
26
|
+
def __init__(self):
|
|
27
|
+
self.project_root = Path(__file__).parent.parent.parent
|
|
28
|
+
self.configs_dir = self.project_root / "configs"
|
|
29
|
+
self.server_processes = {}
|
|
30
|
+
self.test_results = []
|
|
31
|
+
|
|
32
|
+
def kill_process_on_port(self, port: int) -> bool:
|
|
33
|
+
"""Kill process using specific port."""
|
|
34
|
+
try:
|
|
35
|
+
# Find process using the port
|
|
36
|
+
result = subprocess.run(
|
|
37
|
+
["lsof", "-ti", f":{port}"], capture_output=True, text=True, timeout=5
|
|
38
|
+
)
|
|
39
|
+
if result.returncode == 0 and result.stdout.strip():
|
|
40
|
+
pid = result.stdout.strip()
|
|
41
|
+
# Kill the process
|
|
42
|
+
subprocess.run(["kill", "-9", pid], check=True)
|
|
43
|
+
print(f"โ
Killed process {pid} on port {port}")
|
|
44
|
+
time.sleep(1) # Wait for port to be released
|
|
45
|
+
return True
|
|
46
|
+
else:
|
|
47
|
+
print(f"โน๏ธ No process found on port {port}")
|
|
48
|
+
return True
|
|
49
|
+
except subprocess.TimeoutExpired:
|
|
50
|
+
print(f"โ ๏ธ Timeout checking port {port}")
|
|
51
|
+
return False
|
|
52
|
+
except Exception as e:
|
|
53
|
+
print(f"โ Error killing process on port {port}: {e}")
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
def start_server(
|
|
57
|
+
self, config_name: str, config_path: Path
|
|
58
|
+
) -> Optional[subprocess.Popen]:
|
|
59
|
+
"""Start server with proper error handling."""
|
|
60
|
+
try:
|
|
61
|
+
# Get port from config
|
|
62
|
+
with open(config_path) as f:
|
|
63
|
+
config = json.load(f)
|
|
64
|
+
port = config.get("server", {}).get("port", 8000)
|
|
65
|
+
# Kill any existing process on this port
|
|
66
|
+
self.kill_process_on_port(port)
|
|
67
|
+
# Start server
|
|
68
|
+
cmd = [
|
|
69
|
+
sys.executable,
|
|
70
|
+
"-m",
|
|
71
|
+
"mcp_proxy_adapter.main",
|
|
72
|
+
"--config",
|
|
73
|
+
str(config_path.absolute()), # Use absolute path to avoid path issues
|
|
74
|
+
]
|
|
75
|
+
# Always start from examples directory where configs are located
|
|
76
|
+
cwd = self.project_root / "mcp_proxy_adapter" / "examples"
|
|
77
|
+
print(f"๐ Starting {config_name} on port {port}...")
|
|
78
|
+
process = subprocess.Popen(
|
|
79
|
+
cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
|
|
80
|
+
)
|
|
81
|
+
# Wait a bit for server to start
|
|
82
|
+
time.sleep(3)
|
|
83
|
+
# Check if process is still running
|
|
84
|
+
if process.poll() is None:
|
|
85
|
+
print(f"โ
{config_name} started successfully on port {port}")
|
|
86
|
+
return process
|
|
87
|
+
else:
|
|
88
|
+
stdout, stderr = process.communicate()
|
|
89
|
+
print(f"โ {config_name} failed to start:")
|
|
90
|
+
print(f"STDOUT: {stdout}")
|
|
91
|
+
print(f"STDERR: {stderr}")
|
|
92
|
+
return None
|
|
93
|
+
except Exception as e:
|
|
94
|
+
print(f"โ Error starting {config_name}: {e}")
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
def stop_server(self, config_name: str, process: subprocess.Popen):
|
|
98
|
+
"""Stop server gracefully."""
|
|
99
|
+
try:
|
|
100
|
+
print(f"๐ Stopping {config_name}...")
|
|
101
|
+
process.terminate()
|
|
102
|
+
process.wait(timeout=5)
|
|
103
|
+
print(f"โ
{config_name} stopped")
|
|
104
|
+
except subprocess.TimeoutExpired:
|
|
105
|
+
print(f"โ ๏ธ Force killing {config_name}...")
|
|
106
|
+
process.kill()
|
|
107
|
+
process.wait()
|
|
108
|
+
except Exception as e:
|
|
109
|
+
print(f"โ Error stopping {config_name}: {e}")
|
|
110
|
+
|
|
111
|
+
async def test_server(
|
|
112
|
+
self, config_name: str, config_path: Path
|
|
113
|
+
) -> List[TestResult]:
|
|
114
|
+
"""Test a single server configuration."""
|
|
115
|
+
results = []
|
|
116
|
+
|
|
117
|
+
# Get config for port number first
|
|
118
|
+
with open(config_path) as f:
|
|
119
|
+
config = json.load(f)
|
|
120
|
+
port = config.get("server", {}).get("port", 8000)
|
|
121
|
+
|
|
122
|
+
# Start server
|
|
123
|
+
process = self.start_server(config_name, config_path)
|
|
124
|
+
if not process:
|
|
125
|
+
return [
|
|
126
|
+
TestResult(
|
|
127
|
+
test_name=f"{config_name}_startup",
|
|
128
|
+
server_url=f"http://localhost:{port}",
|
|
129
|
+
auth_type="none",
|
|
130
|
+
success=False,
|
|
131
|
+
error_message="Server failed to start",
|
|
132
|
+
)
|
|
133
|
+
]
|
|
134
|
+
try:
|
|
135
|
+
# Get remaining config for client setup
|
|
136
|
+
auth_enabled = config.get("security", {}).get("enabled", False)
|
|
137
|
+
# For new simplified structure, if security is enabled, we use token auth
|
|
138
|
+
auth_methods = ["api_key"] if auth_enabled else []
|
|
139
|
+
# Create test client with correct protocol
|
|
140
|
+
server_protocol = config.get("server", {}).get("protocol", "http")
|
|
141
|
+
protocol = "https" if server_protocol in ["https", "mtls"] else "http"
|
|
142
|
+
client = SecurityTestClient(base_url=f"{protocol}://localhost:{port}")
|
|
143
|
+
print(f"๐ DEBUG: Created client with URL: {client.base_url}")
|
|
144
|
+
client.auth_enabled = auth_enabled
|
|
145
|
+
client.auth_methods = auth_methods
|
|
146
|
+
client.api_keys = config.get("security", {}).get("tokens", {})
|
|
147
|
+
client.roles_file = config.get("security", {}).get("roles_file")
|
|
148
|
+
client.roles = config.get("security", {}).get("roles", {})
|
|
149
|
+
# For mTLS, override SSL context creation and change working directory
|
|
150
|
+
if server_protocol == "mtls":
|
|
151
|
+
client.create_ssl_context = client.create_ssl_context_for_mtls
|
|
152
|
+
# Ensure mTLS uses certificate auth
|
|
153
|
+
client.auth_methods = ["certificate"]
|
|
154
|
+
# Change to examples directory for mTLS tests
|
|
155
|
+
import os
|
|
156
|
+
|
|
157
|
+
os.chdir(self.project_root / "mcp_proxy_adapter" / "examples")
|
|
158
|
+
# Run tests
|
|
159
|
+
async with client:
|
|
160
|
+
# Test 1: Health check
|
|
161
|
+
result = await client.test_health()
|
|
162
|
+
results.append(result)
|
|
163
|
+
# Test 2: Command execution
|
|
164
|
+
result = await client.test_command_execution()
|
|
165
|
+
results.append(result)
|
|
166
|
+
# Test 3: Authentication (if enabled)
|
|
167
|
+
if auth_enabled:
|
|
168
|
+
result = await client.test_authentication()
|
|
169
|
+
results.append(result)
|
|
170
|
+
# Test 4: Negative authentication
|
|
171
|
+
result = await client.test_negative_authentication()
|
|
172
|
+
results.append(result)
|
|
173
|
+
# Test 5: Role-based access
|
|
174
|
+
if "api_key" in auth_methods:
|
|
175
|
+
result = await client.test_role_based_access(
|
|
176
|
+
client.base_url, "api_key", role="admin"
|
|
177
|
+
)
|
|
178
|
+
else:
|
|
179
|
+
result = await client.test_role_based_access(
|
|
180
|
+
client.base_url, "certificate", role="admin"
|
|
181
|
+
)
|
|
182
|
+
results.append(result)
|
|
183
|
+
# Test 6: Role permissions
|
|
184
|
+
if "api_key" in auth_methods:
|
|
185
|
+
result = await client.test_role_permissions(
|
|
186
|
+
client.base_url, "api_key", role="admin", action="read"
|
|
187
|
+
)
|
|
188
|
+
else:
|
|
189
|
+
result = await client.test_role_permissions(
|
|
190
|
+
client.base_url, "certificate", role="admin", action="read"
|
|
191
|
+
)
|
|
192
|
+
results.append(result)
|
|
193
|
+
# Test 7: Multiple roles test
|
|
194
|
+
if "api_key" in auth_methods:
|
|
195
|
+
result = await client.test_multiple_roles(
|
|
196
|
+
client.base_url, "api_key"
|
|
197
|
+
)
|
|
198
|
+
else:
|
|
199
|
+
result = await client.test_multiple_roles(
|
|
200
|
+
client.base_url, "certificate"
|
|
201
|
+
)
|
|
202
|
+
results.append(result)
|
|
203
|
+
else:
|
|
204
|
+
# Test 3: No authentication required
|
|
205
|
+
result = await client.test_no_auth_required()
|
|
206
|
+
results.append(result)
|
|
207
|
+
# Test 4: Negative auth (should fail)
|
|
208
|
+
result = await client.test_negative_authentication()
|
|
209
|
+
results.append(result)
|
|
210
|
+
except Exception as e:
|
|
211
|
+
results.append(
|
|
212
|
+
TestResult(
|
|
213
|
+
test_name=f"{config_name}_client_error",
|
|
214
|
+
server_url=f"{protocol}://localhost:{port}",
|
|
215
|
+
auth_type="none",
|
|
216
|
+
success=False,
|
|
217
|
+
error_message=str(e),
|
|
218
|
+
)
|
|
219
|
+
)
|
|
220
|
+
finally:
|
|
221
|
+
# Stop server
|
|
222
|
+
self.stop_server(config_name, process)
|
|
223
|
+
return results
|
|
224
|
+
|
|
225
|
+
def create_variant_from_full_config(self, full_config_path: Path, protocol: str, auth: str, port: int) -> Path:
|
|
226
|
+
"""
|
|
227
|
+
Create a variant configuration from full config.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
full_config_path: Path to the full configuration file
|
|
231
|
+
protocol: Protocol type (http, https, mtls)
|
|
232
|
+
auth: Authentication type (none, token, token_roles)
|
|
233
|
+
port: Server port
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
Path to the temporary configuration file
|
|
237
|
+
"""
|
|
238
|
+
import tempfile
|
|
239
|
+
import json
|
|
240
|
+
|
|
241
|
+
# Load the full configuration
|
|
242
|
+
with open(full_config_path, 'r') as f:
|
|
243
|
+
full_config = json.load(f)
|
|
244
|
+
|
|
245
|
+
# Create a copy of the full config
|
|
246
|
+
variant_config = full_config.copy()
|
|
247
|
+
|
|
248
|
+
# Set server port and protocol
|
|
249
|
+
variant_config["server"]["port"] = port
|
|
250
|
+
variant_config["server"]["protocol"] = protocol
|
|
251
|
+
|
|
252
|
+
# Apply protocol configuration
|
|
253
|
+
if protocol in variant_config.get("protocol_variants", {}):
|
|
254
|
+
protocol_config = variant_config["protocol_variants"][protocol]
|
|
255
|
+
variant_config["server"].update(protocol_config["server"])
|
|
256
|
+
|
|
257
|
+
# Apply authentication configuration
|
|
258
|
+
if auth in variant_config.get("auth_variants", {}):
|
|
259
|
+
auth_config = variant_config["auth_variants"][auth]
|
|
260
|
+
variant_config["security"].update(auth_config["security"])
|
|
261
|
+
|
|
262
|
+
# Remove the helper sections
|
|
263
|
+
variant_config.pop("protocol_variants", None)
|
|
264
|
+
variant_config.pop("auth_variants", None)
|
|
265
|
+
|
|
266
|
+
# Create temporary config file
|
|
267
|
+
temp_dir = tempfile.mkdtemp(prefix="full_config_test_")
|
|
268
|
+
config_name = f"{protocol}_{auth}.json"
|
|
269
|
+
config_path = Path(temp_dir) / config_name
|
|
270
|
+
|
|
271
|
+
with open(config_path, 'w') as f:
|
|
272
|
+
json.dump(variant_config, f, indent=2, ensure_ascii=False)
|
|
273
|
+
|
|
274
|
+
return config_path
|
|
275
|
+
|
|
276
|
+
async def run_all_tests(self):
|
|
277
|
+
"""Run all security tests."""
|
|
278
|
+
print("๐ Starting Security Testing Suite")
|
|
279
|
+
print("=" * 50)
|
|
280
|
+
# Test configurations
|
|
281
|
+
configs = [
|
|
282
|
+
("basic_http", "http.json"),
|
|
283
|
+
("http_token", "http_token_roles.json"),
|
|
284
|
+
("https", "https.json"),
|
|
285
|
+
("https_token", "https_token_roles.json"),
|
|
286
|
+
("mtls", "mtls.json"),
|
|
287
|
+
]
|
|
288
|
+
total_tests = 0
|
|
289
|
+
passed_tests = 0
|
|
290
|
+
for config_name, config_file in configs:
|
|
291
|
+
config_path = self.configs_dir / config_file
|
|
292
|
+
if not config_path.exists():
|
|
293
|
+
print(f"โ Configuration not found: {config_path}")
|
|
294
|
+
continue
|
|
295
|
+
print(f"\n๐ Testing {config_name.upper()} configuration")
|
|
296
|
+
print("-" * 30)
|
|
297
|
+
results = await self.test_server(config_name, config_path)
|
|
298
|
+
for result in results:
|
|
299
|
+
total_tests += 1
|
|
300
|
+
if result.success:
|
|
301
|
+
passed_tests += 1
|
|
302
|
+
print(f"โ
{result.test_name}: PASS")
|
|
303
|
+
else:
|
|
304
|
+
print(f"โ {result.test_name}: FAIL - {result.error_message}")
|
|
305
|
+
self.test_results.extend(results)
|
|
306
|
+
# Print summary
|
|
307
|
+
print("\n" + "=" * 50)
|
|
308
|
+
print("๐ TEST SUMMARY")
|
|
309
|
+
print("=" * 50)
|
|
310
|
+
print(f"Total tests: {total_tests}")
|
|
311
|
+
print(f"Passed: {passed_tests}")
|
|
312
|
+
print(f"Failed: {total_tests - passed_tests}")
|
|
313
|
+
print(
|
|
314
|
+
f"Success rate: {(passed_tests/total_tests*100):.1f}%"
|
|
315
|
+
if total_tests > 0
|
|
316
|
+
else "N/A"
|
|
317
|
+
)
|
|
318
|
+
# Detailed results
|
|
319
|
+
print("\n๐ DETAILED RESULTS")
|
|
320
|
+
print("-" * 30)
|
|
321
|
+
for result in self.test_results:
|
|
322
|
+
status = "โ
PASS" if result.success else "โ FAIL"
|
|
323
|
+
print(f"{status} {result.test_name}")
|
|
324
|
+
if not result.success and result.error_message:
|
|
325
|
+
print(f" Error: {result.error_message}")
|
|
326
|
+
return passed_tests == total_tests
|
|
327
|
+
|
|
328
|
+
async def run_full_config_tests(self, full_config_path: str):
|
|
329
|
+
"""Run tests using full configuration with all variants."""
|
|
330
|
+
print("๐ Full Configuration Variants Testing")
|
|
331
|
+
print("=" * 60)
|
|
332
|
+
print(f"๐ Using full config: {full_config_path}")
|
|
333
|
+
|
|
334
|
+
full_config_file = Path(full_config_path)
|
|
335
|
+
if not full_config_file.exists():
|
|
336
|
+
print(f"โ Full configuration file not found: {full_config_path}")
|
|
337
|
+
return False
|
|
338
|
+
|
|
339
|
+
# Define all combinations to test
|
|
340
|
+
variants = [
|
|
341
|
+
# HTTP variants
|
|
342
|
+
("http", "none", 20000),
|
|
343
|
+
("http", "token", 20001),
|
|
344
|
+
("http", "token_roles", 20002),
|
|
345
|
+
|
|
346
|
+
# HTTPS variants
|
|
347
|
+
("https", "none", 20003),
|
|
348
|
+
("https", "token", 20004),
|
|
349
|
+
("https", "token_roles", 20005),
|
|
350
|
+
|
|
351
|
+
# mTLS variants
|
|
352
|
+
("mtls", "none", 20006),
|
|
353
|
+
("mtls", "token", 20007),
|
|
354
|
+
("mtls", "token_roles", 20008),
|
|
355
|
+
]
|
|
356
|
+
|
|
357
|
+
total_tests = 0
|
|
358
|
+
passed_tests = 0
|
|
359
|
+
all_results = []
|
|
360
|
+
|
|
361
|
+
for protocol, auth, port in variants:
|
|
362
|
+
print(f"\n{'='*60}")
|
|
363
|
+
print(f"๐งช Testing {protocol.upper()} with {auth.upper()} authentication")
|
|
364
|
+
print(f"{'='*60}")
|
|
365
|
+
|
|
366
|
+
# Create variant configuration
|
|
367
|
+
config_path = self.create_variant_from_full_config(full_config_file, protocol, auth, port)
|
|
368
|
+
|
|
369
|
+
# Test the variant
|
|
370
|
+
config_name = f"{protocol}_{auth}"
|
|
371
|
+
results = await self.test_server(config_name, config_path)
|
|
372
|
+
|
|
373
|
+
# Count results
|
|
374
|
+
for result in results:
|
|
375
|
+
total_tests += 1
|
|
376
|
+
if result.success:
|
|
377
|
+
passed_tests += 1
|
|
378
|
+
print(f"โ
{result.test_name}: PASS")
|
|
379
|
+
else:
|
|
380
|
+
print(f"โ {result.test_name}: FAIL - {result.error_message}")
|
|
381
|
+
|
|
382
|
+
all_results.extend(results)
|
|
383
|
+
|
|
384
|
+
# Clean up temporary config
|
|
385
|
+
import shutil
|
|
386
|
+
shutil.rmtree(config_path.parent)
|
|
387
|
+
|
|
388
|
+
# Print final summary
|
|
389
|
+
print(f"\n{'='*60}")
|
|
390
|
+
print("๐ FULL CONFIG TEST SUMMARY")
|
|
391
|
+
print(f"{'='*60}")
|
|
392
|
+
print(f"Total tests: {total_tests}")
|
|
393
|
+
print(f"Passed: {passed_tests}")
|
|
394
|
+
print(f"Failed: {total_tests - passed_tests}")
|
|
395
|
+
print(f"Success rate: {(passed_tests/total_tests)*100:.1f}%")
|
|
396
|
+
|
|
397
|
+
if total_tests - passed_tests > 0:
|
|
398
|
+
print(f"\nโ Failed tests:")
|
|
399
|
+
for result in all_results:
|
|
400
|
+
if not result.success:
|
|
401
|
+
print(f" โข {result.test_name}: {result.error_message}")
|
|
402
|
+
|
|
403
|
+
return passed_tests == total_tests
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
async def main():
|
|
408
|
+
"""Main function."""
|
|
409
|
+
import argparse
|
|
410
|
+
|
|
411
|
+
parser = argparse.ArgumentParser(description="Security Testing Suite for MCP Proxy Adapter")
|
|
412
|
+
parser.add_argument("--full-config", help="Path to full configuration file for variant testing")
|
|
413
|
+
parser.add_argument("--verbose", action="store_true", help="Enable verbose output")
|
|
414
|
+
|
|
415
|
+
args = parser.parse_args()
|
|
416
|
+
|
|
417
|
+
runner = SecurityTestRunner()
|
|
418
|
+
try:
|
|
419
|
+
if args.full_config:
|
|
420
|
+
# Test full configuration variants
|
|
421
|
+
success = await runner.run_full_config_tests(args.full_config)
|
|
422
|
+
else:
|
|
423
|
+
# Run standard tests
|
|
424
|
+
success = await runner.run_all_tests()
|
|
425
|
+
sys.exit(0 if success else 1)
|
|
426
|
+
except KeyboardInterrupt:
|
|
427
|
+
print("\nโ ๏ธ Testing interrupted by user")
|
|
428
|
+
sys.exit(1)
|
|
429
|
+
except Exception as e:
|
|
430
|
+
print(f"\nโ Testing failed: {e}")
|
|
431
|
+
sys.exit(1)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
if __name__ == "__main__":
|
|
435
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
Security test package for MCP Proxy Adapter.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .test_result import TestResult
|
|
9
|
+
from .ssl_context_manager import SSLContextManager
|
|
10
|
+
from .auth_manager import AuthManager
|
|
11
|
+
from .test_client import SecurityTestClient
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"TestResult",
|
|
15
|
+
"SSLContextManager",
|
|
16
|
+
"AuthManager",
|
|
17
|
+
"SecurityTestClient",
|
|
18
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Vasiliy Zdanovskiy
|
|
3
|
+
email: vasilyvz@gmail.com
|
|
4
|
+
|
|
5
|
+
SSL context manager for security testing.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import ssl
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SSLContextManager:
|
|
15
|
+
"""Manager for SSL contexts in security testing."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, project_root: Optional[Path] = None):
|
|
18
|
+
"""
|
|
19
|
+
Initialize SSL context manager.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
project_root: Root directory of the project (optional)
|
|
23
|
+
"""
|
|
24
|
+
if project_root is None:
|
|
25
|
+
project_root = Path(__file__).parent.parent.parent.parent
|
|
26
|
+
self.project_root = project_root
|
|
27
|
+
|
|
28
|
+
|