Skip to main content
Glama

MCP Gateway

A self-hosted MCP gateway with Google login and role-based access control. One Next.js app provides:

  • An MCP endpoint (/api/mcp, Streamable HTTP) that MCP clients like Claude and Cursor connect to. The first time a user performs any action, the standard MCP OAuth 2.1 flow sends them to Google to sign in — no tokens to copy around.

  • An admin dashboard (shadcn/ui) where the admin configures Zendesk and Chargebee connectors, invites users, and defines roles that control exactly which tools each user gets.

How access control works

  • The first user ever to sign in becomes admin. Admins can use every tool of every connector and manage the dashboard.

  • Everyone else must be invited by email first — un-invited Google accounts are rejected at sign-in.

  • Each user has one role. A role grants access to specific connectors with one of three levels:

    • Read — only that connector's read tools

    • Read + Write — all of its tools

    • Custom — an explicit per-tool selection

  • Enforcement happens at the MCP layer: tools/list only ever contains the tools your role grants, and every tools/call re-checks the database, so permission changes apply immediately.

Connectors are instances, not singletons — you can add two Zendesk accounts and grant a role access to only one of them. Tool names embed the connector slug, e.g. zendesk_support_search_tickets.

Related MCP server: MCP Hub

Setup

1. Prerequisites

  • Node.js 20+

  • PostgreSQL (docker compose up -d starts one locally)

2. Environment

cp .env.example .env

Fill in:

Variable

Value

DATABASE_URL

PostgreSQL connection string (the docker-compose default works out of the box)

BETTER_AUTH_SECRET

openssl rand -base64 32

BETTER_AUTH_URL

The app's public base URL, e.g. http://localhost:3000

GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET

See below

ENCRYPTION_KEY

openssl rand -hex 32 — encrypts connector API keys at rest (AES-256-GCM)

Google credentials: in Google Cloud Console, create an OAuth client of type Web application and add the authorized redirect URI:

{BETTER_AUTH_URL}/api/auth/callback/google

3. Database & run

npm install
npm run db:migrate
npm run dev

Open http://localhost:3000, sign in with Google — you're now the admin.

4. Configure

  1. Connectors → add Zendesk (subdomain + agent email + API token) or Chargebee (site + API key). Use Test connection before saving.

  2. Roles → create a role and pick an access level per connector.

  3. Users → invite teammates by email and assign their role.

5. Connect an MCP client

claude mcp add --transport http gateway http://localhost:3000/api/mcp

or in a JSON-config client (Cursor, Claude Desktop):

{ "mcpServers": { "gateway": { "type": "http", "url": "http://localhost:3000/api/mcp" } } }

The client discovers the OAuth metadata, registers itself dynamically, and opens a browser for Google sign-in. Consent is automatic after login.

Tool catalog

Connector

Read

Write

Zendesk

search_tickets, get_ticket, list_ticket_comments, list_users, get_user

create_ticket, update_ticket, add_ticket_comment

Chargebee

list_customers, get_customer, list_subscriptions, get_subscription, list_invoices, get_invoice

create_customer, update_customer, cancel_subscription

Chargebee's cancel_subscription targets Product Catalog 2.0 sites (cancel_for_items).

Architecture notes

  • Auth: better-auth with the Google social provider for the dashboard and its mcp plugin as the OAuth 2.1 authorization server (discovery metadata, dynamic client registration, PKCE) for MCP clients. Both share one user table.

  • MCP serving: a small built-in Streamable HTTP handler (src/app/api/mcp/route.ts, stateless JSON responses per the MCP spec) that runs on both Node and Cloudflare Workers; the tool list is rebuilt per request from the caller's current role.

  • Secrets: connector credentials are AES-256-GCM encrypted with ENCRYPTION_KEY; they are never sent to the browser.

  • DB: Drizzle ORM + PostgreSQL. npm run auth:generate regenerates src/db/auth-schema.ts after changing the better-auth config; npm run db:generate && npm run db:migrate create and apply migrations.

Dev-only login

Setting ENABLE_DEV_LOGIN=true in .env (ignored in production builds) enables email/password endpoints so you can exercise the dashboard and MCP flow without Google credentials — useful for local testing with multiple fake users.

Deploying to Cloudflare Workers

The app is pre-configured for Cloudflare via @opennextjs/cloudflare (wrangler.jsonc, open-next.config.ts). The gateway stays private on Workers exactly as it is locally: every dashboard page and MCP call requires Google sign-in, only invited emails can log in, and the dev email/password login is compiled out of production builds.

1. You need a Postgres reachable from Cloudflare — e.g. Neon, Supabase, or any managed Postgres. Run the migrations against it once:

DATABASE_URL="postgresql://user:pass@host/db" npm run db:migrate

2. Create a Hyperdrive config (Cloudflare's connection pooler; the app reads its connection string automatically from the HYPERDRIVE binding):

npx wrangler login
npx wrangler hyperdrive create mcp-gateway-db --connection-string="postgresql://user:pass@host/db"

Paste the returned id into wrangler.jsonc under hyperdrive[0].id, and set vars.BETTER_AUTH_URL to your production URL (e.g. https://mcp-gateway.<your-subdomain>.workers.dev).

3. Set secrets:

npx wrangler secret put BETTER_AUTH_SECRET   # openssl rand -base64 32
npx wrangler secret put GOOGLE_CLIENT_ID
npx wrangler secret put GOOGLE_CLIENT_SECRET
npx wrangler secret put ENCRYPTION_KEY       # openssl rand -hex 32

4. Add the production redirect URI to your Google OAuth client: https://<your-domain>/api/auth/callback/google

5. Deploy:

npm run deploy

Then open the deployed URL, sign in with Google (first login = admin), and point MCP clients at https://<your-domain>/api/mcp.

To test the Workers runtime locally before deploying: npm run preview (uses .dev.vars — copy from .dev.vars.example — and the localConnectionString in wrangler.jsonc).

Notes:

  • scripts/patch-pg-cloudflare.mjs (run automatically on npm install) fixes a broken published build of pg's Cloudflare socket adapter; without it the Workers bundle fails with Could not resolve "pg-cloudflare".

  • Old MCP access tokens expire after 1 hour; clients refresh automatically.

F
license - not found
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/anantgarg/mcp-gateway'

If you have feedback or need assistance with the MCP directory API, please join our Discord server