Getting Started with MCP Gateway - Editor
MCP Gateway tutorialLocal with an editor
Connect an MCP client to Linear through your own gateway and call a Linear tool.
Follow along using
Build a Zuplo MCP Gateway fronting Linear, running locally at
http://127.0.0.1:9000/mcp/linear-v1. By the end, Claude Desktop connects over
the gateway's per-user OAuth flow and answers "list my open Linear issues" with
real results.
Any Zuplo project becomes a gateway by adding a plugin, a couple of policies, and a route. This guide uses Linear as the upstream and the built-in dev-login shortcut for sign-in, so you skip identity-provider setup to try it out. For production, swap in your provider: the gateway wraps Auth0, Okta, Microsoft Entra, Google, Clerk, Cognito, Keycloak, Logto, OneLogin, PingOne, and WorkOS, plus a generic OIDC fallback. See the provider catalog.
Prefer the browser with no local setup? The Portal quickstart reaches the same result through the Zuplo Portal UI.
Prerequisites
-
Node.js 24 or higher.
-
A local Zuplo project. Create one with:
CodeThen
cdinto the new directory. Seecreate-zuplo-apifor other options, or import an existing portal project by connecting it to Git and cloning it.
New projects created with create-zuplo-api ship a recent compatibilityDate,
so MCP Gateway features work out of the box. If you're adding the gateway to an
older project and the build complains about the compatibility date, see
Compatibility dates.
Run your MCP Gateway locally
-
Register the MCP Gateway plugin
Open
modules/zuplo.runtime.ts(create it if it doesn't exist) and registerMcpGatewayPlugin:CodeThe plugin registers the OAuth metadata, authorization endpoints, consent page, and upstream connect callbacks the gateway needs.
-
Add an OAuth policy with the dev-login shortcut
The local dev-login endpoint signs you in as
dev-browser-userwithout an identity provider.Open
config/policies.jsonand add the generic OAuth policy pointed at the dev-login URL:CodeThe policy requires
oidc.issuerandoidc.jwksUrl, but dev-login doesn't use them. Keep these placeholder values for this tutorial; the gateway doesn't serve/jwks.Before deploying, configure an OIDC identity provider. The dev-login endpoint only accepts requests from loopback origins and returns
403 Forbiddenfor other origins. See Configure local and deployed environments to use one policy with different environment variables for each environment. -
Add a token-exchange policy for the upstream
Each OAuth-protected upstream gets its own
mcp-token-exchange-inboundpolicy. It looks up the user's upstream credential and attaches it as the upstreamAuthorizationheader. Add this entry toconfig/policies.json:CodeauthMode: "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, so no upstream client credentials 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 runs the OAuth policy followed by the token-exchange policy:CodeoperationIdis the stable identifier for the route. It appears in analytics and is part of the per-user upstream connection key, so pick it once and don't change it. The path is whatever you set;/mcp/<provider>-v<n>is the convention. -
Run the gateway
From the project root:
CodeThe project's
devscript runszuplo dev.The route is now reachable at
http://127.0.0.1:9000/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 confirms the OAuth policy is loaded. If you see a 200, 404, or 500 instead, the OAuth policy isn't attached to the route.Use the same hostname for local URLs
Use
127.0.0.1in your MCP client URL to match the policy in this tutorial. You can uselocalhostinstead, but update both URLs to avoid a callback mismatch. See Local development. -
Connect Claude Desktop
Use a local stdio bridge so Claude Desktop can reach your local gateway. Native remote connectors connect from Anthropic's servers and cannot reach
127.0.0.1on your computer.In Claude Desktop, open Settings → Developer → Edit Config and add this entry to
claude_desktop_config.json, preserving any existing servers:CodeSave the file and restart Claude Desktop. The bridge opens the gateway's OAuth flow on first connection.
In the browser:
- The dev-login shortcut signs you in without any IdP prompt.
- 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 Zuplo MCP server appears under Settings → Developer. Its tools are available in chat. Subsequent requests 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" works well. Claude asks for permission to call the tool, then returns results proxied through the gateway.
You now have a working MCP Gateway in front of Linear, running locally: Claude Desktop signs in through the dev-login shortcut, the gateway exchanges that for a per-user Linear token, and every call is proxied through. 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.
Deploy to production before sharing
The local gateway on 127.0.0.1 is for development only, and the dev-login
shortcut works over loopback alone. Before giving others access, swap in a real
identity provider and ship the gateway through the Zuplo Portal. See
environments for setting up a production
deployment.
Next steps
- Deploy from the Portal: swap the dev-login shortcut for a real identity provider and ship the gateway through the Zuplo Portal.
- Local development: the dev-login shortcut in depth, environment variables, and local-only quirks.
- 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.