---
title: "Create an MCP Server from Your OpenAPI Spec in 5 Minutes"
description: "Turn any OpenAPI spec into a working MCP server with Zuplo — no custom code required. Follow this step-by-step tutorial to deploy in under 5 minutes."
canonicalUrl: "https://zuplo.com/learning-center/create-mcp-server-from-openapi"
pageType: "learning-center"
authors: "nate"
tags: "Model Context Protocol, OpenAPI"
image: "https://zuplo.com/og?text=Create%20an%20MCP%20Server%20from%20Your%20OpenAPI%20Spec%20in%205%20Minutes"
---
AI agents need a way to call your APIs. The
[Model Context Protocol (MCP)](https://modelcontextprotocol.io) gives them
exactly that -- a standardized interface for discovering and invoking API
operations as tools. But building an MCP server from scratch means writing
request handlers, mapping endpoints to tool definitions, managing
authentication, and hosting it all somewhere reliable.

With Zuplo, you can skip all of that. Drop in your OpenAPI spec and Zuplo's
[MCP Server Handler](https://zuplo.com/docs/handlers/mcp-server) automatically
exposes your API endpoints as MCP tools. No custom code. No infrastructure to
manage. In this tutorial, you'll go from an OpenAPI spec to a deployed, secure
MCP server in under five minutes.

## Prerequisites

Before you start, make sure you have:

- **An OpenAPI spec (v3.x)** -- A valid OpenAPI 3.0 or 3.1 document describing
  your API endpoints. If you don't have one yet, we'll provide an example below.
- **A Zuplo account** -- The free tier works for this tutorial.
  [Sign up here](https://portal.zuplo.com/signup) if you haven't already.
- **Node.js installed** -- Required for the Zuplo CLI. Version 18 or later is
  recommended.

## Step 1: Create a Zuplo Project

Start by creating a new Zuplo project. You can do this from the
[Zuplo Portal](https://portal.zuplo.com) dashboard or from the command line
using the CLI:

```bash
npx zuplo init my-mcp-server
cd my-mcp-server
```

The CLI scaffolds a project with the standard Zuplo structure, including a
`config/routes.oas.json` file where your API routes are defined and a
`config/zuplo.jsonc` configuration file.

If you prefer the portal, click **New Project**, give it a name, and you'll land
in the Route Designer where you can configure everything visually.

## Step 2: Add Your OpenAPI Spec

Your OpenAPI spec is the foundation of the MCP server. Zuplo reads it to
understand your API's endpoints, parameters, request bodies, and descriptions --
then maps each operation to an MCP tool automatically.

Replace the contents of `config/routes.oas.json` with your own OpenAPI document.
If you want to follow along with an example, here's a simple todo API spec with
three endpoints:

```json
{
  "openapi": "3.1.0",
  "info": {
    "title": "Todo API",
    "version": "1.0.0"
  },
  "paths": {
    "/todos": {
      "get": {
        "operationId": "listTodos",
        "summary": "List all todos",
        "description": "Retrieves a list of all todo items.",
        "x-zuplo-route": {
          "corsPolicy": "none",
          "handler": {
            "export": "urlForwardHandler",
            "module": "$import(@zuplo/runtime)",
            "options": {
              "baseUrl": "https://your-backend-api.example.com"
            }
          },
          "policies": {
            "inbound": []
          }
        },
        "responses": {
          "200": {
            "description": "A list of todos",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Todo"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createTodo",
        "summary": "Create a new todo",
        "description": "Creates a new todo item with a title and optional completion status.",
        "x-zuplo-route": {
          "corsPolicy": "none",
          "handler": {
            "export": "urlForwardHandler",
            "module": "$import(@zuplo/runtime)",
            "options": {
              "baseUrl": "https://your-backend-api.example.com"
            }
          },
          "policies": {
            "inbound": []
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TodoInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created todo"
          }
        }
      }
    },
    "/todos/{id}": {
      "get": {
        "operationId": "getTodo",
        "summary": "Get a todo by ID",
        "description": "Retrieves a single todo item by its unique identifier.",
        "x-zuplo-route": {
          "corsPolicy": "none",
          "handler": {
            "export": "urlForwardHandler",
            "module": "$import(@zuplo/runtime)",
            "options": {
              "baseUrl": "https://your-backend-api.example.com"
            }
          },
          "policies": {
            "inbound": []
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The unique identifier of the todo"
          }
        ],
        "responses": {
          "200": {
            "description": "The requested todo"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Todo": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "title": { "type": "string" },
          "completed": { "type": "boolean" }
        }
      },
      "TodoInput": {
        "type": "object",
        "required": ["title"],
        "properties": {
          "title": { "type": "string" },
          "completed": { "type": "boolean", "default": false }
        }
      }
    }
  }
}
```

Two things matter here for MCP tool quality:

1. **`operationId`** -- Each operation needs a unique `operationId`. This
   becomes the tool name that AI agents see and call.
2. **`description`** -- Write clear, concise descriptions for every operation
   and parameter. AI agents rely on these descriptions to understand when and
   how to use each tool.

Update the `baseUrl` values to point to your actual backend API. Zuplo acts as a
gateway, forwarding requests to your backend while adding the MCP layer on top.

## Step 3: Enable the MCP Server Handler

Now create a second OpenAPI file for your MCP server endpoint. Add a new file at
`config/mcp.oas.json`:

```json
{
  "openapi": "3.1.0",
  "info": {
    "title": "MCP Server",
    "version": "1.0.0"
  },
  "paths": {
    "/mcp": {
      "post": {
        "operationId": "mcpServer",
        "summary": "MCP Server Endpoint",
        "x-zuplo-route": {
          "corsPolicy": "none",
          "handler": {
            "export": "mcpServerHandler",
            "module": "$import(@zuplo/runtime)",
            "options": {
              "name": "My API MCP Server",
              "version": "1.0.0",
              "sourceRouteFile": "routes.oas.json"
            }
          },
          "policies": {
            "inbound": []
          }
        },
        "responses": {
          "200": {
            "description": "MCP response"
          }
        }
      }
    }
  }
}
```

The key configuration is in the handler options:

- **`name`** -- The display name of your MCP server, visible to AI clients.
- **`version`** -- The version of your MCP server.
- **`sourceRouteFile`** -- Points to your main OpenAPI file (`routes.oas.json`).
  Zuplo reads this file to generate MCP tool definitions from your API
  endpoints.

That's it. Save the file and Zuplo handles the rest -- parsing your OpenAPI
spec, generating tool schemas, and serving the MCP protocol at `/mcp`.

## Step 4: Add Authentication

Before deploying, you should secure your MCP server so only authorized clients
can access it. Zuplo makes this straightforward with inbound policies.

Add an API key authentication policy to your MCP endpoint by updating the
`policies` section in `config/mcp.oas.json`:

```json
"policies": {
  "inbound": ["api-key-auth"]
}
```

Then define the policy in your `config/policies.json` file:

```json
{
  "policies": [
    {
      "name": "api-key-auth",
      "policyType": "api-key-inbound",
      "handler": {
        "export": "ApiKeyInboundPolicy",
        "module": "$import(@zuplo/runtime)"
      }
    }
  ]
}
```

Once deployed, you can create and manage API keys from the Zuplo portal under
the **API Key Consumers** section. Each consumer gets a unique key that must be
included in the `Authorization` header of MCP requests.

This is especially important for MCP servers because AI agents will be making
automated calls to your API. Without authentication, anyone who discovers your
MCP endpoint could use it freely.

## Step 5: Deploy

Deploy your project with a single command:

```bash
npx zuplo deploy
```

Zuplo deploys your API gateway and MCP server to its global edge network. Once
the deployment completes, you'll see the URL of your live gateway -- something
like:

```
https://my-mcp-server-main-abc1234.zuplo.dev
```

Your MCP server is now live at
`https://my-mcp-server-main-abc1234.zuplo.dev/mcp` and ready to accept
connections from any MCP-compatible client.

If you're working in the Zuplo Portal instead of the CLI, click **Save** and the
deployment happens automatically.

## Step 6: Test with an MCP Client

With your MCP server deployed, connect to it from an MCP client to verify
everything works. Here's how to set it up with a few popular clients.

### Claude Desktop

Open your Claude Desktop configuration file and add your MCP server:

```json
{
  "mcpServers": {
    "my-api": {
      "url": "https://my-mcp-server-main-abc1234.zuplo.dev/mcp",
      "headers": {
        "Authorization": "Bearer zpka_your_api_key_here"
      }
    }
  }
}
```

Restart Claude Desktop and you should see your API tools listed in the tools
menu. Try asking Claude to "list all todos" and watch it call your API through
the MCP server.

### Cursor

In Cursor, go to **Settings > MCP** and add a new server with the same URL and
authorization header. Cursor's AI assistant will then be able to use your API
tools when answering questions or writing code.

### MCP Inspector

For debugging and testing, the
[MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector) is an
excellent tool. Point it at your MCP server URL and you can browse available
tools, see their schemas, and invoke them manually to verify the request and
response mapping.

## What Happens Under the Hood

When you set up the MCP Server Handler, Zuplo does the following automatically:

1. **Parses your OpenAPI spec** -- It reads `routes.oas.json` and extracts every
   operation that has an `operationId`.
2. **Generates MCP tool definitions** -- Each operation becomes a tool. The
   `operationId` becomes the tool name. The `summary` and `description` fields
   become the tool's description that AI agents use to decide when to call it.
   Parameters and request body schemas are converted into the tool's input
   schema.
3. **Handles protocol negotiation** -- The `/mcp` endpoint speaks the MCP
   protocol, handling the `initialize`, `tools/list`, and `tools/call` messages
   that clients send.
4. **Forwards requests to your backend** -- When an AI agent calls a tool, Zuplo
   maps the tool invocation back to the corresponding HTTP request (method,
   path, parameters, body) and forwards it to your backend via the URL forward
   handler.
5. **Applies policies** -- Any inbound policies you've configured
   (authentication, rate limiting, request validation) run before the request
   reaches your backend.

The result is that your existing API becomes AI-accessible without changing a
single line of your backend code. The OpenAPI spec you already maintain is the
single source of truth for both human-facing documentation and AI-facing tool
definitions.

## Next Steps

You now have a working MCP server backed by your OpenAPI spec. Here are some
ways to build on this foundation:

- **Add rate limiting** -- Protect your backend from aggressive AI agents by
  adding a
  [rate limiting policy](https://zuplo.com/docs/policies/rate-limit-inbound).
  This is critical in production since AI agents can generate high request
  volumes.
- **Enable request validation** -- Add the
  [JSON schema validation policy](https://zuplo.com/docs/policies/request-validation-inbound)
  to ensure AI agents send well-formed requests that match your OpenAPI schema.
- **Add monitoring** -- Use Zuplo's built-in analytics to track which tools AI
  agents call most frequently, monitor error rates, and understand usage
  patterns.
- **Explore MCP prompts** -- Go beyond tools by adding
  [MCP prompts](/blog/mcp-server-prompts) that guide AI agents through
  multi-step workflows with your API.
- **Set up an MCP Gateway** -- If you're managing multiple MCP servers across
  teams, Zuplo's [MCP Gateway](/blog/zuplo-mcp-gateway) provides centralized
  governance, access control, and observability.

For the full documentation on Zuplo's MCP support, see the
[MCP Server docs](https://zuplo.com/docs/mcp-server/introduction).

## Get Started

Zuplo's MCP Server Handler is available on all plans, including the free tier.
If you already have an OpenAPI spec, you're five minutes away from a deployed
MCP server.

[Sign up for Zuplo](https://portal.zuplo.com/signup) and turn your API into an
AI-ready tool today.