Skip to content

MCP OAuth registration ​

JamRelay separates two unrelated authorization layers:

text
AI / MCP client
   │
   │ MCP OAuth or static Bearer
   ▼
JamRelay
   │
   │ Spotify OAuth
   ▼
Spotify Web API

The MCP client never receives the Spotify client secret, Spotify refresh token, token-encryption key, or JamRelay owner approval secret.

One deployment, several clients ​

A single JamRelay deployment can now accept all of these simultaneously:

  1. operator-defined pre-registered clients from MCP_OAUTH_CLIENTS_PATH;
  2. dynamically registered clients through /oauth/register when MCP_OAUTH_DCR_ENABLED=true;
  3. public OAuth clients using PKCE with token_endpoint_auth_method=none;
  4. confidential clients using client_secret_basic or client_secret_post;
  5. the optional static MCP_API_KEY Bearer path for debugging/manual clients.

Clients must be registered in the current static registry or through DCR.

Current MCP registration landscape ​

The MCP 2026-07-28 specification moved toward Client ID Metadata Documents (CIMD). JamRelay currently supports the static registry and RFC 7591 DCR; CIMD is not advertised until a hardened metadata-fetch path is available.

JamRelay therefore implements:

text
pre-registration  ✅
DCR               ✅
CIMD              not advertised yet

CIMD requires the authorization server to fetch a client-controlled HTTPS metadata URL. JamRelay does not advertise CIMD until that outbound-fetch path can be implemented with a deliberately hardened SSRF/DNS-rebinding model rather than adding an arbitrary server-side fetch feature casually.

Client behavior checked on 2026-09-12 ​

ClientVendor-documented behaviorBest current JamRelay path
ChatGPT custom MCP appuser-defined OAuth client with an exact callback; confidential client credentials supportedstatic registry
Claude hosted surfacesDCR is supported; a custom client ID and client secret can also be supplied. Claude documents https://claude.ai/api/mcp/auth_callback as its callback.DCR, or static pre-registration
Claude Coderemote MCP OAuth uses MCP discovery/registration behavior; use DCR where supported or pre-register credentials when neededDCR or static pre-registration
Gemini CLIautomatic discovery and DCR; random localhost callback by default; validates RFC 9207 issDCR public client
CursorOAuth is supported for remote MCP; static OAuth credentials are documented. Current callbacks are https://www.cursor.com/agents/mcp/oauth/callback for web/Agents and http://localhost:8787/callback for Desktop.DCR or static registry
VS Code / Copilotstarts with DCR and can fall back to configured client credentials; documented redirect URLs are http://127.0.0.1:33418 and https://vscode.dev/redirectDCR
WindsurfOAuth is supported for stdio, Streamable HTTP and SSE; public docs do not currently specify one universal callback/registration contractOAuth discovery when supported; Bearer remains a fallback
MCP Inspectorstandards-oriented OAuth debuggingDCR/static/Bearer depending on test

NOTE

Client support does not mean every row has been manually tested end-to-end against this exact release. JamRelay implements the vendor-documented protocol path; keep untested clients labelled as such until verified.

Automatic DCR flow ​

For clients such as Gemini CLI, Claude Code, or VS Code:

text
1. client calls /mcp without a token
2. JamRelay returns 401 + WWW-Authenticate resource_metadata
3. client reads protected-resource metadata
4. client reads authorization-server metadata
5. client POSTs its metadata to /oauth/register
6. client opens /oauth/authorize with PKCE S256
7. owner enters MCP_OAUTH_OWNER_SECRET
8. JamRelay redirects to the client's registered callback with code + state + iss
9. client POSTs /oauth/token
10. client calls /mcp with the access token
11. refresh tokens rotate when refreshed

A DCR registration alone grants no Spotify access. Owner approval is still required before JamRelay issues an authorization code.

Static multi-client registry ​

Start from:

text
examples/mcp-oauth-clients.example.json

and copy it to the path configured by MCP_OAUTH_CLIENTS_PATH.

Example:

json
{
  "clients": [
    {
      "clientId": "chatgpt-jamrelay",
      "clientName": "ChatGPT",
      "clientSecret": "REPLACE_ME",
      "redirectUris": ["https://chatgpt.com/connector/oauth/REPLACE_WITH_CALLBACK_ID"],
      "tokenEndpointAuthMethods": ["client_secret_basic", "client_secret_post"]
    },
    {
      "clientId": "vscode-jamrelay",
      "clientName": "VS Code",
      "redirectUris": ["http://127.0.0.1:33418", "https://vscode.dev/redirect"],
      "tokenEndpointAuthMethods": ["none"],
      "applicationType": "native"
    }
  ]
}

Restart JamRelay after changing the static registry.

Redirect URI policy ​

JamRelay always validates the authorization request against redirect URIs registered for that exact client.

It accepts:

  • HTTPS callbacks;
  • HTTP callbacks only on localhost, 127.0.0.1, or ::1;
  • private-use URI schemes only for DCR/static clients explicitly declared as application_type=native / applicationType: "native".

It rejects ordinary remote HTTP callbacks and content/file schemes such as file:, data:, and javascript:.

This supports browser-hosted clients and RFC 8252-style native clients without creating a broad arbitrary-HTTP redirect policy.

Resource and issuer binding ​

The protected resource is:

text
https://mcp.example.com/mcp

JamRelay validates a supplied OAuth resource parameter against that endpoint.

Authorization redirects include:

text
iss=https://mcp.example.com

Gemini CLI explicitly requires this RFC 9207 issuer binding, and the MCP 2026-07-28 specification also hardened authorization around iss.

Discovery endpoints ​

JamRelay serves both protected-resource discovery forms:

text
/.well-known/oauth-protected-resource
/.well-known/oauth-protected-resource/mcp

The second path-suffixed form supports clients that follow RFC 9728 discovery from a protected resource hosted at /mcp.

Authorization-server metadata is available at:

text
/.well-known/oauth-authorization-server

Official references checked 2026-09-12 ​

Unofficial community project. Not affiliated with Spotify or AI platform vendors.