mcp-proxy-adapter 6.0.0__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.
Files changed (212) hide show
  1. mcp_proxy_adapter/__main__.py +27 -7
  2. mcp_proxy_adapter/api/app.py +209 -79
  3. mcp_proxy_adapter/api/handlers.py +16 -5
  4. mcp_proxy_adapter/api/middleware/__init__.py +14 -9
  5. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  6. mcp_proxy_adapter/api/middleware/factory.py +36 -12
  7. mcp_proxy_adapter/api/middleware/protocol_middleware.py +84 -18
  8. mcp_proxy_adapter/api/middleware/unified_security.py +197 -0
  9. mcp_proxy_adapter/api/middleware/user_info_middleware.py +158 -0
  10. mcp_proxy_adapter/commands/__init__.py +7 -1
  11. mcp_proxy_adapter/commands/base.py +7 -4
  12. mcp_proxy_adapter/commands/builtin_commands.py +8 -2
  13. mcp_proxy_adapter/commands/command_registry.py +8 -0
  14. mcp_proxy_adapter/commands/echo_command.py +81 -0
  15. mcp_proxy_adapter/commands/health_command.py +1 -1
  16. mcp_proxy_adapter/commands/help_command.py +21 -14
  17. mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
  18. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  19. mcp_proxy_adapter/commands/security_command.py +488 -0
  20. mcp_proxy_adapter/commands/ssl_setup_command.py +234 -351
  21. mcp_proxy_adapter/commands/token_management_command.py +1 -1
  22. mcp_proxy_adapter/config.py +323 -40
  23. mcp_proxy_adapter/core/app_factory.py +410 -0
  24. mcp_proxy_adapter/core/app_runner.py +272 -0
  25. mcp_proxy_adapter/core/certificate_utils.py +291 -73
  26. mcp_proxy_adapter/core/client.py +574 -0
  27. mcp_proxy_adapter/core/client_manager.py +284 -0
  28. mcp_proxy_adapter/core/client_security.py +384 -0
  29. mcp_proxy_adapter/core/logging.py +8 -3
  30. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  31. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  32. mcp_proxy_adapter/core/protocol_manager.py +169 -10
  33. mcp_proxy_adapter/core/proxy_client.py +602 -0
  34. mcp_proxy_adapter/core/proxy_registration.py +299 -47
  35. mcp_proxy_adapter/core/security_adapter.py +12 -15
  36. mcp_proxy_adapter/core/security_integration.py +286 -0
  37. mcp_proxy_adapter/core/server_adapter.py +282 -0
  38. mcp_proxy_adapter/core/server_engine.py +270 -0
  39. mcp_proxy_adapter/core/ssl_utils.py +13 -12
  40. mcp_proxy_adapter/core/transport_manager.py +5 -5
  41. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  42. mcp_proxy_adapter/examples/__init__.py +13 -4
  43. mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
  44. mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
  45. mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
  46. mcp_proxy_adapter/examples/basic_framework/main.py +44 -0
  47. mcp_proxy_adapter/examples/commands/__init__.py +5 -0
  48. mcp_proxy_adapter/examples/create_certificates_simple.py +550 -0
  49. mcp_proxy_adapter/examples/debug_request_state.py +112 -0
  50. mcp_proxy_adapter/examples/debug_role_chain.py +158 -0
  51. mcp_proxy_adapter/examples/demo_client.py +275 -0
  52. mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
  53. mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
  54. mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
  55. mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
  56. mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
  57. mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
  58. mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
  59. mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  60. mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
  61. mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
  62. mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  63. mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
  64. mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
  65. mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
  66. mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
  67. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +80 -0
  68. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  69. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
  70. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +75 -0
  71. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  72. mcp_proxy_adapter/examples/full_application/main.py +173 -0
  73. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
  74. mcp_proxy_adapter/examples/generate_all_certificates.py +362 -0
  75. mcp_proxy_adapter/examples/generate_certificates.py +177 -0
  76. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
  77. mcp_proxy_adapter/examples/generate_test_configs.py +331 -0
  78. mcp_proxy_adapter/examples/proxy_registration_example.py +334 -0
  79. mcp_proxy_adapter/examples/run_example.py +59 -0
  80. mcp_proxy_adapter/examples/run_full_test_suite.py +318 -0
  81. mcp_proxy_adapter/examples/run_proxy_server.py +146 -0
  82. mcp_proxy_adapter/examples/run_security_tests.py +544 -0
  83. mcp_proxy_adapter/examples/run_security_tests_fixed.py +247 -0
  84. mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
  85. mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
  86. mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
  87. mcp_proxy_adapter/examples/security_test_client.py +782 -0
  88. mcp_proxy_adapter/examples/setup_test_environment.py +328 -0
  89. mcp_proxy_adapter/examples/test_config.py +148 -0
  90. mcp_proxy_adapter/examples/test_config_generator.py +86 -0
  91. mcp_proxy_adapter/examples/test_examples.py +281 -0
  92. mcp_proxy_adapter/examples/universal_client.py +620 -0
  93. mcp_proxy_adapter/main.py +66 -148
  94. mcp_proxy_adapter/utils/config_generator.py +1008 -0
  95. mcp_proxy_adapter/version.py +5 -2
  96. mcp_proxy_adapter-6.0.1.dist-info/METADATA +679 -0
  97. mcp_proxy_adapter-6.0.1.dist-info/RECORD +140 -0
  98. mcp_proxy_adapter-6.0.1.dist-info/entry_points.txt +2 -0
  99. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/licenses/LICENSE +2 -2
  100. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  101. mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
  102. mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
  103. mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
  104. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  105. mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
  106. mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
  107. mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
  108. mcp_proxy_adapter/api/middleware/security.py +0 -376
  109. mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
  110. mcp_proxy_adapter/examples/README.md +0 -124
  111. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  112. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  113. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  114. mcp_proxy_adapter/examples/basic_server/config.json +0 -70
  115. mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
  116. mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
  117. mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
  118. mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
  119. mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
  120. mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
  121. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  122. mcp_proxy_adapter/examples/basic_server/server.py +0 -114
  123. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  124. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  125. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
  126. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  127. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  128. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  129. mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
  130. mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
  131. mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
  132. mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
  133. mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
  134. mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
  135. mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
  136. mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
  137. mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
  138. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  139. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  140. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  141. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  142. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  143. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  144. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  145. mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
  146. mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
  147. mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
  148. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  149. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  150. mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
  151. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  152. mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
  153. mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
  154. mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
  155. mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
  156. mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
  157. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  158. mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
  159. mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
  160. mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
  161. mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
  162. mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
  163. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  164. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  165. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  166. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  167. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  168. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  169. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  170. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  171. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  172. mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
  173. mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
  174. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  175. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  176. mcp_proxy_adapter/schemas/roles_schema.json +0 -162
  177. mcp_proxy_adapter/tests/__init__.py +0 -0
  178. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  179. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  180. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  181. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  182. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  183. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  184. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  185. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  186. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  187. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  188. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  189. mcp_proxy_adapter/tests/conftest.py +0 -131
  190. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  191. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  192. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  193. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  194. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  195. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  196. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  197. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  198. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  199. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  200. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  201. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  202. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  203. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  204. mcp_proxy_adapter/tests/test_config.py +0 -127
  205. mcp_proxy_adapter/tests/test_utils.py +0 -65
  206. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  207. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  208. mcp_proxy_adapter/tests/unit/test_config.py +0 -270
  209. mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
  210. mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
  211. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/WHEEL +0 -0
  212. {mcp_proxy_adapter-6.0.0.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()