search-tracks
Search and retrieve Spotify tracks by entering a query, with options to limit the number of results returned, enabling targeted music discovery and access.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Number of results to return | |
| query | Yes | Search query for tracks |
Input Schema (JSON Schema)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"properties": {
"limit": {
"default": 10,
"description": "Number of results to return",
"maximum": 50,
"minimum": 1,
"type": "number"
},
"query": {
"description": "Search query for tracks",
"type": "string"
}
},
"required": [
"query"
],
"type": "object"
}
Implementation Reference
- mcp/spotify-mcp-sse.ts:197-255 (handler)The handler function for the 'search-tracks' tool. It retrieves a valid Spotify access token, performs a search query to the Spotify API for tracks, processes the response to extract relevant track information (id, name, artist, album, uri), and returns the results as a formatted JSON string in the MCP response format. Handles errors appropriately.async ({ query, limit }) => { try { const accessToken = await getValidAccessToken(); const response = await fetch( `https://api.spotify.com/v1/search?q=${encodeURIComponent( query )}&type=track&limit=${limit}`, { headers: { Authorization: `Bearer ${accessToken}`, }, } ); const data = (await response.json()) as any; if (!response.ok) { return { content: [ { type: "text", text: `Error searching tracks: ${JSON.stringify(data)}`, }, ], isError: true, }; } const tracks = data.tracks.items.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 search tracks: ${ error instanceof Error ? error.message : String(error) }`, }, ], isError: true, }; } }
- mcp/spotify-mcp-sse.ts:188-196 (schema)Zod schema defining the input parameters for the search-tracks tool: a required 'query' string and an optional 'limit' number (1-50, default 10).{ query: z.string().describe("Search query for tracks"), limit: z .number() .min(1) .max(50) .default(10) .describe("Number of results to return"), },
- mcp/spotify-mcp-sse.ts:186-256 (registration)Registration of the 'search-tracks' tool on the MCP server using server.tool(), including name, input schema, and handler function.server.tool( "search-tracks", { query: z.string().describe("Search query for tracks"), limit: z .number() .min(1) .max(50) .default(10) .describe("Number of results to return"), }, async ({ query, limit }) => { try { const accessToken = await getValidAccessToken(); const response = await fetch( `https://api.spotify.com/v1/search?q=${encodeURIComponent( query )}&type=track&limit=${limit}`, { headers: { Authorization: `Bearer ${accessToken}`, }, } ); const data = (await response.json()) as any; if (!response.ok) { return { content: [ { type: "text", text: `Error searching tracks: ${JSON.stringify(data)}`, }, ], isError: true, }; } const tracks = data.tracks.items.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 search tracks: ${ error instanceof Error ? error.message : String(error) }`, }, ], isError: true, }; } } );
- mcp/spotify-mcp-sse.ts:35-92 (helper)Helper function to obtain a valid Spotify access token, checking current token validity and refreshing it if expired using the refresh token.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)) ); } }
- mcp/spotify-mcp.ts:184-242 (handler)Identical handler implementation for 'search-tracks' in the stdio transport version of the server.async ({ query, limit }) => { try { const accessToken = await getValidAccessToken(); const response = await fetch( `https://api.spotify.com/v1/search?q=${encodeURIComponent( query )}&type=track&limit=${limit}`, { headers: { Authorization: `Bearer ${accessToken}`, }, } ); const data = (await response.json()) as any; if (!response.ok) { return { content: [ { type: "text", text: `Error searching tracks: ${JSON.stringify(data)}`, }, ], isError: true, }; } const tracks = data.tracks.items.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 search tracks: ${ error instanceof Error ? error.message : String(error) }`, }, ], isError: true, }; } }