---
title: "Turn any OpenAPI spec into an MCP server"
description:
  "Turn any OpenAPI spec into a remote MCP server in your Zuplo gateway — tools, resources, and prompts derived from your existing routes. Streamable HTTP transport. OAuth, API keys, rate limits, and audit logging applied via the same policies you trust for APIs today."
canonicalUrl: "https://zuplo.com/features/mcp-servers"
sourceUrl: "https://zuplo.com/features/mcp-servers"
pageType: "feature"
generatedAt: "2026-10-08"
---

# Your APIs, as MCP tools

> Letting AI agents use your API means running an MCP server — and building one for production means re-inventing auth, rate limits, and hosting. Zuplo turns your existing OpenAPI spec into a secure, edge-hosted MCP server, governed like the rest of your API.

API routes (`GET /orders`, `POST /tickets`, `GET /users/{id}`) go through Zuplo and come out as MCP tools (`list_orders()`, `create_ticket()`, `get_user_status()`) for Claude, ChatGPT, and Gemini.

## Why this matters

**Every MCP server in production today is also someone's auth and rate-limiting side project.** Standing up an MCP server is the easy part. Making it production-grade — auth, rate limits, observability, deploy pipeline — is where most teams stall. The gateway already solved all of this for your APIs. MCP should inherit it.

- **Hand-rolling an MCP server in Python at 2am** — Every product team writing a one-off MCP server. Half-implemented JSON-RPC. Auth handled with environment variables and good intentions. The agent works once, hangs the second time, and nobody knows why.
- **Two definitions, two truths** — Your OpenAPI spec says one thing. The MCP tool schema says another. Drift compounds, the agent picks the wrong tool, the customer experience degrades, and the team blames the LLM.
- **Auth and rate limits, reinvented per agent** — Each MCP server hand-codes its own auth, its own rate limits, its own logging. Every team's interpretation is slightly different. Compliance review is a sprint. The CISO is unhappy.
- **"Production" is a localhost stdio process** — The MCP server runs on a developer's laptop because the team hasn't figured out remote hosting. Every customer demo is a local dev process. Real production for AI agents is still a wishlist item.

## What you get: MCP that inherits your gateway, not invents a parallel one

- **OpenAPI is the source of truth** — Mark operations with `x-zuplo-route.mcp` and Zuplo derives tool descriptions, parameters, and schemas from your existing OpenAPI definitions. No second schema to maintain. No drift between API and MCP.
- **Same policy stack as your APIs** — OAuth, API keys, rate limits, prompt-injection detection, audit logs — anything you can attach to a normal route works on the MCP route. There is no separate MCP-only policy mechanism to learn or maintain.
- **Streamable HTTP at the edge** — Zuplo runs the MCP server on the same 300+ edge POPs as your APIs. One POST endpoint, JSON-RPC 2.0, no stdio process to manage. Claude Desktop, Cursor, ChatGPT, MCP Inspector all connect to the same URL.

## One handler, two flavors of tools

Use OpenAPI operations directly when REST is the right shape. Drop into TypeScript when the agent needs orchestration the API surface doesn't model — and call other gateway routes from inside the handler so each step runs through its own policies.

### OpenAPI → MCP tool

```json
// routes.oas.json (excerpt)
"/orders/{id}": {
  "get": {
    "operationId": "getOrder",
    "summary": "Fetch order by ID",
    "x-zuplo-route": {
      "mcp": { "type": "resource" }
    }
  }
},
"/tickets": {
  "post": {
    "operationId": "createTicket",
    "summary": "Open a support ticket",
    "x-zuplo-route": {
      "mcp": { "type": "tool" }
    }
  }
}

// Register on the MCP handler:
"/mcp" route → mcpServerHandler({
  operations: [
    { file: "routes.oas.json", id: "getOrder" },
    { file: "routes.oas.json", id: "createTicket" }
  ]
})
```

### Custom MCP tool · TypeScript

```ts
import { ZuploRequest, ZuploContext } from "@zuplo/runtime";

// Register in routes.oas.json with x-zuplo-route.mcp = { type: "tool" }
// and x-zuplo-route.handler = $import(./modules/plan-trip)
export default async function planTrip(
  request: ZuploRequest,
  context: ZuploContext,
) {
  const { destination, dates } = await request.json();

  // Compose existing routes — each runs through its own policies
  const weather = await context.invokeRoute(
    `/v1/weather?city=${destination}`,
  );
  const flights = await context.invokeRoute(
    `/v1/flights?dest=${destination}&dates=${dates}`,
  );
  const hotels = await context.invokeRoute(
    `/v1/hotels?city=${destination}&dates=${dates}`,
  );

  return Response.json({ weather, flights, hotels });
}
```

Tags: Streamable HTTP · Tools / resources / prompts · OAuth · API keys · Rate limits · audit logs · Prompt-injection detection · Claude · Cursor · ChatGPT

## Zero to MCP in 5 minutes: from OpenAPI spec to a live MCP server in five steps

No new service, no second schema, no JSON-RPC to hand-roll. Mark the operations you want, add one handler route, and deploy it the same way you ship the rest of your API.

### 1. Mark the operations you want agents to see

Add `x-zuplo-route.mcp` to any OpenAPI operation — `type: "tool"` for actions, `"resource"` for reads, `"prompt"` for templates. Tool names and schemas come straight from the spec, so it stays the single source of truth.

```json
"/tickets": {
  "post": {
    "operationId": "createTicket",
    "summary": "Open a support ticket",
    "x-zuplo-route": {
      "mcp": { "type": "tool" }
    }
  }
}
```

### 2. Add the MCP server handler route

Point a `POST /mcp` route at `mcpServerHandler` and list the operationIds to expose. That one route is your whole MCP server.

```json
"/mcp": {
  "post": {
    "x-zuplo-route": {
      "handler": {
        "export": "mcpServerHandler",
        "module": "$import(@zuplo/runtime)",
        "options": {
          "name": "support-mcp",
          "version": "1.0.0",
          "operations": [
            { "file": "routes.oas.json", "id": "createTicket" }
          ]
        }
      }
    }
  }
}
```

### 3. Run it locally, then ship it

`npm run dev` boots the gateway on `localhost:9000` (Route Designer on `:9100`). Deploy with the same GitOps flow as your API — push a branch for a preview URL, open a PR, merge — or run `zuplo deploy`.

```bash
npm run dev      # gateway → http://localhost:9000
zuplo deploy     # or push a branch → PR → merge to ship
```

### 4. Your server is live at `/mcp`

One Streamable HTTP endpoint — `https://<your-project>.zuplo.app/mcp` — speaking JSON-RPC 2.0. No stdio process, no separate AI infrastructure. Every auth, rate-limit, and audit policy already on your API applies here too.

### 5. Connect Claude, Cursor, or ChatGPT

Point any Streamable-HTTP MCP client at your `/mcp` URL and it discovers your tools automatically. Claude Desktop and Claude Code, Cursor, ChatGPT (as a custom connector), VS Code, and MCP Inspector all connect the same way.

Want the full walkthrough? Read the [MCP quickstart](https://zuplo.com/docs/articles/mcp-quickstart).

## What makes Zuplo different: production MCP, not a Python prototype

- **Tools, resources, and prompts from one handler** — The `mcpServerHandler` exposes all three MCP primitives. Mark a GET route as a resource and clients see it via `resources/list` / `resources/read`. Mark an action as a tool and clients see it via `tools/list` / `tools/call`. Prompts work the same way. One handler, three discovery paths.
- **Custom tools when OpenAPI isn't enough** — Write a TypeScript handler, register it as an MCP tool, orchestrate multi-step workflows by calling other gateway routes via `context.invokeRoute(...)`. Each invoked route runs through its own policies. Build agents that compose your existing API surface without leaving the gateway.
- **MCP-aware OAuth resource metadata** — Set `oAuthResourceMetadataEnabled: true` and Zuplo serves the MCP-spec-compliant OAuth resource metadata clients need to discover your auth server. No custom proxy code. No "works in Cursor but not Claude Desktop" debugging — auth follows the spec.
- **Govern many MCP servers with MCP Gateway** — When the org grows past one MCP server, the MCP Gateway adds auth translation, virtual MCP servers with team-scoped tool access, security policies across all MCP traffic, and observability into every tool invocation — including third-party MCP servers your agents reach.

**Running many MCP servers? Govern them behind one gateway.** The MCP Gateway fronts your servers and third-party ones alike — one OAuth-protected route per upstream, team-scoped tool access, and audit across every tool call. [Explore the MCP Gateway](/mcp-gateway)

## What teams use this for

> "We want Claude to read order data and create support tickets."

Mark `GET /orders/{id}` as `mcp: { type: "resource" }`. Mark `POST /tickets` as `mcp: { type: "tool" }`. Register both in the MCP handler. Point Claude Desktop at your `/mcp` URL. Done — same OpenAPI, two new MCP capabilities, full audit log on every invocation.

> "We need to expose our API to ChatGPT and Cursor users."

One Zuplo MCP server, one URL, every Streamable-HTTP-capable MCP client connects. ChatGPT (as a custom connector), Cursor, Claude Desktop, MCP Inspector — same endpoint, same auth, same rate limits.

> "The agent needs a multi-step workflow our REST API doesn't model."

Write a TypeScript handler. Mark it `mcp: { type: "tool" }`. Inside, call other gateway routes via `context.invokeRoute(...)` — search products, check inventory, calculate shipping, create order — each one running through its own policies. The agent sees one tool; you keep API hygiene.

> "Compliance wants every MCP tool call audited."

MCP requests pass through the same policy stack as everything else, so attach the same audit-log policy. Every `tools/call` lands in your SIEM with caller identity, tool name, parameters, and response code. No separate logging pipeline for AI traffic.

## Frequently Asked Questions

### What is an MCP server?

An MCP (Model Context Protocol) server exposes capabilities — tools, resources, prompts — to AI assistants like Claude, ChatGPT, and Cursor in a standard format they can discover and use. Instead of every AI agent reinventing how to call your API, MCP gives them a uniform, typed interface. Think of it as the "USB-C of AI" — plug an LLM in, and it can use your tools without custom integration work on either side.

### How do I turn my REST API into an MCP server?

If you already have an OpenAPI spec, you don't need to rewrite anything. Zuplo turns any OpenAPI operation into an MCP tool — mark the operations you want exposed in your spec, deploy, and you have a remote MCP server at /mcp. Tool names, descriptions, and parameter schemas come from your OpenAPI definitions, so the spec stays the single source of truth and the LLM gets accurate metadata.

### Which AI assistants work with a Zuplo MCP server?

Any MCP client that speaks Streamable HTTP — Claude Desktop, Claude Code, Cursor, ChatGPT (as a custom connector), VS Code, and MCP Inspector are all documented as working — and the OpenAI Apps SDK is supported for building ChatGPT apps on top. AccuWeather built a ChatGPT app on Zuplo's MCP server in three clicks. Because Zuplo uses the standard Streamable HTTP transport, new MCP clients connect the same way — just point them at your /mcp URL.

### How do I add authentication and rate limits to an MCP server?

Treat the MCP server as just another API route. Attach the same auth (OAuth 2.0, JWT, API keys), rate limits, prompt-injection detection, and audit logging policies you'd put on any production endpoint. Zuplo also supports MCP-spec-compliant OAuth resource metadata so MCP clients negotiate auth correctly. AI agents call your tools through the same governance layer as your APIs.

### Can I write custom MCP tools that aren't in my OpenAPI spec?

Yes. Define a custom MCP tool as a TypeScript handler with a JSON schema for inputs and outputs. Tools can orchestrate multi-step workflows by calling other gateway routes — your MCP tool can compose three internal APIs into one capability the LLM sees. The mcp-server-custom-tools template scaffolds a working example you can fork in minutes.

### What's the difference between an MCP Server and an MCP Gateway?

An MCP Server exposes one set of tools to AI clients — that's what you're building when you turn an API into MCP. An MCP Gateway sits in front of many MCP servers (yours and third-party like Stripe, Office 365, Databricks) and governs them — auth translation, team-scoped tool access, secret masking, audit logging across all MCP traffic. Build with Zuplo's MCP server handler; govern at scale with Zuplo MCP Gateway.

### How do I deploy an MCP server to production?

If you're using Zuplo, the MCP server is just a route in your gateway — push your branch, get a preview URL, open a PR, merge to ship. The same GitOps workflow that deploys your API deploys your MCP server. It runs at the edge in 300+ data centers, scales automatically, and inherits all the security and observability already on your gateway.

### What's the best way to expose an API to AI agents?

MCP has emerged as the standard — it gives AI clients a uniform way to discover and call your tools. The trick is doing it without rewriting your API or running a separate AI infrastructure. Zuplo turns any OpenAPI spec into a remote MCP server with one config block, runs it on the same edge gateway as your existing API, and inherits your auth, rate limits, and audit logging. Talk to a Zuplo MCP expert to walk through your stack.

## Next steps

- Stop hand-rolling MCP servers: free Zuplo project, mark a few OpenAPI operations, ship a real edge-deployed MCP server with auth, rate limits, and audit logs in an afternoon.
- [Get an MCP Demo](/schedule-call)
- [Start for Free](https://portal.zuplo.com/signup)
- [Read the Docs](https://zuplo.com/docs/mcp-server/introduction)
