mcp-proxy-adapter 3.0.0__py3-none-any.whl → 3.0.2__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 (85) hide show
  1. examples/basic_example/README.md +123 -9
  2. examples/basic_example/config.json +4 -0
  3. examples/basic_example/docs/EN/README.md +46 -5
  4. examples/basic_example/docs/RU/README.md +46 -5
  5. examples/basic_example/server.py +127 -21
  6. examples/complete_example/commands/system_command.py +1 -0
  7. examples/complete_example/server.py +68 -40
  8. examples/minimal_example/README.md +20 -6
  9. examples/minimal_example/config.json +7 -14
  10. examples/minimal_example/main.py +109 -40
  11. examples/minimal_example/simple_server.py +53 -14
  12. examples/minimal_example/tests/conftest.py +1 -1
  13. examples/minimal_example/tests/test_integration.py +8 -10
  14. examples/simple_server.py +12 -21
  15. examples/test_server.py +22 -14
  16. examples/tool_description_example.py +82 -0
  17. mcp_proxy_adapter/api/__init__.py +0 -0
  18. mcp_proxy_adapter/api/app.py +391 -0
  19. mcp_proxy_adapter/api/handlers.py +229 -0
  20. mcp_proxy_adapter/api/middleware/__init__.py +49 -0
  21. mcp_proxy_adapter/api/middleware/auth.py +146 -0
  22. mcp_proxy_adapter/api/middleware/base.py +79 -0
  23. mcp_proxy_adapter/api/middleware/error_handling.py +198 -0
  24. mcp_proxy_adapter/api/middleware/logging.py +96 -0
  25. mcp_proxy_adapter/api/middleware/performance.py +83 -0
  26. mcp_proxy_adapter/api/middleware/rate_limit.py +152 -0
  27. mcp_proxy_adapter/api/schemas.py +305 -0
  28. mcp_proxy_adapter/api/tool_integration.py +223 -0
  29. mcp_proxy_adapter/api/tools.py +198 -0
  30. mcp_proxy_adapter/commands/__init__.py +19 -0
  31. mcp_proxy_adapter/commands/base.py +301 -0
  32. mcp_proxy_adapter/commands/command_registry.py +231 -0
  33. mcp_proxy_adapter/commands/config_command.py +113 -0
  34. mcp_proxy_adapter/commands/health_command.py +136 -0
  35. mcp_proxy_adapter/commands/help_command.py +193 -0
  36. mcp_proxy_adapter/commands/result.py +215 -0
  37. mcp_proxy_adapter/config.py +9 -0
  38. mcp_proxy_adapter/core/__init__.py +0 -0
  39. mcp_proxy_adapter/core/errors.py +173 -0
  40. mcp_proxy_adapter/core/logging.py +205 -0
  41. mcp_proxy_adapter/core/utils.py +138 -0
  42. mcp_proxy_adapter/custom_openapi.py +47 -10
  43. mcp_proxy_adapter/py.typed +0 -0
  44. mcp_proxy_adapter/schemas/base_schema.json +114 -0
  45. mcp_proxy_adapter/schemas/openapi_schema.json +314 -0
  46. mcp_proxy_adapter/tests/__init__.py +0 -0
  47. mcp_proxy_adapter/tests/api/__init__.py +3 -0
  48. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +115 -0
  49. mcp_proxy_adapter/tests/api/test_middleware.py +336 -0
  50. mcp_proxy_adapter/tests/commands/__init__.py +3 -0
  51. mcp_proxy_adapter/tests/commands/test_config_command.py +211 -0
  52. mcp_proxy_adapter/tests/commands/test_echo_command.py +127 -0
  53. mcp_proxy_adapter/tests/commands/test_help_command.py +133 -0
  54. mcp_proxy_adapter/tests/conftest.py +131 -0
  55. mcp_proxy_adapter/tests/functional/__init__.py +3 -0
  56. mcp_proxy_adapter/tests/functional/test_api.py +253 -0
  57. mcp_proxy_adapter/tests/integration/__init__.py +3 -0
  58. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +130 -0
  59. mcp_proxy_adapter/tests/integration/test_integration.py +255 -0
  60. mcp_proxy_adapter/tests/performance/__init__.py +3 -0
  61. mcp_proxy_adapter/tests/performance/test_performance.py +189 -0
  62. mcp_proxy_adapter/tests/stubs/__init__.py +10 -0
  63. mcp_proxy_adapter/tests/stubs/echo_command.py +104 -0
  64. mcp_proxy_adapter/tests/test_api_endpoints.py +271 -0
  65. mcp_proxy_adapter/tests/test_api_handlers.py +289 -0
  66. mcp_proxy_adapter/tests/test_base_command.py +123 -0
  67. mcp_proxy_adapter/tests/test_batch_requests.py +117 -0
  68. mcp_proxy_adapter/tests/test_command_registry.py +245 -0
  69. mcp_proxy_adapter/tests/test_config.py +127 -0
  70. mcp_proxy_adapter/tests/test_utils.py +65 -0
  71. mcp_proxy_adapter/tests/unit/__init__.py +3 -0
  72. mcp_proxy_adapter/tests/unit/test_base_command.py +130 -0
  73. mcp_proxy_adapter/tests/unit/test_config.py +217 -0
  74. mcp_proxy_adapter/version.py +1 -1
  75. {mcp_proxy_adapter-3.0.0.dist-info → mcp_proxy_adapter-3.0.2.dist-info}/METADATA +1 -1
  76. mcp_proxy_adapter-3.0.2.dist-info/RECORD +109 -0
  77. examples/basic_example/config.yaml +0 -20
  78. examples/basic_example/main.py +0 -50
  79. examples/complete_example/main.py +0 -67
  80. examples/minimal_example/config.yaml +0 -26
  81. mcp_proxy_adapter/framework.py +0 -109
  82. mcp_proxy_adapter-3.0.0.dist-info/RECORD +0 -58
  83. {mcp_proxy_adapter-3.0.0.dist-info → mcp_proxy_adapter-3.0.2.dist-info}/WHEEL +0 -0
  84. {mcp_proxy_adapter-3.0.0.dist-info → mcp_proxy_adapter-3.0.2.dist-info}/licenses/LICENSE +0 -0
  85. {mcp_proxy_adapter-3.0.0.dist-info → mcp_proxy_adapter-3.0.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,138 @@
1
+ """
2
+ Module with utility functions for the microservice.
3
+ """
4
+
5
+ import hashlib
6
+ import json
7
+ import os
8
+ import sys
9
+ import time
10
+ import uuid
11
+ from datetime import datetime
12
+ from typing import Any, Dict, List, Optional, Tuple, Union
13
+
14
+ from mcp_proxy_adapter.core.logging import logger
15
+
16
+
17
+ def generate_id() -> str:
18
+ """
19
+ Generates a unique identifier.
20
+
21
+ Returns:
22
+ String with unique identifier.
23
+ """
24
+ return str(uuid.uuid4())
25
+
26
+
27
+ def get_timestamp() -> int:
28
+ """
29
+ Returns current timestamp in milliseconds.
30
+
31
+ Returns:
32
+ Integer - timestamp in milliseconds.
33
+ """
34
+ return int(time.time() * 1000)
35
+
36
+
37
+ def format_datetime(dt: Optional[datetime] = None, format_str: str = "%Y-%m-%dT%H:%M:%S.%fZ") -> str:
38
+ """
39
+ Formats date and time as string.
40
+
41
+ Args:
42
+ dt: Datetime object to format. If None, current time is used.
43
+ format_str: Format string for output.
44
+
45
+ Returns:
46
+ Formatted date/time string.
47
+ """
48
+ dt = dt or datetime.utcnow()
49
+ return dt.strftime(format_str)
50
+
51
+
52
+ def parse_datetime(dt_str: str, format_str: str = "%Y-%m-%dT%H:%M:%S.%fZ") -> datetime:
53
+ """
54
+ Parses date/time string into datetime object.
55
+
56
+ Args:
57
+ dt_str: Date/time string.
58
+ format_str: Date/time string format.
59
+
60
+ Returns:
61
+ Datetime object.
62
+ """
63
+ return datetime.strptime(dt_str, format_str)
64
+
65
+
66
+ def safe_json_loads(s: str, default: Any = None) -> Any:
67
+ """
68
+ Safe JSON string loading.
69
+
70
+ Args:
71
+ s: JSON string to load.
72
+ default: Default value on parsing error.
73
+
74
+ Returns:
75
+ Loaded object or default value on error.
76
+ """
77
+ try:
78
+ return json.loads(s)
79
+ except Exception as e:
80
+ logger.error(f"Error parsing JSON: {e}")
81
+ return default
82
+
83
+
84
+ def safe_json_dumps(obj: Any, default: str = "{}", indent: Optional[int] = None) -> str:
85
+ """
86
+ Safe object conversion to JSON string.
87
+
88
+ Args:
89
+ obj: Object to convert.
90
+ default: Default string on serialization error.
91
+ indent: Indentation for JSON formatting.
92
+
93
+ Returns:
94
+ JSON string or default string on error.
95
+ """
96
+ try:
97
+ return json.dumps(obj, ensure_ascii=False, indent=indent)
98
+ except Exception as e:
99
+ logger.error(f"Error serializing to JSON: {e}")
100
+ return default
101
+
102
+
103
+ def calculate_hash(data: Union[str, bytes], algorithm: str = "sha256") -> str:
104
+ """
105
+ Calculates hash for data.
106
+
107
+ Args:
108
+ data: Data to hash (string or bytes).
109
+ algorithm: Hashing algorithm (md5, sha1, sha256, etc.).
110
+
111
+ Returns:
112
+ String with hash in hexadecimal format.
113
+ """
114
+ if isinstance(data, str):
115
+ data = data.encode("utf-8")
116
+
117
+ hash_obj = hashlib.new(algorithm)
118
+ hash_obj.update(data)
119
+ return hash_obj.hexdigest()
120
+
121
+
122
+ def ensure_directory(path: str) -> bool:
123
+ """
124
+ Checks directory existence and creates it if necessary.
125
+
126
+ Args:
127
+ path: Path to directory.
128
+
129
+ Returns:
130
+ True if directory exists or was successfully created, otherwise False.
131
+ """
132
+ try:
133
+ if not os.path.exists(path):
134
+ os.makedirs(path, exist_ok=True)
135
+ return True
136
+ except Exception as e:
137
+ logger.error(f"Error creating directory {path}: {e}")
138
+ return False
@@ -18,8 +18,16 @@ class CustomOpenAPIGenerator:
18
18
  """
19
19
  Custom OpenAPI schema generator for compatibility with MCP-Proxy.
20
20
 
21
+ EN:
21
22
  This generator creates an OpenAPI schema that matches the format expected by MCP-Proxy,
22
23
  enabling dynamic command loading and proper tool representation in AI models.
24
+ Allows overriding title, description, and version for schema customization.
25
+
26
+ RU:
27
+ Кастомный генератор схемы OpenAPI для совместимости с MCP-Proxy.
28
+ Позволяет создавать схему OpenAPI в формате, ожидаемом MCP-Proxy,
29
+ с возможностью динамической подгрузки команд и корректного отображения инструментов для AI-моделей.
30
+ Поддерживает переопределение title, description и version для кастомизации схемы.
23
31
  """
24
32
 
25
33
  def __init__(self):
@@ -88,38 +96,67 @@ class CustomOpenAPIGenerator:
88
96
 
89
97
  return cmd_schema
90
98
 
91
- def generate(self) -> Dict[str, Any]:
99
+ def generate(self, title: Optional[str] = None, description: Optional[str] = None, version: Optional[str] = None) -> Dict[str, Any]:
92
100
  """
101
+ EN:
93
102
  Generate the complete OpenAPI schema compatible with MCP-Proxy.
103
+ Optionally override title, description, and version.
104
+
105
+ RU:
106
+ Генерирует полную схему OpenAPI, совместимую с MCP-Proxy.
107
+ Позволяет опционально переопределить title, description и version.
108
+
109
+ Args:
110
+ title: Custom title for the schema / Кастомный заголовок схемы
111
+ description: Custom description for the schema / Кастомное описание схемы
112
+ version: Custom version for the schema / Кастомная версия схемы
94
113
 
95
114
  Returns:
96
- Dict containing the complete OpenAPI schema.
115
+ Dict containing the complete OpenAPI schema / Словарь с полной схемой OpenAPI
97
116
  """
98
117
  # Deep copy the base schema to avoid modifying it
99
118
  schema = deepcopy(self.base_schema)
100
-
119
+
120
+ # Optionally override info fields
121
+ if title:
122
+ schema["info"]["title"] = title
123
+ if description:
124
+ schema["info"]["description"] = description
125
+ if version:
126
+ schema["info"]["version"] = version
127
+
101
128
  # Add commands to schema
102
129
  self._add_commands_to_schema(schema)
103
-
130
+
104
131
  logger.info(f"Generated OpenAPI schema with {len(registry.get_all_commands())} commands")
105
-
132
+
106
133
  return schema
107
134
 
108
135
 
109
136
  def custom_openapi(app: FastAPI) -> Dict[str, Any]:
110
137
  """
138
+ EN:
111
139
  Create a custom OpenAPI schema for the FastAPI application.
140
+ Passes app's title, description, and version to the generator.
141
+
142
+ RU:
143
+ Создаёт кастомную OpenAPI-схему для FastAPI-приложения.
144
+ Передаёт параметры title, description и version из приложения в генератор схемы.
112
145
 
113
146
  Args:
114
- app: The FastAPI application.
147
+ app: The FastAPI application / FastAPI-приложение
115
148
 
116
149
  Returns:
117
- Dict containing the custom OpenAPI schema.
150
+ Dict containing the custom OpenAPI schema / Словарь с кастомной OpenAPI-схемой
118
151
  """
119
152
  generator = CustomOpenAPIGenerator()
120
- openapi_schema = generator.generate()
121
-
153
+ openapi_schema = generator.generate(
154
+ title=getattr(app, 'title', None),
155
+ description=getattr(app, 'description', None),
156
+ version=getattr(app, 'version', None)
157
+ )
158
+
122
159
  # Cache the schema
123
160
  app.openapi_schema = openapi_schema
124
-
161
+
125
162
  return openapi_schema
File without changes
@@ -0,0 +1,114 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "Base Schema",
4
+ "description": "Basic schema for validating commands and results",
5
+ "definitions": {
6
+ "command": {
7
+ "type": "object",
8
+ "properties": {
9
+ "jsonrpc": {
10
+ "type": "string",
11
+ "enum": ["2.0"],
12
+ "description": "JSON-RPC version"
13
+ },
14
+ "method": {
15
+ "type": "string",
16
+ "description": "Command name"
17
+ },
18
+ "params": {
19
+ "type": "object",
20
+ "description": "Command parameters"
21
+ },
22
+ "id": {
23
+ "oneOf": [
24
+ {"type": "string"},
25
+ {"type": "integer"},
26
+ {"type": "null"}
27
+ ],
28
+ "description": "Request ID"
29
+ }
30
+ },
31
+ "required": ["jsonrpc", "method"]
32
+ },
33
+ "success_response": {
34
+ "type": "object",
35
+ "properties": {
36
+ "jsonrpc": {
37
+ "type": "string",
38
+ "enum": ["2.0"],
39
+ "description": "JSON-RPC version"
40
+ },
41
+ "result": {
42
+ "type": "object",
43
+ "properties": {
44
+ "success": {
45
+ "type": "boolean",
46
+ "enum": [true],
47
+ "description": "Success flag"
48
+ },
49
+ "data": {
50
+ "type": "object",
51
+ "description": "Response data"
52
+ },
53
+ "message": {
54
+ "type": "string",
55
+ "description": "Response message"
56
+ }
57
+ },
58
+ "required": ["success"]
59
+ },
60
+ "id": {
61
+ "oneOf": [
62
+ {"type": "string"},
63
+ {"type": "integer"},
64
+ {"type": "null"}
65
+ ],
66
+ "description": "Request ID"
67
+ }
68
+ },
69
+ "required": ["jsonrpc", "result"]
70
+ },
71
+ "error_response": {
72
+ "type": "object",
73
+ "properties": {
74
+ "jsonrpc": {
75
+ "type": "string",
76
+ "enum": ["2.0"],
77
+ "description": "JSON-RPC version"
78
+ },
79
+ "error": {
80
+ "type": "object",
81
+ "properties": {
82
+ "code": {
83
+ "type": "integer",
84
+ "description": "Error code"
85
+ },
86
+ "message": {
87
+ "type": "string",
88
+ "description": "Error message"
89
+ },
90
+ "details": {
91
+ "type": "object",
92
+ "description": "Detailed error information"
93
+ }
94
+ },
95
+ "required": ["code", "message"]
96
+ },
97
+ "id": {
98
+ "oneOf": [
99
+ {"type": "string"},
100
+ {"type": "integer"},
101
+ {"type": "null"}
102
+ ],
103
+ "description": "Request ID"
104
+ }
105
+ },
106
+ "required": ["jsonrpc", "error"]
107
+ }
108
+ },
109
+ "oneOf": [
110
+ {"$ref": "#/definitions/command"},
111
+ {"$ref": "#/definitions/success_response"},
112
+ {"$ref": "#/definitions/error_response"}
113
+ ]
114
+ }
@@ -0,0 +1,314 @@
1
+ {
2
+ "openapi": "3.0.2",
3
+ "info": {
4
+ "title": "MCP Microservice API",
5
+ "description": "API для выполнения команд микросервиса",
6
+ "version": "1.0.0"
7
+ },
8
+ "paths": {
9
+ "/cmd": {
10
+ "post": {
11
+ "summary": "Execute Command",
12
+ "description": "Executes a command via JSON-RPC protocol.",
13
+ "operationId": "execute_command",
14
+ "requestBody": {
15
+ "content": {
16
+ "application/json": {
17
+ "schema": {
18
+ "oneOf": [
19
+ { "$ref": "#/components/schemas/CommandRequest" },
20
+ { "$ref": "#/components/schemas/JsonRpcRequest" }
21
+ ]
22
+ }
23
+ }
24
+ },
25
+ "required": true
26
+ },
27
+ "responses": {
28
+ "200": {
29
+ "description": "Successful Response",
30
+ "content": {
31
+ "application/json": {
32
+ "schema": {
33
+ "oneOf": [
34
+ { "$ref": "#/components/schemas/CommandResponse" },
35
+ { "$ref": "#/components/schemas/JsonRpcResponse" }
36
+ ]
37
+ }
38
+ }
39
+ }
40
+ },
41
+ "422": {
42
+ "description": "Validation Error",
43
+ "content": {
44
+ "application/json": {
45
+ "schema": {
46
+ "$ref": "#/components/schemas/HTTPValidationError"
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }
52
+ }
53
+ },
54
+ "/health": {
55
+ "get": {
56
+ "summary": "Проверить работоспособность сервиса",
57
+ "description": "Возвращает информацию о состоянии сервиса",
58
+ "operationId": "health_check",
59
+ "responses": {
60
+ "200": {
61
+ "description": "Информация о состоянии сервиса",
62
+ "content": {
63
+ "application/json": {
64
+ "schema": {
65
+ "$ref": "#/components/schemas/HealthResponse"
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ }
72
+ },
73
+ "/openapi.json": {
74
+ "get": {
75
+ "summary": "Get Openapi Schema",
76
+ "description": "Returns OpenAPI schema.",
77
+ "operationId": "get_openapi_schema_openapi_json_get",
78
+ "responses": {
79
+ "200": {
80
+ "description": "Successful Response",
81
+ "content": {
82
+ "application/json": {
83
+ "schema": {}
84
+ }
85
+ }
86
+ }
87
+ }
88
+ }
89
+ },
90
+ "/api/commands": {
91
+ "get": {
92
+ "summary": "Get Commands",
93
+ "description": "Returns list of available commands with their descriptions.",
94
+ "operationId": "get_commands_api_commands_get",
95
+ "responses": {
96
+ "200": {
97
+ "description": "Successful Response",
98
+ "content": {
99
+ "application/json": {
100
+ "schema": {}
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
106
+ }
107
+ },
108
+ "components": {
109
+ "schemas": {
110
+ "CommandRequest": {
111
+ "title": "CommandRequest",
112
+ "description": "Запрос на выполнение команды",
113
+ "type": "object",
114
+ "required": [
115
+ "command"
116
+ ],
117
+ "properties": {
118
+ "command": {
119
+ "title": "Command",
120
+ "description": "Команда для выполнения",
121
+ "type": "string"
122
+ },
123
+ "params": {
124
+ "title": "Parameters",
125
+ "description": "Параметры команды, зависят от типа команды",
126
+ "type": "object",
127
+ "additionalProperties": true
128
+ }
129
+ }
130
+ },
131
+ "CommandResponse": {
132
+ "title": "CommandResponse",
133
+ "description": "Ответ на выполнение команды",
134
+ "type": "object",
135
+ "required": [
136
+ "result"
137
+ ],
138
+ "properties": {
139
+ "result": {
140
+ "title": "Result",
141
+ "description": "Результат выполнения команды"
142
+ }
143
+ }
144
+ },
145
+ "JsonRpcRequest": {
146
+ "properties": {
147
+ "jsonrpc": {
148
+ "type": "string",
149
+ "title": "Jsonrpc",
150
+ "description": "JSON-RPC version",
151
+ "default": "2.0"
152
+ },
153
+ "method": {
154
+ "type": "string",
155
+ "title": "Method",
156
+ "description": "Method name to call"
157
+ },
158
+ "params": {
159
+ "additionalProperties": true,
160
+ "type": "object",
161
+ "title": "Params",
162
+ "description": "Method parameters",
163
+ "default": {}
164
+ },
165
+ "id": {
166
+ "anyOf": [
167
+ {
168
+ "type": "string"
169
+ },
170
+ {
171
+ "type": "integer"
172
+ },
173
+ {
174
+ "type": "null"
175
+ }
176
+ ],
177
+ "title": "Id",
178
+ "description": "Request identifier"
179
+ }
180
+ },
181
+ "type": "object",
182
+ "required": [
183
+ "method"
184
+ ],
185
+ "title": "JsonRpcRequest",
186
+ "description": "Base model for JSON-RPC requests."
187
+ },
188
+ "JsonRpcResponse": {
189
+ "properties": {
190
+ "jsonrpc": {
191
+ "type": "string",
192
+ "title": "Jsonrpc",
193
+ "description": "JSON-RPC version",
194
+ "default": "2.0"
195
+ },
196
+ "result": {
197
+ "anyOf": [
198
+ {},
199
+ {
200
+ "type": "null"
201
+ }
202
+ ],
203
+ "title": "Result",
204
+ "description": "Method execution result"
205
+ },
206
+ "error": {
207
+ "anyOf": [
208
+ {
209
+ "additionalProperties": true,
210
+ "type": "object"
211
+ },
212
+ {
213
+ "type": "null"
214
+ }
215
+ ],
216
+ "title": "Error",
217
+ "description": "Error information"
218
+ },
219
+ "id": {
220
+ "anyOf": [
221
+ {
222
+ "type": "string"
223
+ },
224
+ {
225
+ "type": "integer"
226
+ },
227
+ {
228
+ "type": "null"
229
+ }
230
+ ],
231
+ "title": "Id",
232
+ "description": "Request identifier"
233
+ }
234
+ },
235
+ "type": "object",
236
+ "title": "JsonRpcResponse",
237
+ "description": "Base model for JSON-RPC responses."
238
+ },
239
+ "HealthResponse": {
240
+ "title": "HealthResponse",
241
+ "description": "Информация о состоянии сервиса",
242
+ "type": "object",
243
+ "required": [
244
+ "status",
245
+ "model",
246
+ "version"
247
+ ],
248
+ "properties": {
249
+ "status": {
250
+ "title": "Status",
251
+ "description": "Статус сервиса (ok/error)",
252
+ "type": "string"
253
+ },
254
+ "model": {
255
+ "title": "Model",
256
+ "description": "Текущая активная модель",
257
+ "type": "string"
258
+ },
259
+ "version": {
260
+ "title": "Version",
261
+ "description": "Версия сервиса",
262
+ "type": "string"
263
+ }
264
+ }
265
+ },
266
+ "HTTPValidationError": {
267
+ "properties": {
268
+ "detail": {
269
+ "items": {
270
+ "$ref": "#/components/schemas/ValidationError"
271
+ },
272
+ "type": "array",
273
+ "title": "Detail"
274
+ }
275
+ },
276
+ "type": "object",
277
+ "title": "HTTPValidationError"
278
+ },
279
+ "ValidationError": {
280
+ "properties": {
281
+ "loc": {
282
+ "items": {
283
+ "anyOf": [
284
+ {
285
+ "type": "string"
286
+ },
287
+ {
288
+ "type": "integer"
289
+ }
290
+ ]
291
+ },
292
+ "type": "array",
293
+ "title": "Location"
294
+ },
295
+ "msg": {
296
+ "type": "string",
297
+ "title": "Message"
298
+ },
299
+ "type": {
300
+ "type": "string",
301
+ "title": "Error Type"
302
+ }
303
+ },
304
+ "type": "object",
305
+ "required": [
306
+ "loc",
307
+ "msg",
308
+ "type"
309
+ ],
310
+ "title": "ValidationError"
311
+ }
312
+ }
313
+ }
314
+ }
File without changes
@@ -0,0 +1,3 @@
1
+ """
2
+ API tests package.
3
+ """