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.
Files changed (135) hide show
  1. examples/__init__.py +19 -0
  2. examples/anti_patterns/README.md +51 -0
  3. examples/anti_patterns/__init__.py +9 -0
  4. examples/anti_patterns/bad_design/README.md +72 -0
  5. examples/anti_patterns/bad_design/global_state.py +170 -0
  6. examples/anti_patterns/bad_design/monolithic_command.py +272 -0
  7. examples/basic_example/README.md +245 -0
  8. examples/basic_example/__init__.py +8 -0
  9. examples/basic_example/commands/__init__.py +5 -0
  10. examples/basic_example/commands/echo_command.py +95 -0
  11. examples/basic_example/commands/math_command.py +151 -0
  12. examples/basic_example/commands/time_command.py +152 -0
  13. examples/basic_example/config.json +25 -0
  14. examples/basic_example/docs/EN/README.md +177 -0
  15. examples/basic_example/docs/RU/README.md +177 -0
  16. examples/basic_example/server.py +151 -0
  17. examples/basic_example/tests/conftest.py +243 -0
  18. examples/commands/echo_command.py +52 -0
  19. examples/commands/echo_result.py +65 -0
  20. examples/commands/get_date_command.py +98 -0
  21. examples/commands/new_uuid4_command.py +91 -0
  22. examples/complete_example/Dockerfile +24 -0
  23. examples/complete_example/README.md +92 -0
  24. examples/complete_example/__init__.py +8 -0
  25. examples/complete_example/commands/__init__.py +5 -0
  26. examples/complete_example/commands/system_command.py +328 -0
  27. examples/complete_example/config.json +41 -0
  28. examples/complete_example/configs/config.dev.yaml +40 -0
  29. examples/complete_example/configs/config.docker.yaml +40 -0
  30. examples/complete_example/docker-compose.yml +35 -0
  31. examples/complete_example/requirements.txt +20 -0
  32. examples/complete_example/server.py +139 -0
  33. examples/minimal_example/README.md +65 -0
  34. examples/minimal_example/__init__.py +8 -0
  35. examples/minimal_example/config.json +14 -0
  36. examples/minimal_example/main.py +136 -0
  37. examples/minimal_example/simple_server.py +163 -0
  38. examples/minimal_example/tests/conftest.py +171 -0
  39. examples/minimal_example/tests/test_hello_command.py +111 -0
  40. examples/minimal_example/tests/test_integration.py +181 -0
  41. examples/server.py +69 -0
  42. examples/simple_server.py +128 -0
  43. examples/test_server.py +134 -0
  44. examples/tool_description_example.py +82 -0
  45. mcp_proxy_adapter/__init__.py +33 -1
  46. mcp_proxy_adapter/api/__init__.py +0 -0
  47. mcp_proxy_adapter/api/app.py +391 -0
  48. mcp_proxy_adapter/api/handlers.py +229 -0
  49. mcp_proxy_adapter/api/middleware/__init__.py +49 -0
  50. mcp_proxy_adapter/api/middleware/auth.py +146 -0
  51. mcp_proxy_adapter/api/middleware/base.py +79 -0
  52. mcp_proxy_adapter/api/middleware/error_handling.py +198 -0
  53. mcp_proxy_adapter/api/middleware/logging.py +96 -0
  54. mcp_proxy_adapter/api/middleware/performance.py +83 -0
  55. mcp_proxy_adapter/api/middleware/rate_limit.py +152 -0
  56. mcp_proxy_adapter/api/schemas.py +305 -0
  57. mcp_proxy_adapter/api/tool_integration.py +223 -0
  58. mcp_proxy_adapter/api/tools.py +198 -0
  59. mcp_proxy_adapter/commands/__init__.py +19 -0
  60. mcp_proxy_adapter/commands/base.py +301 -0
  61. mcp_proxy_adapter/commands/command_registry.py +231 -0
  62. mcp_proxy_adapter/commands/config_command.py +113 -0
  63. mcp_proxy_adapter/commands/health_command.py +136 -0
  64. mcp_proxy_adapter/commands/help_command.py +193 -0
  65. mcp_proxy_adapter/commands/result.py +215 -0
  66. mcp_proxy_adapter/config.py +195 -0
  67. mcp_proxy_adapter/core/__init__.py +0 -0
  68. mcp_proxy_adapter/core/errors.py +173 -0
  69. mcp_proxy_adapter/core/logging.py +205 -0
  70. mcp_proxy_adapter/core/utils.py +138 -0
  71. mcp_proxy_adapter/custom_openapi.py +125 -0
  72. mcp_proxy_adapter/openapi.py +403 -0
  73. mcp_proxy_adapter/py.typed +0 -0
  74. mcp_proxy_adapter/schemas/base_schema.json +114 -0
  75. mcp_proxy_adapter/schemas/openapi_schema.json +314 -0
  76. mcp_proxy_adapter/tests/__init__.py +0 -0
  77. mcp_proxy_adapter/tests/api/__init__.py +3 -0
  78. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +115 -0
  79. mcp_proxy_adapter/tests/api/test_middleware.py +336 -0
  80. mcp_proxy_adapter/tests/commands/__init__.py +3 -0
  81. mcp_proxy_adapter/tests/commands/test_config_command.py +211 -0
  82. mcp_proxy_adapter/tests/commands/test_echo_command.py +127 -0
  83. mcp_proxy_adapter/tests/commands/test_help_command.py +133 -0
  84. mcp_proxy_adapter/tests/conftest.py +131 -0
  85. mcp_proxy_adapter/tests/functional/__init__.py +3 -0
  86. mcp_proxy_adapter/tests/functional/test_api.py +235 -0
  87. mcp_proxy_adapter/tests/integration/__init__.py +3 -0
  88. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +130 -0
  89. mcp_proxy_adapter/tests/integration/test_integration.py +255 -0
  90. mcp_proxy_adapter/tests/performance/__init__.py +3 -0
  91. mcp_proxy_adapter/tests/performance/test_performance.py +189 -0
  92. mcp_proxy_adapter/tests/stubs/__init__.py +10 -0
  93. mcp_proxy_adapter/tests/stubs/echo_command.py +104 -0
  94. mcp_proxy_adapter/tests/test_api_endpoints.py +271 -0
  95. mcp_proxy_adapter/tests/test_api_handlers.py +289 -0
  96. mcp_proxy_adapter/tests/test_base_command.py +123 -0
  97. mcp_proxy_adapter/tests/test_batch_requests.py +117 -0
  98. mcp_proxy_adapter/tests/test_command_registry.py +245 -0
  99. mcp_proxy_adapter/tests/test_config.py +127 -0
  100. mcp_proxy_adapter/tests/test_utils.py +65 -0
  101. mcp_proxy_adapter/tests/unit/__init__.py +3 -0
  102. mcp_proxy_adapter/tests/unit/test_base_command.py +130 -0
  103. mcp_proxy_adapter/tests/unit/test_config.py +217 -0
  104. mcp_proxy_adapter/version.py +3 -0
  105. mcp_proxy_adapter-3.0.1.dist-info/METADATA +200 -0
  106. mcp_proxy_adapter-3.0.1.dist-info/RECORD +109 -0
  107. {mcp_proxy_adapter-2.1.17.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/top_level.txt +1 -0
  108. mcp_proxy_adapter/adapter.py +0 -697
  109. mcp_proxy_adapter/analyzers/__init__.py +0 -1
  110. mcp_proxy_adapter/analyzers/docstring_analyzer.py +0 -199
  111. mcp_proxy_adapter/analyzers/type_analyzer.py +0 -151
  112. mcp_proxy_adapter/dispatchers/__init__.py +0 -1
  113. mcp_proxy_adapter/dispatchers/base_dispatcher.py +0 -85
  114. mcp_proxy_adapter/dispatchers/json_rpc_dispatcher.py +0 -262
  115. mcp_proxy_adapter/examples/analyze_config.py +0 -141
  116. mcp_proxy_adapter/examples/basic_integration.py +0 -155
  117. mcp_proxy_adapter/examples/docstring_and_schema_example.py +0 -69
  118. mcp_proxy_adapter/examples/extension_example.py +0 -72
  119. mcp_proxy_adapter/examples/help_best_practices.py +0 -67
  120. mcp_proxy_adapter/examples/help_usage.py +0 -64
  121. mcp_proxy_adapter/examples/mcp_proxy_client.py +0 -131
  122. mcp_proxy_adapter/examples/openapi_server.py +0 -383
  123. mcp_proxy_adapter/examples/project_structure_example.py +0 -47
  124. mcp_proxy_adapter/examples/testing_example.py +0 -64
  125. mcp_proxy_adapter/models.py +0 -47
  126. mcp_proxy_adapter/registry.py +0 -439
  127. mcp_proxy_adapter/schema.py +0 -257
  128. mcp_proxy_adapter/testing_utils.py +0 -112
  129. mcp_proxy_adapter/validators/__init__.py +0 -1
  130. mcp_proxy_adapter/validators/docstring_validator.py +0 -75
  131. mcp_proxy_adapter/validators/metadata_validator.py +0 -76
  132. mcp_proxy_adapter-2.1.17.dist-info/METADATA +0 -376
  133. mcp_proxy_adapter-2.1.17.dist-info/RECORD +0 -30
  134. {mcp_proxy_adapter-2.1.17.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/WHEEL +0 -0
  135. {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
+ }