MCP Gateway quickstart
By the end of this guide you'll have a Zuplo MCP Gateway fronting Linear at
https://<your-gateway>/mcp/linear-v1, Claude Desktop signed in through Auth0,
and your own Linear account connected through the gateway's per-user OAuth flow.
From there, prompting Claude with "list my open Linear issues" returns real
results proxied through the gateway, and you'll see the call show up in the
project's analytics.
The MCP Gateway isn't a separate project type — every Zuplo project can become an MCP Gateway by adding a plugin, a couple of policies, and a route. The example uses Linear as the upstream MCP server and Auth0 as the identity provider, but the same pattern applies to any upstream that speaks the MCP authorization spec and any OIDC-compatible identity provider. For a generic OIDC setup, see Configuring Okta.
Prerequisites
- A Zuplo project. Create one from the new project page if you don't have one already.
- An Auth0 tenant with a Regular Web Application configured. The Auth0 setup section in Configuring Auth0 covers the dashboard side.
- The
AUTH0_DOMAIN,AUTH0_CLIENT_ID, andAUTH0_CLIENT_SECRETfrom your Auth0 application.
-
Pin the compatibility date
MCP Gateway features require
compatibilityDate >= 2026-03-01inzuplo.jsonc:zuplo.jsoncExisting projects on an older date need to bump it before adding MCP features. New projects default to a recent date, so most won't need to change anything.
-
Register the MCP Gateway plugin
Add a
modules/zuplo.runtime.tsfile (or edit the existing one) and registerMcpGatewayPlugin:modules/zuplo.runtime.tsThe plugin registers the OAuth metadata, authorization endpoints, consent page, and upstream connect callbacks the gateway needs.
-
Add an MCP OAuth policy
Open
config/policies.jsonand add the Auth0 MCP OAuth policy. It authenticates inbound MCP requests against your Auth0 tenant:config/policies.jsonauth0Domainis a bare hostname (my-tenant.us.auth0.com), not a URL.Set the three environment variables on the project —
AUTH0_DOMAINin plain config andAUTH0_CLIENT_ID/AUTH0_CLIENT_SECRETin the secret store. -
Add a token-exchange policy for the upstream
Each OAuth-protected upstream gets its own
mcp-token-exchange-inboundpolicy. The policy looks up the user's upstream credential and attaches it as the upstreamAuthorizationheader. Add this entry toconfig/policies.json:config/policies.jsonauthMode: "user-oauth"means each user connects their own Linear account the first time they call the route.clientRegistration: { "mode": "auto" }lets the gateway register itself with Linear's OAuth server on demand, using OAuth Client ID Metadata Documents with a DCR fallback — no upstream client credentials need to live in source control. -
Add the route
Open
config/routes.oas.jsonand add an MCP route. The handler points at Linear's MCP server URL; the inbound policy chain attaches the OAuth policy followed by the token exchange policy:config/routes.oas.jsonoperationIdis the stable identifier for the route. It appears in analytics and is part of the per-user upstream connection key — pick it once and don't change it.The path is whatever you set in the route —
/mcp/<provider>-v<n>is the convention, but any path the OpenAPI router accepts works. -
Run the gateway
Run
zuplo devfrom the project root:CodeThe route is now reachable at
http://127.0.0.1:9000/mcp/linear-v1. Deploy when you're ready to expose it publicly; the route then lives athttps://<your-deployment>/mcp/linear-v1.Checkpoint: confirm the OAuth policy is wired up
Send an unauthenticated POST and expect a
401:CodeThe response should be
401 Unauthorizedwith aWWW-Authenticate: Bearerheader pointing at/.well-known/oauth-protected-resource/mcp/linear-v1. That 401 is the gateway telling a future MCP client "you need to authenticate first" — it confirms the OAuth policy is loaded. If you see a 200, 404, or 500 instead, the OAuth policy isn't attached to the route. -
Connect Claude Desktop
Open Claude Desktop, go to Settings → Connectors, scroll to the bottom of the list, and click Add custom connector. Paste the route URL — for the locally-running gateway, that's
http://127.0.0.1:9000/mcp/linear-v1; for a deployed gateway, use the public URL — and click Add.Claude Desktop opens the gateway's OAuth flow in a browser:
- Sign in with Auth0.
- The gateway's consent page lists Linear with a Connect button.
- Click Connect, complete Linear's OAuth flow, then click Authorize to finish.
Checkpoint: Claude is connected
Back in Claude Desktop, the new connector appears in Settings → Connectors marked as connected. Subsequent requests from Claude reuse the tokens the gateway just issued.
For per-client setup details, see Connect MCP clients.
-
Test it
In Claude Desktop, prompt the model with something that requires Linear — "list my open issues" is a good test. Claude asks for permission to call the tool, then returns results proxied through the gateway.
Open the project's Analytics dashboard and switch to the MCP tab to see the call appear in the events timeline, the success rate, the top capabilities table, and the user breakdown.
You now have a working MCP Gateway in front of Linear: Claude Desktop authenticates against Auth0, the gateway exchanges that for a per-user Linear token, and every call lands in your analytics. The same shape — one OAuth policy, one token-exchange policy per upstream, one route per upstream — scales out to as many upstream MCP servers as you want to front.
Next steps
- Connect more clients — Claude Code, Cursor, VS Code, ChatGPT, and any other MCP client.
- How it works — the request lifecycle and the two OAuth surfaces.
- Add more upstreams — front several upstream MCP servers from one Zuplo project.
- Capability filtering — curate the tools, prompts, and resources each route exposes, including description and annotation overrides.