Skip to main content
Glama

FastAPI-MCP

convert.py11.8 kB
import json import logging from typing import Any, Dict, List, Tuple import mcp.types as types from .utils import ( clean_schema_for_display, generate_example_from_schema, resolve_schema_references, get_single_param_type_from_schema, ) logger = logging.getLogger(__name__) def convert_openapi_to_mcp_tools( openapi_schema: Dict[str, Any], describe_all_responses: bool = False, describe_full_response_schema: bool = False, ) -> Tuple[List[types.Tool], Dict[str, Dict[str, Any]]]: """ Convert OpenAPI operations to MCP tools. Args: openapi_schema: The OpenAPI schema describe_all_responses: Whether to include all possible response schemas in tool descriptions describe_full_response_schema: Whether to include full response schema in tool descriptions Returns: A tuple containing: - A list of MCP tools - A mapping of operation IDs to operation details for HTTP execution """ # Resolve all references in the schema at once resolved_openapi_schema = resolve_schema_references(openapi_schema, openapi_schema) tools = [] operation_map = {} # Process each path in the OpenAPI schema for path, path_item in resolved_openapi_schema.get("paths", {}).items(): for method, operation in path_item.items(): # Skip non-HTTP methods if method not in ["get", "post", "put", "delete", "patch"]: logger.warning(f"Skipping non-HTTP method: {method}") continue # Get operation metadata operation_id = operation.get("operationId") if not operation_id: logger.warning(f"Skipping operation with no operationId: {operation}") continue # Save operation details for later HTTP calls operation_map[operation_id] = { "path": path, "method": method, "parameters": operation.get("parameters", []), "request_body": operation.get("requestBody", {}), } summary = operation.get("summary", "") description = operation.get("description", "") # Build tool description tool_description = f"{summary}" if summary else f"{method.upper()} {path}" if description: tool_description += f"\n\n{description}" # Add response information to the description responses = operation.get("responses", {}) if responses: response_info = "\n\n### Responses:\n" # Find the success response success_codes = range(200, 300) success_response = None for status_code in success_codes: if str(status_code) in responses: success_response = responses[str(status_code)] break # Get the list of responses to include responses_to_include = responses if not describe_all_responses and success_response: # If we're not describing all responses, only include the success response success_code = next((code for code in success_codes if str(code) in responses), None) if success_code: responses_to_include = {str(success_code): success_response} # Process all selected responses for status_code, response_data in responses_to_include.items(): response_desc = response_data.get("description", "") response_info += f"\n**{status_code}**: {response_desc}" # Highlight if this is the main success response if response_data == success_response: response_info += " (Success Response)" # Add schema information if available if "content" in response_data: for content_type, content_data in response_data["content"].items(): if "schema" in content_data: schema = content_data["schema"] response_info += f"\nContent-Type: {content_type}" # Clean the schema for display display_schema = clean_schema_for_display(schema) # Try to get example response example_response = None # Check if content has examples if "examples" in content_data: for example_key, example_data in content_data["examples"].items(): if "value" in example_data: example_response = example_data["value"] break # If content has example elif "example" in content_data: example_response = content_data["example"] # If we have an example response, add it to the docs if example_response: response_info += "\n\n**Example Response:**\n```json\n" response_info += json.dumps(example_response, indent=2) response_info += "\n```" # Otherwise generate an example from the schema else: generated_example = generate_example_from_schema(display_schema) if generated_example: response_info += "\n\n**Example Response:**\n```json\n" response_info += json.dumps(generated_example, indent=2) response_info += "\n```" # Only include full schema information if requested if describe_full_response_schema: # Format schema information based on its type if display_schema.get("type") == "array" and "items" in display_schema: items_schema = display_schema["items"] response_info += "\n\n**Output Schema:** Array of items with the following structure:\n```json\n" response_info += json.dumps(items_schema, indent=2) response_info += "\n```" elif "properties" in display_schema: response_info += "\n\n**Output Schema:**\n```json\n" response_info += json.dumps(display_schema, indent=2) response_info += "\n```" else: response_info += "\n\n**Output Schema:**\n```json\n" response_info += json.dumps(display_schema, indent=2) response_info += "\n```" tool_description += response_info # Organize parameters by type path_params = [] query_params = [] header_params = [] body_params = [] for param in operation.get("parameters", []): param_name = param.get("name") param_in = param.get("in") required = param.get("required", False) if param_in == "path": path_params.append((param_name, param)) elif param_in == "query": query_params.append((param_name, param)) elif param_in == "header": header_params.append((param_name, param)) # Process request body if present request_body = operation.get("requestBody", {}) if request_body and "content" in request_body: content_type = next(iter(request_body["content"]), None) if content_type and "schema" in request_body["content"][content_type]: schema = request_body["content"][content_type]["schema"] if "properties" in schema: for prop_name, prop_schema in schema["properties"].items(): required = prop_name in schema.get("required", []) body_params.append( ( prop_name, { "name": prop_name, "schema": prop_schema, "required": required, }, ) ) # Create input schema properties for all parameters properties = {} required_props = [] # Add path parameters to properties for param_name, param in path_params: param_schema = param.get("schema", {}) param_desc = param.get("description", "") param_required = param.get("required", True) # Path params are usually required properties[param_name] = { "type": param_schema.get("type", "string"), "title": param_name, "description": param_desc, } if param_required: required_props.append(param_name) # Add query parameters to properties for param_name, param in query_params: param_schema = param.get("schema", {}) param_desc = param.get("description", "") param_required = param.get("required", False) properties[param_name] = { "type": get_single_param_type_from_schema(param_schema), "title": param_name, "description": param_desc, } if "default" in param_schema: properties[param_name]["default"] = param_schema["default"] if param_required: required_props.append(param_name) # Add body parameters to properties for param_name, param in body_params: param_schema = param.get("schema", {}) param_required = param.get("required", False) properties[param_name] = { "type": get_single_param_type_from_schema(param_schema), "title": param_name, } if "default" in param_schema: properties[param_name]["default"] = param_schema["default"] if param_required: required_props.append(param_name) # Create a proper input schema for the tool input_schema = {"type": "object", "properties": properties, "title": f"{operation_id}Arguments"} if required_props: input_schema["required"] = required_props # Create the MCP tool definition tool = types.Tool(name=operation_id, description=tool_description, inputSchema=input_schema) tools.append(tool) return tools, operation_map

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/nguyendinhsinh361/fastapi-mcp'

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