From 39e3f4c70c13226398d0630a04dd24431bd58c6e Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Sat, 13 Dec 2025 11:55:23 -0500 Subject: [PATCH] MCP - get_spotify_search --- lib/mcp/tools/index.ts | 2 + lib/mcp/tools/spotify/index.ts | 11 ++ .../spotify/registerGetSpotifySearchTool.ts | 118 ++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 lib/mcp/tools/spotify/index.ts create mode 100644 lib/mcp/tools/spotify/registerGetSpotifySearchTool.ts diff --git a/lib/mcp/tools/index.ts b/lib/mcp/tools/index.ts index 7cd56e952..b3fa8ab3a 100644 --- a/lib/mcp/tools/index.ts +++ b/lib/mcp/tools/index.ts @@ -5,6 +5,7 @@ import { registerAllTaskTools } from "./tasks"; import { registerAllImageTools } from "./images"; import { registerAllCatalogTools } from "./catalogs"; import { registerAllSora2Tools } from "./sora2"; +import { registerAllSpotifyTools } from "./spotify"; import { registerContactTeamTool } from "./registerContactTeamTool"; /** @@ -20,5 +21,6 @@ export const registerAllTools = (server: McpServer): void => { registerAllImageTools(server); registerAllCatalogTools(server); registerAllSora2Tools(server); + registerAllSpotifyTools(server); registerContactTeamTool(server); }; diff --git a/lib/mcp/tools/spotify/index.ts b/lib/mcp/tools/spotify/index.ts new file mode 100644 index 000000000..9b3196855 --- /dev/null +++ b/lib/mcp/tools/spotify/index.ts @@ -0,0 +1,11 @@ +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { registerGetSpotifySearchTool } from "./registerGetSpotifySearchTool"; + +/** + * Registers all Spotify-related MCP tools on the server. + * + * @param server - The MCP server instance to register tools on. + */ +export const registerAllSpotifyTools = (server: McpServer): void => { + registerGetSpotifySearchTool(server); +}; diff --git a/lib/mcp/tools/spotify/registerGetSpotifySearchTool.ts b/lib/mcp/tools/spotify/registerGetSpotifySearchTool.ts new file mode 100644 index 000000000..d664e6d12 --- /dev/null +++ b/lib/mcp/tools/spotify/registerGetSpotifySearchTool.ts @@ -0,0 +1,118 @@ +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { z } from "zod"; +import generateAccessToken from "@/lib/spotify/generateAccessToken"; +import getSearch from "@/lib/spotify/getSearch"; + +// Supported Spotify search types +const SPOTIFY_TYPES = [ + "album", + "artist", + "playlist", + "track", + "show", + "episode", + "audiobook", +] as const; + +// Zod schema for the MCP tool - matches the original tool interface +const getSpotifySearchSchema = z.object({ + name: z.string().min(1, "Search query is required"), + type: z.array(z.enum(SPOTIFY_TYPES)).min(1, "At least one type is required"), + limit: z.number().min(1).max(50).optional().default(5), +}); + +type GetSpotifySearchArgs = z.infer; + +/** + * Registers the "get_spotify_search" tool on the MCP server. + * Search for Spotify items (artist, album, track, playlist, etc.) by name. + * Returns results for each requested type. + * + * @param server - The MCP server instance to register the tool on. + */ +export function registerGetSpotifySearchTool(server: McpServer): void { + server.registerTool( + "get_spotify_search", + { + description: + "Search for Spotify items (artist, album, track, playlist, etc.) by name. Returns results for each requested type.", + inputSchema: getSpotifySearchSchema, + }, + async (args: GetSpotifySearchArgs) => { + try { + // Generate Spotify access token + const tokenResult = await generateAccessToken(); + + if (!tokenResult || tokenResult.error || !tokenResult.access_token) { + return { + content: [ + { + type: "text", + text: JSON.stringify({ + success: false, + message: "Failed to generate Spotify access token", + }), + }, + ], + }; + } + + // Convert type array to comma-separated string for API + const typeString = args.type.join(","); + + // Call Spotify search API + const { data, error } = await getSearch({ + q: args.name, + type: typeString, + limit: args.limit, + accessToken: tokenResult.access_token, + }); + + if (error || !data) { + return { + content: [ + { + type: "text", + text: JSON.stringify({ + success: false, + message: error?.message || "Failed to search Spotify", + }), + }, + ], + }; + } + + // Filter results to only include requested types (matching original tool behavior) + const result: Record = { success: true }; + for (const t of args.type) { + const key = `${t}s`; + if (data[key]) { + result[key] = data[key]; + } + } + + return { + content: [ + { + type: "text", + text: JSON.stringify(result), + }, + ], + }; + } catch (error) { + console.error("Error searching Spotify:", error); + return { + content: [ + { + type: "text", + text: JSON.stringify({ + success: false, + message: error instanceof Error ? error.message : "Failed to search Spotify", + }), + }, + ], + }; + } + }, + ); +}