Spaces:
Running
title: Spotify MCP Server
emoji: 🎧
colorFrom: green
colorTo: gray
sdk: docker
app_port: 7860
license: mit
Spotify MCP Server
A self-hostable, Pydantic-first MCP v2 server that bundles Spotify Web API operations into nine agent-friendly tools. It can run locally with operating-system keyring storage or remotely with Scalekit OAuth 2.1, CIMD client discovery, and encrypted per-user credentials in Neon Postgres.
The server targets MCP 2026-07-28, uses the official Python mcp v2 SDK, and serves stateless
Streamable HTTP. Local mode is loopback-only. Hosted mode refuses to start without OAuth, database,
and encryption configuration.
Tools
| Tool | Purpose | Side effects |
|---|---|---|
search_catalog |
Search albums, artists, playlists, tracks, shows, episodes, and audiobooks | None |
get_item |
Fetch heterogeneous items with natural child expansions | None |
player_status |
Fetch playback, devices, and queue together | None |
player_control |
Run up to 20 ordered playback actions | Changes playback |
playlist_read |
List playlists and retrieve owned/collaborative contents | None |
playlist_modify |
Create and mutate playlists in an ordered batch | Changes playlists |
library_read |
Read saved items, followed artists, and membership | None |
library_modify |
Save/remove/follow/unfollow up to 40 URIs per action | Changes library |
listening_activity |
Read recent tracks and top tracks/artists | None |
Prompts and resources
The server exposes four curated workflows that compose the nine tools without adding another API surface:
| Prompt | Purpose |
|---|---|
catch_up_on_podcasts |
Prioritize unfinished or unplayed episodes from saved shows |
weekly_music_recap |
Summarize patterns in recent plays and top music |
build_playlist_for_mood |
Search and create a private playlist for a mood or activity |
now_playing_briefing |
Produce a compact playback, device, progress, and queue summary |
The intentionally small resource catalog contains spotify://me. It returns the current user's
Spotify display name and stable account_id; dynamic playback, library, and playlist state remains
behind tools.
Podcast transcripts and inferred podcast listening history are intentionally out of scope because Spotify does not expose them through the supported Web API. The server does not embed, train on, download, or persist Spotify content.
Local setup
Prerequisites:
- Python 3.11+
uv- A Spotify developer application
In the Spotify developer dashboard, register
http://127.0.0.1:8765/callbackas a redirect URI.Install the locked project environment and create local configuration:
uv sync --locked --extra dev cp .env.example .envSet
SPOTIFY_CLIENT_IDin.envor your process environment.Authorize once; the refresh token is stored in the operating system keychain while access tokens remain in memory:
uv run spotify-mcp-authStart the loopback server:
uv run spotify-mcp-server
The MCP endpoint is http://127.0.0.1:8000/mcp by default. Configure an MCP v2 client to use that
Streamable HTTP URL. The server rejects non-loopback MCP_HOST values.
Hosted deployment
The included Dockerfile is configured for a Hugging Face Docker Space on port 7860. Hosted mode
uses Scalekit as the MCP authorization server and creates a single spotify_credentials table in
Neon. The table contains a Scalekit subject, an encrypted Spotify refresh token, and an update
timestamp. Spotify access tokens and Spotify content are not persisted.
Create a Spotify developer application, add each permitted Spotify account to its development allowlist, and register this redirect URI:
https://<space-owner>-<space-name>.hf.space/spotify/callbackIn Scalekit, register
https://<space-owner>-<space-name>.hf.space/mcpas the MCP Server URL and enable CIMD. Configure DCR separately only if compatibility with a non-CIMD client is required.Create a Neon database and use its pooled connection string for
DATABASE_URL.Generate a stable encryption key once:
uv run python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"Configure the Space variables and secrets listed below, then deploy this repository to the Docker Space. The remote MCP endpoint is the same URL registered in Scalekit.
After a user authorizes the MCP client through Scalekit, their first Spotify tool request returns a
short-lived /spotify/connect link. They authorize the shared Spotify developer application with
PKCE, return to the MCP client, and retry the request. Spotify's development-account limits still
apply; this server does not bypass them.
Configuration
| Variable | Default | Purpose |
|---|---|---|
SPOTIFY_CLIENT_ID |
— | Public Spotify application client ID; required for authorization |
SPOTIFY_REDIRECT_URI |
http://127.0.0.1:8765/callback |
Registered loopback OAuth callback |
SPOTIFY_API_BASE_URL |
https://api.spotify.com/v1 |
Spotify API base; primarily useful in tests |
SPOTIFY_ACCOUNTS_BASE_URL |
https://accounts.spotify.com |
Spotify OAuth base; primarily useful in tests |
MCP_HOST |
127.0.0.1 |
Loopback bind address only |
MCP_PORT |
8000 |
Local Streamable HTTP port |
Hosted mode sets MCP_DEPLOYMENT_MODE=hosted in the Docker image and additionally requires:
| Variable | Storage | Purpose |
|---|---|---|
MCP_SERVER_URL |
Variable | Exact public endpoint ending in /mcp; also the validated token audience |
SCALEKIT_ENVIRONMENT_URL |
Secret or variable | Scalekit environment issuer URL |
SCALEKIT_RESOURCE_ID |
Variable | Scalekit MCP resource ID beginning with res_ |
DATABASE_URL |
Secret | Neon pooled PostgreSQL connection string |
TOKEN_ENCRYPTION_KEY |
Secret | Stable base64 key used to derive separate refresh-token and OAuth-state keys |
SPOTIFY_CLIENT_ID |
Secret or variable | Public client ID of the hosted Spotify developer application |
MCP_ALLOWED_SUBJECTS |
Variable, optional | Comma-separated Scalekit user IDs allowed to connect Spotify |
Spotify OAuth credentials are never returned from MCP tools. Access tokens remain memory-only, and
refresh tokens are stored either by the operating system credential backend or encrypted in Neon.
If MCP_ALLOWED_SUBJECTS is unset, any user whom your Scalekit and Spotify configurations admit may
connect; set it for a server-side allowlist.
The server validates Scalekit access tokens locally against the public signing keys at
<SCALEKIT_ENVIRONMENT_URL>/keys; Scalekit client credentials are not required by the runtime.
Development
uv run ruff check .
uv run ruff format --check .
uv run pytest
Tests use mocked Spotify HTTP responses and the MCP in-memory client; they do not require a Spotify account or make network calls. A live smoke test additionally needs an authorized account and, for playback controls, an active Spotify device and any account capabilities Spotify requires.
API compatibility and policy
The HTTP client enforces an explicit allowlist audited against Spotify's post-February 2026 Web API
surface. Deprecated bulk and type-specific mutation endpoints are rejected before a request is
sent. Request limits mirror Spotify's published limits. A 429 response is retried within a small
bounded budget and, if still unsuccessful, is returned as a structured warning alongside any
successful partial results.
Before distribution, recheck Spotify's current Developer Terms for the intended MCP host and LLM runtime. Spotify content must not be used to train or fine-tune a model, and this server provides no long-lived content cache or embeddings.
License
MIT