get-recommendations
Discover personalized music suggestions based on your favorite Spotify tracks. Specify up to 5 seed tracks and set the number of recommendations to receive tailored results.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Number of recommendations to return | |
| seedTracks | Yes | Spotify track IDs to use as seeds (max 5) |
Implementation Reference
- mcp/spotify-mcp.ts:458-528 (handler)Handler function that fetches Spotify track recommendations based on seed tracks and limit, using a valid access token. Processes the API response and formats tracks for output.async ({ seedTracks, limit }) => { try { const accessToken = await getValidAccessToken(); if (seedTracks.length === 0) { return { content: [ { type: "text", text: "Error: At least one seed track is required", }, ], isError: true, }; } const response = await fetch( `https://api.spotify.com/v1/recommendations?seed_tracks=${seedTracks.join( "," )}&limit=${limit}`, { headers: { Authorization: `Bearer ${accessToken}`, }, } ); const data = (await response.json()) as any; if (!response.ok) { return { content: [ { type: "text", text: `Error getting recommendations: ${JSON.stringify(data)}`, }, ], isError: true, }; } const tracks = data.tracks.map((track: any) => ({ id: track.id, name: track.name, artist: track.artists.map((artist: any) => artist.name).join(", "), album: track.album.name, uri: track.uri, })); return { content: [ { type: "text", text: JSON.stringify(tracks, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text", text: `Failed to get recommendations: ${ error instanceof Error ? error.message : String(error) }`, }, ], isError: true, }; } }
- mcp/spotify-mcp.ts:446-457 (schema)Zod schema defining input parameters for the get-recommendations tool: seedTracks (array of up to 5 track IDs) and limit (1-100, default 20).{ seedTracks: z .array(z.string()) .max(5) .describe("Spotify track IDs to use as seeds (max 5)"), limit: z .number() .min(1) .max(100) .default(20) .describe("Number of recommendations to return"), },
- mcp/spotify-mcp.ts:444-529 (registration)Registration of the get-recommendations tool on the MCP server, including name, input schema, and handler function.server.tool( "get-recommendations", { seedTracks: z .array(z.string()) .max(5) .describe("Spotify track IDs to use as seeds (max 5)"), limit: z .number() .min(1) .max(100) .default(20) .describe("Number of recommendations to return"), }, async ({ seedTracks, limit }) => { try { const accessToken = await getValidAccessToken(); if (seedTracks.length === 0) { return { content: [ { type: "text", text: "Error: At least one seed track is required", }, ], isError: true, }; } const response = await fetch( `https://api.spotify.com/v1/recommendations?seed_tracks=${seedTracks.join( "," )}&limit=${limit}`, { headers: { Authorization: `Bearer ${accessToken}`, }, } ); const data = (await response.json()) as any; if (!response.ok) { return { content: [ { type: "text", text: `Error getting recommendations: ${JSON.stringify(data)}`, }, ], isError: true, }; } const tracks = data.tracks.map((track: any) => ({ id: track.id, name: track.name, artist: track.artists.map((artist: any) => artist.name).join(", "), album: track.album.name, uri: track.uri, })); return { content: [ { type: "text", text: JSON.stringify(tracks, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text", text: `Failed to get recommendations: ${ error instanceof Error ? error.message : String(error) }`, }, ], isError: true, }; } } );
- mcp/spotify-mcp.ts:22-79 (helper)Shared helper function used by the handler to obtain a valid Spotify access token, refreshing it if necessary using stored credentials.async function getValidAccessToken() { if (!spotifyAuthInfo.accessToken || !spotifyAuthInfo.refreshToken) { throw new Error( "No access token available. Please set credentials first using the set-spotify-credentials tool." ); } try { // Try using current token const response = await fetch("https://api.spotify.com/v1/me", { headers: { Authorization: `Bearer ${spotifyAuthInfo.accessToken}`, }, }); // If token works, return it if (response.ok) { return spotifyAuthInfo.accessToken; } console.error("Access token expired, refreshing..."); // If token doesn't work, refresh it const refreshResponse = await fetch( "https://accounts.spotify.com/api/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", Authorization: "Basic " + Buffer.from( spotifyAuthInfo.clientId + ":" + spotifyAuthInfo.clientSecret ).toString("base64"), }, body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: spotifyAuthInfo.refreshToken, }), } ); const data = (await refreshResponse.json()) as any; if (data.access_token) { console.error("Successfully refreshed access token"); spotifyAuthInfo.accessToken = data.access_token; return spotifyAuthInfo.accessToken; } throw new Error("Failed to refresh access token"); } catch (error) { throw new Error( "Error with access token: " + (error instanceof Error ? error.message : String(error)) ); } }