mcp-proxy-adapter 6.9.27__py3-none-any.whl → 6.9.29__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 +0 -6
  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 -912
  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.27.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/METADATA +2 -1
  208. mcp_proxy_adapter-6.9.29.dist-info/RECORD +235 -0
  209. {mcp_proxy_adapter-6.9.27.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/entry_points.txt +1 -1
  210. mcp_proxy_adapter-6.9.27.dist-info/RECORD +0 -149
  211. {mcp_proxy_adapter-6.9.27.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/WHEEL +0 -0
  212. {mcp_proxy_adapter-6.9.27.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/top_level.txt +0 -0
@@ -1,677 +1,58 @@
1
1
  """
2
2
  Custom OpenAPI schema generator for MCP Microservice compatible with MCP-Proxy.
3
+
4
+ Author: Vasiliy Zdanovskiy
5
+ email: vasilyvz@gmail.com
3
6
  """
4
7
 
5
- import json
6
- from copy import deepcopy
7
- from pathlib import Path
8
- from typing import Any, Dict, List, Optional, Set, Type, Callable
9
8
 
10
9
  from fastapi import FastAPI
11
- from fastapi.openapi.utils import get_openapi
12
-
13
- from mcp_proxy_adapter.commands.command_registry import registry
14
- from mcp_proxy_adapter.commands.base import Command
15
- from mcp_proxy_adapter.core.logging import get_global_logger
16
-
17
-
18
- class CustomOpenAPIGenerator:
19
- """
20
- Custom OpenAPI schema generator for compatibility with MCP-Proxy.
21
-
22
- EN:
23
- This generator creates an OpenAPI schema that matches the format expected by MCP-Proxy,
24
- enabling dynamic command loading and proper tool representation in AI models.
25
- Allows overriding title, description, and version for schema customization.
26
-
27
- RU:
28
- Кастомный генератор схемы OpenAPI для совместимости с MCP-Proxy.
29
- Позволяет создавать схему OpenAPI в формате, ожидаемом MCP-Proxy,
30
- с возможностью динамической подгрузки команд и корректного отображения инструментов для AI-моделей.
31
- Поддерживает переопределение title, description и version для кастомизации схемы.
32
- """
33
-
34
- def __init__(self):
35
- """Initialize the generator."""
36
- self.base_schema_path = (
37
- Path(__file__).parent / "schemas" / "openapi_schema.json"
38
- )
39
- self.base_schema = self._load_base_schema()
40
-
41
- def _load_base_schema(self) -> Dict[str, Any]:
42
- """
43
- Load the base OpenAPI schema from file.
44
-
45
- Returns:
46
- Dict containing the base OpenAPI schema.
47
- """
48
- try:
49
- with open(self.base_schema_path, "r", encoding="utf-8") as f:
50
- return json.load(f)
51
- except FileNotFoundError:
52
- get_global_logger().warning(f"Base schema file not found at {self.base_schema_path}, using fallback schema")
53
- return self._get_fallback_schema()
54
-
55
- def _get_fallback_schema(self) -> Dict[str, Any]:
56
- """
57
- Get a fallback OpenAPI schema when the base schema file is not available.
58
-
59
- Returns:
60
- Dict containing a basic OpenAPI schema.
61
- """
62
- return {
63
- "openapi": "3.0.2",
64
- "info": {
65
- "title": "MCP Microservice API",
66
- "description": "API для выполнения команд микросервиса",
67
- "version": "1.0.0"
68
- },
69
- "paths": {
70
- "/cmd": {
71
- "post": {
72
- "summary": "Execute Command",
73
- "description": "Executes a command via JSON-RPC protocol.",
74
- "operationId": "execute_command",
75
- "requestBody": {
76
- "content": {
77
- "application/json": {
78
- "schema": {
79
- "oneOf": [
80
- { "$ref": "#/components/schemas/CommandRequest" },
81
- { "$ref": "#/components/schemas/JsonRpcRequest" }
82
- ]
83
- }
84
- }
85
- },
86
- "required": True
87
- },
88
- "responses": {
89
- "200": {
90
- "description": "Successful Response",
91
- "content": {
92
- "application/json": {
93
- "schema": {
94
- "oneOf": [
95
- { "$ref": "#/components/schemas/CommandResponse" },
96
- { "$ref": "#/components/schemas/JsonRpcResponse" }
97
- ]
98
- }
99
- }
100
- }
101
- },
102
- "422": {
103
- "description": "Validation Error",
104
- "content": {
105
- "application/json": {
106
- "schema": {
107
- "$ref": "#/components/schemas/HTTPValidationError"
108
- }
109
- }
110
- }
111
- }
112
- }
113
- }
114
- },
115
- "/health": {
116
- "get": {
117
- "summary": "Проверить работоспособность сервиса",
118
- "description": "Возвращает информацию о состоянии сервиса",
119
- "operationId": "health_check",
120
- "responses": {
121
- "200": {
122
- "description": "Информация о состоянии сервиса",
123
- "content": {
124
- "application/json": {
125
- "schema": {
126
- "$ref": "#/components/schemas/HealthResponse"
127
- }
128
- }
129
- }
130
- }
131
- }
132
- }
133
- },
134
- "/openapi.json": {
135
- "get": {
136
- "summary": "Get Openapi Schema",
137
- "description": "Returns OpenAPI schema.",
138
- "operationId": "get_openapi_schema_openapi_json_get",
139
- "responses": {
140
- "200": {
141
- "description": "Successful Response",
142
- "content": {
143
- "application/json": {
144
- "schema": {}
145
- }
146
- }
147
- }
148
- }
149
- }
150
- },
151
- "/api/commands": {
152
- "get": {
153
- "summary": "Get Commands",
154
- "description": "Returns list of available commands with their descriptions.",
155
- "operationId": "get_commands_api_commands_get",
156
- "responses": {
157
- "200": {
158
- "description": "Successful Response",
159
- "content": {
160
- "application/json": {
161
- "schema": {}
162
- }
163
- }
164
- }
165
- }
166
- }
167
- }
168
- },
169
- "components": {
170
- "schemas": {
171
- "CommandRequest": {
172
- "title": "CommandRequest",
173
- "description": "Запрос на выполнение команды",
174
- "type": "object",
175
- "required": ["command"],
176
- "properties": {
177
- "command": {
178
- "title": "Command",
179
- "description": "Команда для выполнения",
180
- "type": "string"
181
- },
182
- "params": {
183
- "title": "Parameters",
184
- "description": "Параметры команды, зависят от типа команды",
185
- "type": "object",
186
- "additionalProperties": True
187
- }
188
- }
189
- },
190
- "CommandResponse": {
191
- "title": "CommandResponse",
192
- "description": "Ответ на выполнение команды",
193
- "type": "object",
194
- "required": ["result"],
195
- "properties": {
196
- "result": {
197
- "title": "Result",
198
- "description": "Результат выполнения команды"
199
- }
200
- }
201
- },
202
- "JsonRpcRequest": {
203
- "properties": {
204
- "jsonrpc": {
205
- "type": "string",
206
- "title": "Jsonrpc",
207
- "description": "JSON-RPC version",
208
- "default": "2.0"
209
- },
210
- "method": {
211
- "type": "string",
212
- "title": "Method",
213
- "description": "Method name to call"
214
- },
215
- "params": {
216
- "additionalProperties": True,
217
- "type": "object",
218
- "title": "Params",
219
- "description": "Method parameters",
220
- "default": {}
221
- },
222
- "id": {
223
- "anyOf": [
224
- {"type": "string"},
225
- {"type": "integer"},
226
- {"type": "null"}
227
- ],
228
- "title": "Id",
229
- "description": "Request identifier"
230
- }
231
- },
232
- "type": "object",
233
- "required": ["method"],
234
- "title": "JsonRpcRequest",
235
- "description": "Base model for JSON-RPC requests."
236
- },
237
- "JsonRpcResponse": {
238
- "properties": {
239
- "jsonrpc": {
240
- "type": "string",
241
- "title": "Jsonrpc",
242
- "description": "JSON-RPC version",
243
- "default": "2.0"
244
- },
245
- "result": {
246
- "anyOf": [
247
- {},
248
- {"type": "null"}
249
- ],
250
- "title": "Result",
251
- "description": "Method execution result"
252
- },
253
- "error": {
254
- "anyOf": [
255
- {
256
- "additionalProperties": True,
257
- "type": "object"
258
- },
259
- {"type": "null"}
260
- ],
261
- "title": "Error",
262
- "description": "Error information"
263
- },
264
- "id": {
265
- "anyOf": [
266
- {"type": "string"},
267
- {"type": "integer"},
268
- {"type": "null"}
269
- ],
270
- "title": "Id",
271
- "description": "Request identifier"
272
- }
273
- },
274
- "type": "object",
275
- "title": "JsonRpcResponse",
276
- "description": "Base model for JSON-RPC responses."
277
- },
278
- "HealthResponse": {
279
- "title": "HealthResponse",
280
- "description": "Информация о состоянии сервиса",
281
- "type": "object",
282
- "required": ["status", "model", "version"],
283
- "properties": {
284
- "status": {
285
- "title": "Status",
286
- "description": "Статус сервиса (ok/error)",
287
- "type": "string"
288
- },
289
- "model": {
290
- "title": "Model",
291
- "description": "Текущая активная модель",
292
- "type": "string"
293
- },
294
- "version": {
295
- "title": "Version",
296
- "description": "Версия сервиса",
297
- "type": "string"
298
- }
299
- }
300
- },
301
- "HTTPValidationError": {
302
- "properties": {
303
- "detail": {
304
- "items": {
305
- "$ref": "#/components/schemas/ValidationError"
306
- },
307
- "type": "array",
308
- "title": "Detail"
309
- }
310
- },
311
- "type": "object",
312
- "title": "HTTPValidationError"
313
- },
314
- "ValidationError": {
315
- "properties": {
316
- "loc": {
317
- "items": {
318
- "anyOf": [
319
- {"type": "string"},
320
- {"type": "integer"}
321
- ]
322
- },
323
- "type": "array",
324
- "title": "Location"
325
- },
326
- "msg": {
327
- "type": "string",
328
- "title": "Message"
329
- },
330
- "type": {
331
- "type": "string",
332
- "title": "Error Type"
333
- }
334
- },
335
- "type": "object",
336
- "required": ["loc", "msg", "type"],
337
- "title": "ValidationError"
338
- }
339
- }
340
- }
341
- }
342
-
343
- def _add_commands_to_schema(self, schema: Dict[str, Any]) -> None:
344
- """
345
- Add all registered commands to the OpenAPI schema.
346
-
347
- Args:
348
- schema: The OpenAPI schema to update.
349
- """
350
- # Get all commands from the registry
351
- commands = registry.get_all_commands()
352
-
353
- # Ensure CommandRequest exists in schemas
354
- if "CommandRequest" not in schema["components"]["schemas"]:
355
- schema["components"]["schemas"]["CommandRequest"] = {
356
- "properties": {
357
- "command": {"type": "string", "enum": []},
358
- "params": {"type": "object", "oneOf": []},
359
- }
360
- }
361
-
362
- # Add command names to the CommandRequest enum
363
- schema["components"]["schemas"]["CommandRequest"]["properties"]["command"][
364
- "enum"
365
- ] = [cmd for cmd in commands.keys()]
366
-
367
- # Add command parameters to oneOf
368
- params_refs = []
369
-
370
- for name, cmd_class in commands.items():
371
- # Create schema for command parameters
372
- param_schema_name = f"{name.capitalize()}Params"
373
- schema["components"]["schemas"][param_schema_name] = (
374
- self._create_params_schema(cmd_class)
375
- )
376
-
377
- # Add to oneOf
378
- params_refs.append({"$ref": f"#/components/schemas/{param_schema_name}"})
379
-
380
- # Add null option for commands without parameters
381
- params_refs.append({"type": "null"})
382
-
383
- # Set oneOf for params
384
- schema["components"]["schemas"]["CommandRequest"]["properties"]["params"][
385
- "oneOf"
386
- ] = params_refs
387
-
388
- def _create_params_schema(self, cmd_class: Type[Command]) -> Dict[str, Any]:
389
- """
390
- Create a schema for command parameters.
391
-
392
- Args:
393
- cmd_class: The command class.
394
-
395
- Returns:
396
- Dict containing the parameter schema.
397
- """
398
- try:
399
- # Get command schema
400
- cmd_schema = cmd_class.get_schema()
401
-
402
- # Add title and description
403
- cmd_schema["title"] = f"Parameters for {cmd_class.name}"
404
- cmd_schema["description"] = f"Parameters for the {cmd_class.name} command"
405
-
406
- return cmd_schema
407
- except Exception as e:
408
- # Return default schema if command schema generation fails
409
- get_global_logger().warning(f"Failed to get schema for command {cmd_class.name}: {e}")
410
- return {
411
- "type": "object",
412
- "title": f"Parameters for {cmd_class.name}",
413
- "description": f"Parameters for the {cmd_class.name} command (schema generation failed)",
414
- "properties": {},
415
- "additionalProperties": True,
416
- }
417
-
418
- def generate(
419
- self,
420
- title: Optional[str] = None,
421
- description: Optional[str] = None,
422
- version: Optional[str] = None,
423
- ) -> Dict[str, Any]:
424
- """
425
- EN:
426
- Generate the complete OpenAPI schema compatible with MCP-Proxy.
427
- Optionally override title, description, and version.
428
-
429
- RU:
430
- Генерирует полную схему OpenAPI, совместимую с MCP-Proxy.
431
- Позволяет опционально переопределить title, description и version.
432
-
433
- Args:
434
- title: Custom title for the schema / Кастомный заголовок схемы
435
- description: Custom description for the schema / Кастомное описание схемы
436
- version: Custom version for the schema / Кастомная версия схемы
437
-
438
- Returns:
439
- Dict containing the complete OpenAPI schema / Словарь с полной схемой OpenAPI
440
- """
441
- # Deep copy the base schema to avoid modifying it
442
- schema = deepcopy(self.base_schema)
443
-
444
- # Optionally override info fields
445
- if title:
446
- schema["info"]["title"] = title
447
-
448
- # Get all commands for help information
449
- commands = registry.get_all_commands()
450
- command_names = list(commands.keys())
451
-
452
- # Create help examples
453
- help_examples = {
454
- "without_params": {"jsonrpc": "2.0", "method": "help", "id": 1},
455
- "with_params": {
456
- "jsonrpc": "2.0",
457
- "method": "help",
458
- "params": {
459
- "command": command_names[0] if command_names else "example_command"
460
- },
461
- "id": 1,
462
- },
463
- }
464
-
465
- # Enhance description with help format and commands list for OpenAPI docs
466
- base_description = description or schema["info"]["description"]
467
- # В тестах ожидается точное совпадение с исходным описанием
468
- if "title" in schema["info"] and schema["info"]["title"] == "Custom Title":
469
- # Для теста оставляем описание без изменений
470
- enhanced_description = base_description
471
- else:
472
- # Для обычного использования добавляем информацию о командах и справке
473
- commands_str = ", ".join(command_names)
474
- help_command_simple = '{"jsonrpc": "2.0", "method": "help", "id": 1}'
475
- help_command_with_param = (
476
- '{"jsonrpc": "2.0", "method": "help", "params": {"command": "'
477
- )
478
- if command_names:
479
- help_command_with_param += command_names[0]
480
- else:
481
- help_command_with_param += "example_command"
482
- help_command_with_param += '"}, "id": 1}'
483
-
484
- enhanced_description = (
485
- base_description
486
- + "\n\n## Available commands:\n"
487
- + commands_str
488
- + "\n\n## Getting help\n\n"
489
- + "Without parameters (list of all commands):\n"
490
- + "```json\n"
491
- + help_command_simple
492
- + "\n```\n\n"
493
- + "With parameters (information about a specific command):\n"
494
- + "```json\n"
495
- + help_command_with_param
496
- + "\n```\n"
497
- )
498
-
499
- # Set enhanced description for OpenAPI docs
500
- schema["info"]["description"] = enhanced_description
501
-
502
- # Update tool description visible in MCP-Proxy
503
- if "components" not in schema:
504
- schema["components"] = {}
505
- if "schemas" not in schema["components"]:
506
- schema["components"]["schemas"] = {}
10
+ from typing import Dict, Any
507
11
 
508
- # Create ToolDescription if it doesn't exist
509
- if "ToolDescription" not in schema["components"]["schemas"]:
510
- schema["components"]["schemas"]["ToolDescription"] = {
511
- "type": "object",
512
- "title": "Tool Description",
513
- "description": "Description of the microservice tool",
514
- "properties": {
515
- "name": {"type": "string", "description": "Name of the tool"},
516
- "description": {
517
- "type": "string",
518
- "description": "Description of the tool",
519
- },
520
- "version": {"type": "string", "description": "Tool version"},
521
- },
522
- "required": ["name", "description"],
523
- }
524
-
525
- # Update tool description content
526
- tool_desc = schema["components"]["schemas"]["ToolDescription"]
527
-
528
- # Add help format and commands information to the tool description
529
- tool_desc_text = "Tool for executing microservice commands.\n\n"
530
- tool_desc_text += "## Available commands:\n"
531
- tool_desc_text += ", ".join(command_names)
532
- tool_desc_text += "\n\n## Getting help:\n"
533
- tool_desc_text += "- Without parameters (list of all commands): \n"
534
- tool_desc_text += ' {"jsonrpc": "2.0", "method": "help", "id": 1}\n'
535
- tool_desc_text += " \n"
536
- tool_desc_text += "- With parameters (information about a specific command): \n"
537
- tool_desc_text += ' {"jsonrpc": "2.0", "method": "help", "params": {"command": "command_name"}, "id": 1}\n'
538
-
539
- tool_desc["properties"]["description"]["description"] = tool_desc_text
540
-
541
- # Add help examples as a new property
542
- tool_desc["properties"]["help_examples"] = {
543
- "type": "object",
544
- "description": "Examples of using the help command",
545
- "properties": {
546
- "without_params": {
547
- "type": "object",
548
- "description": "Get a list of all commands",
549
- },
550
- "with_params": {
551
- "type": "object",
552
- "description": "Get information about a specific command",
553
- },
554
- },
555
- "example": help_examples,
556
- }
557
-
558
- # Add available commands as a new property
559
- tool_desc["properties"]["available_commands"] = {
560
- "type": "array",
561
- "description": "List of available commands",
562
- "items": {"type": "string"},
563
- "example": command_names,
564
- }
565
-
566
- if version:
567
- schema["info"]["version"] = version
568
-
569
- # Add commands to schema
570
- self._add_commands_to_schema(schema)
571
-
572
- get_global_logger().debug(
573
- f"Generated OpenAPI schema with {len(registry.get_all_commands())} commands"
574
- )
575
-
576
- return schema
12
+ # from mcp_proxy_adapter.api.openapi import (
13
+ # CustomOpenAPIGenerator,
14
+ # register_openapi_generator,
15
+ # get_openapi_generator,
16
+ # list_openapi_generators,
17
+ # )
577
18
 
578
19
 
579
20
  def custom_openapi(app: FastAPI) -> Dict[str, Any]:
580
21
  """
581
- EN:
582
- Create a custom OpenAPI schema for the FastAPI application.
583
- Passes app's title, description, and version to the generator.
584
-
585
- RU:
586
- Создаёт кастомную OpenAPI-схему для FastAPI-приложения.
587
- Передаёт параметры title, description и version из приложения в генератор схемы.
22
+ Generate custom OpenAPI schema for the application.
588
23
 
589
24
  Args:
590
- app: The FastAPI application / FastAPI-приложение
25
+ app: FastAPI application instance
591
26
 
592
27
  Returns:
593
- Dict containing the custom OpenAPI schema / Словарь с кастомной OpenAPI-схемой
28
+ Generated OpenAPI schema
594
29
  """
595
30
  generator = CustomOpenAPIGenerator()
596
- openapi_schema = generator.generate(
597
- title=getattr(app, "title", None),
598
- description=getattr(app, "description", None),
599
- version=getattr(app, "version", None),
600
- )
601
-
602
- # Cache the schema
603
- app.openapi_schema = openapi_schema
604
-
605
- return openapi_schema
606
-
607
-
608
- # Registry for custom OpenAPI generators
609
- _openapi_generators: Dict[str, Callable] = {}
610
-
611
-
612
- def register_openapi_generator(
613
- name: str, generator_func: Callable[[FastAPI], Dict[str, Any]]
614
- ) -> None:
615
- """
616
- Register a custom OpenAPI generator.
617
-
618
- Args:
619
- name: Generator name.
620
- generator_func: Function that generates OpenAPI schema.
621
- """
622
- _openapi_generators[name] = generator_func
623
- get_global_logger().info(f"Registered custom OpenAPI generator: {name}")
624
-
625
-
626
- def get_openapi_generator(name: str) -> Optional[Callable[[FastAPI], Dict[str, Any]]]:
627
- """
628
- Get a custom OpenAPI generator by name.
629
-
630
- Args:
631
- name: Generator name.
632
-
633
- Returns:
634
- Generator function or None if not found.
635
- """
636
- return _openapi_generators.get(name)
637
-
638
-
639
- def list_openapi_generators() -> List[str]:
640
- """
641
- Get list of registered OpenAPI generators.
642
-
643
- Returns:
644
- List of generator names.
645
- """
646
- return list(_openapi_generators.keys())
31
+ return generator.generate(app)
647
32
 
648
33
 
649
34
  def custom_openapi_with_fallback(app: FastAPI) -> Dict[str, Any]:
650
35
  """
651
- EN:
652
- Create a custom OpenAPI schema for the FastAPI application.
653
- Checks for custom generators first, then falls back to default generator.
654
- Passes app's title, description, and version to the generator.
655
-
656
- RU:
657
- Создаёт кастомную OpenAPI-схему для FastAPI-приложения.
658
- Сначала проверяет наличие кастомных генераторов, затем использует встроенный генератор.
659
- Передаёт параметры title, description и version из приложения в генератор схемы.
36
+ Generate custom OpenAPI schema with fallback to default generator.
660
37
 
661
38
  Args:
662
- app: The FastAPI application / FastAPI-приложение
39
+ app: FastAPI application instance
663
40
 
664
41
  Returns:
665
- Dict containing the custom OpenAPI schema / Словарь с кастомной OpenAPI-схемой
666
- """
667
- # Check if there are any custom generators
668
- if _openapi_generators:
669
- # Use the first registered generator
670
- generator_name = list(_openapi_generators.keys())[0]
671
- generator_func = _openapi_generators[generator_name]
672
- get_global_logger().debug(f"Using custom OpenAPI generator: {generator_name}")
673
- return generator_func(app)
674
-
675
- # Fall back to default generator
676
- get_global_logger().debug("Using default OpenAPI generator")
677
- return custom_openapi(app)
42
+ Generated OpenAPI schema
43
+ """
44
+ try:
45
+ return custom_openapi(app)
46
+ except Exception as e:
47
+ from mcp_proxy_adapter.core.logging import get_global_logger
48
+ logger = get_global_logger()
49
+ logger.warning(f"Custom OpenAPI generation failed, using fallback: {e}")
50
+
51
+ # Fallback to default FastAPI OpenAPI generator
52
+ from fastapi.openapi.utils import get_openapi
53
+ return get_openapi(
54
+ title=app.title,
55
+ version=app.version,
56
+ description=app.description,
57
+ routes=app.routes,
58
+ )