DEV Community

Cover image for Claude Code OAuth deep dive: how EmblemAI's MCP server handles auth
EmblemAI
EmblemAI

Posted on • Edited on • Originally published at emblemvault.ai

Claude Code OAuth deep dive: how EmblemAI's MCP server handles auth

Claude Code OAuth deep dive: how EmblemAI's MCP server handles auth

Most posts about MCP skip the auth part. Understandable — MCP is exciting, OAuth is dental work. But if you're shipping an MCP server that touches anything more sensitive than a weather API, you need OAuth to be right, and the gap between "technically compliant" and "actually works with a given MCP client" is wider than you'd hope.

This post walks through the OAuth flow EmblemAI shipped for its hosted MCP server (https://emblemvault.ai/api/mcp). It's a minimal, standards-compliant public-client flow that installs cleanly in Claude Code via RFC 9728 client ID metadata documents. If you're building an MCP server with user-scoped data, copy the pattern — and read the client-compatibility section at the end so you ship the install path your users actually run.

2026-04-23 update. This post originally claimed client_id_metadata_document_supported: true was "the RFC 7591 extension." That is incorrect. RFC 7591 is Dynamic Client Registration — a /register endpoint clients POST to. RFC 9728 is Client ID Metadata Document — the mechanism our server actually implements, which Claude Code supports natively. Clients that rely on the mcp-remote stdio bridge (Cursor, Windsurf, Gemini CLI, Claude Desktop via bridge, ElizaOS via @fleek-platform/eliza-plugin-mcp) require RFC 7591 DCR and will fail today against our discovery document. DCR support is on the roadmap. Corrections inline below.

What you need to support

Claude Code's mcp add --transport http <url> command assumes the server exposes:

  1. .well-known/oauth-authorization-server metadata at the authorization server origin.
  2. An authorize endpoint that handles PKCE (code_challenge + code_challenge_method=S256).
  3. A token endpoint that accepts a public client (no client secret).
  4. A JWKS endpoint for token verification (/.well-known/jwks.json).
  5. Loopback redirect URIs (http://127.0.0.1:<random-port>).
  6. Either RFC 9728 client_id_metadata_document_supported: true (what Claude Code consumes) or RFC 7591 dynamic client registration at /register (what mcp-remote consumes). Ship both if you want frictionless install across all MCP clients. EmblemAI currently ships RFC 9728 only; RFC 7591 DCR is next.

Skip any of these and you'll get a confusing "failed to install" error from the client with no useful log line.

EmblemAI's implementation

Discovery document:

$ curl -s https://api.emblemvault.ai/.well-known/oauth-authorization-server | jq
{
  "issuer": "https://api.emblemvault.ai",
  "authorization_endpoint": "https://api.emblemvault.ai/oauth/authorize",
  "token_endpoint": "https://api.emblemvault.ai/oauth/token",
  "jwks_uri": "https://api.emblemvault.ai/.well-known/jwks.json",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code"],
  "token_endpoint_auth_methods_supported": ["none"],
  "code_challenge_methods_supported": ["S256"],
  "client_id_metadata_document_supported": true,
  "scopes_supported": ["vault:read"]
}
Enter fullscreen mode Exit fullscreen mode

Note the absence of a registration_endpoint. That is the missing RFC 7591 piece — see the compatibility table at the end.

A few choices worth calling out:

token_endpoint_auth_methods_supported: ["none"]

Correct for public clients. Claude Code cannot keep a client secret, so the server should not require one. Anyone shipping an MCP server for a native-client use case should use none here. Do not use client_secret_basic unless your only clients are server-to-server.

client_id_metadata_document_supported: true

This is the RFC 9728 Client ID Metadata Document mechanism. The client hosts a JSON file at a URL it controls (e.g. https://claude.ai/.well-known/mcp-client-metadata) and passes that URL as its client_id. The server fetches and validates the metadata at authorize time. Claude Code uses this. It is separate from RFC 7591 Dynamic Client Registration, which is the /register POST flow most non-Claude-Code clients use.

If you advertise client_id_metadata_document_supported: true and your clients are Claude Code, you are done on the client-identification axis. If your clients also include mcp-remote-based agents, you additionally need RFC 7591 /register.

Loopback redirect URIs

The authorize page must accept http://127.0.0.1:<random-port>/callback (or http://localhost:<port>/callback) as a redirect URI. If you only allow https:// redirects, native clients cannot install.

PKCE mandatory

code_challenge_methods_supported: ["S256"] — no plain fallback. If you ship an MCP server without PKCE, a malicious process on the same machine can steal the auth code. Don't.

Token lifecycle

The token is a standard JWT signed with RS256:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "auth-key-local-001"
}
Enter fullscreen mode Exit fullscreen mode
{
  "iss": "https://api.emblemvault.ai",
  "aud": "https://agenthustle.ai",
  "sub": "<vault_id>",
  "appId": "emblem-agent-wallet",
  "scope": "vault:read",
  "exp": <unix>,
  "iat": <unix>
}
Enter fullscreen mode Exit fullscreen mode

Validate it against the JWKS at /.well-known/jwks.json. The kid in the JWT header matches a key in the JWKS; standard RS256 verification from there.

Refresh tokens are one-time-use. Present an already-used refresh token and the server returns {"error": "refresh_replayed"}. If you are building a client, write the fresh refresh token back to disk before any subsequent request, or you will burn the token and lock the user out.

Client compatibility matrix (as of 2026-04-23)

MCP client Auth path that works today
Claude Code OAuth via RFC 9728 client ID metadata document — one-command install
Claude Desktop (native MCP) OAuth via RFC 9728 — if the build supports CIMD
Cursor, Windsurf, Gemini CLI, ElizaOS, others API key via mcp-remote --header "x-api-key:..." today. OAuth via mcp-remote currently fails because mcp-remote requires RFC 7591 DCR which our authorization server does not yet advertise.

The API-key path works for every MCP client that can invoke npx mcp-remote or equivalent, because it bypasses the OAuth flow entirely. Generate a key at emblemvault.ai/welcome.

Why this matters for MCP server authors

MCP is going to eat a lot of integration work over the next year. The pattern to recommend if you are building a server that touches user-scoped data:

  1. Host the OAuth authorization server yourself if you want tight control over consent UX and scope semantics. External IDPs work, but their MCP-shaped flows are still rough.
  2. Support public clients with PKCE + loopback. Full stop. Do not require client secrets.
  3. Ship both RFC 9728 CIMD and RFC 7591 DCR. CIMD handles Claude Code with zero friction; DCR handles mcp-remote-based clients with zero friction. If you only ship one, half your client surface breaks.
  4. Use short-lived access tokens (~15 min) with one-time refresh tokens. EmblemAI's shipped config is 15 minutes, which feels right for wallet ops.
  5. Ship discovery metadata correctly on day one. .well-known/oauth-authorization-server + .well-known/jwks.json + .well-known/oauth-protected-resource for the resource server. Without these, installers silently fail.

What shipped in PR 97

The PR that enabled the Claude Code OAuth flow was PR #97 on api.emblemvault.ai. It added:

  • OAuth discovery endpoints (/.well-known/oauth-authorization-server, jwks.json).
  • PKCE/public-client authorization-code flow.
  • RFC 9728 Client ID Metadata Document support (client_id_metadata_document_supported: true).
  • Authorize UX via Emblem Auth.
  • Loopback redirect compatibility for native clients.

The actual MCP server itself was already deployed from the hustle-v2 side. PR 97 was the auth unlock for Claude Code. The RFC 7591 DCR follow-up is tracked as a separate workstream.

Try it

claude mcp add --transport http emblem https://emblemvault.ai/api/mcp
Enter fullscreen mode Exit fullscreen mode

And if you are working on an MCP server of your own, these are the corners to get right first — including the RFC 7591 corner we are fixing now.

Links

Top comments (0)