UNPKG

@rocksky/cli

Version:

Command-line interface for Rocksky – scrobble tracks, view stats, and manage your listening history

270 lines (253 loc) 6.91 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { RockskyClient } from "client"; import { z } from "zod"; import { albums } from "./tools/albums"; import { artists } from "./tools/artists"; import { createApiKey } from "./tools/create"; import { myscrobbles } from "./tools/myscrobbles"; import { nowplaying } from "./tools/nowplaying"; import { scrobbles } from "./tools/scrobbles"; import { search } from "./tools/search"; import { stats } from "./tools/stats"; import { tracks } from "./tools/tracks"; import { whoami } from "./tools/whoami"; class RockskyMcpServer { private readonly server: McpServer; private readonly client: RockskyClient; constructor() { this.server = new McpServer({ name: "rocksky-mcp", version: "0.1.0", }); const client = new RockskyClient(); this.setupTools(); } private setupTools() { this.server.tool("whoami", "get the current logged-in user.", async () => { return { content: [ { type: "text", text: await whoami(), }, ], }; }); this.server.tool( "nowplaying", "get the currently playing track.", { did: z .string() .optional() .describe( "the DID or handle of the user to get the now playing track for." ), }, async ({ did }) => { return { content: [ { type: "text", text: await nowplaying(did), }, ], }; } ); this.server.tool( "scrobbles", "display recently played tracks (recent scrobbles).", { did: z .string() .optional() .describe("the DID or handle of the user to get the scrobbles for."), skip: z.number().optional().describe("number of scrobbles to skip"), limit: z.number().optional().describe("number of scrobbles to limit"), }, async ({ did, skip = 0, limit = 10 }) => { return { content: [ { type: "text", text: await scrobbles(did, { skip, limit }), }, ], }; } ); this.server.tool( "my-scrobbles", "display my recently played tracks (recent scrobbles).", { skip: z.number().optional().describe("number of scrobbles to skip"), limit: z.number().optional().describe("number of scrobbles to limit"), }, async ({ skip = 0, limit = 10 }) => { return { content: [ { type: "text", text: await myscrobbles({ skip, limit }), }, ], }; } ); this.server.tool( "search", "search for tracks, artists, albums or users.", { query: z .string() .describe("the search query, e.g., artist, album, title or account"), limit: z.number().optional().describe("number of results to limit"), albums: z.boolean().optional().describe("search for albums"), tracks: z.boolean().optional().describe("search for tracks"), users: z.boolean().optional().describe("search for users"), artists: z.boolean().optional().describe("search for artists"), }, async ({ query, limit = 10, albums = false, tracks = false, users = false, artists = false, }) => { return { content: [ { type: "text", text: await search(query, { limit, albums, tracks, users, artists, }), }, ], }; } ); this.server.tool( "artists", "get the user's top artists or current user's artists if no did is provided.", { did: z .string() .optional() .describe("the DID or handle of the user to get artists for."), limit: z.number().optional().describe("number of results to limit"), }, async ({ did, limit }) => { return { content: [ { type: "text", text: await artists(did, { skip: 0, limit }), }, ], }; } ); this.server.tool( "albums", "get the user's top albums or current user's albums if no did is provided.", { did: z .string() .optional() .describe("the DID or handle of the user to get albums for."), limit: z.number().optional().describe("number of results to limit"), }, async ({ did, limit }) => { return { content: [ { type: "text", text: await albums(did, { skip: 0, limit }), }, ], }; } ); this.server.tool( "tracks", "get the user's top tracks or current user's tracks if no did is provided.", { did: z .string() .optional() .describe("the DID or handle of the user to get tracks for."), limit: z.number().optional().describe("number of results to limit"), }, async ({ did, limit }) => { return { content: [ { type: "text", text: await tracks(did, { skip: 0, limit }), }, ], }; } ); this.server.tool( "stats", "get the user's listening stats or current user's stats if no did is provided.", { did: z .string() .optional() .describe("the DID or handle of the user to get stats for."), }, async ({ did }) => { return { content: [ { type: "text", text: await stats(did), }, ], }; } ); this.server.tool( "create-apikey", "create an API key.", { name: z.string().describe("the name of the API key"), description: z .string() .optional() .describe("the description of the API key"), }, async ({ name, description }) => { return { content: [ { type: "text", text: await createApiKey(name, { description }), }, ], }; } ); } async run() { const stdioTransport = new StdioServerTransport(); try { await this.server.connect(stdioTransport); } catch (error) { process.exit(1); } } public getServer(): McpServer { return this.server; } } export const rockskyMcpServer = new RockskyMcpServer();