eq-mcp-gateway
OfficialActs as an OAuth 2.1 authorization server that bridges to Auth0 for MCP clients: implements dynamic client registration (a capability Auth0 lacks), runs the /authorize → Auth0 sign-in → /callback round trip, exchanges authorization codes for tokens, and stores the upstream Auth0 access token on the issued grant so the user's own token is forwarded on every tool call rather than a shared gateway credential.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@eq-mcp-gatewayshow my org's ranking metrics for the Northeast region"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
eq-mcp-gateway
Remote MCP gateway for Equity Quotient. It owns protocol and identity; services stay REST.
Built as a general connector entrypoint rather than an adapter for one service — eq-data
or Rosa's web-search tools are plausible second and third connections, and adding one is a
tool module plus an entry in SERVICE_CONNECTIONS, not a change to the transport or the auth flow.
Design invariants
These are the things not to break; each exists for a reason that already bit us.
Forward the caller's own token. Never hold a service credential. Organization scoping in the service keys off whichever user's token arrives. A shared credential would flatten every org boundary at once, silently, for every user. The user's Auth0 access token rides on the OAuth grant (
props) and is forwarded on every tool call.Composition stays in the service. This Worker does not orchestrate multi-step queries. If a tool needs resolve-then-fetch or fetch-then-rank, whichever service can reach the data exposes it as one endpoint. Composing here pulls query semantics into a second codebase and turns one round trip into several.
Everything is read-only, and every tool says so via
annotations.readOnlyHint. Both directories check this at review.Tool names and shapes are the client contract. Hosting, runtime and platform can change silently. Names, parameters and return shapes cannot, once published — they become an API consumed by people you cannot contact.
Related MCP server: mcp-auth-proxy
Two surfaces, one tool set
Route | Auth | Who |
| OAuth 2.1 — the Worker is the authorization server | Claude, ChatGPT, anything that cannot be handed a token out of band |
| Raw bearer pass-through | eq-app, which already holds the user's Auth0 token |
Separate paths rather than one route with two auth modes, because this is also the seam the narrower connector envelope needs later: same tools, different entitlements, decided by which door the request arrived through.
How the OAuth flow works
Auth0 does not support dynamic client registration. MCP clients require it. That mismatch is the entire reason this layer exists.
MCP client gateway (Worker) Auth0
│ register ──────────────▶ /oauth/register
│ authorize ─────────────▶ /authorize ───────────────▶ /authorize
│ (user signs in)
│ /callback ◀──────────────── code
│ exchange ─────────────────▶ /oauth/token
│ ◀──────────── redirect + gateway code
│ token ─────────────────▶ /oauth/token
│ tools/call ────────────▶ /mcp ──── forwards the user's Auth0 token ──▶ serviceThe gateway issues its own tokens to MCP clients and stores the upstream Auth0 access token on the grant. The service therefore still sees the person, not the gateway.
Run locally
npm install
cp .dev.vars.example .dev.vars # add AUTH0_CLIENT_SECRET
npm run devBefore the full flow works you need, once:
An Auth0 application for the gateway (regular web app), with
https://<gateway-host>/callbackandhttp://localhost:8787/callbackas allowed callback URLs. Put its client id and the user-mgmt API identifier inwrangler.jsonc, the secret in.dev.vars.A KV namespace:
npx wrangler kv namespace create OAUTH_KV, then paste the id intowrangler.jsonc. Local dev uses a local namespace automatically.
USER_MGMT_BASE_URL and EQ_APP_BASE_URL are origins only — the route prefixes belong to
the REST client, not to configuration. Both default to the deployed dev hosts in
wrangler.jsonc, so tool calls work without anything running locally; override either in
.dev.vars to point at a local service. Listing tools needs neither.
Verified probes
All of these were run against wrangler dev:
curl -s localhost:8787/health
curl -s localhost:8787/.well-known/oauth-authorization-server # issuer, endpoints, S256
# 401 carrying the discovery pointer public clients need
curl -i -X POST localhost:8787/mcp -H 'content-type: application/json' -d '{}'
# dynamic client registration — the capability Auth0 lacks
curl -s -X POST localhost:8787/oauth/register -H 'content-type: application/json' \
-d '{"client_name":"Probe","redirect_uris":["https://example.com/cb"],
"token_endpoint_auth_method":"none","grant_types":["authorization_code"],
"response_types":["code"]}'
# first-party surface
curl -s -X POST localhost:8787/internal/mcp \
-H 'authorization: Bearer <JWT>' -H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'The MCP endpoint serves both protocol generations: the 2025-era initialize handshake,
and the stateless 2026-07-28 revision, which has no handshake at all and carries a _meta
envelope on every request. Both return the same tools.
The /authorize → Auth0 → /callback round trip is verified too, against the
eq-development tenant: a real sign-in through to a tool call returning real segment data.
The tool set
Three modules, one per source of truth. src/tools/toolkit.ts holds what they share —
result shaping, the read-only annotation, and the traced() wrapper that logs every call.
Module | Served by | Tools |
| the caller's own token, no round trip |
|
| eq-user-mgmt-service, one tool per REST endpoint |
|
| eq-user-mgmt-service and eq-app |
|
Two of those reach eq-app rather than eq-user-mgmt-service, because only eq-app can serve
them: rank_places needs the nationwide insight values and PlanetScale geography
names, and available_data needs the dashboard-tree-to-insight rules that live where the
dashboards are rendered. Both are /api/tools/* routes there, bearer-authed, sharing one
implementation with Rosa's own code.
available_data is the one tool with no counterpart in Rosa: eq-app pastes the
organization's whole KPI table into Rosa's system prompt, so it never has to ask.
Each tool declares a title as well as a name — a plain sentence ("Find a county or city
inside a state") alongside the identifier the model calls. Both are user-visible: clients
show the title where they have room, and fall back to the name in compact surfaces like the
tool-search step, which is why the names read as words rather than as functions. Change a
title freely; a name is covered by invariant 4.
Pending
Consent screen — /authorize currently redirects straight to Auth0. Auth0
authenticates the person but does not tell them which MCP client is being granted
access. Directory review expects explicit permission, so this is required before listing.
webSearch, the one Rosa tool not ported. It is the only one that reaches outside the
platform, and an MCP client generally has its own.
Authorized regions. eq-app pastes a table of the org's region ids into Rosa's prompt;
nothing exposes them over HTTP, so a connector resolves geography from scratch through
find_place.
Per-client tool allowlists, so the public set and Rosa's set can differ. The set is decided when a client connects, not per message.
The narrower external envelope — a connector session should not reach as far as a first-party one. Enforced in the service on a token scope, never here.
This server cannot be deployed
Maintenance
Related MCP Connectors
Self-hosted federated MCP gateway: one OAuth 2.1 MCP server in front of N apps, user-level scopes.
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
An authenticated remote MCP server for user-owned devices and one-shot capability invocation.
Public, read-only MCP server for FarmNeural company facts, packages, and capabilities.
Related MCP Servers
- FlicenseNot gradedqualityNot gradedmaintenanceAggregates multiple MCP servers behind a single, secure endpoint with unified tool/resource discovery, OAuth authentication, and resilient request routing. Enables users to manage and interact with multiple MCP backends through one centralized interface with load balancing and circuit breakers.2-
- AlicenseNot gradedqualityCmaintenanceAuthenticating reverse proxy for MCP servers providing credential isolation, OAuth2 token management, and composite tool aggregation.BSD Zero Clause
- AlicenseNot gradedqualityAmaintenanceA spec-compliant remote MCP server with built-in OAuth 2.1 and Dynamic Client Registration, enabling Notion Custom Agents to connect via 'Sign in with OAuth' without bearer tokens. It supports SSO federation to Google and Microsoft Entra, and includes basic tools like whoami, echo, and slow_task.MIT
- FlicenseNot gradedqualityCmaintenanceA universal MCP server for registering internal, external, and OpenAPI-based APIs as MCP tools. It exposes them to MCP clients via Streamable HTTP and provides admin portal, RBAC/session auth, credential injection, and audit logging.-