Skip to content

MCP Gateway — Client Setup

Operational guide for connecting Claude Code and Claude Desktop to the self-hosted agentgateway MCP gateway. agentgateway fronts six upstream MCP servers behind a single host (mcp-gw.fzymgc.house) and exposes each at its own path. Two auth models run side by side: engram uses Keycloak OAuth with real RFC 7591 Dynamic Client Registration (DCR), and the SaaS servers use a per-client virtual key.

Replaces the LiteLLM MCP gateway, which is fully decommissioned (hl-0sr Phase 5; litellm.fzymgc.house no longer resolves and the litellm HostMapping is gone). All MCP clients MUST use mcp-gw.fzymgc.house; there is no LiteLLM fallback.

PropertyValue
Gateway hosthttps://mcp-gw.fzymgc.house
MCP endpoint (per server)https://mcp-gw.fzymgc.house/mcp/<server>
Serversengram, exa, firecrawl, context7, fal, deepwiki
engram authOAuth/JWT — Keycloak mcp-public client (PKCE + DCR, no static key)
SaaS authVirtual key — Authorization: Bearer <vk_claude_code>
Client virtual keyVault secret/fzymgc-house/cluster/agentgateway, property vk_claude_code
Admin UIhttps://agentgateway.fzymgc.house (shared oauth2-proxy forward-auth)
Claude Code supportNative — handles OAuth and custom headers directly
Claude Desktop supportengram natively (OAuth connector); SaaS via the mcp-remote bridge

agentgateway gates each route differently, so the client credential depends on the server:

  • engram — the route enforces JWT validation (mcp-engram-oauth policy, Keycloak mcp-public issuer). The client performs the OAuth 2.1 PKCE flow, registering itself dynamically via RFC 7591 DCR (agentgateway proxies the anonymous /register call to Keycloak — see ADR hl-ll5p), and presents the resulting bearer JWT. No static key is minted or stored on the client. agentgateway validates the token, then re-attaches it to the upstream so engram derives memory ownership from the verified email claim.
  • SaaS servers (exa, firecrawl, context7, fal, deepwiki) — the route enforces apiKeyAuthentication (Strict) against the agentgateway-vkeys accept-list. The client sends its own virtual key as Authorization: Bearer <vk_claude_code>. agentgateway then injects the real per-upstream API key (from Vault) on the way out, so upstream secrets never reach the client. deepwiki needs no upstream key.

Because engram is OAuth and the SaaS servers are virtual-key, they are added to a client as separate entries — engram with no header, the rest with the bearer header.

Virtual keys are GitOps-governed, not minted via a runtime API. The accept-list lives in the agentgateway-vkeys ExternalSecret (argocd/app-configs/agentgateway/secrets.yaml), templated from Vault:

Terminal window
# Read the existing human client key (read-only)
vault kv get -field=vk_claude_code secret/fzymgc-house/cluster/agentgateway

To add a new client key: add a vk_<name> property to Vault fzymgc-house/cluster/agentgateway, then add a matching accept-list entry to secrets.yaml (the value is JSON: {"key":"{{ .vk_<name> }}","metadata":{"group":"<group>"}}). ArgoCD + ESO propagate it. Existing keys: vk_claude_code (human), vk_octopus (service), vk_engram_embedder (embedder).

engram (OAuth — Claude Code runs the PKCE flow and caches the token):

Terminal window
claude mcp add --transport http engram https://mcp-gw.fzymgc.house/mcp/engram --scope user

On first use Claude Code opens the Keycloak login (the mcp-public consent flow) and stores the token; re-auth is handled automatically on expiry.

Each SaaS server (virtual key in a header). Add only the ones you need:

Terminal window
claude mcp add --transport http exa https://mcp-gw.fzymgc.house/mcp/exa \
--header "Authorization: Bearer <vk_claude_code>" --scope user
  • --scope user makes the server available across all projects; use --scope local to limit it to the current repo.
  • Repeat with firecrawl, context7, fal, deepwiki, swapping the server name and /mcp/<server> path; the same vk_claude_code works for all of them.
  • All flags must precede the server name in the claude mcp add command.
  • engram — add as a remote connector pointing at https://mcp-gw.fzymgc.house/mcp/engram; Claude Desktop’s OAuth connector completes the Keycloak PKCE + DCR flow natively (no bridge needed).
  • SaaS servers — Claude Desktop has no field for a static auth header on remote connectors, so the mcp-remote stdio bridge injects it. Edit ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"exa": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://mcp-gw.fzymgc.house/mcp/exa",
"--header", "Authorization:${AGENTGATEWAY_VK}"
],
"env": {
"AGENTGATEWAY_VK": "Bearer <vk_claude_code>"
}
}
}
}

Header-quoting gotcha. Claude Desktop’s argument parser mangles header values that contain spaces (like Bearer <vk>). Put the value in an env var and write the --header flag with no space after the colon.

Fully quit Claude Desktop (⌘Q) and relaunch — it reads the config only at startup.

engram (OAuth) — confirm the connected state and a memory round-trip:

Terminal window
claude mcp get engram # shows connected state + tool count after OAuth

A SaaS server — smoke-test the virtual key directly (no client needed):

Terminal window
curl -sS -X POST https://mcp-gw.fzymgc.house/mcp/exa \
-H "Authorization: Bearer <vk_claude_code>" \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}' | jq '.result.tools[].name'

An unauthenticated request to a gated route returns 401 with a WWW-Authenticate: Bearer resource_metadata=… header (engram) or a plain 401 (SaaS) — a 200 with tools confirms the key and endpoint, isolating any remaining fault to client config.

LiteLLM is fully decommissioned (hl-0sr Phase 5); litellm.fzymgc.house no longer resolves and there is no LiteLLM fallback to roll back to. If an agentgateway MCP route fails, the rollback is a GitOps revert of the agentgateway app manifests (ArgoCD), not a client repoint. For the historical LiteLLM endpoint/key shape, see the git history of this file. New work MUST NOT target litellm.fzymgc.house.

engram: client never connects / no OAuth prompt

Section titled “engram: client never connects / no OAuth prompt”

The route requires a valid mcp-public token. Confirm the server URL is exactly https://mcp-gw.fzymgc.house/mcp/engram; a wrong path (e.g. /mcp) returns 404 route not found rather than triggering the OAuth flow.

The virtual key is wrong, not sent, or sent without the Bearer scheme. Re-run the Verify curl; a 200 with tools confirms the key and endpoint.

Claude Desktop SaaS server fails to authenticate

Section titled “Claude Desktop SaaS server fails to authenticate”

Almost always the header-quoting gotcha — confirm the --header arg is Authorization:${AGENTGATEWAY_VK} (no space after the colon) and that env.AGENTGATEWAY_VK holds Bearer <vk>. Quit and relaunch after any edit.

  • AI Tooling — general MCP server configuration
  • Services catalog — agentgateway architecture and the canonical .mcp.json block
  • Secrets & Vault Paths — Vault path conventions
  • agentgateway manifests: argocd/app-configs/agentgateway/ (routes, policies, and the agentgateway-vkeys accept-list in secrets.yaml)