mcp-proxy-adapter 2.1.17__py3-none-any.whl → 3.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.
- examples/__init__.py +19 -0
- examples/anti_patterns/README.md +51 -0
- examples/anti_patterns/__init__.py +9 -0
- examples/anti_patterns/bad_design/README.md +72 -0
- examples/anti_patterns/bad_design/global_state.py +170 -0
- examples/anti_patterns/bad_design/monolithic_command.py +272 -0
- examples/basic_example/README.md +245 -0
- examples/basic_example/__init__.py +8 -0
- examples/basic_example/commands/__init__.py +5 -0
- examples/basic_example/commands/echo_command.py +95 -0
- examples/basic_example/commands/math_command.py +151 -0
- examples/basic_example/commands/time_command.py +152 -0
- examples/basic_example/config.json +25 -0
- examples/basic_example/docs/EN/README.md +177 -0
- examples/basic_example/docs/RU/README.md +177 -0
- examples/basic_example/server.py +151 -0
- examples/basic_example/tests/conftest.py +243 -0
- examples/commands/echo_command.py +52 -0
- examples/commands/echo_result.py +65 -0
- examples/commands/get_date_command.py +98 -0
- examples/commands/new_uuid4_command.py +91 -0
- examples/complete_example/Dockerfile +24 -0
- examples/complete_example/README.md +92 -0
- examples/complete_example/__init__.py +8 -0
- examples/complete_example/commands/__init__.py +5 -0
- examples/complete_example/commands/system_command.py +328 -0
- examples/complete_example/config.json +41 -0
- examples/complete_example/configs/config.dev.yaml +40 -0
- examples/complete_example/configs/config.docker.yaml +40 -0
- examples/complete_example/docker-compose.yml +35 -0
- examples/complete_example/requirements.txt +20 -0
- examples/complete_example/server.py +139 -0
- examples/minimal_example/README.md +65 -0
- examples/minimal_example/__init__.py +8 -0
- examples/minimal_example/config.json +14 -0
- examples/minimal_example/main.py +136 -0
- examples/minimal_example/simple_server.py +163 -0
- examples/minimal_example/tests/conftest.py +171 -0
- examples/minimal_example/tests/test_hello_command.py +111 -0
- examples/minimal_example/tests/test_integration.py +181 -0
- examples/server.py +69 -0
- examples/simple_server.py +128 -0
- examples/test_server.py +134 -0
- examples/tool_description_example.py +82 -0
- mcp_proxy_adapter/__init__.py +33 -1
- mcp_proxy_adapter/api/__init__.py +0 -0
- mcp_proxy_adapter/api/app.py +391 -0
- mcp_proxy_adapter/api/handlers.py +229 -0
- mcp_proxy_adapter/api/middleware/__init__.py +49 -0
- mcp_proxy_adapter/api/middleware/auth.py +146 -0
- mcp_proxy_adapter/api/middleware/base.py +79 -0
- mcp_proxy_adapter/api/middleware/error_handling.py +198 -0
- mcp_proxy_adapter/api/middleware/logging.py +96 -0
- mcp_proxy_adapter/api/middleware/performance.py +83 -0
- mcp_proxy_adapter/api/middleware/rate_limit.py +152 -0
- mcp_proxy_adapter/api/schemas.py +305 -0
- mcp_proxy_adapter/api/tool_integration.py +223 -0
- mcp_proxy_adapter/api/tools.py +198 -0
- mcp_proxy_adapter/commands/__init__.py +19 -0
- mcp_proxy_adapter/commands/base.py +301 -0
- mcp_proxy_adapter/commands/command_registry.py +231 -0
- mcp_proxy_adapter/commands/config_command.py +113 -0
- mcp_proxy_adapter/commands/health_command.py +136 -0
- mcp_proxy_adapter/commands/help_command.py +193 -0
- mcp_proxy_adapter/commands/result.py +215 -0
- mcp_proxy_adapter/config.py +195 -0
- mcp_proxy_adapter/core/__init__.py +0 -0
- mcp_proxy_adapter/core/errors.py +173 -0
- mcp_proxy_adapter/core/logging.py +205 -0
- mcp_proxy_adapter/core/utils.py +138 -0
- mcp_proxy_adapter/custom_openapi.py +125 -0
- mcp_proxy_adapter/openapi.py +403 -0
- mcp_proxy_adapter/py.typed +0 -0
- mcp_proxy_adapter/schemas/base_schema.json +114 -0
- mcp_proxy_adapter/schemas/openapi_schema.json +314 -0
- mcp_proxy_adapter/tests/__init__.py +0 -0
- mcp_proxy_adapter/tests/api/__init__.py +3 -0
- mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +115 -0
- mcp_proxy_adapter/tests/api/test_middleware.py +336 -0
- mcp_proxy_adapter/tests/commands/__init__.py +3 -0
- mcp_proxy_adapter/tests/commands/test_config_command.py +211 -0
- mcp_proxy_adapter/tests/commands/test_echo_command.py +127 -0
- mcp_proxy_adapter/tests/commands/test_help_command.py +133 -0
- mcp_proxy_adapter/tests/conftest.py +131 -0
- mcp_proxy_adapter/tests/functional/__init__.py +3 -0
- mcp_proxy_adapter/tests/functional/test_api.py +235 -0
- mcp_proxy_adapter/tests/integration/__init__.py +3 -0
- mcp_proxy_adapter/tests/integration/test_cmd_integration.py +130 -0
- mcp_proxy_adapter/tests/integration/test_integration.py +255 -0
- mcp_proxy_adapter/tests/performance/__init__.py +3 -0
- mcp_proxy_adapter/tests/performance/test_performance.py +189 -0
- mcp_proxy_adapter/tests/stubs/__init__.py +10 -0
- mcp_proxy_adapter/tests/stubs/echo_command.py +104 -0
- mcp_proxy_adapter/tests/test_api_endpoints.py +271 -0
- mcp_proxy_adapter/tests/test_api_handlers.py +289 -0
- mcp_proxy_adapter/tests/test_base_command.py +123 -0
- mcp_proxy_adapter/tests/test_batch_requests.py +117 -0
- mcp_proxy_adapter/tests/test_command_registry.py +245 -0
- mcp_proxy_adapter/tests/test_config.py +127 -0
- mcp_proxy_adapter/tests/test_utils.py +65 -0
- mcp_proxy_adapter/tests/unit/__init__.py +3 -0
- mcp_proxy_adapter/tests/unit/test_base_command.py +130 -0
- mcp_proxy_adapter/tests/unit/test_config.py +217 -0
- mcp_proxy_adapter/version.py +3 -0
- mcp_proxy_adapter-3.0.1.dist-info/METADATA +200 -0
- mcp_proxy_adapter-3.0.1.dist-info/RECORD +109 -0
- {mcp_proxy_adapter-2.1.17.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/top_level.txt +1 -0
- mcp_proxy_adapter/adapter.py +0 -697
- mcp_proxy_adapter/analyzers/__init__.py +0 -1
- mcp_proxy_adapter/analyzers/docstring_analyzer.py +0 -199
- mcp_proxy_adapter/analyzers/type_analyzer.py +0 -151
- mcp_proxy_adapter/dispatchers/__init__.py +0 -1
- mcp_proxy_adapter/dispatchers/base_dispatcher.py +0 -85
- mcp_proxy_adapter/dispatchers/json_rpc_dispatcher.py +0 -262
- mcp_proxy_adapter/examples/analyze_config.py +0 -141
- mcp_proxy_adapter/examples/basic_integration.py +0 -155
- mcp_proxy_adapter/examples/docstring_and_schema_example.py +0 -69
- mcp_proxy_adapter/examples/extension_example.py +0 -72
- mcp_proxy_adapter/examples/help_best_practices.py +0 -67
- mcp_proxy_adapter/examples/help_usage.py +0 -64
- mcp_proxy_adapter/examples/mcp_proxy_client.py +0 -131
- mcp_proxy_adapter/examples/openapi_server.py +0 -383
- mcp_proxy_adapter/examples/project_structure_example.py +0 -47
- mcp_proxy_adapter/examples/testing_example.py +0 -64
- mcp_proxy_adapter/models.py +0 -47
- mcp_proxy_adapter/registry.py +0 -439
- mcp_proxy_adapter/schema.py +0 -257
- mcp_proxy_adapter/testing_utils.py +0 -112
- mcp_proxy_adapter/validators/__init__.py +0 -1
- mcp_proxy_adapter/validators/docstring_validator.py +0 -75
- mcp_proxy_adapter/validators/metadata_validator.py +0 -76
- mcp_proxy_adapter-2.1.17.dist-info/METADATA +0 -376
- mcp_proxy_adapter-2.1.17.dist-info/RECORD +0 -30
- {mcp_proxy_adapter-2.1.17.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-2.1.17.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,403 @@
|
|
1
|
+
"""
|
2
|
+
OpenAPI schema generator for MCP Microservice
|
3
|
+
"""
|
4
|
+
from typing import Dict, Any, List, Optional
|
5
|
+
from dataclasses import dataclass
|
6
|
+
import inspect
|
7
|
+
import json
|
8
|
+
from pathlib import Path
|
9
|
+
|
10
|
+
from .registry import CommandRegistry, Command
|
11
|
+
from .exceptions import SchemaValidationError
|
12
|
+
|
13
|
+
|
14
|
+
@dataclass
|
15
|
+
class TypeInfo:
|
16
|
+
"""Information about a type for OpenAPI schema"""
|
17
|
+
openapi_type: str
|
18
|
+
format: Optional[str] = None
|
19
|
+
items: Optional[Dict[str, Any]] = None
|
20
|
+
properties: Optional[Dict[str, Any]] = None
|
21
|
+
required: Optional[List[str]] = None
|
22
|
+
|
23
|
+
|
24
|
+
class OpenApiGenerator:
|
25
|
+
"""Generates OpenAPI schema for MCP Microservice"""
|
26
|
+
|
27
|
+
PYTHON_TO_OPENAPI_TYPES = {
|
28
|
+
str: TypeInfo("string"),
|
29
|
+
int: TypeInfo("integer", "int64"),
|
30
|
+
float: TypeInfo("number", "float"),
|
31
|
+
bool: TypeInfo("boolean"),
|
32
|
+
list: TypeInfo("array"),
|
33
|
+
dict: TypeInfo("object"),
|
34
|
+
None: TypeInfo("null"),
|
35
|
+
}
|
36
|
+
|
37
|
+
def __init__(self, registry: CommandRegistry):
|
38
|
+
"""
|
39
|
+
Initialize generator
|
40
|
+
|
41
|
+
Args:
|
42
|
+
registry: Command registry instance
|
43
|
+
"""
|
44
|
+
self.registry = registry
|
45
|
+
self._base_schema = self._load_base_schema()
|
46
|
+
|
47
|
+
def _load_base_schema(self) -> Dict[str, Any]:
|
48
|
+
"""Load base schema from file"""
|
49
|
+
schema_path = Path(__file__).parent / "schemas" / "base_schema.json"
|
50
|
+
with open(schema_path) as f:
|
51
|
+
return json.load(f)
|
52
|
+
|
53
|
+
def _get_type_info(self, python_type: Any) -> TypeInfo:
|
54
|
+
"""
|
55
|
+
Get OpenAPI type info for Python type
|
56
|
+
|
57
|
+
Args:
|
58
|
+
python_type: Python type annotation
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
TypeInfo object with OpenAPI type information
|
62
|
+
"""
|
63
|
+
# Handle Optional types
|
64
|
+
origin = getattr(python_type, "__origin__", None)
|
65
|
+
if origin is Optional:
|
66
|
+
return self._get_type_info(python_type.__args__[0])
|
67
|
+
|
68
|
+
# Handle List and Dict
|
69
|
+
if origin is list:
|
70
|
+
item_type = self._get_type_info(python_type.__args__[0])
|
71
|
+
return TypeInfo("array", items={"type": item_type.openapi_type})
|
72
|
+
|
73
|
+
if origin is dict:
|
74
|
+
return TypeInfo("object", additionalProperties=True)
|
75
|
+
|
76
|
+
# Handle basic types
|
77
|
+
if python_type in self.PYTHON_TO_OPENAPI_TYPES:
|
78
|
+
return self.PYTHON_TO_OPENAPI_TYPES[python_type]
|
79
|
+
|
80
|
+
# Handle custom classes
|
81
|
+
if inspect.isclass(python_type):
|
82
|
+
properties = {}
|
83
|
+
required = []
|
84
|
+
|
85
|
+
for name, field in inspect.get_annotations(python_type).items():
|
86
|
+
field_info = self._get_type_info(field)
|
87
|
+
properties[name] = {
|
88
|
+
"type": field_info.openapi_type
|
89
|
+
}
|
90
|
+
if field_info.format:
|
91
|
+
properties[name]["format"] = field_info.format
|
92
|
+
required.append(name)
|
93
|
+
|
94
|
+
return TypeInfo(
|
95
|
+
"object",
|
96
|
+
properties=properties,
|
97
|
+
required=required
|
98
|
+
)
|
99
|
+
|
100
|
+
raise ValueError(f"Unsupported type: {python_type}")
|
101
|
+
|
102
|
+
def _add_command_params(self, schema: Dict[str, Any], command: Command):
|
103
|
+
"""
|
104
|
+
Add command parameters to schema
|
105
|
+
|
106
|
+
Args:
|
107
|
+
schema: OpenAPI schema
|
108
|
+
command: Command instance
|
109
|
+
"""
|
110
|
+
params = {}
|
111
|
+
required = []
|
112
|
+
|
113
|
+
# Get parameters from function signature
|
114
|
+
sig = inspect.signature(command.func)
|
115
|
+
for name, param in sig.parameters.items():
|
116
|
+
param_schema = {}
|
117
|
+
|
118
|
+
# Get type info
|
119
|
+
type_info = self._get_type_info(param.annotation)
|
120
|
+
param_schema["type"] = type_info.openapi_type
|
121
|
+
|
122
|
+
if type_info.format:
|
123
|
+
param_schema["format"] = type_info.format
|
124
|
+
|
125
|
+
if type_info.items:
|
126
|
+
param_schema["items"] = type_info.items
|
127
|
+
|
128
|
+
# Get description from docstring
|
129
|
+
if command.doc and command.doc.params:
|
130
|
+
for doc_param in command.doc.params:
|
131
|
+
if doc_param.arg_name == name:
|
132
|
+
param_schema["description"] = doc_param.description
|
133
|
+
break
|
134
|
+
|
135
|
+
# Handle default value
|
136
|
+
if param.default is not param.empty:
|
137
|
+
param_schema["default"] = param.default
|
138
|
+
else:
|
139
|
+
required.append(name)
|
140
|
+
|
141
|
+
params[name] = param_schema
|
142
|
+
|
143
|
+
# Add to schema
|
144
|
+
method_schema = {
|
145
|
+
"type": "object",
|
146
|
+
"properties": params
|
147
|
+
}
|
148
|
+
if required:
|
149
|
+
method_schema["required"] = required
|
150
|
+
|
151
|
+
schema["components"]["schemas"][f"Params{command.name}"] = method_schema
|
152
|
+
|
153
|
+
def _add_commands_to_schema(self, schema: Dict[str, Any]):
|
154
|
+
"""
|
155
|
+
Add all commands to schema
|
156
|
+
|
157
|
+
Args:
|
158
|
+
schema: OpenAPI schema
|
159
|
+
"""
|
160
|
+
for command in self.registry.get_commands():
|
161
|
+
self._add_command_params(schema, command)
|
162
|
+
|
163
|
+
def _add_cmd_endpoint(self, schema: Dict[str, Any]) -> None:
|
164
|
+
"""
|
165
|
+
Add /cmd endpoint to OpenAPI schema.
|
166
|
+
|
167
|
+
Args:
|
168
|
+
schema: OpenAPI schema to update
|
169
|
+
"""
|
170
|
+
schema["paths"]["/cmd"] = {
|
171
|
+
"post": {
|
172
|
+
"summary": "Execute command",
|
173
|
+
"description": "Universal endpoint for executing any command",
|
174
|
+
"operationId": "execute_command",
|
175
|
+
"requestBody": {
|
176
|
+
"content": {
|
177
|
+
"application/json": {
|
178
|
+
"schema": {"$ref": "#/components/schemas/CommandRequest"}
|
179
|
+
}
|
180
|
+
},
|
181
|
+
"required": True
|
182
|
+
},
|
183
|
+
"responses": {
|
184
|
+
"200": {
|
185
|
+
"description": "Command execution result",
|
186
|
+
"content": {
|
187
|
+
"application/json": {
|
188
|
+
"schema": {
|
189
|
+
"oneOf": [
|
190
|
+
{"$ref": "#/components/schemas/CommandSuccessResponse"},
|
191
|
+
{"$ref": "#/components/schemas/CommandErrorResponse"}
|
192
|
+
]
|
193
|
+
}
|
194
|
+
}
|
195
|
+
}
|
196
|
+
}
|
197
|
+
}
|
198
|
+
}
|
199
|
+
}
|
200
|
+
|
201
|
+
def _add_cmd_models(self, schema: Dict[str, Any]) -> None:
|
202
|
+
"""
|
203
|
+
Add models for /cmd endpoint to OpenAPI schema.
|
204
|
+
|
205
|
+
Args:
|
206
|
+
schema: OpenAPI schema to update
|
207
|
+
"""
|
208
|
+
# Add command request model
|
209
|
+
schema["components"]["schemas"]["CommandRequest"] = {
|
210
|
+
"type": "object",
|
211
|
+
"required": ["command"],
|
212
|
+
"properties": {
|
213
|
+
"command": {
|
214
|
+
"type": "string",
|
215
|
+
"description": "Command name to execute"
|
216
|
+
},
|
217
|
+
"params": {
|
218
|
+
"type": "object",
|
219
|
+
"description": "Command parameters (specific to command)",
|
220
|
+
"additionalProperties": True
|
221
|
+
}
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
# Add command success response model
|
226
|
+
schema["components"]["schemas"]["CommandSuccessResponse"] = {
|
227
|
+
"type": "object",
|
228
|
+
"required": ["result"],
|
229
|
+
"properties": {
|
230
|
+
"result": {
|
231
|
+
"type": "object",
|
232
|
+
"description": "Command execution result",
|
233
|
+
"additionalProperties": True
|
234
|
+
}
|
235
|
+
}
|
236
|
+
}
|
237
|
+
|
238
|
+
# Add command error response model
|
239
|
+
schema["components"]["schemas"]["CommandErrorResponse"] = {
|
240
|
+
"type": "object",
|
241
|
+
"required": ["error"],
|
242
|
+
"properties": {
|
243
|
+
"error": {
|
244
|
+
"type": "object",
|
245
|
+
"required": ["code", "message"],
|
246
|
+
"properties": {
|
247
|
+
"code": {
|
248
|
+
"type": "integer",
|
249
|
+
"description": "Error code"
|
250
|
+
},
|
251
|
+
"message": {
|
252
|
+
"type": "string",
|
253
|
+
"description": "Error message"
|
254
|
+
},
|
255
|
+
"data": {
|
256
|
+
"type": "object",
|
257
|
+
"description": "Additional error data",
|
258
|
+
"additionalProperties": True
|
259
|
+
}
|
260
|
+
}
|
261
|
+
}
|
262
|
+
}
|
263
|
+
}
|
264
|
+
|
265
|
+
def _add_cmd_examples(self, schema: Dict[str, Any]) -> None:
|
266
|
+
"""
|
267
|
+
Add examples for /cmd endpoint to OpenAPI schema.
|
268
|
+
|
269
|
+
Args:
|
270
|
+
schema: OpenAPI schema to update
|
271
|
+
"""
|
272
|
+
# Create examples section if it doesn't exist
|
273
|
+
if "examples" not in schema["components"]:
|
274
|
+
schema["components"]["examples"] = {}
|
275
|
+
|
276
|
+
# Add help command example request
|
277
|
+
schema["components"]["examples"]["help_request"] = {
|
278
|
+
"summary": "Get list of commands",
|
279
|
+
"value": {
|
280
|
+
"command": "help"
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
284
|
+
# Add help command example response
|
285
|
+
schema["components"]["examples"]["help_response"] = {
|
286
|
+
"summary": "Response with list of commands",
|
287
|
+
"value": {
|
288
|
+
"result": {
|
289
|
+
"commands": {
|
290
|
+
"help": {
|
291
|
+
"description": "Get help information about available commands"
|
292
|
+
},
|
293
|
+
"health": {
|
294
|
+
"description": "Check server health"
|
295
|
+
}
|
296
|
+
}
|
297
|
+
}
|
298
|
+
}
|
299
|
+
}
|
300
|
+
|
301
|
+
# Add specific command help example request
|
302
|
+
schema["components"]["examples"]["help_specific_request"] = {
|
303
|
+
"summary": "Get information about specific command",
|
304
|
+
"value": {
|
305
|
+
"command": "help",
|
306
|
+
"params": {
|
307
|
+
"cmdname": "health"
|
308
|
+
}
|
309
|
+
}
|
310
|
+
}
|
311
|
+
|
312
|
+
# Add error example
|
313
|
+
schema["components"]["examples"]["command_error"] = {
|
314
|
+
"summary": "Command not found error",
|
315
|
+
"value": {
|
316
|
+
"error": {
|
317
|
+
"code": -32601,
|
318
|
+
"message": "Command 'unknown_command' not found"
|
319
|
+
}
|
320
|
+
}
|
321
|
+
}
|
322
|
+
|
323
|
+
# Link examples to endpoint
|
324
|
+
schema["paths"]["/cmd"]["post"]["requestBody"]["content"]["application/json"]["examples"] = {
|
325
|
+
"help": {"$ref": "#/components/examples/help_request"},
|
326
|
+
"help_specific": {"$ref": "#/components/examples/help_specific_request"}
|
327
|
+
}
|
328
|
+
|
329
|
+
schema["paths"]["/cmd"]["post"]["responses"]["200"]["content"]["application/json"]["examples"] = {
|
330
|
+
"help": {"$ref": "#/components/examples/help_response"},
|
331
|
+
"error": {"$ref": "#/components/examples/command_error"}
|
332
|
+
}
|
333
|
+
|
334
|
+
def _validate_required_paths(self, schema: Dict[str, Any]) -> None:
|
335
|
+
"""
|
336
|
+
Validate that required paths exist in schema.
|
337
|
+
|
338
|
+
Args:
|
339
|
+
schema: OpenAPI schema to validate
|
340
|
+
|
341
|
+
Raises:
|
342
|
+
SchemaValidationError: If required paths are missing
|
343
|
+
"""
|
344
|
+
required_paths = ['/cmd', '/api/commands']
|
345
|
+
|
346
|
+
for path in required_paths:
|
347
|
+
if path not in schema['paths']:
|
348
|
+
raise SchemaValidationError(f"Missing required path: {path}")
|
349
|
+
|
350
|
+
def generate(self) -> Dict[str, Any]:
|
351
|
+
"""
|
352
|
+
Generate complete OpenAPI schema
|
353
|
+
|
354
|
+
Returns:
|
355
|
+
OpenAPI schema as dictionary
|
356
|
+
"""
|
357
|
+
schema = self._base_schema.copy()
|
358
|
+
|
359
|
+
# Add commands to schema
|
360
|
+
self._add_commands_to_schema(schema)
|
361
|
+
|
362
|
+
# Add /cmd endpoint regardless of commands
|
363
|
+
self._add_cmd_endpoint(schema)
|
364
|
+
self._add_cmd_models(schema)
|
365
|
+
self._add_cmd_examples(schema)
|
366
|
+
|
367
|
+
# Validate required paths
|
368
|
+
self._validate_required_paths(schema)
|
369
|
+
|
370
|
+
self.validate_schema(schema)
|
371
|
+
return schema
|
372
|
+
|
373
|
+
def validate_schema(self, schema: Dict[str, Any]):
|
374
|
+
"""
|
375
|
+
Validate generated schema
|
376
|
+
|
377
|
+
Args:
|
378
|
+
schema: OpenAPI schema to validate
|
379
|
+
|
380
|
+
Raises:
|
381
|
+
SchemaValidationError: If schema is invalid
|
382
|
+
"""
|
383
|
+
try:
|
384
|
+
# Check that required components exist
|
385
|
+
required_components = ['CommandRequest', 'CommandSuccessResponse', 'CommandErrorResponse']
|
386
|
+
for component in required_components:
|
387
|
+
if component not in schema['components']['schemas']:
|
388
|
+
raise SchemaValidationError(f"Missing required component: {component}")
|
389
|
+
|
390
|
+
# Validate that all paths return 200 status
|
391
|
+
for path in schema['paths'].values():
|
392
|
+
for method in path.values():
|
393
|
+
if '200' not in method['responses']:
|
394
|
+
raise SchemaValidationError("All endpoints must return 200 status code")
|
395
|
+
|
396
|
+
response = method['responses']['200']
|
397
|
+
if 'application/json' not in response['content']:
|
398
|
+
raise SchemaValidationError("All responses must be application/json")
|
399
|
+
except Exception as e:
|
400
|
+
raise SchemaValidationError(f"Schema validation failed: {str(e)}")
|
401
|
+
|
402
|
+
# Here we would normally use a library like openapi-spec-validator
|
403
|
+
# to validate the schema against the OpenAPI 3.0 specification
|
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
|
+
}
|