---
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-08-03"
---

# 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.

## 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.

```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" }
  ]
})
```

```typescript
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 });
}
```

Capabilities in this handler: Streamable HTTP · Tools / resources / prompts ·
OAuth · API keys · Rate limits · audit logs · Prompt-injection detection ·
Claude · Cursor · ChatGPT.

## 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.

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

## 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.

## FAQ

**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

- Start a free account: https://portal.zuplo.com/signup
- Read the docs: https://zuplo.com/docs/mcp-server/introduction
- Read the MCP quickstart: https://zuplo.com/docs/articles/mcp-quickstart
- Explore the MCP Gateway: [/mcp-gateway](/mcp-gateway)
- Get an MCP demo: [/schedule-call](/schedule-call)
