mcp-proxy-adapter 6.3.4__py3-none-any.whl → 6.3.5__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.
- mcp_proxy_adapter/__init__.py +9 -5
- mcp_proxy_adapter/__main__.py +1 -1
- mcp_proxy_adapter/api/app.py +227 -176
- mcp_proxy_adapter/api/handlers.py +68 -60
- mcp_proxy_adapter/api/middleware/__init__.py +7 -5
- mcp_proxy_adapter/api/middleware/base.py +19 -16
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +44 -34
- mcp_proxy_adapter/api/middleware/error_handling.py +57 -67
- mcp_proxy_adapter/api/middleware/factory.py +50 -52
- mcp_proxy_adapter/api/middleware/logging.py +46 -30
- mcp_proxy_adapter/api/middleware/performance.py +19 -16
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +80 -50
- mcp_proxy_adapter/api/middleware/transport_middleware.py +26 -24
- mcp_proxy_adapter/api/middleware/unified_security.py +70 -51
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +43 -34
- mcp_proxy_adapter/api/schemas.py +69 -43
- mcp_proxy_adapter/api/tool_integration.py +83 -63
- mcp_proxy_adapter/api/tools.py +60 -50
- mcp_proxy_adapter/commands/__init__.py +15 -6
- mcp_proxy_adapter/commands/auth_validation_command.py +107 -110
- mcp_proxy_adapter/commands/base.py +108 -112
- mcp_proxy_adapter/commands/builtin_commands.py +28 -18
- mcp_proxy_adapter/commands/catalog_manager.py +394 -265
- mcp_proxy_adapter/commands/cert_monitor_command.py +222 -204
- mcp_proxy_adapter/commands/certificate_management_command.py +210 -213
- mcp_proxy_adapter/commands/command_registry.py +275 -226
- mcp_proxy_adapter/commands/config_command.py +48 -33
- mcp_proxy_adapter/commands/dependency_container.py +22 -23
- mcp_proxy_adapter/commands/dependency_manager.py +65 -56
- mcp_proxy_adapter/commands/echo_command.py +15 -15
- mcp_proxy_adapter/commands/health_command.py +31 -29
- mcp_proxy_adapter/commands/help_command.py +97 -61
- mcp_proxy_adapter/commands/hooks.py +65 -49
- mcp_proxy_adapter/commands/key_management_command.py +148 -147
- mcp_proxy_adapter/commands/load_command.py +58 -40
- mcp_proxy_adapter/commands/plugins_command.py +80 -54
- mcp_proxy_adapter/commands/protocol_management_command.py +60 -48
- mcp_proxy_adapter/commands/proxy_registration_command.py +107 -115
- mcp_proxy_adapter/commands/reload_command.py +43 -37
- mcp_proxy_adapter/commands/result.py +26 -33
- mcp_proxy_adapter/commands/role_test_command.py +26 -26
- mcp_proxy_adapter/commands/roles_management_command.py +176 -173
- mcp_proxy_adapter/commands/security_command.py +134 -122
- mcp_proxy_adapter/commands/settings_command.py +47 -56
- mcp_proxy_adapter/commands/ssl_setup_command.py +109 -129
- mcp_proxy_adapter/commands/token_management_command.py +129 -158
- mcp_proxy_adapter/commands/transport_management_command.py +41 -36
- mcp_proxy_adapter/commands/unload_command.py +42 -37
- mcp_proxy_adapter/config.py +36 -35
- mcp_proxy_adapter/core/__init__.py +19 -21
- mcp_proxy_adapter/core/app_factory.py +30 -9
- mcp_proxy_adapter/core/app_runner.py +81 -64
- mcp_proxy_adapter/core/auth_validator.py +176 -182
- mcp_proxy_adapter/core/certificate_utils.py +469 -426
- mcp_proxy_adapter/core/client.py +155 -126
- mcp_proxy_adapter/core/client_manager.py +60 -54
- mcp_proxy_adapter/core/client_security.py +108 -88
- mcp_proxy_adapter/core/config_converter.py +176 -143
- mcp_proxy_adapter/core/config_validator.py +12 -4
- mcp_proxy_adapter/core/crl_utils.py +21 -7
- mcp_proxy_adapter/core/errors.py +64 -20
- mcp_proxy_adapter/core/logging.py +34 -29
- mcp_proxy_adapter/core/mtls_asgi.py +29 -25
- mcp_proxy_adapter/core/mtls_asgi_app.py +66 -54
- mcp_proxy_adapter/core/protocol_manager.py +154 -104
- mcp_proxy_adapter/core/proxy_client.py +202 -144
- mcp_proxy_adapter/core/proxy_registration.py +7 -3
- mcp_proxy_adapter/core/role_utils.py +139 -125
- mcp_proxy_adapter/core/security_adapter.py +88 -77
- mcp_proxy_adapter/core/security_factory.py +50 -44
- mcp_proxy_adapter/core/security_integration.py +72 -24
- mcp_proxy_adapter/core/server_adapter.py +68 -64
- mcp_proxy_adapter/core/server_engine.py +71 -53
- mcp_proxy_adapter/core/settings.py +68 -58
- mcp_proxy_adapter/core/ssl_utils.py +69 -56
- mcp_proxy_adapter/core/transport_manager.py +72 -60
- mcp_proxy_adapter/core/unified_config_adapter.py +201 -150
- mcp_proxy_adapter/core/utils.py +4 -2
- mcp_proxy_adapter/custom_openapi.py +107 -99
- mcp_proxy_adapter/examples/basic_framework/main.py +9 -2
- mcp_proxy_adapter/examples/commands/__init__.py +1 -1
- mcp_proxy_adapter/examples/create_certificates_simple.py +182 -71
- mcp_proxy_adapter/examples/debug_request_state.py +38 -19
- mcp_proxy_adapter/examples/debug_role_chain.py +53 -20
- mcp_proxy_adapter/examples/demo_client.py +48 -36
- mcp_proxy_adapter/examples/examples/basic_framework/main.py +9 -2
- mcp_proxy_adapter/examples/examples/full_application/__init__.py +1 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +22 -10
- mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +24 -17
- mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +16 -3
- mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +13 -3
- mcp_proxy_adapter/examples/examples/full_application/main.py +27 -2
- mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +48 -14
- mcp_proxy_adapter/examples/full_application/__init__.py +1 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +22 -10
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +24 -17
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +16 -3
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +13 -3
- mcp_proxy_adapter/examples/full_application/main.py +27 -2
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +48 -14
- mcp_proxy_adapter/examples/generate_all_certificates.py +198 -73
- mcp_proxy_adapter/examples/generate_certificates.py +31 -16
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +220 -74
- mcp_proxy_adapter/examples/generate_test_configs.py +68 -91
- mcp_proxy_adapter/examples/proxy_registration_example.py +76 -75
- mcp_proxy_adapter/examples/run_example.py +23 -5
- mcp_proxy_adapter/examples/run_full_test_suite.py +109 -71
- mcp_proxy_adapter/examples/run_proxy_server.py +22 -9
- mcp_proxy_adapter/examples/run_security_tests.py +103 -41
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +72 -36
- mcp_proxy_adapter/examples/scripts/config_generator.py +288 -187
- mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +185 -72
- mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +220 -74
- mcp_proxy_adapter/examples/security_test_client.py +196 -127
- mcp_proxy_adapter/examples/setup_test_environment.py +17 -29
- mcp_proxy_adapter/examples/test_config.py +19 -4
- mcp_proxy_adapter/examples/test_config_generator.py +23 -7
- mcp_proxy_adapter/examples/test_examples.py +84 -56
- mcp_proxy_adapter/examples/universal_client.py +119 -62
- mcp_proxy_adapter/openapi.py +108 -115
- mcp_proxy_adapter/utils/config_generator.py +429 -274
- mcp_proxy_adapter/version.py +1 -2
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/METADATA +1 -1
- mcp_proxy_adapter-6.3.5.dist-info/RECORD +143 -0
- mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/licenses/LICENSE +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.5.dist-info}/top_level.txt +0 -0
mcp_proxy_adapter/openapi.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
"""
|
2
2
|
OpenAPI schema generator for MCP Microservice
|
3
3
|
"""
|
4
|
+
|
4
5
|
from typing import Dict, Any, List, Optional
|
5
6
|
from dataclasses import dataclass
|
6
7
|
import inspect
|
@@ -15,6 +16,7 @@ from .core.errors import ValidationError as SchemaValidationError
|
|
15
16
|
@dataclass
|
16
17
|
class TypeInfo:
|
17
18
|
"""Information about a type for OpenAPI schema"""
|
19
|
+
|
18
20
|
openapi_type: str
|
19
21
|
format: Optional[str] = None
|
20
22
|
items: Optional[Dict[str, Any]] = None
|
@@ -24,7 +26,7 @@ class TypeInfo:
|
|
24
26
|
|
25
27
|
class OpenApiGenerator:
|
26
28
|
"""Generates OpenAPI schema for MCP Microservice"""
|
27
|
-
|
29
|
+
|
28
30
|
PYTHON_TO_OPENAPI_TYPES = {
|
29
31
|
str: TypeInfo("string"),
|
30
32
|
int: TypeInfo("integer", "int64"),
|
@@ -38,7 +40,7 @@ class OpenApiGenerator:
|
|
38
40
|
def __init__(self, registry: CommandRegistry):
|
39
41
|
"""
|
40
42
|
Initialize generator
|
41
|
-
|
43
|
+
|
42
44
|
Args:
|
43
45
|
registry: Command registry instance
|
44
46
|
"""
|
@@ -54,10 +56,10 @@ class OpenApiGenerator:
|
|
54
56
|
def _get_type_info(self, python_type: Any) -> TypeInfo:
|
55
57
|
"""
|
56
58
|
Get OpenAPI type info for Python type
|
57
|
-
|
59
|
+
|
58
60
|
Args:
|
59
61
|
python_type: Python type annotation
|
60
|
-
|
62
|
+
|
61
63
|
Returns:
|
62
64
|
TypeInfo object with OpenAPI type information
|
63
65
|
"""
|
@@ -70,7 +72,7 @@ class OpenApiGenerator:
|
|
70
72
|
if origin is list:
|
71
73
|
item_type = self._get_type_info(python_type.__args__[0])
|
72
74
|
return TypeInfo("array", items={"type": item_type.openapi_type})
|
73
|
-
|
75
|
+
|
74
76
|
if origin is dict:
|
75
77
|
return TypeInfo("object", additionalProperties=True)
|
76
78
|
|
@@ -82,89 +84,80 @@ class OpenApiGenerator:
|
|
82
84
|
if inspect.isclass(python_type):
|
83
85
|
properties = {}
|
84
86
|
required = []
|
85
|
-
|
87
|
+
|
86
88
|
for name, field in inspect.get_annotations(python_type).items():
|
87
89
|
field_info = self._get_type_info(field)
|
88
|
-
properties[name] = {
|
89
|
-
"type": field_info.openapi_type
|
90
|
-
}
|
90
|
+
properties[name] = {"type": field_info.openapi_type}
|
91
91
|
if field_info.format:
|
92
92
|
properties[name]["format"] = field_info.format
|
93
93
|
required.append(name)
|
94
|
-
|
95
|
-
return TypeInfo(
|
96
|
-
"object",
|
97
|
-
properties=properties,
|
98
|
-
required=required
|
99
|
-
)
|
94
|
+
|
95
|
+
return TypeInfo("object", properties=properties, required=required)
|
100
96
|
|
101
97
|
raise ValueError(f"Unsupported type: {python_type}")
|
102
98
|
|
103
99
|
def _add_command_params(self, schema: Dict[str, Any], command: Command):
|
104
100
|
"""
|
105
101
|
Add command parameters to schema
|
106
|
-
|
102
|
+
|
107
103
|
Args:
|
108
104
|
schema: OpenAPI schema
|
109
105
|
command: Command instance
|
110
106
|
"""
|
111
107
|
params = {}
|
112
108
|
required = []
|
113
|
-
|
109
|
+
|
114
110
|
# Get parameters from function signature
|
115
111
|
sig = inspect.signature(command.func)
|
116
112
|
for name, param in sig.parameters.items():
|
117
113
|
param_schema = {}
|
118
|
-
|
114
|
+
|
119
115
|
# Get type info
|
120
116
|
type_info = self._get_type_info(param.annotation)
|
121
117
|
param_schema["type"] = type_info.openapi_type
|
122
|
-
|
118
|
+
|
123
119
|
if type_info.format:
|
124
120
|
param_schema["format"] = type_info.format
|
125
|
-
|
121
|
+
|
126
122
|
if type_info.items:
|
127
123
|
param_schema["items"] = type_info.items
|
128
|
-
|
124
|
+
|
129
125
|
# Get description from docstring
|
130
126
|
if command.doc and command.doc.params:
|
131
127
|
for doc_param in command.doc.params:
|
132
128
|
if doc_param.arg_name == name:
|
133
129
|
param_schema["description"] = doc_param.description
|
134
130
|
break
|
135
|
-
|
131
|
+
|
136
132
|
# Handle default value
|
137
133
|
if param.default is not param.empty:
|
138
134
|
param_schema["default"] = param.default
|
139
135
|
else:
|
140
136
|
required.append(name)
|
141
|
-
|
137
|
+
|
142
138
|
params[name] = param_schema
|
143
|
-
|
139
|
+
|
144
140
|
# Add to schema
|
145
|
-
method_schema = {
|
146
|
-
"type": "object",
|
147
|
-
"properties": params
|
148
|
-
}
|
141
|
+
method_schema = {"type": "object", "properties": params}
|
149
142
|
if required:
|
150
143
|
method_schema["required"] = required
|
151
|
-
|
144
|
+
|
152
145
|
schema["components"]["schemas"][f"Params{command.name}"] = method_schema
|
153
146
|
|
154
147
|
def _add_commands_to_schema(self, schema: Dict[str, Any]):
|
155
148
|
"""
|
156
149
|
Add all commands to schema
|
157
|
-
|
150
|
+
|
158
151
|
Args:
|
159
152
|
schema: OpenAPI schema
|
160
153
|
"""
|
161
154
|
for command in self.registry.get_commands():
|
162
155
|
self._add_command_params(schema, command)
|
163
|
-
|
156
|
+
|
164
157
|
def _add_cmd_endpoint(self, schema: Dict[str, Any]) -> None:
|
165
158
|
"""
|
166
159
|
Add /cmd endpoint to OpenAPI schema.
|
167
|
-
|
160
|
+
|
168
161
|
Args:
|
169
162
|
schema: OpenAPI schema to update
|
170
163
|
"""
|
@@ -179,7 +172,7 @@ class OpenApiGenerator:
|
|
179
172
|
"schema": {"$ref": "#/components/schemas/CommandRequest"}
|
180
173
|
}
|
181
174
|
},
|
182
|
-
"required": True
|
175
|
+
"required": True,
|
183
176
|
},
|
184
177
|
"responses": {
|
185
178
|
"200": {
|
@@ -188,21 +181,25 @@ class OpenApiGenerator:
|
|
188
181
|
"application/json": {
|
189
182
|
"schema": {
|
190
183
|
"oneOf": [
|
191
|
-
{
|
192
|
-
|
184
|
+
{
|
185
|
+
"$ref": "#/components/schemas/CommandSuccessResponse"
|
186
|
+
},
|
187
|
+
{
|
188
|
+
"$ref": "#/components/schemas/CommandErrorResponse"
|
189
|
+
},
|
193
190
|
]
|
194
191
|
}
|
195
192
|
}
|
196
|
-
}
|
193
|
+
},
|
197
194
|
}
|
198
|
-
}
|
195
|
+
},
|
199
196
|
}
|
200
197
|
}
|
201
|
-
|
198
|
+
|
202
199
|
def _add_cmd_models(self, schema: Dict[str, Any]) -> None:
|
203
200
|
"""
|
204
201
|
Add models for /cmd endpoint to OpenAPI schema.
|
205
|
-
|
202
|
+
|
206
203
|
Args:
|
207
204
|
schema: OpenAPI schema to update
|
208
205
|
"""
|
@@ -211,18 +208,15 @@ class OpenApiGenerator:
|
|
211
208
|
"type": "object",
|
212
209
|
"required": ["command"],
|
213
210
|
"properties": {
|
214
|
-
"command": {
|
215
|
-
"type": "string",
|
216
|
-
"description": "Command name to execute"
|
217
|
-
},
|
211
|
+
"command": {"type": "string", "description": "Command name to execute"},
|
218
212
|
"params": {
|
219
213
|
"type": "object",
|
220
214
|
"description": "Command parameters (specific to command)",
|
221
|
-
"additionalProperties": True
|
222
|
-
}
|
223
|
-
}
|
215
|
+
"additionalProperties": True,
|
216
|
+
},
|
217
|
+
},
|
224
218
|
}
|
225
|
-
|
219
|
+
|
226
220
|
# Add command success response model
|
227
221
|
schema["components"]["schemas"]["CommandSuccessResponse"] = {
|
228
222
|
"type": "object",
|
@@ -231,11 +225,11 @@ class OpenApiGenerator:
|
|
231
225
|
"result": {
|
232
226
|
"type": "object",
|
233
227
|
"description": "Command execution result",
|
234
|
-
"additionalProperties": True
|
228
|
+
"additionalProperties": True,
|
235
229
|
}
|
236
|
-
}
|
230
|
+
},
|
237
231
|
}
|
238
|
-
|
232
|
+
|
239
233
|
# Add command error response model
|
240
234
|
schema["components"]["schemas"]["CommandErrorResponse"] = {
|
241
235
|
"type": "object",
|
@@ -245,43 +239,35 @@ class OpenApiGenerator:
|
|
245
239
|
"type": "object",
|
246
240
|
"required": ["code", "message"],
|
247
241
|
"properties": {
|
248
|
-
"code": {
|
249
|
-
|
250
|
-
"description": "Error code"
|
251
|
-
},
|
252
|
-
"message": {
|
253
|
-
"type": "string",
|
254
|
-
"description": "Error message"
|
255
|
-
},
|
242
|
+
"code": {"type": "integer", "description": "Error code"},
|
243
|
+
"message": {"type": "string", "description": "Error message"},
|
256
244
|
"data": {
|
257
245
|
"type": "object",
|
258
246
|
"description": "Additional error data",
|
259
|
-
"additionalProperties": True
|
260
|
-
}
|
261
|
-
}
|
247
|
+
"additionalProperties": True,
|
248
|
+
},
|
249
|
+
},
|
262
250
|
}
|
263
|
-
}
|
251
|
+
},
|
264
252
|
}
|
265
|
-
|
253
|
+
|
266
254
|
def _add_cmd_examples(self, schema: Dict[str, Any]) -> None:
|
267
255
|
"""
|
268
256
|
Add examples for /cmd endpoint to OpenAPI schema.
|
269
|
-
|
257
|
+
|
270
258
|
Args:
|
271
259
|
schema: OpenAPI schema to update
|
272
260
|
"""
|
273
261
|
# Create examples section if it doesn't exist
|
274
262
|
if "examples" not in schema["components"]:
|
275
263
|
schema["components"]["examples"] = {}
|
276
|
-
|
264
|
+
|
277
265
|
# Add help command example request
|
278
266
|
schema["components"]["examples"]["help_request"] = {
|
279
267
|
"summary": "Get list of commands",
|
280
|
-
"value": {
|
281
|
-
"command": "help"
|
282
|
-
}
|
268
|
+
"value": {"command": "help"},
|
283
269
|
}
|
284
|
-
|
270
|
+
|
285
271
|
# Add help command example response
|
286
272
|
schema["components"]["examples"]["help_response"] = {
|
287
273
|
"summary": "Response with list of commands",
|
@@ -291,114 +277,121 @@ class OpenApiGenerator:
|
|
291
277
|
"help": {
|
292
278
|
"description": "Get help information about available commands"
|
293
279
|
},
|
294
|
-
"health": {
|
295
|
-
"description": "Check server health"
|
296
|
-
}
|
280
|
+
"health": {"description": "Check server health"},
|
297
281
|
}
|
298
282
|
}
|
299
|
-
}
|
283
|
+
},
|
300
284
|
}
|
301
|
-
|
285
|
+
|
302
286
|
# Add specific command help example request
|
303
287
|
schema["components"]["examples"]["help_specific_request"] = {
|
304
288
|
"summary": "Get information about specific command",
|
305
|
-
"value": {
|
306
|
-
"command": "help",
|
307
|
-
"params": {
|
308
|
-
"cmdname": "health"
|
309
|
-
}
|
310
|
-
}
|
289
|
+
"value": {"command": "help", "params": {"cmdname": "health"}},
|
311
290
|
}
|
312
|
-
|
291
|
+
|
313
292
|
# Add error example
|
314
293
|
schema["components"]["examples"]["command_error"] = {
|
315
294
|
"summary": "Command not found error",
|
316
295
|
"value": {
|
317
296
|
"error": {
|
318
297
|
"code": -32601,
|
319
|
-
"message": "Command 'unknown_command' not found"
|
298
|
+
"message": "Command 'unknown_command' not found",
|
320
299
|
}
|
321
|
-
}
|
300
|
+
},
|
322
301
|
}
|
323
|
-
|
302
|
+
|
324
303
|
# Link examples to endpoint
|
325
|
-
schema["paths"]["/cmd"]["post"]["requestBody"]["content"]["application/json"][
|
304
|
+
schema["paths"]["/cmd"]["post"]["requestBody"]["content"]["application/json"][
|
305
|
+
"examples"
|
306
|
+
] = {
|
326
307
|
"help": {"$ref": "#/components/examples/help_request"},
|
327
|
-
"help_specific": {"$ref": "#/components/examples/help_specific_request"}
|
308
|
+
"help_specific": {"$ref": "#/components/examples/help_specific_request"},
|
328
309
|
}
|
329
|
-
|
330
|
-
schema["paths"]["/cmd"]["post"]["responses"]["200"]["content"][
|
310
|
+
|
311
|
+
schema["paths"]["/cmd"]["post"]["responses"]["200"]["content"][
|
312
|
+
"application/json"
|
313
|
+
]["examples"] = {
|
331
314
|
"help": {"$ref": "#/components/examples/help_response"},
|
332
|
-
"error": {"$ref": "#/components/examples/command_error"}
|
315
|
+
"error": {"$ref": "#/components/examples/command_error"},
|
333
316
|
}
|
334
|
-
|
317
|
+
|
335
318
|
def _validate_required_paths(self, schema: Dict[str, Any]) -> None:
|
336
319
|
"""
|
337
320
|
Validate that required paths exist in schema.
|
338
|
-
|
321
|
+
|
339
322
|
Args:
|
340
323
|
schema: OpenAPI schema to validate
|
341
|
-
|
324
|
+
|
342
325
|
Raises:
|
343
326
|
SchemaValidationError: If required paths are missing
|
344
327
|
"""
|
345
|
-
required_paths = [
|
346
|
-
|
328
|
+
required_paths = ["/cmd", "/api/commands"]
|
329
|
+
|
347
330
|
for path in required_paths:
|
348
|
-
if path not in schema[
|
331
|
+
if path not in schema["paths"]:
|
349
332
|
raise SchemaValidationError(f"Missing required path: {path}")
|
350
333
|
|
351
334
|
def generate(self) -> Dict[str, Any]:
|
352
335
|
"""
|
353
336
|
Generate complete OpenAPI schema
|
354
|
-
|
337
|
+
|
355
338
|
Returns:
|
356
339
|
OpenAPI schema as dictionary
|
357
340
|
"""
|
358
341
|
schema = self._base_schema.copy()
|
359
|
-
|
342
|
+
|
360
343
|
# Add commands to schema
|
361
344
|
self._add_commands_to_schema(schema)
|
362
|
-
|
345
|
+
|
363
346
|
# Add /cmd endpoint regardless of commands
|
364
347
|
self._add_cmd_endpoint(schema)
|
365
348
|
self._add_cmd_models(schema)
|
366
349
|
self._add_cmd_examples(schema)
|
367
|
-
|
350
|
+
|
368
351
|
# Validate required paths
|
369
352
|
self._validate_required_paths(schema)
|
370
|
-
|
353
|
+
|
371
354
|
self.validate_schema(schema)
|
372
355
|
return schema
|
373
356
|
|
374
357
|
def validate_schema(self, schema: Dict[str, Any]):
|
375
358
|
"""
|
376
359
|
Validate generated schema
|
377
|
-
|
360
|
+
|
378
361
|
Args:
|
379
362
|
schema: OpenAPI schema to validate
|
380
|
-
|
363
|
+
|
381
364
|
Raises:
|
382
365
|
SchemaValidationError: If schema is invalid
|
383
366
|
"""
|
384
367
|
try:
|
385
368
|
# Check that required components exist
|
386
|
-
required_components = [
|
369
|
+
required_components = [
|
370
|
+
"CommandRequest",
|
371
|
+
"CommandSuccessResponse",
|
372
|
+
"CommandErrorResponse",
|
373
|
+
]
|
387
374
|
for component in required_components:
|
388
|
-
if component not in schema[
|
389
|
-
raise SchemaValidationError(
|
390
|
-
|
375
|
+
if component not in schema["components"]["schemas"]:
|
376
|
+
raise SchemaValidationError(
|
377
|
+
f"Missing required component: {component}"
|
378
|
+
)
|
379
|
+
|
391
380
|
# Validate that all paths return 200 status
|
392
|
-
for path in schema[
|
381
|
+
for path in schema["paths"].values():
|
393
382
|
for method in path.values():
|
394
|
-
if
|
395
|
-
raise SchemaValidationError(
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
383
|
+
if "200" not in method["responses"]:
|
384
|
+
raise SchemaValidationError(
|
385
|
+
"All endpoints must return 200 status code"
|
386
|
+
)
|
387
|
+
|
388
|
+
response = method["responses"]["200"]
|
389
|
+
if "application/json" not in response["content"]:
|
390
|
+
raise SchemaValidationError(
|
391
|
+
"All responses must be application/json"
|
392
|
+
)
|
400
393
|
except Exception as e:
|
401
394
|
raise SchemaValidationError(f"Schema validation failed: {str(e)}")
|
402
|
-
|
395
|
+
|
403
396
|
# Here we would normally use a library like openapi-spec-validator
|
404
|
-
# to validate the schema against the OpenAPI 3.0 specification
|
397
|
+
# to validate the schema against the OpenAPI 3.0 specification
|