# Spotify MCP Server — Design Spec **Date:** 2026-05-22 ## Overview A Python MCP server that exposes Spotify library and playlist management as tools for Claude. Connects via stdio transport (Claude launches it as a subprocess). Authentication uses the OAuth PKCE flow via `spotipy`; tokens are cached locally and auto-refreshed. ## Project Structure ``` mcp-spotify/ ├── server.py # MCP server entry point, tool registrations ├── spotify_client.py # Thin wrapper around spotipy (auth + API calls) ├── pyproject.toml # Dependencies: mcp, spotipy, python-dotenv ├── .env.example # SPOTIPY_CLIENT_ID, SPOTIPY_CLIENT_SECRET, SPOTIPY_REDIRECT_URI └── README.md # Setup instructions (creating a Spotify Developer app, registering with Claude Code) ``` ## Dependencies - `mcp` — official MCP Python SDK (stdio server) - `spotipy` — Spotify Web API client with built-in OAuth and token caching - `python-dotenv` — load `.env` at startup ## MCP Tools | Tool | Description | Parameters | |---|---|---| | `list_playlists` | List the authenticated user's playlists | — | | `get_playlist_tracks` | Get all tracks in a playlist with full metadata | `playlist_id: str` | | `list_saved_tracks` | Get the user's liked/saved tracks | `limit: int = 50` | | `search_tracks` | Search Spotify for tracks | `query: str`, `limit: int = 10` | | `create_playlist` | Create a new playlist | `name: str`, `description: str = ""`, `public: bool = False` | | `add_tracks_to_playlist` | Add tracks to a playlist by URI | `playlist_id: str`, `track_uris: list[str]` | ### Track data returned by `get_playlist_tracks` Each track entry includes: - `name` — track title - `artists` — list of artist names - `album` — album name - `duration_ms` — duration in milliseconds - `duration` — duration formatted as `mm:ss` - `popularity` — Spotify popularity score (0–100) - `uri` — Spotify track URI (used to add tracks to playlists) - `added_at` — ISO timestamp of when the track was added to the playlist All fields come from a single paginated API call (`playlist_items`); no additional requests needed. ## Authentication ### Setup (once per user) 1. Create a Spotify Developer app at [developer.spotify.com](https://developer.spotify.com) 2. Set the redirect URI to `http://localhost:8888/callback` 3. Copy `.env.example` to `.env` and fill in: ``` SPOTIPY_CLIENT_ID= SPOTIPY_CLIENT_SECRET= SPOTIPY_REDIRECT_URI=http://localhost:8888/callback ``` ### First run `spotify_client.py` initializes `spotipy.Spotify` with `SpotifyOAuth`. spotipy opens the browser to Spotify's auth page. After login, Spotify redirects to `localhost:8888/callback`. spotipy exchanges the code for tokens and caches them to `~/.cache/spotipy/.cache`. ### Subsequent runs spotipy reads the cached token. If expired, it silently refreshes using the stored refresh token. No browser interaction needed. ### OAuth scopes ``` playlist-read-private playlist-read-collaborative playlist-modify-public playlist-modify-private user-library-read ``` ## Architecture & Data Flow ``` Claude (MCP client) │ stdio (JSON-RPC) ▼ server.py (mcp SDK — tool definitions + request routing) │ Python function calls ▼ spotify_client.py (spotipy wrapper) │ HTTPS ▼ Spotify Web API ``` `server.py` owns all MCP concerns: tool schemas, request parsing, response formatting. `spotify_client.py` owns all Spotify concerns: auth, API calls, pagination, data shaping. ## Error Handling - **Startup:** If required env vars are missing, raise immediately with a descriptive message before the server accepts any connections. - **API errors:** `spotipy` raises `SpotifyException` for all API-level errors. These are caught in `server.py` and returned as MCP error responses containing the HTTP status code and Spotify error message. - **Token expiry:** Handled transparently by spotipy's `SpotifyOAuth` — no special handling needed. ## Claude Code Registration Add to `~/.claude/settings.json` (or project `.claude/settings.json`): ```json { "mcpServers": { "spotify": { "command": "python", "args": ["/path/to/mcp-spotify/server.py"], "env": { "SPOTIPY_CLIENT_ID": "", "SPOTIPY_CLIENT_SECRET": "", "SPOTIPY_REDIRECT_URI": "http://localhost:8888/callback" } } } } ``` Alternatively, env vars can be loaded from `.env` by `python-dotenv` and the `env` block omitted. ## Testing No automated tests. The server is a thin adapter over `spotipy` and the Spotify Web API; mocking the API would test the wrong thing. Verification is done by running the server and calling tools from Claude.