MCP OAuth
JamRelay includes an OAuth authorization server for remote MCP clients. Spotify OAuth is separate and continues to use the SPOTIFY_* variables.
Supported features
- Authorization Code flow;
- PKCE
S256enforced; - exact per-client redirect validation;
- RFC 9207
issin authorization responses; - RFC 9728 protected-resource metadata at root and path-suffixed discovery URLs;
- authorization-server metadata;
- MCP
resourcevalidation; - one-hour access tokens;
- rotating refresh tokens;
- public clients with
token_endpoint_auth_method=none; - confidential clients with
client_secret_basicorclient_secret_post; - multiple static clients;
- RFC 7591 Dynamic Client Registration for deployed-client compatibility;
- owner approval protected by
MCP_OAUTH_OWNER_SECRET.
Recommended configuration for automatic clients
For clients that support DCR:
PUBLIC_BASE_URL=https://mcp.example.com
MCP_OAUTH_OWNER_SECRET=<STRONG_RANDOM_SECRET>
MCP_OAUTH_DCR_ENABLED=trueNo client ID or callback needs to be created manually. The client registers itself first.
Discovery chain:
/.well-known/oauth-protected-resource
/.well-known/oauth-protected-resource/mcp
/.well-known/oauth-authorization-server
/oauth/register
/oauth/authorize
/oauth/tokenExisting ChatGPT setup stays valid
The original single pre-registered client variables remain supported:
MCP_OAUTH_CLIENT_ID=chatgpt-jamrelay
MCP_OAUTH_CLIENT_SECRET=<STRONG_RANDOM_SECRET>
MCP_OAUTH_REDIRECT_URI=<EXACT_CALLBACK_FROM_CHATGPT>
MCP_OAUTH_OWNER_SECRET=<OWNER_APPROVAL_SECRET>That client can coexist with DCR and the multi-client registry.
Multiple static clients
Set MCP_OAUTH_CLIENTS_PATH to a JSON registry. Locally its default is:
./data/mcp-oauth-clients.jsonand in production Docker:
/data/mcp-oauth-clients.jsonStart from:
examples/mcp-oauth-clients.example.jsonEach static client can have several redirect URIs. Clients without a secret can use tokenEndpointAuthMethods: ["none"].
DCR security model
POST /oauth/register is unauthenticated by design because an unknown MCP client must obtain a client ID before authorization can begin. Registration does not authorize Spotify access.
JamRelay limits the exposed registration surface by:
- OAuth endpoint rate limiting;
- a bounded dynamic-client registry;
- short retention for registrations that were never approved;
- eventual pruning of inactive approved registrations;
- mandatory owner approval before code issuance;
- PKCE
S256; - redirect scheme/host validation;
- hashed storage of dynamically generated client secrets.
Disable DCR if you only want pre-registered clients:
MCP_OAUTH_DCR_ENABLED=falseNative clients and callbacks
Native/CLI clients may register a random loopback callback such as:
http://localhost:49152/oauth/callbackJamRelay accepts loopback HTTP callbacks. For clients that explicitly register as application_type=native, it also accepts private-use callback schemes while rejecting dangerous content/file schemes.
Persistent OAuth store
OAuth state defaults to:
./data/mcp-oauth.jsonlocally and:
/data/mcp-oauth.jsonin the production Docker image.
The store contains hashed authorization codes/tokens, refresh-token state, and DCR metadata. Keep it persistent and private.
CIMD status
The MCP 2026-07-28 specification prefers Client ID Metadata Documents (CIMD) and deprecates DCR long-term. JamRelay intentionally does not advertise CIMD in this release.
Supporting CIMD on the authorization-server side means fetching client-controlled HTTPS metadata. That should be added only with deliberate SSRF and DNS-rebinding protections. Current compatibility is provided through pre-registration plus DCR.
See OAuth and multi-client compatibility for client-specific behavior.