Configuration
Use jamrelay setup or jamrelay config for managed installations; source deployments may still copy .env.example to .env. Never commit private configuration or runtime token stores.
The complete variable table is generated from .env.example:
Open the generated environment reference →
Sources of truth
src/config.ts runtime validation/defaults
.env.example public configuration contract + human notes
↓
npm run docs:generate
↓
docs/environment-reference.mdWhen adding a variable, update the runtime schema and .env.example; do not hand-edit five translated tables.
Storage defaults
Managed installations use platform-specific directories chosen by setup; see installation. Existing .env source deployments retain their paths. Legacy local development defaults:
./data/mcp-oauth.json
./data/mcp-oauth-clients.jsonProduction Docker (NODE_ENV=production):
/data/mcp-oauth.json
/data/mcp-oauth-clients.jsonAll paths can be overridden.
Provider credentials are stored by connection ID in the encrypted provider credential store. It is separate from the MCP OAuth store.
SoundCloud provider
SoundCloud is disabled unless all three variables are set. The provider uses OAuth 2.1 Authorization Code + PKCE and stores its rotating tokens in the encrypted provider credential store; the client secret and tokens must never be committed.
SOUNDCLOUD_CLIENT_ID=
SOUNDCLOUD_CLIENT_SECRET=
SOUNDCLOUD_REDIRECT_URI=http://127.0.0.1:5267/auth/providers/soundcloud/callback
PROVIDER_CREDENTIAL_STORE_PATH=./data/provider-credentials.json
TOKEN_ENCRYPTION_KEY=<32-byte-base64-key>SoundCloud currently exposes identity, track catalog, playlist reads and playlist writes through the generic provider surface. Spotify Connect, library, playback and listening-history capabilities are not claimed for SoundCloud.
Apple Music provider
Apple Music does not use Spotify-style OAuth. The server signs a short-lived Developer Token with a Media Services .p8 private key. Catalog requests use that token. Library playlists require a Music User Token obtained by MusicKit on an Apple platform or in a browser/client context; a server-only process cannot securely mint it. An operator may submit that token through the owner-protected POST /auth/apple-music/user-token route using the x-jamrelay-owner-secret header. The token is encrypted at rest and never returned by MCP. Apple Music currently exposes catalog search/get and library playlist list/get/create/add; remove, reorder and replace are explicitly unsupported.
YouTube provider
YouTube uses Google's OAuth 2.0 web-server flow with the youtube.force-ssl scope. It integrates the official YouTube Data API v3, whose playlist items are videos. It does not use or imply an official YouTube Music library API. Search and playlist operations report estimated Data API quota units: search.list currently costs 1 unit, with a separate default daily limit of 100 calls, and playlistItems.insert costs 50 units per call. Tokens are encrypted by connection ID and refresh tokens are handled through Google's OAuth flow.
MCP authentication
JamRelay can accept OAuth and an optional static Bearer key at the same time.
MCP_AUTH_MODE=bearer enables MCP_API_KEY as an additional authentication path.
MCP_AUTH_MODE=none disables only the static API key. It does not make an OAuth-protected /mcp endpoint public.
OAuth owner secret
OAuth authorization is enabled when an owner secret is configured:
MCP_OAUTH_OWNER_SECRET=<STRONG_RANDOM_SECRET>That secret is entered only on JamRelay's owner approval page. It is never sent to the MCP client.
Dynamic clients
For Claude, Gemini CLI, VS Code, and other clients that can register automatically:
MCP_OAUTH_OWNER_SECRET=<STRONG_RANDOM_SECRET>
MCP_OAUTH_DCR_ENABLED=trueSeveral pre-registered clients
Use:
MCP_OAUTH_CLIENTS_PATH=./data/mcp-oauth-clients.jsonand start from examples/mcp-oauth-clients.example.json.
The registry can contain ChatGPT, Cursor, VS Code, Claude, or any other known client simultaneously. DCR may remain enabled for automatic clients at the same time.
Local Bearer-only development
PUBLIC_BASE_URL=http://127.0.0.1:5267
HOST=127.0.0.1
PORT=5267
MCP_AUTH_MODE=bearer
MCP_API_KEY=GENERATE_A_SEPARATE_RANDOM_VALUE
PROVIDER_CREDENTIAL_STORE_PATH=./data/provider-credentials.json
MCP_OAUTH_STORE_PATH=./data/mcp-oauth.json
TRUST_PROXY=falseLeave MCP_OAUTH_OWNER_SECRET empty if you do not want OAuth at all.
Remote deployment: ChatGPT + automatic OAuth clients
PUBLIC_BASE_URL=https://mcp.example.com
HOST=0.0.0.0
PORT=5267
MCP_AUTH_MODE=bearer
MCP_API_KEY=GENERATE_A_SEPARATE_RANDOM_VALUE
MCP_OAUTH_OWNER_SECRET=GENERATE_A_SEPARATE_RANDOM_VALUE
MCP_OAUTH_DCR_ENABLED=true
MCP_OAUTH_STORE_PATH=/data/mcp-oauth.json
MCP_OAUTH_CLIENTS_PATH=/data/mcp-oauth-clients.json
TRUST_PROXY=trueThis configuration lets the existing ChatGPT static client coexist with DCR clients without changing ChatGPT's callback.
Generating secrets
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"Use separate random values for the encryption key, API key, OAuth client secret, and owner secret. TOKEN_ENCRYPTION_KEY must decode from base64 to exactly 32 bytes. PLAYLIST_READ_CONCURRENCY controls the bounded worker count used for safe offset-based playlist pagination. Cursor-based providers remain sequential.
Provider authorization
Follow the owner consent flow before connecting or replacing a provider account. Configure all required values in a provider group or leave the entire group empty. Operations describes local administration; no web management panel is required.