mcpopenapiswagger

Swagger to MCP Server: The Pattern Explained

Auto-generate MCP server tool definitions from OpenAPI specs. Each endpoint becomes a callable tool for your LLM.

8 min

Any API with an OpenAPI (Swagger) specification can be automatically converted into an MCP server. Each endpoint becomes a callable tool, path parameters become tool arguments, and response schemas define the output format. This pattern lets you expose any REST API to LLMs without writing tool definitions by hand.

How the pattern works

  1. Parse the OpenAPI spec (YAML or JSON)
  2. Extract each endpoint as a tool definition
  3. Map path/query parameters to tool input schema
  4. Generate the MCP server with HTTP call handlers
  5. Register authentication from environment variables

Step 1: Parse the OpenAPI spec

Python
import yaml, json

def load_spec(path):
    with open(path) as f:
        if path.endswith(".yaml") or path.endswith(".yml"):
            return yaml.safe_load(f)
        return json.load(f)

spec = load_spec("openapi.yaml")
base_url = spec.get("servers", [{}])[0].get("url", "http://localhost:8000")
paths = spec.get("paths", {})

Step 2: Generate tool definitions

Python
def spec_to_tools(spec):
    tools = []
    for path, methods in spec.get("paths", {}).items():
        for method, details in methods.items():
            if method not in ("get", "post", "put", "delete"):
                continue
            params = details.get("parameters", [])
            properties = {}
            required = []
            for p in params:
                properties[p["name"]] = {
                    "type": p.get("schema", {}).get("type", "string"),
                    "description": p.get("description", ""),
                }
                if p.get("required", False):
                    required.append(p["name"])

            # Handle request body for POST/PUT
            body = details.get("requestBody", {})
            if body:
                schema = body.get("content", {}).get(
                    "application/json", {}
                ).get("schema", {})
                for prop_name, prop_def in schema.get("properties", {}).items():
                    properties[prop_name] = prop_def
                required.extend(schema.get("required", []))

            tools.append({
                "name": details.get("operationId", f"{method}_{path}"),
                "description": details.get("summary", path),
                "method": method.upper(),
                "path": path,
                "input_schema": {
                    "type": "object",
                    "properties": properties,
                    "required": required,
                },
            })
    return tools

Step 3: Build the MCP server

Python
from mcp.server import Server
from mcp.types import TextContent
import requests, os

app = Server("openapi-bridge")

def make_handler(tool_def, base_url):
    async def handler(**kwargs):
        path = tool_def["path"]
        # Replace path parameters
        for key, value in kwargs.items():
            path = path.replace("{" + key + "}", str(value))

        url = f"{base_url}{path}"
        headers = {"Authorization": f"Bearer {os.environ.get('API_TOKEN', '')}"}

        if tool_def["method"] == "GET":
            resp = requests.get(url, headers=headers, params=kwargs)
        else:
            resp = requests.request(
                tool_def["method"], url, headers=headers, json=kwargs,
            )

        return [TextContent(type="text", text=resp.text)]
    return handler

# Register all tools from spec
spec = load_spec("openapi.yaml")
base_url = spec["servers"][0]["url"]
for tool_def in spec_to_tools(spec):
    handler = make_handler(tool_def, base_url)
    handler.__name__ = tool_def["name"]
    handler.__doc__ = tool_def["description"]
    app.tool()(handler)

Real-world example: search API

The Scavio search API has an OpenAPI spec. Converting it to MCP gives your LLM access to web search, TikTok search, and YouTube search as callable tools -- or you can skip the conversion and use the hosted MCP endpoint directly.

JSON
{
  "mcpServers": {
    "scavio": {
      "url": "https://mcp.scavio.dev/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}

Limitations of auto-generation

  • Complex authentication flows (OAuth2) need manual handling
  • Paginated endpoints need wrapper logic for multi-page fetching
  • Tool descriptions from Swagger summaries are often too terse for LLMs
  • Large APIs generate too many tools -- LLMs struggle with 50+ tools

Best practices

  • Filter to 5-15 most-used endpoints, not the entire API
  • Rewrite tool descriptions for LLM consumption (what it does, when to use it)
  • Group related endpoints into a single tool with an action parameter
  • Add error handling that returns human-readable messages