Skip to main content
Glama
PlanningDoc.md15.6 kB
# Spotify MCP Server - Node/TypeScript Implementation Plan ## Project Overview **Project Name:** Spotify MCP Server for Claude (Node/TypeScript) **Purpose:** Build a lightweight MCP server to connect Claude Desktop with Spotify Web API **Target Platform:** Claude Desktop app (personal use) **Development Language:** TypeScript/Node.js **Project Type:** MVP / Proof of Concept **Timeline:** 1 Day Build ### Key Objectives (MVP) 1. Search for tracks, albums, and artists 2. Control basic playback (play, pause, next, previous) 3. Get current playback state 4. Create and manage playlists 5. Save tracks to library --- ## Technical Architecture ### Simplified MCP Server Structure ``` mcp-spotify/ ├── src/ │ ├── index.ts # Main MCP server entry point │ ├── spotify-client.ts # Spotify API wrapper │ ├── auth.ts # OAuth 2.0 handler │ ├── tools/ │ │ ├── search.ts # Search functionality │ │ ├── playback.ts # Playback control │ │ └── playlists.ts # Playlist management │ └── types.ts # TypeScript types ├── config.json # Configuration ├── package.json ├── tsconfig.json └── README.md ``` ### Core Components #### 1. MCP Server (index.ts) - Initialize MCP server using TypeScript SDK - Register essential tools only - Handle stdio transport - Simple error handling #### 2. Spotify Client (spotify-client.ts) - Basic HTTP wrapper for Spotify API - Token management - Simple retry logic - Response typing #### 3. Authentication (auth.ts) - OAuth 2.0 Authorization Code with PKCE - Local token storage (JSON file) - Auto-refresh on expiry - Scope configuration --- ## Spotify Web API Integration ### API Fundamentals - **Base URL:** `https://api.spotify.com/v1` - **Auth:** OAuth 2.0 Bearer tokens - **Token Lifetime:** 1 hour - **Response:** JSON ### Authentication Flow (Simplified) **Authorization Code with PKCE:** 1. Generate PKCE code verifier/challenge 2. Open browser to Spotify auth URL 3. User authorizes app 4. Receive code via local callback server 5. Exchange for tokens 6. Save tokens to `~/.mcp-spotify/tokens.json` 7. Auto-refresh when needed ### Essential Scopes ```typescript const SCOPES = [ 'user-read-private', 'user-read-playback-state', 'user-modify-playback-state', 'user-read-currently-playing', 'playlist-read-private', 'playlist-modify-public', 'playlist-modify-private', 'user-library-read', 'user-library-modify' ]; ``` --- ## MVP Tools Implementation ### Tool 1: search_spotify **Description:** Search Spotify catalog **Parameters:** - `query` (string, required) - `type` (string[], required): ["track", "album", "artist"] - `limit` (number, optional, default: 10) **Implementation:** ```typescript async function searchSpotify(query: string, types: string[], limit = 10) { const typeStr = types.join(','); const response = await spotifyClient.get('/search', { q: query, type: typeStr, limit }); return response.data; } ``` ### Tool 2: get_playback_state **Description:** Get current playback information **Parameters:** None **Implementation:** ```typescript async function getPlaybackState() { const response = await spotifyClient.get('/me/player'); return response.data; } ``` ### Tool 3: control_playback **Description:** Control playback **Parameters:** - `action` (string, required): "play" | "pause" | "next" | "previous" - `uri` (string, optional): Spotify URI for play action **Implementation:** ```typescript async function controlPlayback(action: string, uri?: string) { switch(action) { case 'play': return spotifyClient.put('/me/player/play', uri ? { uris: [uri] } : {}); case 'pause': return spotifyClient.put('/me/player/pause'); case 'next': return spotifyClient.post('/me/player/next'); case 'previous': return spotifyClient.post('/me/player/previous'); } } ``` ### Tool 4: get_user_playlists **Description:** Get user's playlists **Parameters:** - `limit` (number, optional, default: 20) **Implementation:** ```typescript async function getUserPlaylists(limit = 20) { const response = await spotifyClient.get('/me/playlists', { limit }); return response.data; } ``` ### Tool 5: create_playlist **Description:** Create a new playlist **Parameters:** - `name` (string, required) - `description` (string, optional) - `public` (boolean, optional, default: true) **Implementation:** ```typescript async function createPlaylist(name: string, description?: string, isPublic = true) { const userId = await getCurrentUserId(); const response = await spotifyClient.post(`/users/${userId}/playlists`, { name, description, public: isPublic }); return response.data; } ``` ### Tool 6: add_tracks_to_playlist **Description:** Add tracks to playlist **Parameters:** - `playlist_id` (string, required) - `track_uris` (string[], required) **Implementation:** ```typescript async function addTracksToPlaylist(playlistId: string, trackUris: string[]) { const response = await spotifyClient.post( `/playlists/${playlistId}/tracks`, { uris: trackUris } ); return response.data; } ``` ### Tool 7: save_track **Description:** Save track to library **Parameters:** - `track_id` (string, required) **Implementation:** ```typescript async function saveTrack(trackId: string) { await spotifyClient.put('/me/tracks', { ids: [trackId] }); return { success: true }; } ``` ### Tool 8: get_recommendations **Description:** Get track recommendations **Parameters:** - `seed_tracks` (string[], optional): Track IDs - `seed_artists` (string[], optional): Artist IDs - `limit` (number, optional, default: 10) **Implementation:** ```typescript async function getRecommendations( seedTracks?: string[], seedArtists?: string[], limit = 10 ) { const params: any = { limit }; if (seedTracks) params.seed_tracks = seedTracks.join(','); if (seedArtists) params.seed_artists = seedArtists.join(','); const response = await spotifyClient.get('/recommendations', params); return response.data; } ``` --- ## Implementation Timeline (1 Day) ### Phase 1: Setup (1-2 hours) - [x] Initialize Node.js project - [x] Install dependencies (MCP SDK, axios, etc.) - [x] Set up TypeScript configuration - [x] Create project structure ### Phase 2: Authentication (2-3 hours) - [ ] Implement PKCE flow - [ ] Create local callback server - [ ] Token storage system - [ ] Auto-refresh logic - [ ] Test auth flow ### Phase 3: Spotify Client (1-2 hours) - [ ] HTTP client wrapper - [ ] Error handling - [ ] Token injection - [ ] Basic retry logic ### Phase 4: MCP Tools (3-4 hours) - [ ] Implement 8 core tools - [ ] Tool registration - [ ] Type definitions - [ ] Error handling ### Phase 5: Integration & Testing (1-2 hours) - [ ] Configure Claude Desktop - [ ] Test each tool manually - [ ] Fix bugs - [ ] Document usage --- ## Dependencies ### Package.json ```json { "name": "mcp-spotify", "version": "0.1.0", "main": "dist/index.js", "scripts": { "build": "tsc", "dev": "tsx src/index.ts", "start": "node dist/index.js" }, "dependencies": { "@modelcontextprotocol/sdk": "^0.5.0", }, "devDependencies": { "tsx": "^4.0.0", "typescript": "^5.3.0" } } ``` --- ## Configuration ### Environment Variables (.env) ```bash SPOTIFY_CLIENT_ID=your_client_id_here SPOTIFY_CLIENT_SECRET=your_client_secret_here SPOTIFY_REDIRECT_URI=http://localhost:15732/callback ``` ### Claude Desktop Config ```json "mcpServers": { "spotify": { "command": "node", "args": ["/path/to/mcp-spotify/build/index.js"], "env": { "SPOTIFY_CLIENT_ID": "your_client_id_here", "SPOTIFY_CLIENT_SECRET": "your_client_secret_here", "SPOTIFY_REDIRECT_URI": "http://localhost:15732/callback" } } } ``` --- ## Security Considerations ### MVP Security (Simplified) 1. **Token Storage:** Store in `~/.mcp-spotify/tokens.json` with file permissions 600 2. **PKCE Flow:** Use code challenge to secure auth 3. **Local Only:** No network exposure beyond Spotify API 4. **Basic Validation:** Input validation on tool parameters --- ## Success Criteria ### MVP Goals ✅ Successfully authenticate with Spotify ✅ Search for music from Claude Desktop ✅ Control playback from Claude Desktop ✅ Create and manage playlists from Claude Desktop ✅ Save tracks from Claude Desktop ✅ Get recommendations from Claude Desktop **That's it!** If these 6 things work, the MVP is successful. --- ## Known Limitations (MVP) ### Intentional Simplifications - No comprehensive testing suite - No error recovery beyond basic retries - No rate limit handling (rely on Spotify's defaults) - No caching - No logging beyond console - Single user only - No graceful shutdown handling - Minimal TypeScript strict mode ### Spotify API Limitations - Some endpoints may require extended quota mode - Development mode limited to 25 users - May hit rate limits with heavy use --- ## Quick Start Guide ### 1. Setup Spotify App ```bash # Go to https://developer.spotify.com/dashboard # Create new app # Add redirect URI: http://localhost:15732/callback # Copy Client ID and Client Secret ``` ### 2. Initialize Project ```bash mkdir mcp-spotify cd mcp-spotify npm init -y npm install @modelcontextprotocol/sdk axios dotenv express npm install -D @types/express @types/node tsx typescript ``` ### 3. Create .env ```bash echo "SPOTIFY_CLIENT_ID=your_id" > .env echo "SPOTIFY_CLIENT_SECRET=your_secret" >> .env echo "SPOTIFY_REDIRECT_URI=http://localhost:15732/callback" >> .env ``` ### 4. Run First Auth ```bash npm start # Opens browser for authorization # Tokens saved to ~/.mcp-spotify/tokens.json ``` ### 5. Configure Claude Desktop ```json "mcpServers": { "spotify": { "command": "node", "args": ["/path/to/mcp-spotify/build/index.js"], "env": { "SPOTIFY_CLIENT_ID": "your_client_id_here", "SPOTIFY_CLIENT_SECRET": "your_client_secret_here", "SPOTIFY_REDIRECT_URI": "http://localhost:15732/callback" } } } ``` ```bash # Edit ~/Library/Application Support/Claude/claude_desktop_config.json # Add spotify server configuration ``` ### 6. Test in Claude Desktop ``` Try asking Claude: "Search Spotify for Bohemian Rhapsody" "What's currently playing on Spotify?" "Create a playlist called 'Test Playlist'" ``` --- ## Code Examples ### Main Server Setup ```typescript import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { SpotifyClient } from './spotify-client.js'; const server = new Server({ name: 'mcp-spotify', version: '0.1.0', }, { capabilities: { tools: {}, }, }); // Register tools server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: 'search_spotify', description: 'Search for tracks, albums, or artists', inputSchema: { type: 'object', properties: { query: { type: 'string' }, type: { type: 'array', items: { type: 'string' } }, limit: { type: 'number' } }, required: ['query', 'type'] } }, // ... other tools ] })); // Start server const transport = new StdioServerTransport(); await server.connect(transport); ``` ### Spotify Client ```typescript import axios, { AxiosInstance } from 'axios'; import { TokenManager } from './auth.js'; export class SpotifyClient { private client: AxiosInstance; private tokenManager: TokenManager; constructor(tokenManager: TokenManager) { this.tokenManager = tokenManager; this.client = axios.create({ baseURL: 'https://api.spotify.com/v1', }); // Add token to requests this.client.interceptors.request.use(async (config) => { const token = await this.tokenManager.getValidToken(); config.headers.Authorization = `Bearer ${token}`; return config; }); } async get(endpoint: string, params?: any) { return this.client.get(endpoint, { params }); } async post(endpoint: string, data?: any) { return this.client.post(endpoint, data); } async put(endpoint: string, data?: any) { return this.client.put(endpoint, data); } } ``` --- ## Future Ideas (Post-MVP) ### Enhanced Features - Advanced audio features analysis - Queue management - Volume control - Shuffle/repeat modes - Recently played tracks - User's top artists/tracks - Album and artist details - Podcast support ### Production Improvements - Comprehensive test suite (unit, integration, e2e) - Proper logging system - Rate limiting with backoff - Response caching - Better error messages - Graceful shutdown - Health checks - Metrics/monitoring ### Distribution - NPM package publication - Docker containerization - MCP registry submission - Documentation website - Example use cases - Video tutorials ### Enterprise Features - Multi-user support - Remote MCP server deployment - Organization-wide authentication - Usage analytics dashboard - Admin controls - Backup/restore ### Integrations - Last.fm scrobbling - Discord rich presence - Smart home systems - Calendar-based playback - Workout app sync - Sleep tracking integration --- ## Troubleshooting ### Common Issues **Auth fails:** - Check client ID/secret are correct - Verify redirect URI matches exactly - Clear `~/.mcp-spotify/tokens.json` and retry **Tools don't work:** - Check Claude Desktop config path - Verify server starts without errors - Check tokens haven't expired - Ensure Spotify app is open **Rate limiting:** - Wait a few minutes - Reduce request frequency - Consider extended quota mode **TypeScript errors:** - Run `npm run build` to check compilation - Fix type errors before testing - Check SDK version compatibility --- ## Resources & References ### Essential Documentation - [Spotify Web API](https://developer.spotify.com/documentation/web-api) - [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) - [OAuth 2.0 PKCE](https://developer.spotify.com/documentation/web-api/tutorials/code-pkce-flow) ### Development Tools - [Spotify Developer Dashboard](https://developer.spotify.com/dashboard) - [MCP Inspector](https://github.com/modelcontextprotocol/inspector) - [Postman Spotify Collection](https://www.postman.com/postman/workspace/spotify) --- ## Project Status **Status:** Planning Complete **Next Step:** Initialize Node.js project and start authentication **Estimated Time to MVP:** 8-10 hours **Start Date:** October 11, 2025 --- ## TypeScript Type Definitions ```typescript // types.ts export interface SpotifyTrack { id: string; name: string; artists: Array<{ id: string; name: string }>; album: { id: string; name: string; images: Array<{ url: string }>; }; duration_ms: number; uri: string; } export interface PlaybackState { is_playing: boolean; item: SpotifyTrack | null; progress_ms: number; device: { id: string; name: string; type: string; }; } export interface Playlist { id: string; name: string; description: string; public: boolean; tracks: { total: number; }; uri: string; } export interface TokenResponse { access_token: string; token_type: string; expires_in: number; refresh_token: string; scope: string; } ``` --- **End of MVP Planning Document** *Focus: Build working prototype today. Optimize later.*

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/nicklaustrup/mcp-spotify'

If you have feedback or need assistance with the MCP directory API, please join our Discord server