Your team wants Claude and Cursor to hit the internal billing API, the inventory
service, the admin tooling. The fast way is to mint an API key, paste it into
mcp.json, and point an agent at the service directly.
Now a long-lived key with write access to an internal system lives in a dotfile on a laptop, and the agent can call every operation the API exposes, including the ones that delete things.
You don’t have to choose between giving agents the API and keeping it safe. Two Zuplo capabilities chain together: the MCP Server handler turns your API’s routes into MCP tools, the discrete actions an agent like Claude can call, and the MCP Gateway puts those tools behind SSO, strips the dangerous ones, and logs every call. This walkthrough wires them together in the portal.
- Platform and security teams giving agents internal APIs without handing out raw keys
- API owners ready to expose a safe subset of their service as agent tools
- Anyone who wants the whole flow in the portal, no config files required
Start with your internal API
This walkthrough assumes your internal API is managed in a Zuplo project, or is about to be: its routes are described by an OpenAPI spec in the project, the way Zuplo manages every gateway. If the API lives elsewhere today, put a Zuplo gateway in front of it and import its OpenAPI spec. Once the routes are in the project, exposing a slice of them as MCP tools takes just a few additional clicks, as you’ll see.
Keep this in its own project. It’s how we split it internally: the API team owns the gateway and the MCP server that sits on it, while platform or security owns the separate gateway project we build later. One account, two projects, and neither team waits on the other to ship.
Turn routes into MCP tools
Open the Code tab, add a route, and pick the Dynamic OpenAPI to MCP
Server handler from the dropdown. The portal scaffolds it on POST /mcp and
serves it over HTTP, the transport MCP clients connect with. You choose the
handler rather than writing it by hand, so the route is wired up without
touching JSON.

You do not surface the whole API. On the route, click Select Tools and tick only the operations agents should reach. A read-only reporting endpoint and a “create draft invoice” call are reasonable; “delete customer” is not.
Each checked operation becomes a tool, named from its operationId and
described from its OpenAPI summary, with the input schema derived from the
operation so the server validates arguments before your handler runs.

Dynamic MCP Server Quickstart
The full handler flow in text, including local development and every option the handler accepts.
Lock the route with an API key
The MCP server is a normal Zuplo route, so it takes the same inbound policies as any other. For an internal service the simplest control is an API key: open the route’s policy editor and add Zuplo’s API key authentication policy, backed by a key you create in the project. The server then rejects anyone without a valid key. Skip the policy and the route still serves tools, but so does anyone who finds the URL.

That single key is the only credential that reaches your internal MCP server. What matters is who holds it: not your developers, and not their editors. It goes into the gateway in the next step, which keeps it server-side and never hands it to a client. Your internal API is now reachable as MCP tools, but only by a caller holding a key your developers never see.
Front it with the MCP Gateway
Create a second project in the same account for the gateway. Open its Code tab, click Add Route, and pick MCP Gateway Virtual Server. A virtual server fronts exactly one upstream MCP server, putting it behind your own auth and tool policy, so this one points at the internal MCP server you just built.
The wizard opens on a library of known servers like Linear and Stripe. Yours isn’t in it, so choose the custom MCP server option and paste your internal MCP server’s URL as the upstream. The same wizard, pointed at a third-party server, is the one we walk through in fronting a third-party server; here the upstream is just yours.

Gate access with SSO
The wizard’s next step is inbound auth: who is allowed to connect to the gateway. This is where your internal API stops being reachable by anyone with a pasted key and starts being reachable only by your team. Pick the identity provider your organization already runs, Auth0, Okta, Entra, Google, or another OIDC provider, and the same SSO login that gates everything else now gates the MCP server.
The wizard names the environment variables that provider needs, the domain, client ID, and secret, which you add under Settings before anyone connects.
The gateway runs full OAuth on the inbound side. Clients authenticate through your IdP, and the gateway issues its own short-lived token scoped to that one virtual server. The developer’s editor only holds that token, never a credential for the internal API.
Curate tools per team
The wizard’s Tools step decides which of the MCP server’s tools this gateway hands to your team. Passthrough forwards all of them; Curate lists them and lets you tick only the ones you want.
You already trimmed the list once, back on the MCP server. That trim is the
ceiling: the most any caller could ever see. Curate narrows it again, just for
this gateway, and never touches the API project. So even though the server
offers createDraftInvoice, you can stand up a read-only gateway here that
exposes only listInvoices and getRevenueReport.
Pro tip:
Leaving a tool off the curated list doesn’t just hide it, it removes it. The gateway rejects any call to a tool you didn’t expose before that call reaches your API, so no cleverly worded prompt can talk an agent into using one.
Broker the key, don’t share it
The wizard’s last step is outbound auth: how the gateway authenticates to your internal MCP server. Your server uses an API key, not OAuth, so make two choices here:
- None for upstream credentials, so the gateway doesn’t attempt an OAuth exchange with your server.
- Remove auth token, so the inbound client’s
Authorizationheader is stripped before forwarding and the developer’s token never reaches your internal API.

That clears the inbound token but doesn’t yet present your internal key. Store
the key as a secret environment variable under Settings, then add a
set-headers policy to the end of the route’s inbound pipeline that injects it as
the upstream Authorization header.

The policy reads the key with $env(...), so the value lives only in your
secret environment variable and never appears in source control:
Connect to an API-key upstream
The full outbound-auth flow, including the set-headers policy config and the secret environment variable.
This is the move that makes the whole thing safe. The internal API key lives in one place, the gateway, and is never copied to a laptop or an editor config. The token a developer’s agent presents and the key that reaches your internal API are two different things, exactly the boundary the MCP authorization spec is built to enforce: an MCP server must not pass through the token it received.
It’s the same boundary we argued for in governing shadow MCP. Rotate the key in one place and every connected agent keeps working.

Connect your team and audit
Hand your team the gateway URL: the project’s gateway URL plus the route path,
something like /mcp/internal-billing. In Claude, that’s Settings,
Connectors, Add custom connector, paste the URL, and run the connect
flow. The developer authenticates through your IdP once and their agent lists
exactly the curated tools, with no key to paste and nothing to leak.

Alternatively, skip the chat-thread URL and publish the instructions. Every gateway project ships a developer portal, and its MCP setup page generates copy-paste config for Claude, ChatGPT, Codex, Cursor, VS Code, or a generic client, all pointed at your gateway route. Hand your team that page instead of a raw URL.

Lock that portal down to match the gateway:
- Whole portal behind login, so only your organization can see it.
- Individual pages behind protected routes, so the internal-billing instructions reach only the teams that should have them.
Protected routes in the developer portal
Gate portal pages behind authentication so setup instructions reach only the right teams.
Then open Observability, select Analytics, and choose MCP from the sidebar. Every tool call shows up: which operation ran, who invoked it, success rates, and upstream errors. That per-call record is the inventory you never had when developers wired up their own connections, and it’s the same governance you’d want on any MCP server you ship.

Step back and the walkthrough stacked five independent controls between an agent and your internal API. Each one was a single step above, each closes a different gap, and none depends on the others holding:
| Control | Where you set it | The gap it closes |
|---|---|---|
| Operation selection | MCP server handler, Select Tools | Limits which operations can ever become tools |
| SSO inbound auth | Gateway wizard, your IdP | Keeps anyone outside your team off the gateway |
| Tool curation | Gateway wizard, Curate | Blocks curated-out tools before they reach upstream |
| Key brokering | Gateway, set-headers + secret env var | Stops the internal key landing in an editor config |
| Per-call analytics | Observability, Analytics → MCP | Removes blind spots about who called which tool |
Going to production
Everything you just built lives in your Working Copy, Zuplo’s development environment, which deploys on save and is ideal for testing the connect flow. For production, two changes apply to both projects:
- Connect source control, so deploys come from a git branch with review and rollback.
- Put the gateway on a custom domain, so the URL you hand your team is one of yours.
Get started today
The MCP Gateway is in public beta, free to try on a new project. Spin up a free Zuplo project and put your internal API in front of your team’s agents without handing over a single key.
