Skip to main content
Glama
equityquotient

eq-mcp-gateway

Official

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.

  1. 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.

  2. 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.

  3. Everything is read-only, and every tool says so via annotations.readOnlyHint. Both directories check this at review.

  4. 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

/mcp

OAuth 2.1 — the Worker is the authorization server

Claude, ChatGPT, anything that cannot be handed a token out of band

/internal/mcp

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 ──▶ service

The 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 dev

Before the full flow works you need, once:

  1. An Auth0 application for the gateway (regular web app), with https://<gateway-host>/callback and http://localhost:8787/callback as allowed callback URLs. Put its client id and the user-mgmt API identifier in wrangler.jsonc, the secret in .dev.vars.

  2. A KV namespace: npx wrangler kv namespace create OAUTH_KV, then paste the id into wrangler.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

identity.ts

the caller's own token, no round trip

my_organization

platform.ts

eq-user-mgmt-service, one tool per REST endpoint

dashboard_setup, browse_places, place_details, browse_place_sets, place_set_details

analytics.ts

eq-user-mgmt-service and eq-app

available_data, metric_values, find_place, find_region_in_state, rank_places

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.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    Not graded
    maintenance
    Aggregates 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
    -
  • A
    license
    Not graded
    quality
    A
    maintenance
    A 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
  • F
    license
    Not graded
    quality
    C
    maintenance
    A 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.
    -