mcp-proxy-adapter 6.9.28__py3-none-any.whl → 6.9.30__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.

Files changed (212) hide show
  1. mcp_proxy_adapter/__init__.py +10 -0
  2. mcp_proxy_adapter/__main__.py +8 -21
  3. mcp_proxy_adapter/api/app.py +10 -913
  4. mcp_proxy_adapter/api/core/__init__.py +18 -0
  5. mcp_proxy_adapter/api/core/app_factory.py +243 -0
  6. mcp_proxy_adapter/api/core/lifespan_manager.py +55 -0
  7. mcp_proxy_adapter/api/core/registration_manager.py +166 -0
  8. mcp_proxy_adapter/api/core/ssl_context_factory.py +88 -0
  9. mcp_proxy_adapter/api/handlers.py +78 -199
  10. mcp_proxy_adapter/api/middleware/__init__.py +1 -44
  11. mcp_proxy_adapter/api/middleware/base.py +0 -42
  12. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +0 -85
  13. mcp_proxy_adapter/api/middleware/error_handling.py +1 -127
  14. mcp_proxy_adapter/api/middleware/factory.py +0 -94
  15. mcp_proxy_adapter/api/middleware/logging.py +0 -112
  16. mcp_proxy_adapter/api/middleware/performance.py +0 -35
  17. mcp_proxy_adapter/api/middleware/protocol_middleware.py +2 -98
  18. mcp_proxy_adapter/api/middleware/transport_middleware.py +0 -37
  19. mcp_proxy_adapter/api/middleware/unified_security.py +10 -10
  20. mcp_proxy_adapter/api/middleware/user_info_middleware.py +0 -118
  21. mcp_proxy_adapter/api/openapi/__init__.py +21 -0
  22. mcp_proxy_adapter/api/openapi/command_integration.py +105 -0
  23. mcp_proxy_adapter/api/openapi/openapi_generator.py +40 -0
  24. mcp_proxy_adapter/api/openapi/openapi_registry.py +62 -0
  25. mcp_proxy_adapter/api/openapi/schema_loader.py +116 -0
  26. mcp_proxy_adapter/api/schemas.py +0 -61
  27. mcp_proxy_adapter/api/tool_integration.py +0 -117
  28. mcp_proxy_adapter/api/tools.py +0 -46
  29. mcp_proxy_adapter/cli/__init__.py +12 -0
  30. mcp_proxy_adapter/cli/commands/__init__.py +15 -0
  31. mcp_proxy_adapter/cli/commands/client.py +100 -0
  32. mcp_proxy_adapter/cli/commands/config_generate.py +21 -0
  33. mcp_proxy_adapter/cli/commands/config_validate.py +36 -0
  34. mcp_proxy_adapter/cli/commands/generate.py +259 -0
  35. mcp_proxy_adapter/cli/commands/server.py +174 -0
  36. mcp_proxy_adapter/cli/commands/sets.py +128 -0
  37. mcp_proxy_adapter/cli/commands/testconfig.py +177 -0
  38. mcp_proxy_adapter/cli/examples/__init__.py +8 -0
  39. mcp_proxy_adapter/cli/examples/http_basic.py +82 -0
  40. mcp_proxy_adapter/cli/examples/https_token.py +96 -0
  41. mcp_proxy_adapter/cli/examples/mtls_roles.py +103 -0
  42. mcp_proxy_adapter/cli/main.py +63 -0
  43. mcp_proxy_adapter/cli/parser.py +324 -0
  44. mcp_proxy_adapter/cli/validators.py +231 -0
  45. mcp_proxy_adapter/client/jsonrpc_client.py +406 -0
  46. mcp_proxy_adapter/client/proxy.py +45 -0
  47. mcp_proxy_adapter/commands/__init__.py +44 -28
  48. mcp_proxy_adapter/commands/auth_validation_command.py +7 -344
  49. mcp_proxy_adapter/commands/base.py +19 -43
  50. mcp_proxy_adapter/commands/builtin_commands.py +0 -75
  51. mcp_proxy_adapter/commands/catalog/__init__.py +20 -0
  52. mcp_proxy_adapter/commands/catalog/catalog_loader.py +34 -0
  53. mcp_proxy_adapter/commands/catalog/catalog_manager.py +122 -0
  54. mcp_proxy_adapter/commands/catalog/catalog_syncer.py +149 -0
  55. mcp_proxy_adapter/commands/catalog/command_catalog.py +43 -0
  56. mcp_proxy_adapter/commands/catalog/dependency_manager.py +37 -0
  57. mcp_proxy_adapter/commands/catalog_manager.py +58 -928
  58. mcp_proxy_adapter/commands/cert_monitor_command.py +0 -88
  59. mcp_proxy_adapter/commands/certificate_management_command.py +0 -45
  60. mcp_proxy_adapter/commands/command_registry.py +172 -904
  61. mcp_proxy_adapter/commands/config_command.py +0 -28
  62. mcp_proxy_adapter/commands/dependency_container.py +1 -70
  63. mcp_proxy_adapter/commands/dependency_manager.py +0 -128
  64. mcp_proxy_adapter/commands/echo_command.py +0 -34
  65. mcp_proxy_adapter/commands/health_command.py +0 -3
  66. mcp_proxy_adapter/commands/help_command.py +0 -159
  67. mcp_proxy_adapter/commands/hooks.py +0 -137
  68. mcp_proxy_adapter/commands/key_management_command.py +0 -25
  69. mcp_proxy_adapter/commands/load_command.py +7 -78
  70. mcp_proxy_adapter/commands/plugins_command.py +0 -16
  71. mcp_proxy_adapter/commands/protocol_management_command.py +0 -28
  72. mcp_proxy_adapter/commands/proxy_registration_command.py +0 -88
  73. mcp_proxy_adapter/commands/queue_commands.py +750 -0
  74. mcp_proxy_adapter/commands/registration_status_command.py +0 -43
  75. mcp_proxy_adapter/commands/registry/__init__.py +18 -0
  76. mcp_proxy_adapter/commands/registry/command_info.py +103 -0
  77. mcp_proxy_adapter/commands/registry/command_loader.py +207 -0
  78. mcp_proxy_adapter/commands/registry/command_manager.py +119 -0
  79. mcp_proxy_adapter/commands/registry/command_registry.py +217 -0
  80. mcp_proxy_adapter/commands/reload_command.py +0 -80
  81. mcp_proxy_adapter/commands/result.py +25 -77
  82. mcp_proxy_adapter/commands/role_test_command.py +0 -44
  83. mcp_proxy_adapter/commands/roles_management_command.py +0 -199
  84. mcp_proxy_adapter/commands/security_command.py +0 -30
  85. mcp_proxy_adapter/commands/settings_command.py +0 -68
  86. mcp_proxy_adapter/commands/ssl_setup_command.py +0 -42
  87. mcp_proxy_adapter/commands/token_management_command.py +0 -1
  88. mcp_proxy_adapter/commands/transport_management_command.py +0 -20
  89. mcp_proxy_adapter/commands/unload_command.py +0 -71
  90. mcp_proxy_adapter/config.py +15 -626
  91. mcp_proxy_adapter/core/__init__.py +5 -39
  92. mcp_proxy_adapter/core/app_factory.py +14 -36
  93. mcp_proxy_adapter/core/app_runner.py +0 -27
  94. mcp_proxy_adapter/core/auth_validator.py +1 -93
  95. mcp_proxy_adapter/core/certificate/__init__.py +20 -0
  96. mcp_proxy_adapter/core/certificate/certificate_creator.py +371 -0
  97. mcp_proxy_adapter/core/certificate/certificate_extractor.py +183 -0
  98. mcp_proxy_adapter/core/certificate/certificate_utils.py +249 -0
  99. mcp_proxy_adapter/core/certificate/certificate_validator.py +110 -0
  100. mcp_proxy_adapter/core/certificate/ssl_context_manager.py +70 -0
  101. mcp_proxy_adapter/core/certificate_utils.py +64 -903
  102. mcp_proxy_adapter/core/client.py +10 -9
  103. mcp_proxy_adapter/core/client_manager.py +0 -19
  104. mcp_proxy_adapter/core/client_security.py +0 -2
  105. mcp_proxy_adapter/core/config/__init__.py +18 -0
  106. mcp_proxy_adapter/core/config/config.py +195 -0
  107. mcp_proxy_adapter/core/config/config_factory.py +22 -0
  108. mcp_proxy_adapter/core/config/config_loader.py +66 -0
  109. mcp_proxy_adapter/core/config/feature_manager.py +31 -0
  110. mcp_proxy_adapter/core/config/simple_config.py +112 -0
  111. mcp_proxy_adapter/core/config/simple_config_generator.py +50 -0
  112. mcp_proxy_adapter/core/config/simple_config_validator.py +96 -0
  113. mcp_proxy_adapter/core/config_converter.py +0 -186
  114. mcp_proxy_adapter/core/config_validator.py +96 -1238
  115. mcp_proxy_adapter/core/errors.py +7 -42
  116. mcp_proxy_adapter/core/job_manager.py +54 -0
  117. mcp_proxy_adapter/core/logging.py +2 -22
  118. mcp_proxy_adapter/core/mtls_asgi.py +0 -20
  119. mcp_proxy_adapter/core/mtls_asgi_app.py +0 -12
  120. mcp_proxy_adapter/core/mtls_proxy.py +0 -80
  121. mcp_proxy_adapter/core/mtls_server.py +3 -173
  122. mcp_proxy_adapter/core/protocol_manager.py +1 -191
  123. mcp_proxy_adapter/core/proxy/__init__.py +22 -0
  124. mcp_proxy_adapter/core/proxy/auth_manager.py +27 -0
  125. mcp_proxy_adapter/core/proxy/proxy_registration_manager.py +137 -0
  126. mcp_proxy_adapter/core/proxy/registration_client.py +60 -0
  127. mcp_proxy_adapter/core/proxy/ssl_manager.py +101 -0
  128. mcp_proxy_adapter/core/proxy_client.py +0 -1
  129. mcp_proxy_adapter/core/proxy_registration.py +36 -913
  130. mcp_proxy_adapter/core/role_utils.py +0 -308
  131. mcp_proxy_adapter/core/security_adapter.py +1 -36
  132. mcp_proxy_adapter/core/security_factory.py +1 -150
  133. mcp_proxy_adapter/core/security_integration.py +0 -33
  134. mcp_proxy_adapter/core/server_adapter.py +1 -40
  135. mcp_proxy_adapter/core/server_engine.py +2 -173
  136. mcp_proxy_adapter/core/settings.py +0 -127
  137. mcp_proxy_adapter/core/signal_handler.py +0 -65
  138. mcp_proxy_adapter/core/ssl_utils.py +19 -137
  139. mcp_proxy_adapter/core/transport_manager.py +0 -151
  140. mcp_proxy_adapter/core/unified_config_adapter.py +1 -193
  141. mcp_proxy_adapter/core/utils.py +1 -182
  142. mcp_proxy_adapter/core/validation/__init__.py +21 -0
  143. mcp_proxy_adapter/core/validation/config_validator.py +211 -0
  144. mcp_proxy_adapter/core/validation/file_validator.py +73 -0
  145. mcp_proxy_adapter/core/validation/protocol_validator.py +191 -0
  146. mcp_proxy_adapter/core/validation/security_validator.py +58 -0
  147. mcp_proxy_adapter/core/validation/validation_result.py +27 -0
  148. mcp_proxy_adapter/custom_openapi.py +33 -652
  149. mcp_proxy_adapter/examples/bugfix_certificate_config.py +0 -23
  150. mcp_proxy_adapter/examples/check_config.py +0 -2
  151. mcp_proxy_adapter/examples/client_usage_example.py +164 -0
  152. mcp_proxy_adapter/examples/config_builder.py +13 -2
  153. mcp_proxy_adapter/examples/config_cli.py +0 -1
  154. mcp_proxy_adapter/examples/create_test_configs.py +0 -46
  155. mcp_proxy_adapter/examples/debug_request_state.py +0 -1
  156. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +0 -47
  157. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +0 -45
  158. mcp_proxy_adapter/examples/full_application/commands/echo_command.py +0 -12
  159. mcp_proxy_adapter/examples/full_application/commands/help_command.py +0 -12
  160. mcp_proxy_adapter/examples/full_application/commands/list_command.py +0 -7
  161. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +0 -2
  162. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +0 -59
  163. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +0 -54
  164. mcp_proxy_adapter/examples/full_application/main.py +186 -150
  165. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +0 -107
  166. mcp_proxy_adapter/examples/full_application/test_minimal_server.py +0 -24
  167. mcp_proxy_adapter/examples/full_application/test_server.py +0 -58
  168. mcp_proxy_adapter/examples/generate_config.py +65 -11
  169. mcp_proxy_adapter/examples/queue_demo_simple.py +632 -0
  170. mcp_proxy_adapter/examples/queue_integration_example.py +578 -0
  171. mcp_proxy_adapter/examples/queue_server_demo.py +82 -0
  172. mcp_proxy_adapter/examples/queue_server_example.py +85 -0
  173. mcp_proxy_adapter/examples/queue_server_simple.py +173 -0
  174. mcp_proxy_adapter/examples/required_certificates.py +0 -2
  175. mcp_proxy_adapter/examples/run_full_test_suite.py +0 -29
  176. mcp_proxy_adapter/examples/run_proxy_server.py +31 -71
  177. mcp_proxy_adapter/examples/run_security_tests_fixed.py +0 -27
  178. mcp_proxy_adapter/examples/security_test/__init__.py +18 -0
  179. mcp_proxy_adapter/examples/security_test/auth_manager.py +14 -0
  180. mcp_proxy_adapter/examples/security_test/ssl_context_manager.py +28 -0
  181. mcp_proxy_adapter/examples/security_test/test_client.py +159 -0
  182. mcp_proxy_adapter/examples/security_test/test_result.py +22 -0
  183. mcp_proxy_adapter/examples/security_test_client.py +24 -1075
  184. mcp_proxy_adapter/examples/setup/__init__.py +24 -0
  185. mcp_proxy_adapter/examples/setup/certificate_manager.py +215 -0
  186. mcp_proxy_adapter/examples/setup/config_generator.py +12 -0
  187. mcp_proxy_adapter/examples/setup/config_validator.py +118 -0
  188. mcp_proxy_adapter/examples/setup/environment_setup.py +62 -0
  189. mcp_proxy_adapter/examples/setup/test_files_generator.py +10 -0
  190. mcp_proxy_adapter/examples/setup/test_runner.py +89 -0
  191. mcp_proxy_adapter/examples/setup_test_environment.py +133 -1425
  192. mcp_proxy_adapter/examples/test_config.py +0 -3
  193. mcp_proxy_adapter/examples/test_config_builder.py +25 -405
  194. mcp_proxy_adapter/examples/test_examples.py +0 -1
  195. mcp_proxy_adapter/examples/test_framework_complete.py +0 -2
  196. mcp_proxy_adapter/examples/test_mcp_server.py +0 -1
  197. mcp_proxy_adapter/examples/test_protocol_examples.py +0 -1
  198. mcp_proxy_adapter/examples/universal_client.py +0 -6
  199. mcp_proxy_adapter/examples/update_config_certificates.py +0 -1
  200. mcp_proxy_adapter/examples/validate_generator_compatibility.py +0 -1
  201. mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +0 -187
  202. mcp_proxy_adapter/integrations/__init__.py +25 -0
  203. mcp_proxy_adapter/integrations/queuemgr_integration.py +462 -0
  204. mcp_proxy_adapter/main.py +70 -62
  205. mcp_proxy_adapter/openapi.py +0 -22
  206. mcp_proxy_adapter/version.py +1 -1
  207. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/METADATA +2 -1
  208. mcp_proxy_adapter-6.9.30.dist-info/RECORD +235 -0
  209. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/entry_points.txt +1 -1
  210. mcp_proxy_adapter-6.9.28.dist-info/RECORD +0 -149
  211. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/WHEEL +0 -0
  212. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/top_level.txt +0 -0
@@ -72,14 +72,6 @@ class TSTCommandExecutor:
72
72
  raise
73
73
 
74
74
  @classmethod
75
- def get_schema(cls) -> Dict[str, Any]:
76
- """
77
- Возвращает схему инструмента в формате OpenAPI.
78
-
79
- Returns:
80
- Словарь со схемой инструмента
81
- """
82
- return ToolIntegration.generate_tool_schema(cls.name, registry, cls.description)
83
75
 
84
76
  @classmethod
85
77
  def get_description(cls, format: str = "json") -> Union[Dict[str, Any], str]:
@@ -167,43 +159,5 @@ class TSTCommandExecutor:
167
159
  available_tools = {TSTCommandExecutor.name: TSTCommandExecutor}
168
160
 
169
161
 
170
- def get_tool_description(
171
- tool_name: str, format: str = "json"
172
- ) -> Union[Dict[str, Any], str]:
173
- """
174
- Получает описание инструмента API по имени.
175
-
176
- Args:
177
- tool_name: Имя инструмента API
178
- format: Формат описания (json, markdown, html)
179
-
180
- Returns:
181
- Описание инструмента в указанном формате
182
162
 
183
- Raises:
184
- NotFoundError: Если инструмент не найден
185
- """
186
- if tool_name in available_tools:
187
- return available_tools[tool_name].get_description(format)
188
- else:
189
- raise NotFoundError(f"Инструмент '{tool_name}' не найден")
190
-
191
-
192
- async def execute_tool(tool_name: str, **params) -> Dict[str, Any]:
193
- """
194
- Выполняет инструмент API с указанными параметрами.
195
163
 
196
- Args:
197
- tool_name: Имя инструмента API
198
- **params: Параметры инструмента
199
-
200
- Returns:
201
- Результат выполнения инструмента
202
-
203
- Raises:
204
- NotFoundError: Если инструмент не найден
205
- """
206
- if tool_name in available_tools:
207
- return await available_tools[tool_name].execute(**params)
208
- else:
209
- raise NotFoundError(f"Инструмент '{tool_name}' не найден")
@@ -0,0 +1,12 @@
1
+ """
2
+ MCP Proxy Adapter CLI Module
3
+
4
+ This module provides command-line interface for MCP Proxy Adapter framework.
5
+
6
+ Author: Vasiliy Zdanovskiy
7
+ email: vasilyvz@gmail.com
8
+ """
9
+
10
+ from .main import main
11
+
12
+ __all__ = ['main']
@@ -0,0 +1,15 @@
1
+ """
2
+ CLI Commands Module
3
+
4
+ This module contains all CLI commands for MCP Proxy Adapter.
5
+
6
+ Author: Vasiliy Zdanovskiy
7
+ email: vasilyvz@gmail.com
8
+ """
9
+
10
+ from .generate import GenerateCommand
11
+ from .testconfig import TestConfigCommand
12
+ from .server import ServerCommand
13
+ from .sets import SetsCommand
14
+
15
+ __all__ = ['GenerateCommand', 'TestConfigCommand', 'ServerCommand', 'SetsCommand']
@@ -0,0 +1,100 @@
1
+ """
2
+ MCP Client Command
3
+
4
+ Client CLI for calling health and JSON-RPC endpoints in various modes.
5
+
6
+ Author: Vasiliy Zdanovskiy
7
+ email: vasilyvz@gmail.com
8
+ """
9
+
10
+ import json
11
+ from pathlib import Path
12
+ from typing import Any, Dict, Optional
13
+
14
+ import requests
15
+ from mcp_proxy_adapter.client.proxy import ProxyClient
16
+
17
+
18
+ def _build_base_url(protocol: str, host: str, port: int) -> str:
19
+ scheme = "https" if protocol == "https" else "http"
20
+ return f"{scheme}://{host}:{port}"
21
+
22
+
23
+ def _request_kwargs(protocol: str, token_header: Optional[str], token: Optional[str],
24
+ cert: Optional[str], key: Optional[str], ca: Optional[str]) -> Dict[str, Any]:
25
+ headers: Dict[str, str] = {"Content-Type": "application/json"}
26
+ if token_header and token:
27
+ headers[token_header] = token
28
+
29
+ kwargs: Dict[str, Any] = {"headers": headers, "timeout": 10}
30
+
31
+ if protocol == "https":
32
+ if cert and key:
33
+ kwargs["cert"] = (cert, key)
34
+ # For examples we allow self-signed; if CA provided, use it, otherwise disable verification
35
+ if ca:
36
+ kwargs["verify"] = str(Path(ca))
37
+ else:
38
+ kwargs["verify"] = False
39
+
40
+ return kwargs
41
+
42
+
43
+ def client_command(args) -> int:
44
+ """Dispatch client subcommands."""
45
+ protocol = args.protocol
46
+ host = args.host
47
+ port = args.port
48
+ base = _build_base_url(protocol, host, port)
49
+ kwargs = _request_kwargs(
50
+ protocol,
51
+ getattr(args, "token_header", None),
52
+ getattr(args, "token", None),
53
+ getattr(args, "cert", None),
54
+ getattr(args, "key", None),
55
+ getattr(args, "ca", None),
56
+ )
57
+
58
+ try:
59
+ if args.client_command == "health":
60
+ resp = requests.get(f"{base}/health", **kwargs)
61
+ print(json.dumps(resp.json(), ensure_ascii=False))
62
+ return 0 if resp.ok else 1
63
+
64
+ if args.client_command == "jsonrpc":
65
+ payload: Dict[str, Any] = {
66
+ "jsonrpc": "2.0",
67
+ "method": args.method,
68
+ "params": json.loads(args.params) if args.params else {},
69
+ "id": args.id,
70
+ }
71
+ resp = requests.post(f"{base}/api/jsonrpc", data=json.dumps(payload), **kwargs)
72
+ print(json.dumps(resp.json(), ensure_ascii=False))
73
+ return 0 if resp.ok else 1
74
+
75
+ if args.client_command == "proxy-register":
76
+ pc = ProxyClient(args.proxy_url)
77
+ res = pc.register(args.name, args.url, capabilities=args.capabilities or [], metadata=None)
78
+ print(json.dumps(res, ensure_ascii=False))
79
+ return 0
80
+
81
+ if args.client_command == "proxy-unregister":
82
+ pc = ProxyClient(args.proxy_url)
83
+ res = pc.unregister(args.name)
84
+ print(json.dumps(res, ensure_ascii=False))
85
+ return 0
86
+
87
+ if args.client_command == "proxy-list":
88
+ pc = ProxyClient(args.proxy_url)
89
+ res = pc.list_servers()
90
+ print(json.dumps(res, ensure_ascii=False))
91
+ return 0
92
+
93
+ print("Available: client health|jsonrpc")
94
+ return 1
95
+
96
+ except Exception as exc: # noqa: BLE001 (keep simple CLI handling)
97
+ print(f"❌ Client error: {exc}")
98
+ return 1
99
+
100
+
@@ -0,0 +1,21 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ CLI command: config generate (Simple configuration generator)
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from argparse import Namespace
11
+
12
+ from mcp_proxy_adapter.core.config.simple_config_generator import SimpleConfigGenerator
13
+
14
+
15
+ def config_generate_command(args: Namespace) -> int:
16
+ generator = SimpleConfigGenerator()
17
+ out = generator.generate(protocol=args.protocol, with_proxy=args.with_proxy, out_path=args.out)
18
+ print(f"✅ Configuration generated: {out}")
19
+ return 0
20
+
21
+
@@ -0,0 +1,36 @@
1
+ """
2
+ Author: Vasiliy Zdanovskiy
3
+ email: vasilyvz@gmail.com
4
+
5
+ CLI command: config validate (Simple configuration validation)
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import sys
11
+ from argparse import Namespace
12
+
13
+ from mcp_proxy_adapter.core.config.simple_config import SimpleConfig
14
+ from mcp_proxy_adapter.core.config.simple_config_validator import SimpleConfigValidator
15
+
16
+
17
+ def config_validate_command(args: Namespace) -> int:
18
+ cfg = SimpleConfig(args.file)
19
+ try:
20
+ model = cfg.load()
21
+ except Exception as e:
22
+ print(f"❌ Failed to load config: {e}")
23
+ return 1
24
+
25
+ validator = SimpleConfigValidator()
26
+ errors = validator.validate(model)
27
+ if errors:
28
+ print("❌ Validation failed:")
29
+ for err in errors:
30
+ print(f" - {err.message}")
31
+ return 1
32
+
33
+ print("✅ Validation OK")
34
+ return 0
35
+
36
+
@@ -0,0 +1,259 @@
1
+ """
2
+ Generate Command
3
+
4
+ This module implements the generate command for creating configuration files.
5
+
6
+ Author: Vasiliy Zdanovskiy
7
+ email: vasilyvz@gmail.com
8
+ """
9
+
10
+ import json
11
+ from pathlib import Path
12
+ from typing import Dict, Any, Optional
13
+
14
+ # Import the existing config generator
15
+ try:
16
+ from mcp_proxy_adapter.examples.config_builder import generate_complete_config
17
+ from mcp_proxy_adapter.core.config_validator import ConfigValidator
18
+ VALIDATION_AVAILABLE = True
19
+ except ImportError:
20
+ VALIDATION_AVAILABLE = False
21
+ print("Warning: Configuration validation not available. Install the package to enable validation.")
22
+
23
+
24
+ class GenerateCommand:
25
+ """Command for generating configuration files."""
26
+
27
+ def __init__(self):
28
+ """Initialize the generate command."""
29
+ pass
30
+
31
+ def execute(self, args: Dict[str, Any]) -> int:
32
+ """
33
+ Execute the generate command.
34
+
35
+ Args:
36
+ args: Parsed command arguments
37
+
38
+ Returns:
39
+ Exit code (0 for success, 1 for error)
40
+ """
41
+ try:
42
+ # Handle special cases
43
+ if args.get('all'):
44
+ return self._generate_all_configs(args)
45
+
46
+ # Generate single configuration
47
+ return self._generate_single_config(args)
48
+
49
+ except Exception as e:
50
+ print(f"❌ Error generating configuration: {e}")
51
+ return 1
52
+
53
+ def _generate_single_config(self, args: Dict[str, Any]) -> int:
54
+ """Generate a single configuration file."""
55
+ # Create configuration
56
+ config = self._create_config_from_args(args)
57
+
58
+ # Save configuration
59
+ if args.get('stdout'):
60
+ # Output to stdout
61
+ print(json.dumps(config, indent=2, ensure_ascii=False))
62
+ else:
63
+ # Save to file
64
+ config_file = self._save_config(config, args)
65
+ print(f"✅ Configuration saved to: {config_file}")
66
+
67
+ return 0
68
+
69
+ def _generate_all_configs(self, args: Dict[str, Any]) -> int:
70
+ """Generate all standard configurations."""
71
+ print("🔧 Generating MCP Proxy Adapter configurations...")
72
+ print("=" * 60)
73
+
74
+ # Define all standard configurations
75
+ configs = [
76
+ # HTTP configurations
77
+ ("http", False, False, 20000),
78
+ ("http", True, True, 20001), # token + roles
79
+ ("http", True, False, 20002), # token only
80
+
81
+ # HTTPS configurations
82
+ ("https", False, False, 20003),
83
+ ("https", True, True, 20004), # token + roles
84
+ ("https", True, False, 20005), # token only
85
+
86
+ # mTLS configurations
87
+ ("mtls", False, False, 20006),
88
+ ("mtls", False, True, 20007), # roles only (from certificate)
89
+ ]
90
+
91
+ generated_files = []
92
+
93
+ for protocol, token, roles, port in configs:
94
+ # Create configuration name
95
+ name_parts = [protocol]
96
+ if token:
97
+ name_parts.append("token")
98
+ if roles:
99
+ name_parts.append("roles")
100
+
101
+ config_name = "_".join(name_parts)
102
+
103
+ # Create args for this configuration
104
+ config_args = args.copy()
105
+ config_args.update({
106
+ 'protocol': protocol,
107
+ 'token': token,
108
+ 'roles': roles,
109
+ 'port': port
110
+ })
111
+
112
+ # Generate configuration
113
+ config = self._create_config_from_args(config_args)
114
+
115
+ # Save configuration
116
+ config_file = self._save_config(config, config_args, config_name)
117
+ generated_files.append(config_file)
118
+
119
+ print(f"✅ Created {config_name}.json (port {port})")
120
+
121
+ # Create roles.json file if any role-based configs were generated
122
+ self._create_roles_file(args.get('output_dir', './configs'))
123
+
124
+ print(f"\n🎉 Generated {len(generated_files)} configurations in {args.get('output_dir', './configs')}/")
125
+ print("\n📋 Generated configurations:")
126
+ for config_file in generated_files:
127
+ print(f" - {config_file.name}")
128
+
129
+ return 0
130
+
131
+ def _create_config_from_args(self, args: Dict[str, Any]) -> Dict[str, Any]:
132
+ """Create configuration dictionary from arguments."""
133
+ # Start with basic configuration
134
+ config = generate_complete_config(
135
+ args.get('host', '127.0.0.1'),
136
+ args.get('port', 8000)
137
+ )
138
+
139
+ # Set protocol
140
+ config["server"]["protocol"] = args.get('protocol', 'http')
141
+
142
+ # Configure SSL based on protocol
143
+ if args.get('protocol') == 'https':
144
+ config["ssl"]["enabled"] = True
145
+ config["ssl"]["cert_file"] = f"{args.get('cert_dir', './certs')}/server.crt"
146
+ config["ssl"]["key_file"] = f"{args.get('key_dir', './keys')}/server.key"
147
+ elif args.get('protocol') == 'mtls':
148
+ config["ssl"]["enabled"] = True
149
+ config["ssl"]["cert_file"] = f"{args.get('cert_dir', './certs')}/server.crt"
150
+ config["ssl"]["key_file"] = f"{args.get('key_dir', './keys')}/server.key"
151
+ config["ssl"]["ca_cert"] = f"{args.get('cert_dir', './certs')}/ca.crt"
152
+ config["transport"]["verify_client"] = True
153
+
154
+ # Configure security if token authentication is enabled
155
+ if args.get('token'):
156
+ config["security"]["enabled"] = True
157
+ config["security"]["tokens"] = {
158
+ "admin": "admin-secret-key",
159
+ "user": "user-secret-key",
160
+ "readonly": "readonly-secret-key"
161
+ }
162
+
163
+ if args.get('roles'):
164
+ config["security"]["roles"] = {
165
+ "admin": ["read", "write", "delete", "admin"],
166
+ "user": ["read", "write"],
167
+ "readonly": ["read"]
168
+ }
169
+ config["security"]["roles_file"] = f"{args.get('output_dir', './configs')}/roles.json"
170
+ config["roles"]["enabled"] = True
171
+ config["roles"]["config_file"] = f"{args.get('output_dir', './configs')}/roles.json"
172
+ elif args.get('roles') and args.get('protocol') == 'mtls':
173
+ # For mTLS, roles can be enabled without tokens (from certificate)
174
+ config["roles"]["enabled"] = True
175
+ config["roles"]["config_file"] = f"{args.get('output_dir', './configs')}/roles.json"
176
+
177
+ # Configure proxy registration if enabled
178
+ if args.get('proxy_url'):
179
+ config["proxy_registration"]["enabled"] = True
180
+ config["proxy_registration"]["proxy_url"] = args['proxy_url']
181
+ config["proxy_registration"]["server_id"] = args.get('server_id', 'mcp-proxy-adapter')
182
+
183
+ return config
184
+
185
+ def _save_config(self, config: Dict[str, Any], args: Dict[str, Any], filename: Optional[str] = None) -> Path:
186
+ """Save configuration to file with optional validation."""
187
+ output_dir = Path(args.get('output_dir', './configs'))
188
+ output_dir.mkdir(parents=True, exist_ok=True)
189
+
190
+ # Determine filename
191
+ if filename:
192
+ config_name = filename
193
+ elif args.get('output'):
194
+ config_name = args['output']
195
+ else:
196
+ # Generate filename from arguments
197
+ name_parts = [args.get('protocol', 'http')]
198
+ if args.get('token'):
199
+ name_parts.append("token")
200
+ if args.get('roles'):
201
+ name_parts.append("roles")
202
+ config_name = "_".join(name_parts)
203
+
204
+ config_file = output_dir / f"{config_name}.json"
205
+
206
+ # Save configuration
207
+ with open(config_file, 'w', encoding='utf-8') as f:
208
+ json.dump(config, f, indent=2, ensure_ascii=False)
209
+
210
+ # Validate configuration if requested and validation is available
211
+ if not args.get('no_validate') and VALIDATION_AVAILABLE:
212
+ print(f"🔍 Validating configuration: {config_file}")
213
+ validator = ConfigValidator()
214
+ validator.config_data = config
215
+ results = validator.validate_config()
216
+
217
+ if results:
218
+ print("⚠️ Validation issues found:")
219
+ for result in results:
220
+ level_symbol = "❌" if result.level == "error" else "⚠️" if result.level == "warning" else "ℹ️"
221
+ print(f" {level_symbol} {result.message}")
222
+ if hasattr(result, 'suggestion') and result.suggestion:
223
+ print(f" Suggestion: {result.suggestion}")
224
+ else:
225
+ print("✅ Configuration validation passed!")
226
+
227
+ return config_file
228
+
229
+ def _create_roles_file(self, output_dir: str) -> None:
230
+ """Create roles.json file for role-based configurations."""
231
+ roles_config = {
232
+ "enabled": True,
233
+ "default_policy": {
234
+ "deny_by_default": False,
235
+ "require_role_match": False,
236
+ "case_sensitive": False,
237
+ "allow_wildcard": False
238
+ },
239
+ "roles": {
240
+ "admin": ["read", "write", "delete", "admin"],
241
+ "user": ["read", "write"],
242
+ "readonly": ["read"],
243
+ "guest": ["read"],
244
+ "proxy": ["read", "write"]
245
+ },
246
+ "permissions": {
247
+ "read": ["GET"],
248
+ "write": ["POST", "PUT", "PATCH"],
249
+ "delete": ["DELETE"],
250
+ "admin": ["*"]
251
+ }
252
+ }
253
+
254
+ roles_file = Path(output_dir) / "roles.json"
255
+ with open(roles_file, 'w', encoding='utf-8') as f:
256
+ json.dump(roles_config, f, indent=2, ensure_ascii=False)
257
+ print(f"✅ Created roles.json")
258
+
259
+
@@ -0,0 +1,174 @@
1
+ """
2
+ Server Command
3
+
4
+ This module implements the server command for starting MCP Proxy Adapter server.
5
+
6
+ Author: Vasiliy Zdanovskiy
7
+ email: vasilyvz@gmail.com
8
+ """
9
+
10
+ import json
11
+ from pathlib import Path
12
+ from typing import Dict, Any
13
+
14
+ try:
15
+ from mcp_proxy_adapter.core.config_validator import ConfigValidator
16
+ from mcp_proxy_adapter.core.server_adapter import UnifiedServerRunner
17
+ from mcp_proxy_adapter.api.app import create_app
18
+ VALIDATION_AVAILABLE = True
19
+ except ImportError:
20
+ VALIDATION_AVAILABLE = False
21
+
22
+
23
+ class ServerCommand:
24
+ """Command for starting the MCP Proxy Adapter server."""
25
+
26
+ def __init__(self):
27
+ """Initialize the server command."""
28
+ pass
29
+
30
+ def execute(self, args: Dict[str, Any]) -> int:
31
+ """
32
+ Execute the server command.
33
+
34
+ Args:
35
+ args: Parsed command arguments
36
+
37
+ Returns:
38
+ Exit code (0 for success, 1 for error)
39
+ """
40
+ config_file = args['config']
41
+ no_validate = args.get('no_validate', False)
42
+
43
+ try:
44
+ # Load configuration
45
+ print(f"🔍 Loading configuration from: {config_file}")
46
+ with open(config_file, 'r', encoding='utf-8') as f:
47
+ config = json.load(f)
48
+
49
+ # Validate configuration if not disabled
50
+ if not no_validate and VALIDATION_AVAILABLE:
51
+ print("🔍 Validating configuration...")
52
+ if not self._validate_config(config, config_file):
53
+ print("❌ Configuration validation failed. Server not started.")
54
+ return 1
55
+ print("✅ Configuration validation passed!")
56
+ elif no_validate:
57
+ print("⚠️ Configuration validation skipped (--no-validate)")
58
+ else:
59
+ print("⚠️ Configuration validation not available")
60
+
61
+ # Override configuration with command line arguments
62
+ if args.get('port'):
63
+ config['server']['port'] = args['port']
64
+ print(f"🔧 Overriding port to: {args['port']}")
65
+
66
+ if args.get('host'):
67
+ config['server']['host'] = args['host']
68
+ print(f"🔧 Overriding host to: {args['host']}")
69
+
70
+ # Create and start server
71
+ print("🚀 Starting MCP Proxy Adapter server...")
72
+ self._start_server(config, args)
73
+
74
+ except FileNotFoundError:
75
+ print(f"❌ Configuration file not found: {config_file}")
76
+ return 1
77
+ except json.JSONDecodeError as e:
78
+ print(f"❌ Invalid JSON in configuration file: {e}")
79
+ return 1
80
+ except Exception as e:
81
+ print(f"❌ Error starting server: {e}")
82
+ return 1
83
+
84
+ def _validate_config(self, config: Dict[str, Any], config_file: str) -> bool:
85
+ """
86
+ Validate configuration using ConfigValidator.
87
+
88
+ Args:
89
+ config: Configuration dictionary
90
+ config_file: Path to configuration file
91
+
92
+ Returns:
93
+ True if configuration is valid, False otherwise
94
+ """
95
+ try:
96
+ validator = ConfigValidator()
97
+ validator.config_data = config
98
+ results = validator.validate_config()
99
+
100
+ # Check for errors
101
+ errors = [r for r in results if r.level == "error"]
102
+ warnings = [r for r in results if r.level == "warning"]
103
+
104
+ if errors:
105
+ print("❌ Configuration validation errors:")
106
+ for error in errors:
107
+ print(f" • {error.message}")
108
+ if hasattr(error, 'suggestion') and error.suggestion:
109
+ print(f" → {error.suggestion}")
110
+ return False
111
+
112
+ if warnings:
113
+ print("⚠️ Configuration validation warnings:")
114
+ for warning in warnings:
115
+ print(f" • {warning.message}")
116
+ if hasattr(warning, 'suggestion') and warning.suggestion:
117
+ print(f" → {warning.suggestion}")
118
+
119
+ return True
120
+
121
+ except Exception as e:
122
+ print(f"❌ Error during configuration validation: {e}")
123
+ return False
124
+
125
+ def _start_server(self, config: Dict[str, Any], args: Dict[str, Any]) -> None:
126
+ """
127
+ Start the MCP Proxy Adapter server.
128
+
129
+ Args:
130
+ config: Server configuration
131
+ args: Command line arguments
132
+ """
133
+ try:
134
+ # Create ASGI application
135
+ app = create_app(config)
136
+
137
+ # Prepare server configuration
138
+ server_config = {
139
+ 'host': config['server']['host'],
140
+ 'port': config['server']['port'],
141
+ 'log_level': config['server'].get('log_level', 'INFO'),
142
+ 'reload': args.get('reload', False)
143
+ }
144
+
145
+ # Add SSL configuration if present
146
+ if 'ssl' in config and config['ssl'].get('enabled'):
147
+ server_config.update({
148
+ 'certfile': config['ssl'].get('cert_file'),
149
+ 'keyfile': config['ssl'].get('key_file'),
150
+ 'ca_certs': config['ssl'].get('ca_cert'),
151
+ 'verify_mode': 'CERT_REQUIRED' if config.get('transport', {}).get('verify_client') else 'CERT_NONE'
152
+ })
153
+
154
+ # Start server
155
+ print(f"🌐 Server starting on {server_config['host']}:{server_config['port']}")
156
+ print(f"📋 Protocol: {config['server']['protocol']}")
157
+ print(f"🔐 Security: {'Enabled' if config.get('security', {}).get('enabled') else 'Disabled'}")
158
+ print(f"🔑 Authentication: {'Token-based' if config.get('security', {}).get('tokens') else 'Certificate-based' if config['server']['protocol'] == 'mtls' else 'None'}")
159
+ print(f"👥 Roles: {'Enabled' if config.get('roles', {}).get('enabled') else 'Disabled'}")
160
+ print("=" * 60)
161
+
162
+ # Use UnifiedServerRunner to start the server
163
+ runner = UnifiedServerRunner()
164
+ runner.run_server(app, server_config)
165
+
166
+ except ImportError as e:
167
+ print(f"❌ Missing required dependencies: {e}")
168
+ print("💡 Install required packages: pip install hypercorn")
169
+ raise
170
+ except Exception as e:
171
+ print(f"❌ Failed to start server: {e}")
172
+ raise
173
+
174
+