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,331 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Author: Vasiliy Zdanovskiy
|
4
|
+
email: vasilyvz@gmail.com
|
5
|
+
Script for generating test configurations for MCP Proxy Adapter.
|
6
|
+
Generates 6 different configuration types for testing various security scenarios.
|
7
|
+
"""
|
8
|
+
import json
|
9
|
+
import os
|
10
|
+
import argparse
|
11
|
+
from typing import Dict, Any
|
12
|
+
def generate_http_simple_config(port: int = 20000, certs_dir: str = "certs", keys_dir: str = "keys") -> Dict[str, Any]:
|
13
|
+
"""Generate HTTP configuration without authorization."""
|
14
|
+
return {
|
15
|
+
"server": {"host": "127.0.0.1", "port": port},
|
16
|
+
"ssl": {"enabled": False},
|
17
|
+
"security": {"enabled": False},
|
18
|
+
"registration": {
|
19
|
+
"enabled": False,
|
20
|
+
"auth_method": "token",
|
21
|
+
"server_url": "http://127.0.0.1:3004/proxy",
|
22
|
+
"token": {"enabled": True, "token": "proxy_registration_token_123"},
|
23
|
+
"proxy_info": {
|
24
|
+
"name": "mcp_example_server",
|
25
|
+
"capabilities": ["jsonrpc", "rest", "proxy_registration"],
|
26
|
+
"endpoints": {"jsonrpc": "/api/jsonrpc", "rest": "/cmd", "health": "/health"}
|
27
|
+
},
|
28
|
+
"heartbeat": {"enabled": True, "interval": 30}
|
29
|
+
},
|
30
|
+
"protocols": {"enabled": True, "allowed_protocols": ["http"]}
|
31
|
+
}
|
32
|
+
def generate_http_token_config(port: int = 20001, certs_dir: str = "certs", keys_dir: str = "keys", roles_file: str = "configs/roles.json") -> Dict[str, Any]:
|
33
|
+
"""Generate HTTP configuration with token authorization."""
|
34
|
+
return {
|
35
|
+
"server": {"host": "127.0.0.1", "port": port},
|
36
|
+
"ssl": {"enabled": False},
|
37
|
+
"security": {
|
38
|
+
"enabled": True,
|
39
|
+
"auth": {
|
40
|
+
"enabled": True,
|
41
|
+
"methods": ["api_key"],
|
42
|
+
# Map API tokens to roles for testing
|
43
|
+
"api_keys": {
|
44
|
+
"test-token-123": "admin",
|
45
|
+
"user-token-456": "user",
|
46
|
+
"readonly-token-123": "readonly",
|
47
|
+
"guest-token-123": "guest",
|
48
|
+
"proxy-token-123": "proxy"
|
49
|
+
}
|
50
|
+
},
|
51
|
+
"permissions": {"enabled": True, "roles_file": roles_file}
|
52
|
+
},
|
53
|
+
"registration": {
|
54
|
+
"enabled": True,
|
55
|
+
"url": "http://127.0.0.1:3004/proxy",
|
56
|
+
"name": "http_token_adapter",
|
57
|
+
"capabilities": ["http", "token_auth"],
|
58
|
+
"retry_count": 3,
|
59
|
+
"retry_delay": 5,
|
60
|
+
"heartbeat": {"enabled": True, "interval": 30}
|
61
|
+
},
|
62
|
+
"protocols": {"enabled": True, "allowed_protocols": ["http"]}
|
63
|
+
}
|
64
|
+
def generate_https_simple_config(port: int = 20002, certs_dir: str = "certs", keys_dir: str = "keys") -> Dict[str, Any]:
|
65
|
+
"""Generate HTTPS configuration without client certificate verification and authorization."""
|
66
|
+
return {
|
67
|
+
"server": {"host": "127.0.0.1", "port": port},
|
68
|
+
"ssl": {
|
69
|
+
"enabled": True,
|
70
|
+
"cert_file": f"{certs_dir}/localhost_server.crt",
|
71
|
+
"key_file": f"{keys_dir}/localhost_server.key"
|
72
|
+
},
|
73
|
+
"security": {"enabled": False},
|
74
|
+
"registration": {
|
75
|
+
"enabled": True,
|
76
|
+
"url": "http://127.0.0.1:3004/proxy",
|
77
|
+
"name": "https_simple_adapter",
|
78
|
+
"capabilities": ["https"],
|
79
|
+
"retry_count": 3,
|
80
|
+
"retry_delay": 5,
|
81
|
+
"heartbeat": {"enabled": True, "interval": 30}
|
82
|
+
},
|
83
|
+
"protocols": {"enabled": True, "allowed_protocols": ["http", "https"]}
|
84
|
+
}
|
85
|
+
def generate_https_token_config(port: int = 20003, certs_dir: str = "certs", keys_dir: str = "keys") -> Dict[str, Any]:
|
86
|
+
"""Generate HTTPS configuration without client certificate verification with token authorization."""
|
87
|
+
return {
|
88
|
+
"server": {"host": "127.0.0.1", "port": port},
|
89
|
+
"ssl": {
|
90
|
+
"enabled": True,
|
91
|
+
"cert_file": f"{certs_dir}/localhost_server.crt",
|
92
|
+
"key_file": f"{keys_dir}/localhost_server.key"
|
93
|
+
},
|
94
|
+
"security": {
|
95
|
+
"enabled": True,
|
96
|
+
"auth": {
|
97
|
+
"enabled": True,
|
98
|
+
"methods": ["api_key"],
|
99
|
+
"api_keys": {
|
100
|
+
"test-token-123": "admin",
|
101
|
+
"user-token-456": "user",
|
102
|
+
"readonly-token-123": "readonly",
|
103
|
+
"guest-token-123": "guest",
|
104
|
+
"proxy-token-123": "proxy"
|
105
|
+
}
|
106
|
+
},
|
107
|
+
"permissions": {"enabled": True, "roles_file": "./configs/roles.json"}
|
108
|
+
},
|
109
|
+
"registration": {
|
110
|
+
"enabled": True,
|
111
|
+
"url": "http://127.0.0.1:3004/proxy",
|
112
|
+
"name": "https_token_adapter",
|
113
|
+
"capabilities": ["https", "token_auth"],
|
114
|
+
"retry_count": 3,
|
115
|
+
"retry_delay": 5,
|
116
|
+
"heartbeat": {"enabled": True, "interval": 30}
|
117
|
+
},
|
118
|
+
"protocols": {"enabled": True, "allowed_protocols": ["http", "https"]}
|
119
|
+
}
|
120
|
+
def generate_mtls_no_roles_config(port: int = 20004, certs_dir: str = "certs", keys_dir: str = "keys") -> Dict[str, Any]:
|
121
|
+
"""Generate mTLS configuration without roles."""
|
122
|
+
return {
|
123
|
+
"server": {"host": "127.0.0.1", "port": port},
|
124
|
+
"ssl": {
|
125
|
+
"enabled": True,
|
126
|
+
"cert_file": f"{certs_dir}/localhost_server.crt",
|
127
|
+
"key_file": f"{keys_dir}/localhost_server.key",
|
128
|
+
"ca_cert": f"{certs_dir}/mcp_proxy_adapter_ca_ca.crt",
|
129
|
+
"verify_client": True
|
130
|
+
},
|
131
|
+
"security": {
|
132
|
+
"enabled": True,
|
133
|
+
"auth": {"enabled": True, "methods": ["certificate"]},
|
134
|
+
"permissions": {"enabled": False}
|
135
|
+
},
|
136
|
+
"protocols": {"enabled": True, "allowed_protocols": ["https", "mtls"]}
|
137
|
+
}
|
138
|
+
def generate_mtls_with_roles_config(port: int = 20005, certs_dir: str = "certs", keys_dir: str = "keys", roles_file: str = "configs/roles.json") -> Dict[str, Any]:
|
139
|
+
"""Generate mTLS configuration with roles."""
|
140
|
+
return {
|
141
|
+
"server": {"host": "127.0.0.1", "port": port},
|
142
|
+
"ssl": {
|
143
|
+
"enabled": True,
|
144
|
+
"cert_file": f"{certs_dir}/localhost_server.crt",
|
145
|
+
"key_file": f"{keys_dir}/localhost_server.key",
|
146
|
+
"ca_cert": f"{certs_dir}/mcp_proxy_adapter_ca_ca.crt",
|
147
|
+
"verify_client": True
|
148
|
+
},
|
149
|
+
"registration": {
|
150
|
+
"enabled": True,
|
151
|
+
"auth_method": "token",
|
152
|
+
"server_url": "http://127.0.0.1:3004/proxy",
|
153
|
+
"token": {"enabled": True, "token": "proxy_registration_token_123"},
|
154
|
+
"proxy_info": {
|
155
|
+
"name": "mcp_example_server",
|
156
|
+
"capabilities": ["jsonrpc", "rest", "security", "proxy_registration"],
|
157
|
+
"endpoints": {"jsonrpc": "/api/jsonrpc", "rest": "/cmd", "health": "/health"}
|
158
|
+
},
|
159
|
+
"heartbeat": {"enabled": True, "interval": 30}
|
160
|
+
},
|
161
|
+
"security": {
|
162
|
+
"enabled": True,
|
163
|
+
"auth": {"enabled": True, "methods": ["certificate"]},
|
164
|
+
"permissions": {"enabled": True, "roles_file": roles_file}
|
165
|
+
},
|
166
|
+
"protocols": {"enabled": True, "allowed_protocols": ["https", "mtls"]}
|
167
|
+
}
|
168
|
+
def generate_roles_config() -> Dict[str, Any]:
|
169
|
+
"""Generate roles configuration for testing."""
|
170
|
+
return {
|
171
|
+
"admin": {
|
172
|
+
"description": "Administrator role with full access",
|
173
|
+
"permissions": [
|
174
|
+
"read",
|
175
|
+
"write",
|
176
|
+
"execute",
|
177
|
+
"delete",
|
178
|
+
"admin",
|
179
|
+
"register",
|
180
|
+
"unregister",
|
181
|
+
"heartbeat",
|
182
|
+
"discover"
|
183
|
+
],
|
184
|
+
"tokens": ["test-token-123"]
|
185
|
+
},
|
186
|
+
"user": {
|
187
|
+
"description": "User role with limited access",
|
188
|
+
"permissions": [
|
189
|
+
"read",
|
190
|
+
"execute",
|
191
|
+
"register",
|
192
|
+
"unregister",
|
193
|
+
"heartbeat",
|
194
|
+
"discover"
|
195
|
+
],
|
196
|
+
"tokens": ["user-token-456"]
|
197
|
+
},
|
198
|
+
"readonly": {
|
199
|
+
"description": "Read-only role",
|
200
|
+
"permissions": [
|
201
|
+
"read",
|
202
|
+
"discover"
|
203
|
+
],
|
204
|
+
"tokens": ["readonly-token-123"]
|
205
|
+
},
|
206
|
+
"guest": {
|
207
|
+
"description": "Guest role with read-only access",
|
208
|
+
"permissions": [
|
209
|
+
"read",
|
210
|
+
"discover"
|
211
|
+
],
|
212
|
+
"tokens": ["guest-token-123"]
|
213
|
+
},
|
214
|
+
"proxy": {
|
215
|
+
"description": "Proxy role for registration",
|
216
|
+
"permissions": [
|
217
|
+
"register",
|
218
|
+
"unregister",
|
219
|
+
"heartbeat",
|
220
|
+
"discover"
|
221
|
+
],
|
222
|
+
"tokens": ["proxy-token-123"]
|
223
|
+
}
|
224
|
+
}
|
225
|
+
def generate_all_configs(output_dir: str, certs_dir: str = "certs", keys_dir: str = "keys", roles_file: str = "configs/roles.json") -> None:
|
226
|
+
"""Generate all 6 configuration types and save them to files."""
|
227
|
+
# Ensure output directory exists first
|
228
|
+
os.makedirs(output_dir, exist_ok=True)
|
229
|
+
|
230
|
+
configs = {
|
231
|
+
"http_simple": generate_http_simple_config(20000, certs_dir, keys_dir),
|
232
|
+
"http_token": generate_http_token_config(20001, certs_dir, keys_dir, roles_file),
|
233
|
+
"https_simple": generate_https_simple_config(20002, certs_dir, keys_dir),
|
234
|
+
"https_token": generate_https_token_config(20003, certs_dir, keys_dir),
|
235
|
+
"mtls_no_roles": generate_mtls_no_roles_config(20004, certs_dir, keys_dir),
|
236
|
+
"mtls_with_roles": generate_mtls_with_roles_config(20005, certs_dir, keys_dir, roles_file)
|
237
|
+
}
|
238
|
+
# Ensure output directory exists
|
239
|
+
os.makedirs(output_dir, exist_ok=True)
|
240
|
+
# Generate each configuration
|
241
|
+
for name, config in configs.items():
|
242
|
+
filename = os.path.join(output_dir, f"{name}.json")
|
243
|
+
with open(filename, 'w', encoding='utf-8') as f:
|
244
|
+
json.dump(config, f, indent=2, ensure_ascii=False)
|
245
|
+
print(f"Generated: {filename}")
|
246
|
+
# Generate roles configuration
|
247
|
+
roles_config = generate_roles_config()
|
248
|
+
|
249
|
+
# Create roles.json in the root directory (test environment root) for compatibility
|
250
|
+
# When running as module, we need to create roles.json in the current working directory
|
251
|
+
# This is the directory where the user is running the command from
|
252
|
+
try:
|
253
|
+
# Get the current working directory where the user is running the command
|
254
|
+
current_dir = os.getcwd()
|
255
|
+
root_roles_filename = os.path.join(current_dir, "roles.json")
|
256
|
+
|
257
|
+
# Create roles.json in the current working directory
|
258
|
+
with open(root_roles_filename, 'w', encoding='utf-8') as f:
|
259
|
+
json.dump(roles_config, f, indent=2, ensure_ascii=False)
|
260
|
+
print(f"Generated: {root_roles_filename}")
|
261
|
+
|
262
|
+
# Also create a copy in the output directory for reference
|
263
|
+
backup_roles_filename = os.path.join(output_dir, "roles_backup.json")
|
264
|
+
with open(backup_roles_filename, 'w', encoding='utf-8') as f:
|
265
|
+
json.dump(roles_config, f, indent=2, ensure_ascii=False)
|
266
|
+
print(f"Generated backup: {backup_roles_filename}")
|
267
|
+
|
268
|
+
except Exception as e:
|
269
|
+
print(f"Warning: Could not create roles.json in current directory: {e}")
|
270
|
+
print(f"Current working directory: {os.getcwd()}")
|
271
|
+
print(f"Script directory: {os.path.dirname(os.path.abspath(__file__))}")
|
272
|
+
|
273
|
+
# Also create roles.json in configs directory for reference
|
274
|
+
roles_filename = os.path.join(output_dir, "roles.json")
|
275
|
+
with open(roles_filename, 'w', encoding='utf-8') as f:
|
276
|
+
json.dump(roles_config, f, indent=2, ensure_ascii=False)
|
277
|
+
print(f"Generated: {roles_filename}")
|
278
|
+
print(f"\nGenerated {len(configs)} configuration files and roles.json in {output_dir}")
|
279
|
+
|
280
|
+
print("\n" + "=" * 60)
|
281
|
+
print("ā
CONFIGURATION GENERATION COMPLETED SUCCESSFULLY")
|
282
|
+
print("=" * 60)
|
283
|
+
print("\nš NEXT STEPS:")
|
284
|
+
print("1. Run security tests:")
|
285
|
+
print(" python -m mcp_proxy_adapter.examples.run_security_tests")
|
286
|
+
print("\n2. Start basic framework example:")
|
287
|
+
print(" python -m mcp_proxy_adapter.examples.basic_framework.main --config configs/https_simple.json")
|
288
|
+
print("\n3. Start full application example:")
|
289
|
+
print(" python -m mcp_proxy_adapter.examples.full_application.main --config configs/mtls_with_roles.json")
|
290
|
+
print("=" * 60)
|
291
|
+
def main():
|
292
|
+
"""Main function for command line execution."""
|
293
|
+
parser = argparse.ArgumentParser(
|
294
|
+
description="Generate test configurations for MCP Proxy Adapter"
|
295
|
+
)
|
296
|
+
parser.add_argument(
|
297
|
+
"--output-dir",
|
298
|
+
default="configs",
|
299
|
+
help="Output directory for configuration files (default: configs)"
|
300
|
+
)
|
301
|
+
parser.add_argument(
|
302
|
+
"--certs-dir",
|
303
|
+
default="certs",
|
304
|
+
help="Certificates directory (default: certs)"
|
305
|
+
)
|
306
|
+
parser.add_argument(
|
307
|
+
"--keys-dir",
|
308
|
+
default="keys",
|
309
|
+
help="Keys directory (default: keys)"
|
310
|
+
)
|
311
|
+
parser.add_argument(
|
312
|
+
"--roles-file",
|
313
|
+
default="configs/roles.json",
|
314
|
+
help="Roles file path (default: configs/roles.json)"
|
315
|
+
)
|
316
|
+
args = parser.parse_args()
|
317
|
+
try:
|
318
|
+
generate_all_configs(args.output_dir, args.certs_dir, args.keys_dir, args.roles_file)
|
319
|
+
print("Configuration generation completed successfully!")
|
320
|
+
except Exception as e:
|
321
|
+
print(f"\nā CONFIGURATION GENERATION FAILED: {e}")
|
322
|
+
print("=" * 60)
|
323
|
+
print("\nš§ TROUBLESHOOTING:")
|
324
|
+
print("1. Check if output directory is writable")
|
325
|
+
print("2. Verify JSON encoding support")
|
326
|
+
print("3. Check available disk space")
|
327
|
+
print("=" * 60)
|
328
|
+
return 1
|
329
|
+
return 0
|
330
|
+
if __name__ == "__main__":
|
331
|
+
exit(main())
|
@@ -0,0 +1,334 @@
|
|
1
|
+
"""
|
2
|
+
Proxy Registration Example
|
3
|
+
This example demonstrates how to use the MCP Proxy Adapter framework
|
4
|
+
for proxy registration with different authentication methods.
|
5
|
+
Author: Vasiliy Zdanovskiy
|
6
|
+
email: vasilyvz@gmail.com
|
7
|
+
"""
|
8
|
+
import asyncio
|
9
|
+
import json
|
10
|
+
import sys
|
11
|
+
import os
|
12
|
+
from pathlib import Path
|
13
|
+
from typing import Dict, Any, Optional
|
14
|
+
# Add project root to path
|
15
|
+
project_root = Path(__file__).parent.parent.parent
|
16
|
+
sys.path.insert(0, str(project_root))
|
17
|
+
import aiohttp
|
18
|
+
from aiohttp import ClientTimeout, TCPConnector
|
19
|
+
import ssl
|
20
|
+
from mcp_proxy_adapter.core.logging import logger
|
21
|
+
class ProxyRegistrationExample:
|
22
|
+
"""Example client for testing proxy registration functionality."""
|
23
|
+
def __init__(self, server_url: str, auth_token: Optional[str] = None):
|
24
|
+
"""
|
25
|
+
Initialize example client.
|
26
|
+
Args:
|
27
|
+
server_url: Server URL
|
28
|
+
auth_token: Authentication token
|
29
|
+
"""
|
30
|
+
self.server_url = server_url
|
31
|
+
self.auth_token = auth_token
|
32
|
+
self.session: Optional[aiohttp.ClientSession] = None
|
33
|
+
# Test data
|
34
|
+
self.test_servers = [
|
35
|
+
{
|
36
|
+
"server_id": "example-server-1",
|
37
|
+
"server_url": "http://localhost:8001",
|
38
|
+
"server_name": "Example Server 1",
|
39
|
+
"description": "Example server for registration testing",
|
40
|
+
"version": "1.0.0",
|
41
|
+
"capabilities": ["jsonrpc", "rest"],
|
42
|
+
"endpoints": {
|
43
|
+
"jsonrpc": "/api/jsonrpc",
|
44
|
+
"rest": "/cmd",
|
45
|
+
"health": "/health"
|
46
|
+
},
|
47
|
+
"auth_method": "api_key",
|
48
|
+
"security_enabled": True
|
49
|
+
},
|
50
|
+
{
|
51
|
+
"server_id": "example-server-2",
|
52
|
+
"server_url": "http://localhost:8002",
|
53
|
+
"server_name": "Example Server 2",
|
54
|
+
"description": "Another example server",
|
55
|
+
"version": "1.0.0",
|
56
|
+
"capabilities": ["jsonrpc", "rest", "security"],
|
57
|
+
"endpoints": {
|
58
|
+
"jsonrpc": "/api/jsonrpc",
|
59
|
+
"rest": "/cmd",
|
60
|
+
"health": "/health"
|
61
|
+
},
|
62
|
+
"auth_method": "certificate",
|
63
|
+
"security_enabled": True
|
64
|
+
}
|
65
|
+
]
|
66
|
+
async def __aenter__(self):
|
67
|
+
"""Async context manager entry."""
|
68
|
+
# Create SSL context for HTTPS
|
69
|
+
ssl_context = None
|
70
|
+
if self.server_url.startswith("https"):
|
71
|
+
ssl_context = ssl.create_default_context()
|
72
|
+
ssl_context.check_hostname = False
|
73
|
+
ssl_context.verify_mode = ssl.CERT_NONE
|
74
|
+
# Create connector
|
75
|
+
connector = TCPConnector(ssl=ssl_context) if ssl_context else None
|
76
|
+
# Create session
|
77
|
+
timeout = ClientTimeout(total=30)
|
78
|
+
self.session = aiohttp.ClientSession(
|
79
|
+
connector=connector,
|
80
|
+
timeout=timeout
|
81
|
+
)
|
82
|
+
return self
|
83
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
84
|
+
"""Async context manager exit."""
|
85
|
+
if self.session:
|
86
|
+
await self.session.close()
|
87
|
+
def _get_headers(self) -> Dict[str, str]:
|
88
|
+
"""Get request headers with authentication."""
|
89
|
+
headers = {
|
90
|
+
"Content-Type": "application/json"
|
91
|
+
}
|
92
|
+
if self.auth_token:
|
93
|
+
headers["X-API-Key"] = self.auth_token
|
94
|
+
return headers
|
95
|
+
async def test_registration(self, server_data: Dict[str, Any]) -> Dict[str, Any]:
|
96
|
+
"""
|
97
|
+
Test registration with authentication.
|
98
|
+
Args:
|
99
|
+
server_data: Server registration data
|
100
|
+
Returns:
|
101
|
+
Test result
|
102
|
+
"""
|
103
|
+
try:
|
104
|
+
# Prepare JSON-RPC request
|
105
|
+
request_data = {
|
106
|
+
"jsonrpc": "2.0",
|
107
|
+
"id": 1,
|
108
|
+
"method": "proxy_registration",
|
109
|
+
"params": {
|
110
|
+
"operation": "register",
|
111
|
+
**server_data
|
112
|
+
}
|
113
|
+
}
|
114
|
+
logger.info(f"Testing registration for server: {server_data['server_id']}")
|
115
|
+
logger.debug(f"Request data: {json.dumps(request_data, indent=2)}")
|
116
|
+
# Make request
|
117
|
+
async with self.session.post(
|
118
|
+
f"{self.server_url}/cmd",
|
119
|
+
json=request_data,
|
120
|
+
headers=self._get_headers()
|
121
|
+
) as response:
|
122
|
+
result = await response.json()
|
123
|
+
logger.info(f"Response status: {response.status}")
|
124
|
+
logger.debug(f"Response: {json.dumps(result, indent=2)}")
|
125
|
+
return {
|
126
|
+
"success": response.status == 200,
|
127
|
+
"status_code": response.status,
|
128
|
+
"result": result,
|
129
|
+
"server_id": server_data["server_id"]
|
130
|
+
}
|
131
|
+
except Exception as e:
|
132
|
+
logger.error(f"Registration test failed: {e}")
|
133
|
+
return {
|
134
|
+
"success": False,
|
135
|
+
"error": str(e),
|
136
|
+
"server_id": server_data["server_id"]
|
137
|
+
}
|
138
|
+
async def test_discovery(self) -> Dict[str, Any]:
|
139
|
+
"""
|
140
|
+
Test discovery operation.
|
141
|
+
Returns:
|
142
|
+
Test result
|
143
|
+
"""
|
144
|
+
try:
|
145
|
+
# Prepare JSON-RPC request
|
146
|
+
request_data = {
|
147
|
+
"jsonrpc": "2.0",
|
148
|
+
"id": 1,
|
149
|
+
"method": "proxy_registration",
|
150
|
+
"params": {
|
151
|
+
"operation": "discover"
|
152
|
+
}
|
153
|
+
}
|
154
|
+
logger.info("Testing discovery operation")
|
155
|
+
# Make request
|
156
|
+
async with self.session.post(
|
157
|
+
f"{self.server_url}/cmd",
|
158
|
+
json=request_data,
|
159
|
+
headers=self._get_headers()
|
160
|
+
) as response:
|
161
|
+
result = await response.json()
|
162
|
+
logger.info(f"Response status: {response.status}")
|
163
|
+
logger.debug(f"Response: {json.dumps(result, indent=2)}")
|
164
|
+
return {
|
165
|
+
"success": response.status == 200,
|
166
|
+
"status_code": response.status,
|
167
|
+
"result": result
|
168
|
+
}
|
169
|
+
except Exception as e:
|
170
|
+
logger.error(f"Discovery test failed: {e}")
|
171
|
+
return {
|
172
|
+
"success": False,
|
173
|
+
"error": str(e)
|
174
|
+
}
|
175
|
+
async def test_heartbeat(self, server_key: str) -> Dict[str, Any]:
|
176
|
+
"""
|
177
|
+
Test heartbeat operation.
|
178
|
+
Args:
|
179
|
+
server_key: Server key for heartbeat
|
180
|
+
Returns:
|
181
|
+
Test result
|
182
|
+
"""
|
183
|
+
try:
|
184
|
+
# Prepare JSON-RPC request
|
185
|
+
request_data = {
|
186
|
+
"jsonrpc": "2.0",
|
187
|
+
"id": 1,
|
188
|
+
"method": "proxy_registration",
|
189
|
+
"params": {
|
190
|
+
"operation": "heartbeat",
|
191
|
+
"server_key": server_key,
|
192
|
+
"timestamp": 1234567890,
|
193
|
+
"status": "healthy"
|
194
|
+
}
|
195
|
+
}
|
196
|
+
logger.info(f"Testing heartbeat for server: {server_key}")
|
197
|
+
# Make request
|
198
|
+
async with self.session.post(
|
199
|
+
f"{self.server_url}/cmd",
|
200
|
+
json=request_data,
|
201
|
+
headers=self._get_headers()
|
202
|
+
) as response:
|
203
|
+
result = await response.json()
|
204
|
+
logger.info(f"Response status: {response.status}")
|
205
|
+
logger.debug(f"Response: {json.dumps(result, indent=2)}")
|
206
|
+
return {
|
207
|
+
"success": response.status == 200,
|
208
|
+
"status_code": response.status,
|
209
|
+
"result": result,
|
210
|
+
"server_key": server_key
|
211
|
+
}
|
212
|
+
except Exception as e:
|
213
|
+
logger.error(f"Heartbeat test failed: {e}")
|
214
|
+
return {
|
215
|
+
"success": False,
|
216
|
+
"error": str(e),
|
217
|
+
"server_key": server_key
|
218
|
+
}
|
219
|
+
async def run_proxy_registration_example():
|
220
|
+
"""Run proxy registration example."""
|
221
|
+
logger.info("š Starting proxy registration example")
|
222
|
+
# Test configurations
|
223
|
+
test_configs = [
|
224
|
+
{
|
225
|
+
"name": "Admin Token",
|
226
|
+
"server_url": "http://localhost:8002",
|
227
|
+
"auth_token": "test-token-123"
|
228
|
+
},
|
229
|
+
{
|
230
|
+
"name": "User Token",
|
231
|
+
"server_url": "http://localhost:8002",
|
232
|
+
"auth_token": "user-token-456"
|
233
|
+
},
|
234
|
+
{
|
235
|
+
"name": "Readonly Token",
|
236
|
+
"server_url": "http://localhost:8002",
|
237
|
+
"auth_token": "readonly-token-123"
|
238
|
+
}
|
239
|
+
]
|
240
|
+
results = []
|
241
|
+
for config in test_configs:
|
242
|
+
logger.info(f"\nš Testing: {config['name']}")
|
243
|
+
logger.info(f"Server URL: {config['server_url']}")
|
244
|
+
logger.info(f"Auth Token: {config['auth_token']}")
|
245
|
+
async with ProxyRegistrationExample(
|
246
|
+
config['server_url'],
|
247
|
+
config['auth_token']
|
248
|
+
) as client:
|
249
|
+
# Test registration
|
250
|
+
for server_data in client.test_servers:
|
251
|
+
result = await client.test_registration(server_data)
|
252
|
+
results.append({
|
253
|
+
"test": f"{config['name']} - Registration",
|
254
|
+
"server_id": server_data["server_id"],
|
255
|
+
**result
|
256
|
+
})
|
257
|
+
# If registration successful, test heartbeat
|
258
|
+
if result["success"] and "result" in result:
|
259
|
+
server_key = result["result"].get("result", {}).get("server_key")
|
260
|
+
if server_key:
|
261
|
+
heartbeat_result = await client.test_heartbeat(server_key)
|
262
|
+
results.append({
|
263
|
+
"test": f"{config['name']} - Heartbeat",
|
264
|
+
"server_key": server_key,
|
265
|
+
**heartbeat_result
|
266
|
+
})
|
267
|
+
# Test discovery
|
268
|
+
discovery_result = await client.test_discovery()
|
269
|
+
results.append({
|
270
|
+
"test": f"{config['name']} - Discovery",
|
271
|
+
**discovery_result
|
272
|
+
})
|
273
|
+
# Test without authentication
|
274
|
+
logger.info(f"\nš Testing: No Authentication")
|
275
|
+
async with ProxyRegistrationExample("http://localhost:8002") as client:
|
276
|
+
for server_data in client.test_servers:
|
277
|
+
result = await client.test_registration(server_data)
|
278
|
+
results.append({
|
279
|
+
"test": "No Auth - Registration",
|
280
|
+
"server_id": server_data["server_id"],
|
281
|
+
**result
|
282
|
+
})
|
283
|
+
# Print results
|
284
|
+
logger.info("\n" + "="*80)
|
285
|
+
logger.info("š EXAMPLE RESULTS")
|
286
|
+
logger.info("="*80)
|
287
|
+
passed = 0
|
288
|
+
failed = 0
|
289
|
+
for result in results:
|
290
|
+
status = "ā
PASS" if result["success"] else "ā FAIL"
|
291
|
+
logger.info(f"{status} {result['test']}")
|
292
|
+
if result["success"]:
|
293
|
+
passed += 1
|
294
|
+
else:
|
295
|
+
failed += 1
|
296
|
+
if "error" in result:
|
297
|
+
logger.error(f" Error: {result['error']}")
|
298
|
+
elif "result" in result:
|
299
|
+
result_data = result["result"]
|
300
|
+
if "error" in result_data:
|
301
|
+
logger.error(f" API Error: {result_data['error']}")
|
302
|
+
elif "result" in result_data:
|
303
|
+
api_result = result_data["result"]
|
304
|
+
if "server_key" in api_result:
|
305
|
+
logger.info(f" Server Key: {api_result['server_key']}")
|
306
|
+
if "message" in api_result:
|
307
|
+
logger.info(f" Message: {api_result['message']}")
|
308
|
+
logger.info("\n" + "="*80)
|
309
|
+
logger.info(f"š SUMMARY: {passed} passed, {failed} failed")
|
310
|
+
logger.info("="*80)
|
311
|
+
return passed, failed
|
312
|
+
def main():
|
313
|
+
"""Main function for the example."""
|
314
|
+
logger.info("š§ MCP Proxy Adapter - Proxy Registration Example")
|
315
|
+
logger.info("="*60)
|
316
|
+
# Check if server is running
|
317
|
+
import socket
|
318
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
319
|
+
result = sock.connect_ex(('localhost', 8002))
|
320
|
+
sock.close()
|
321
|
+
if result != 0:
|
322
|
+
logger.error("ā Server is not running on localhost:8002")
|
323
|
+
logger.info("š” Please start the server first:")
|
324
|
+
logger.info(" cd mcp_proxy_adapter/examples")
|
325
|
+
logger.info(" python -m mcp_proxy_adapter.main --config server_configs/config_proxy_registration.json")
|
326
|
+
sys.exit(1)
|
327
|
+
logger.info("ā
Server is running on localhost:8002")
|
328
|
+
logger.info("š Starting proxy registration example...")
|
329
|
+
# Run example
|
330
|
+
passed, failed = asyncio.run(run_proxy_registration_example())
|
331
|
+
# Exit with appropriate code
|
332
|
+
sys.exit(0 if failed == 0 else 1)
|
333
|
+
if __name__ == "__main__":
|
334
|
+
main()
|