coverartarchive-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # Cover Art Archive MCP
2
+
3
+ Keyless MCP server for the Cover Art Archive: album and single artwork by MusicBrainz ID, front/back covers and thumbnails.
4
+
5
+ Pairs well with `musicbrainz-mcp` (find the MBID), `audius-mcp`, and `vgmdb-mcp`.
6
+
7
+ ## Quick start
8
+
9
+ ```bash
10
+ npm install
11
+ npm run build
12
+ node dist/index.js
13
+ ```
14
+
15
+ The server uses stdio, so it can be connected to Claude Desktop, Cursor, VS Code, MCP Inspector, or another compatible MCP client.
16
+
17
+ ## Tools at a glance
18
+
19
+ - `get_cover`: Archived artwork for a release or release-group MBID.
20
+ - `front_url`: Direct front-cover image URL builder (no fetch).
21
+
22
+ ## Limits and privacy
23
+
24
+ This project is intentionally narrow. It should be treated as a practical helper, not a complete certification or security audit. Check the implementation and the returned data before using it with sensitive material. No credentials are required.
package/dist/api.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ export declare class CoverArtError extends Error {
2
+ }
3
+ export interface CoverInfo {
4
+ mbid: string;
5
+ kind: string;
6
+ front?: string;
7
+ frontThumb?: string;
8
+ back?: string;
9
+ imageCount: number;
10
+ allImages: string[];
11
+ }
12
+ export declare function getCover(mbid: string, kind: "release" | "release-group"): Promise<CoverInfo>;
13
+ export declare function frontUrl(mbid: string, kind: "release" | "release-group", size?: 250 | 500 | 1200): string;
14
+ export declare function formatCover(c: CoverInfo): string;
package/dist/api.js ADDED
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Cover Art Archive client, keyless.
3
+ * Look up artwork for a MusicBrainz release or release-group MBID.
4
+ * Docs: https://musicbrainz.org/doc/Cover_Art_Archive/API
5
+ */
6
+ const BASE = "https://coverartarchive.org";
7
+ export class CoverArtError extends Error {
8
+ }
9
+ const MBID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10
+ async function fetchJson(url) {
11
+ const res = await fetch(url, {
12
+ headers: { "User-Agent": "coverartarchive-mcp/1.0", Accept: "application/json" },
13
+ redirect: "follow",
14
+ signal: AbortSignal.timeout(20000),
15
+ });
16
+ if (res.status === 404)
17
+ throw new CoverArtError("No cover art archived for this MBID yet.");
18
+ if (!res.ok)
19
+ throw new CoverArtError(`Cover Art Archive error ${res.status}`);
20
+ return (await res.json());
21
+ }
22
+ export async function getCover(mbid, kind) {
23
+ const clean = mbid.trim().toLowerCase();
24
+ if (!MBID.test(clean))
25
+ throw new CoverArtError(`Not a valid MBID: "${mbid}". Find one with musicbrainz-mcp first.`);
26
+ const data = await fetchJson(`${BASE}/${kind}/${clean}/`);
27
+ const images = Array.isArray(data.images) ? data.images : [];
28
+ const front = images.find((i) => i.front === true);
29
+ const back = images.find((i) => i.back === true);
30
+ return {
31
+ mbid: clean,
32
+ kind,
33
+ front: front?.image,
34
+ frontThumb: front?.thumbnails?.["500"] ?? front?.thumbnails?.small,
35
+ back: back?.image,
36
+ imageCount: images.length,
37
+ allImages: images.map((i) => String(i.image)).slice(0, 10),
38
+ };
39
+ }
40
+ export function frontUrl(mbid, kind, size = 500) {
41
+ return `${BASE}/${kind}/${mbid.trim().toLowerCase()}/front-${size}`;
42
+ }
43
+ export function formatCover(c) {
44
+ const lines = [
45
+ `Cover art for ${c.kind} ${c.mbid} (${c.imageCount} image${c.imageCount === 1 ? "" : "s"} archived)`,
46
+ c.front ? `Front: ${c.front}` : "No front cover archived.",
47
+ c.frontThumb ? `Front thumbnail: ${c.frontThumb}` : "",
48
+ c.back ? `Back: ${c.back}` : "",
49
+ c.allImages.length > 1 ? `All images:\n- ${c.allImages.join("\n- ")}` : "",
50
+ ].filter(Boolean);
51
+ return lines.join("\n");
52
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2
+ import { createServer } from "./server.js";
3
+ async function main() {
4
+ const server = createServer();
5
+ const transport = new StdioServerTransport();
6
+ await server.connect(transport);
7
+ console.error("MCP server running on stdio");
8
+ }
9
+ main().catch((err) => {
10
+ console.error("Fatal error:", err);
11
+ process.exit(1);
12
+ });
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function createServer(): McpServer;
package/dist/server.js ADDED
@@ -0,0 +1,53 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { CoverArtError, formatCover, frontUrl, getCover } from "./api.js";
4
+ const text = (t) => ({ content: [{ type: "text", text: t }] });
5
+ const textError = (t) => ({ content: [{ type: "text", text: t }], isError: true });
6
+ const READ_ONLY = { readOnlyHint: true, openWorldHint: true };
7
+ export function createServer() {
8
+ const server = new McpServer({
9
+ name: "coverartarchive-mcp",
10
+ version: "1.0.0",
11
+ });
12
+ server.registerTool("get_cover", {
13
+ title: "Get cover art",
14
+ description: "Get archived cover art for a MusicBrainz release or release-group: front/back URLs, thumbnails, image count.",
15
+ inputSchema: z.object({
16
+ mbid: z.string().describe("MusicBrainz ID, e.g. '76df3287-6cda-33eb-8e9a-044b5e15ffdd' (find it with musicbrainz-mcp)"),
17
+ kind: z.enum(["release", "release-group"]).default("release"),
18
+ }),
19
+ annotations: READ_ONLY,
20
+ }, async ({ mbid, kind }) => {
21
+ try {
22
+ return text(formatCover(await getCover(mbid, kind)));
23
+ }
24
+ catch (e) {
25
+ return textError(errorMessage(e));
26
+ }
27
+ });
28
+ server.registerTool("front_url", {
29
+ title: "Front cover URL",
30
+ description: "Build a direct front-cover image URL for a MusicBrainz ID without fetching. Useful for embedding artwork.",
31
+ inputSchema: z.object({
32
+ mbid: z.string().describe("MusicBrainz release or release-group MBID"),
33
+ kind: z.enum(["release", "release-group"]).default("release"),
34
+ size: z.enum(["250", "500", "1200"]).default("500").describe("Thumbnail size in px"),
35
+ }),
36
+ annotations: READ_ONLY,
37
+ }, async ({ mbid, kind, size }) => {
38
+ try {
39
+ return text(frontUrl(mbid, kind, Number(size)));
40
+ }
41
+ catch (e) {
42
+ return textError(errorMessage(e));
43
+ }
44
+ });
45
+ return server;
46
+ }
47
+ function errorMessage(e) {
48
+ if (e instanceof CoverArtError)
49
+ return `Error: ${e.message}`;
50
+ if (e instanceof Error)
51
+ return `Error: ${e.message}`;
52
+ return `Error: ${String(e)}`;
53
+ }
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "coverartarchive-mcp",
3
+ "version": "1.0.0",
4
+ "description": "Keyless MCP server for the Cover Art Archive: album and single artwork by MusicBrainz ID, front/back covers and thumbnails.",
5
+ "type": "module",
6
+ "mcpName": "io.github.mrfentmen/coverartarchive-mcp",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/mrfentmen/awesome-mcps.git"
10
+ },
11
+ "bin": {
12
+ "coverartarchive-mcp": "./dist/index.js"
13
+ },
14
+ "main": "./dist/index.js",
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc -p tsconfig.json",
20
+ "start": "node dist/index.js",
21
+ "dev": "rm -rf dist && tsc -p tsconfig.json && node dist/index.js",
22
+ "inspect": "npx @modelcontextprotocol/inspector node dist/index.js"
23
+ },
24
+ "keywords": [
25
+ "mcp",
26
+ "cover-art",
27
+ "musicbrainz",
28
+ "api"
29
+ ],
30
+ "license": "MIT",
31
+ "dependencies": {
32
+ "@modelcontextprotocol/sdk": "^1.0.4",
33
+ "zod": "^3.23.8"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^22.0.0",
37
+ "typescript": "^5.6.0"
38
+ },
39
+ "engines": {
40
+ "node": ">=20"
41
+ }
42
+ }