Skip to main content
Glama
convert_openapi_to_30.py5.57 kB
#!/usr/bin/env python3 """Convert OpenAPI 3.1 spec to 3.0 for better tool compatibility.""" import json import sys from pathlib import Path def convert_31_to_30(spec: dict) -> dict: """Convert OpenAPI 3.1 spec to 3.0. This handles the main compatibility issues: - Change version from 3.1.x to 3.0.3 - Convert nullable types from type arrays to nullable property """ spec = spec.copy() # Update version spec["openapi"] = "3.0.3" # Process components if "components" in spec and "schemas" in spec["components"]: spec["components"]["schemas"] = convert_schemas(spec["components"]["schemas"]) # Process paths if "paths" in spec: spec["paths"] = convert_paths(spec["paths"]) return spec def convert_schemas(schemas: dict) -> dict: """Convert schema definitions.""" result = {} for name, schema in schemas.items(): result[name] = convert_schema(schema) return result def _handle_type_array(schema: dict) -> dict: """Handle type arrays with null (OpenAPI 3.1 feature).""" if "type" not in schema or not isinstance(schema["type"], list): return schema types = schema["type"] if "null" not in types: return schema schema["nullable"] = True non_null_types = [t for t in types if t != "null"] if len(non_null_types) == 1: schema["type"] = non_null_types[0] elif non_null_types: schema.pop("type") schema["anyOf"] = [{"type": t} for t in non_null_types] return schema def _handle_any_of(schema: dict) -> dict: """Handle anyOf with null.""" if "anyOf" not in schema: return schema any_of = schema["anyOf"] null_schemas = [s for s in any_of if s.get("type") == "null"] non_null_schemas = [s for s in any_of if s.get("type") != "null"] if not null_schemas: return schema schema["nullable"] = True if non_null_schemas: schema["anyOf"] = [convert_schema(s) for s in non_null_schemas] else: schema.pop("anyOf", None) return schema def convert_schema(schema: dict | list) -> dict | list: """Convert a single schema, handling nullable types.""" if isinstance(schema, list): return [convert_schema(s) for s in schema] if not isinstance(schema, dict): return schema schema = schema.copy() schema = _handle_type_array(schema) schema = _handle_any_of(schema) # Recursively process nested schemas for key in ["properties", "items", "additionalProperties"]: if key in schema: if key == "properties": schema[key] = {k: convert_schema(v) for k, v in schema[key].items()} else: schema[key] = convert_schema(schema[key]) if "allOf" in schema: schema["allOf"] = [convert_schema(s) for s in schema["allOf"]] return schema def convert_paths(paths: dict) -> dict: """Convert path definitions.""" result = {} for path, path_item in paths.items(): result[path] = convert_path_item(path_item) return result def convert_path_item(path_item: dict) -> dict: """Convert a path item.""" path_item = path_item.copy() for method in ["get", "post", "put", "delete", "patch", "options", "head", "trace"]: if method in path_item: path_item[method] = convert_operation(path_item[method]) return path_item def convert_operation(operation: dict) -> dict: """Convert an operation.""" operation = operation.copy() # Convert parameters if "parameters" in operation: operation["parameters"] = [ convert_parameter(p) for p in operation["parameters"] ] # Convert request body if "requestBody" in operation: operation["requestBody"] = convert_request_body(operation["requestBody"]) # Convert responses if "responses" in operation: operation["responses"] = { status: convert_response(resp) for status, resp in operation["responses"].items() } return operation def convert_parameter(param: dict) -> dict: """Convert a parameter.""" param = param.copy() if "schema" in param: param["schema"] = convert_schema(param["schema"]) return param def convert_request_body(request_body: dict) -> dict: """Convert a request body.""" request_body = request_body.copy() if "content" in request_body: request_body["content"] = convert_content(request_body["content"]) return request_body def convert_response(response: dict) -> dict: """Convert a response.""" response = response.copy() if "content" in response: response["content"] = convert_content(response["content"]) return response def convert_content(content: dict) -> dict: """Convert content definitions.""" result = {} for media_type, media_type_obj in content.items(): converted_obj = media_type_obj.copy() if "schema" in converted_obj: converted_obj["schema"] = convert_schema(converted_obj["schema"]) result[media_type] = converted_obj return result def main() -> None: """Convert OpenAPI file from 3.1 to 3.0 format.""" if len(sys.argv) != 3: sys.exit(1) input_file = Path(sys.argv[1]) output_file = Path(sys.argv[2]) with input_file.open() as f: spec = json.load(f) converted = convert_31_to_30(spec) with output_file.open("w") as f: json.dump(converted, f, indent=2) if __name__ == "__main__": main()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/helixml/kodit'

If you have feedback or need assistance with the MCP directory API, please join our Discord server