mcp-proxy-adapter 6.3.4__py3-none-any.whl → 6.3.6__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 (131) hide show
  1. mcp_proxy_adapter/__init__.py +9 -5
  2. mcp_proxy_adapter/__main__.py +1 -1
  3. mcp_proxy_adapter/api/app.py +227 -176
  4. mcp_proxy_adapter/api/handlers.py +68 -60
  5. mcp_proxy_adapter/api/middleware/__init__.py +7 -5
  6. mcp_proxy_adapter/api/middleware/base.py +19 -16
  7. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +44 -34
  8. mcp_proxy_adapter/api/middleware/error_handling.py +57 -67
  9. mcp_proxy_adapter/api/middleware/factory.py +50 -52
  10. mcp_proxy_adapter/api/middleware/logging.py +46 -30
  11. mcp_proxy_adapter/api/middleware/performance.py +19 -16
  12. mcp_proxy_adapter/api/middleware/protocol_middleware.py +80 -50
  13. mcp_proxy_adapter/api/middleware/transport_middleware.py +26 -24
  14. mcp_proxy_adapter/api/middleware/unified_security.py +70 -51
  15. mcp_proxy_adapter/api/middleware/user_info_middleware.py +43 -34
  16. mcp_proxy_adapter/api/schemas.py +69 -43
  17. mcp_proxy_adapter/api/tool_integration.py +83 -63
  18. mcp_proxy_adapter/api/tools.py +60 -50
  19. mcp_proxy_adapter/commands/__init__.py +15 -6
  20. mcp_proxy_adapter/commands/auth_validation_command.py +107 -110
  21. mcp_proxy_adapter/commands/base.py +108 -112
  22. mcp_proxy_adapter/commands/builtin_commands.py +28 -18
  23. mcp_proxy_adapter/commands/catalog_manager.py +394 -265
  24. mcp_proxy_adapter/commands/cert_monitor_command.py +222 -204
  25. mcp_proxy_adapter/commands/certificate_management_command.py +210 -213
  26. mcp_proxy_adapter/commands/command_registry.py +275 -226
  27. mcp_proxy_adapter/commands/config_command.py +48 -33
  28. mcp_proxy_adapter/commands/dependency_container.py +22 -23
  29. mcp_proxy_adapter/commands/dependency_manager.py +65 -56
  30. mcp_proxy_adapter/commands/echo_command.py +15 -15
  31. mcp_proxy_adapter/commands/health_command.py +31 -29
  32. mcp_proxy_adapter/commands/help_command.py +97 -61
  33. mcp_proxy_adapter/commands/hooks.py +65 -49
  34. mcp_proxy_adapter/commands/key_management_command.py +148 -147
  35. mcp_proxy_adapter/commands/load_command.py +58 -40
  36. mcp_proxy_adapter/commands/plugins_command.py +80 -54
  37. mcp_proxy_adapter/commands/protocol_management_command.py +60 -48
  38. mcp_proxy_adapter/commands/proxy_registration_command.py +107 -115
  39. mcp_proxy_adapter/commands/reload_command.py +43 -37
  40. mcp_proxy_adapter/commands/result.py +26 -33
  41. mcp_proxy_adapter/commands/role_test_command.py +26 -26
  42. mcp_proxy_adapter/commands/roles_management_command.py +176 -173
  43. mcp_proxy_adapter/commands/security_command.py +134 -122
  44. mcp_proxy_adapter/commands/settings_command.py +47 -56
  45. mcp_proxy_adapter/commands/ssl_setup_command.py +109 -129
  46. mcp_proxy_adapter/commands/token_management_command.py +129 -158
  47. mcp_proxy_adapter/commands/transport_management_command.py +41 -36
  48. mcp_proxy_adapter/commands/unload_command.py +42 -37
  49. mcp_proxy_adapter/config.py +36 -35
  50. mcp_proxy_adapter/core/__init__.py +19 -21
  51. mcp_proxy_adapter/core/app_factory.py +30 -9
  52. mcp_proxy_adapter/core/app_runner.py +81 -64
  53. mcp_proxy_adapter/core/auth_validator.py +176 -182
  54. mcp_proxy_adapter/core/certificate_utils.py +469 -426
  55. mcp_proxy_adapter/core/client.py +155 -126
  56. mcp_proxy_adapter/core/client_manager.py +60 -54
  57. mcp_proxy_adapter/core/client_security.py +120 -91
  58. mcp_proxy_adapter/core/config_converter.py +176 -143
  59. mcp_proxy_adapter/core/config_validator.py +12 -4
  60. mcp_proxy_adapter/core/crl_utils.py +21 -7
  61. mcp_proxy_adapter/core/errors.py +64 -20
  62. mcp_proxy_adapter/core/logging.py +34 -29
  63. mcp_proxy_adapter/core/mtls_asgi.py +29 -25
  64. mcp_proxy_adapter/core/mtls_asgi_app.py +66 -54
  65. mcp_proxy_adapter/core/protocol_manager.py +154 -104
  66. mcp_proxy_adapter/core/proxy_client.py +202 -144
  67. mcp_proxy_adapter/core/proxy_registration.py +7 -3
  68. mcp_proxy_adapter/core/role_utils.py +139 -125
  69. mcp_proxy_adapter/core/security_adapter.py +88 -77
  70. mcp_proxy_adapter/core/security_factory.py +50 -44
  71. mcp_proxy_adapter/core/security_integration.py +72 -24
  72. mcp_proxy_adapter/core/server_adapter.py +68 -64
  73. mcp_proxy_adapter/core/server_engine.py +71 -53
  74. mcp_proxy_adapter/core/settings.py +68 -58
  75. mcp_proxy_adapter/core/ssl_utils.py +69 -56
  76. mcp_proxy_adapter/core/transport_manager.py +72 -60
  77. mcp_proxy_adapter/core/unified_config_adapter.py +201 -150
  78. mcp_proxy_adapter/core/utils.py +4 -2
  79. mcp_proxy_adapter/custom_openapi.py +107 -99
  80. mcp_proxy_adapter/examples/basic_framework/main.py +9 -2
  81. mcp_proxy_adapter/examples/commands/__init__.py +1 -1
  82. mcp_proxy_adapter/examples/create_certificates_simple.py +182 -71
  83. mcp_proxy_adapter/examples/debug_request_state.py +38 -19
  84. mcp_proxy_adapter/examples/debug_role_chain.py +53 -20
  85. mcp_proxy_adapter/examples/demo_client.py +48 -36
  86. mcp_proxy_adapter/examples/examples/basic_framework/main.py +9 -2
  87. mcp_proxy_adapter/examples/examples/full_application/__init__.py +1 -0
  88. mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +22 -10
  89. mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +24 -17
  90. mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +16 -3
  91. mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +13 -3
  92. mcp_proxy_adapter/examples/examples/full_application/main.py +27 -2
  93. mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +48 -14
  94. mcp_proxy_adapter/examples/full_application/__init__.py +1 -0
  95. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +22 -10
  96. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +24 -17
  97. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +16 -3
  98. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +13 -3
  99. mcp_proxy_adapter/examples/full_application/main.py +27 -2
  100. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +48 -14
  101. mcp_proxy_adapter/examples/generate_all_certificates.py +198 -73
  102. mcp_proxy_adapter/examples/generate_certificates.py +31 -16
  103. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +220 -74
  104. mcp_proxy_adapter/examples/generate_test_configs.py +68 -91
  105. mcp_proxy_adapter/examples/proxy_registration_example.py +76 -75
  106. mcp_proxy_adapter/examples/run_example.py +23 -5
  107. mcp_proxy_adapter/examples/run_full_test_suite.py +109 -71
  108. mcp_proxy_adapter/examples/run_proxy_server.py +22 -9
  109. mcp_proxy_adapter/examples/run_security_tests.py +103 -41
  110. mcp_proxy_adapter/examples/run_security_tests_fixed.py +72 -36
  111. mcp_proxy_adapter/examples/scripts/config_generator.py +288 -187
  112. mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +185 -72
  113. mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +220 -74
  114. mcp_proxy_adapter/examples/security_test_client.py +196 -127
  115. mcp_proxy_adapter/examples/setup_test_environment.py +17 -29
  116. mcp_proxy_adapter/examples/test_config.py +19 -4
  117. mcp_proxy_adapter/examples/test_config_generator.py +23 -7
  118. mcp_proxy_adapter/examples/test_examples.py +84 -56
  119. mcp_proxy_adapter/examples/universal_client.py +119 -62
  120. mcp_proxy_adapter/openapi.py +108 -115
  121. mcp_proxy_adapter/utils/config_generator.py +429 -274
  122. mcp_proxy_adapter/version.py +1 -2
  123. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/METADATA +1 -1
  124. mcp_proxy_adapter-6.3.6.dist-info/RECORD +144 -0
  125. mcp_proxy_adapter-6.3.6.dist-info/top_level.txt +2 -0
  126. mcp_proxy_adapter_issue_package/demonstrate_issue.py +178 -0
  127. mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
  128. mcp_proxy_adapter-6.3.4.dist-info/top_level.txt +0 -1
  129. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/WHEEL +0 -0
  130. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/entry_points.txt +0 -0
  131. {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/licenses/LICENSE +0 -0
@@ -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
- {"$ref": "#/components/schemas/CommandSuccessResponse"},
192
- {"$ref": "#/components/schemas/CommandErrorResponse"}
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
- "type": "integer",
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"]["examples"] = {
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"]["application/json"]["examples"] = {
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 = ['/cmd', '/api/commands']
346
-
328
+ required_paths = ["/cmd", "/api/commands"]
329
+
347
330
  for path in required_paths:
348
- if path not in schema['paths']:
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 = ['CommandRequest', 'CommandSuccessResponse', 'CommandErrorResponse']
369
+ required_components = [
370
+ "CommandRequest",
371
+ "CommandSuccessResponse",
372
+ "CommandErrorResponse",
373
+ ]
387
374
  for component in required_components:
388
- if component not in schema['components']['schemas']:
389
- raise SchemaValidationError(f"Missing required component: {component}")
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['paths'].values():
381
+ for path in schema["paths"].values():
393
382
  for method in path.values():
394
- if '200' not in method['responses']:
395
- raise SchemaValidationError("All endpoints must return 200 status code")
396
-
397
- response = method['responses']['200']
398
- if 'application/json' not in response['content']:
399
- raise SchemaValidationError("All responses must be application/json")
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