{
  "markdown": "<div align=\"center\">\n  <h1>@cyanheads/musicbrainz-mcp-server</h1>\n  <p><b>Search artists, releases, recordings, works, and labels; traverse relationships; resolve ISRC/ISWC/barcode; fetch cover art via MCP. STDIO or Streamable HTTP.</b>\n  <div>10 Tools • 1 Resource</div>\n  </p>\n</div>\n\n<div align=\"center\">\n\n[![Version](https://img.shields.io/badge/Version-0.1.6-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.0.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.0-blueviolet.svg?style=flat-square)](https://bun.sh/)\n\n</div>\n\n<div align=\"center\">\n\n[![Install in Claude Desktop](https://img.shields.io/badge/Install_in-Claude_Desktop-D97757?style=for-the-badge&logo=anthropic&logoColor=white)](https://github.com/cyanheads/musicbrainz-mcp-server/releases/latest/download/musicbrainz-mcp-server.mcpb) [![Install in Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=musicbrainz-mcp-server&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBjeWFuaGVhZHMvbXVzaWNicmFpbnotbWNwLXNlcnZlciJdfQ==) [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=for-the-badge&logo=visualstudiocode&logoColor=white)](https://vscode.dev/redirect?url=vscode:mcp/install?%7B%22name%22%3A%22musicbrainz-mcp-server%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40cyanheads/musicbrainz-mcp-server%22%5D%7D)\n\n</div>\n\n<div align=\"center\">\n\n[![Framework](https://img.shields.io/badge/Built%20on-@cyanheads/mcp--ts--core-67E8F9?style=flat-square)](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)\n\n</div>\n\n<div align=\"center\">\n\n**Public Hosted Server:** [https://musicbrainz.caseyjhand.com/mcp](https://musicbrainz.caseyjhand.com/mcp)\n\n</div>\n\n---\n\n## Tools\n\nTen read-only tools mapping the three MusicBrainz access modes — **search** when you have text, **lookup** (`get_*` and `lookup_identifier`) when you hold an MBID or standard identifier, and **browse** when you need the complete linked set beyond the single page that lookup folds in:\n\n| Tool | Description |\n|:---|:---|\n| `musicbrainz_search_entities` | Full-text Lucene search across an entity type (artist, release-group, release, recording, work, label). Returns ranked matches with MBID and a 0–100 relevance score. The first step when starting from a name. |\n| `musicbrainz_get_artist` | Artist profile by MBID — type, country, life span, aliases, tags/genres, discography (release-groups), band-membership relationships, and external links. |\n| `musicbrainz_get_release_group` | Release-group (\"the album\" above specific pressings) by MBID — primary/secondary type, first-release date, artist credit, editions, and a cover-art availability flag. |\n| `musicbrainz_get_release` | One edition's full detail by MBID — tracklist (media → tracks → recordings), label + catalog number, barcode, packaging, and a cover-art stub. |\n| `musicbrainz_get_recording` | Recording (a specific performance/track) by MBID — length, artist credits, ISRCs, the releases it appears on, the work(s) it performs, and performance/production relationships. |\n| `musicbrainz_get_work` | Work (a composition, distinct from any recording) by MBID — type, languages, ISWCs, writer/composer relationships, and the recordings that perform it. |\n| `musicbrainz_get_label` | Label by MBID — type, country, life span, label code, area, aliases, tags, and external links. |\n| `musicbrainz_lookup_identifier` | Resolve a standard identifier without a name search — ISRC → recordings, ISWC → works, barcode → releases. Output is discriminated on the resolved entity type. |\n| `musicbrainz_browse_entities` | Paginate the complete set of entities linked to a parent MBID — every release on a label, every release-group by an artist, every recording of a work. The only complete-enumeration path. |\n| `musicbrainz_get_cover_art` | Cover Art Archive images for a release or release-group MBID — front/back flags, image types, full-resolution URLs, and 250/500/1200px thumbnails. No art returns an empty set, not an error. |\n\n### `musicbrainz_search_entities`\n\nResolve a name to an MBID with full-text Lucene search.\n\n- Searches one entity type per call: artist, release-group, release, recording, work, or label\n- Field-scoped Lucene syntax (e.g. `artist:radiohead AND country:GB`)\n- Surfaces the raw 0–100 relevance `score` per hit (100 = exact); results stay in MusicBrainz score-descending order, not re-ranked\n- Type-specific fields appear only for the relevant entity (ISRCs on recordings, ISWCs on works, artist credit on release-groups/releases/recordings)\n- Pagination via `limit` (1–100) and `offset`; echoes the effective query and the true upstream total\n\n---\n\n### `musicbrainz_get_artist`\n\nThe 80% artist-detail call.\n\n- Folds discography (release-groups), band-membership / collaboration relationships, aliases, and tags/genres into one request via `inc`\n- External links (Wikidata QID, Discogs, official site) surface as `url-rels` — chainable to `wikidata-mcp-server` and friends; this server does not chase them itself\n- `inc_release_groups` and `inc_relationships` toggle the expensive sub-resources\n- Discography and relationships are capped at one page (25); for a prolific artist's complete release-group list, use `musicbrainz_browse_entities` (`target_type=release-group`, artist link)\n\n---\n\n### `musicbrainz_get_release`\n\nOne edition's full detail, the level with an actual tracklist.\n\n- Tracklist as media → tracks → recordings, each with length and recording MBID (lengths rendered `m:ss`, stored as milliseconds upstream)\n- Label + catalog number, barcode, country, release date, format, packaging, and text representation (language/script)\n- Carries a cover-art availability stub from the WS/2 payload; call `musicbrainz_get_cover_art` with the release MBID for the actual image URLs\n\n---\n\n### `musicbrainz_lookup_identifier`\n\nThe deterministic path when you already hold a standard identifier — no name search.\n\n- `id_type=isrc` → recordings (a recording-level code, often shared by several recordings)\n- `id_type=iswc` → works (a composition-level code)\n- `id_type=barcode` → releases (UPC/EAN)\n- ISRC and ISWC hit dedicated exact endpoints; barcode is a Lucene search filter, so its results are ranked (exact match scores 100)\n- The output `kind` field tells you which entity type came back\n\n---\n\n### `musicbrainz_browse_entities`\n\nThe complete-enumeration path — a correctness tool, not just convenience.\n\n- Paginates the full linked set: every release-group by an artist, every release on a label, every recording of a work, every release in a release-group\n- Pages arbitrarily deep via `offset`; `totalCount` is the true upstream total\n- Use it whenever a linked set may exceed a page — the `get_*` tools embed at most one page (25), and a partial list read as complete is a silent correctness gap\n- Provide exactly one `link` MBID matching a valid parent→child relationship for the `target_type`\n\n---\n\n### `musicbrainz_get_cover_art`\n\nCover Art Archive images, kept separate from the release record.\n\n- Front/back flags, image types, full-resolution URLs, and 250/500/1200px thumbnail URLs\n- Returns an empty image set (not an error) when the entity has no art — absence of art is information\n- Art is served at the release level; a release-group MBID resolves to a representative release's art automatically\n- Image URLs are linked, never rehosted — image copyright stays with the rights holders (only the MusicBrainz core metadata is CC0)\n\n## Resources\n\n| Type | Name | Description |\n|:---|:---|:---|\n| Resource | `musicbrainz://{entity_type}/{mbid}` | A single MusicBrainz entity by type and MBID, with default linked sub-resources folded in. Mirrors the matching `musicbrainz_get_*` tool. `entity_type` ∈ artist, release-group, release, recording, work, label. |\n\nAll entity data is also reachable via the `get_*` tools, so tool-only clients (the majority) lose nothing. There is no resource `list()` — the corpus is millions of entities; discovery is via `musicbrainz_search_entities`, not resource enumeration.\n\n## Features\n\nBuilt on [`@cyanheads/mcp-ts-core`](https://www.npmjs.com/package/@cyanheads/mcp-ts-core):\n\n- Declarative tool and resource definitions — single file per primitive, framework handles registration and validation\n- Unified error handling — handlers throw, framework catches, classifies, and formats\n- Pluggable auth: `none`, `jwt`, `oauth`\n- Swappable storage backends: `in-memory`, `filesystem`, `Supabase`, `Cloudflare KV/R2/D1`\n- Structured logging with optional OpenTelemetry tracing\n- STDIO and Streamable HTTP transports\n\nMusicBrainz-specific:\n\n- Type-safe client over the MusicBrainz Web Service v2 (`musicbrainz.org/ws/2`, JSON) plus the Cover Art Archive\n- Process-wide ~1 req/sec token-bucket rate limiter — concurrent requests serialize to stay under MusicBrainz's per-IP ceiling, so multi-tenant load shares one budget\n- Response caching keyed on the full request (including the `inc` set) — MBIDs are stable and entity data changes slowly, keeping most repeat lookups off the wire\n- `inc`-driven lookups fold discography, relationships, tracklists, and external IDs into a single call rather than serial requests\n- Retry with backoff over the full fetch + parse pipeline; an HTML error page served under load is classified transient, not as a parse error\n\nAgent-friendly output:\n\n- Provenance on search/browse — the effective query is echoed and the true upstream total is reported, so an agent can tell a partial window from a complete result\n- Truncation honesty — `get_*` tools disclose when an embedded linked list is capped at one page and name `musicbrainz_browse_entities` as the complete-enumeration path\n- Discriminated outputs — `musicbrainz_lookup_identifier` returns a `kind`-tagged union (recordings | works | releases) so callers branch on data, not string parsing\n- Raw upstream relevance `score` surfaced as-is (not a fabricated confidence metric), and missing upstream fields are preserved as absent rather than invented\n\n## Getting started\n\n`musicbrainz-mcp-server` is keyless — no API key or account. MusicBrainz does require a descriptive `User-Agent` with a contact and rate-limits to ~1 request/second per IP; the server ships a default contact so it works out of the box, but operators running a shared or hosted instance should set `MUSICBRAINZ_CONTACT` to their own email or URL.\n\n### Public Hosted Instance\n\nA public instance is available at `https://musicbrainz.caseyjhand.com/mcp` — no installation required. Point any MCP client at it via Streamable HTTP, with this client config:\n\n```json\n{\n  \"mcpServers\": {\n    \"musicbrainz-mcp-server\": {\n      \"type\": \"streamable-http\",\n      \"url\": \"https://musicbrainz.caseyjhand.com/mcp\"\n    }\n  }\n}\n```\n\nAdd the following to your MCP client configuration file.\n\n```json\n{\n  \"mcpServers\": {\n    \"musicbrainz-mcp-server\": {\n      \"type\": \"stdio\",\n      \"command\": \"bunx\",\n      \"args\": [\"@cyanheads/musicbrainz-mcp-server@latest\"],\n      \"env\": {\n        \"MCP_TRANSPORT_TYPE\": \"stdio\",\n        \"MCP_LOG_LEVEL\": \"info\",\n        \"MUSICBRAINZ_CONTACT\": \"you@example.com\"\n      }\n    }\n  }\n}\n```\n\nOr with npx (no Bun required):\n\n```json\n{\n  \"mcpServers\": {\n    \"musicbrainz-mcp-server\": {\n      \"type\": \"stdio\",\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@cyanheads/musicbrainz-mcp-server@latest\"],\n      \"env\": {\n        \"MCP_TRANSPORT_TYPE\": \"stdio\",\n        \"MCP_LOG_LEVEL\": \"info\",\n        \"MUSICBRAINZ_CONTACT\": \"you@example.com\"\n      }\n    }\n  }\n}\n```\n\nOr with Docker:\n\n```json\n{\n  \"mcpServers\": {\n    \"musicbrainz-mcp-server\": {\n      \"type\": \"stdio\",\n      \"command\": \"docker\",\n      \"args\": [\n        \"run\", \"-i\", \"--rm\",\n        \"-e\", \"MCP_TRANSPORT_TYPE=stdio\",\n        \"-e\", \"MUSICBRAINZ_CONTACT=you@example.com\",\n        \"ghcr.io/cyanheads/musicbrainz-mcp-server:latest\"\n      ]\n    }\n  }\n}\n```\n\nFor Streamable HTTP, set the transport and start the server:\n\n```sh\nMCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 MUSICBRAINZ_CONTACT=you@example.com bun run start:http\n# Server listens at http://localhost:3010/mcp\n```\n\n### Prerequisites\n\n- [Bun v1.4.0](https://bun.sh/) or higher (or Node.js v24+).\n- No API key. Optionally set `MUSICBRAINZ_CONTACT` to your email or URL — recommended for any shared or hosted deployment.\n\n### Installation\n\n1. **Clone the repository:**\n\n```sh\ngit clone https://github.com/cyanheads/musicbrainz-mcp-server.git\n```\n\n2. **Navigate into the directory:**\n\n```sh\ncd musicbrainz-mcp-server\n```\n\n3. **Install dependencies:**\n\n```sh\nbun install\n```\n\n4. **Configure environment:**\n\n```sh\ncp .env.example .env\n# edit .env and set MUSICBRAINZ_CONTACT (optional but recommended)\n```\n\n## Configuration\n\nConfiguration is validated at startup via Zod schemas in `src/config/server-config.ts`.\n\n| Variable | Description | Default |\n|:---|:---|:---|\n| `MUSICBRAINZ_CONTACT` | Contact (email or URL) embedded in the mandatory descriptive `User-Agent`. Not start-blocking — a default is provided — but operators of a shared/hosted instance should set their own so MusicBrainz can reach them about traffic. | repo URL |\n| `MUSICBRAINZ_BASE_URL` | MusicBrainz Web Service v2 base URL. Override for a private mirror or `beta.musicbrainz.org`. | `https://musicbrainz.org/ws/2` |\n| `MUSICBRAINZ_RATE_LIMIT_RPS` | Client-side request-per-second ceiling. ~1 is the documented limit; lower it for shared-hosting headroom. | `1` |\n| `MUSICBRAINZ_CACHE_TTL` | Response cache TTL in seconds. MBIDs are stable, so data changes slowly. `0` disables caching. | `86400` |\n| `MUSICBRAINZ_TIMEOUT_MS` | Per-request HTTP timeout in milliseconds. | `30000` |\n| `MUSICBRAINZ_MAX_RETRIES` | Retry attempts for transient upstream failures (503 / 5xx / HTML error page). | `3` |\n| `COVER_ART_BASE_URL` | Cover Art Archive base URL. | `https://coverartarchive.org` |\n| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |\n| `MCP_HTTP_PORT` | Port for the HTTP server. | `3010` |\n| `MCP_AUTH_MODE` | Auth mode: `none`, `jwt`, or `oauth`. | `none` |\n| `MCP_LOG_LEVEL` | Log level (RFC 5424). | `info` |\n| `OTEL_ENABLED` | Enable [OpenTelemetry instrumentation](https://github.com/cyanheads/mcp-ts-core/tree/main/docs/telemetry). | `false` |\n\nSee [`.env.example`](./.env.example) for the full list of optional overrides.\n\n### Rate limit and User-Agent\n\nMusicBrainz enforces a **~1 request/second** average rate limit per IP across the whole hosted instance and **blocks requests without a descriptive `User-Agent`** that identifies the application and a contact. This server satisfies both: it sends `musicbrainz-mcp-server/<version> (<contact>)` as the `User-Agent` and serializes all upstream calls through a process-wide token-bucket limiter, with response caching to keep repeat lookups off the wire. On a shared or hosted instance every client shares the one limiter, so bulk enumeration via `musicbrainz_browse_entities` paces accordingly. Set `MUSICBRAINZ_CONTACT` to your own email or URL when you deploy.\n\n### Attribution and licensing\n\nMusicBrainz core entity data is released under **CC0** (public-domain dedication) — see the [MusicBrainz license](https://musicbrainz.org/doc/About/Data_License). This server stays on that core metadata and does not fetch annotation text (which carries a different, non-CC0 license). Cover art is served by the [Cover Art Archive](https://coverartarchive.org/), a joint project of MusicBrainz and the Internet Archive; image URLs are linked, never rehosted, and each image's copyright stays with its rights holders. Cite MusicBrainz and the Cover Art Archive in downstream use.\n\n## Running the server\n\n### Local development\n\n- **Build and run:**\n\n  ```sh\n  # One-time build\n  bun run rebuild\n\n  # Run the built server\n  bun run start:stdio\n  # or\n  bun run start:http\n  ```\n\n- **Run checks and tests:**\n\n  ```sh\n  bun run devcheck   # Lint, format, typecheck, security, packaging\n  bun run test       # Vitest test suite\n  bun run lint:mcp   # Validate MCP definitions against the linter rules\n  ```\n\n### Docker\n\n```sh\ndocker build -t musicbrainz-mcp-server .\ndocker run --rm -e MUSICBRAINZ_CONTACT=you@example.com -p 3010:3010 musicbrainz-mcp-server\n```\n\nThe Dockerfile defaults to HTTP transport, stateless session mode, and logs to `/var/log/musicbrainz-mcp-server`. OpenTelemetry peer dependencies are installed by default — build with `--build-arg OTEL_ENABLED=false` to omit them.\n\n## Project structure\n\n| Directory | Purpose |\n|:---|:---|\n| `src/index.ts` | `createApp()` entry point — registers tools and the resource, inits both services. |\n| `src/config` | Server-specific environment variable parsing and validation with Zod. |\n| `src/services/musicbrainz` | MusicBrainz WS/2 client — User-Agent, rate limiter, response cache, retry, and domain types. |\n| `src/services/cover-art` | Cover Art Archive client — maps 404 to an empty image set, follows the release-group redirect. |\n| `src/mcp-server/tools` | Tool definitions (`*.tool.ts`). Ten read-only tools across search, lookup, browse, and cover art. |\n| `src/mcp-server/resources` | Resource definitions. The `musicbrainz://{entity_type}/{mbid}` entity mirror. |\n| `tests/` | Unit and integration tests mirroring `src/`. |\n\n## Development guide\n\nSee [`CLAUDE.md`/`AGENTS.md`](./CLAUDE.md) for development guidelines and architectural rules. The short version:\n\n- Handlers throw, framework catches — no `try/catch` in tool logic\n- Use `ctx.log` for request-scoped logging, `ctx.state` for tenant-scoped storage\n- Register new tools and resources via the barrels in `src/mcp-server/*/definitions/index.ts`\n- All upstream calls route through the services — never `fetch()` MusicBrainz directly, or you bypass the User-Agent, rate limiter, and cache\n- Wrap external API data: validate raw → normalize to domain type → return output schema; never fabricate missing fields\n\n## Contributing\n\nIssues and pull requests are welcome. Run checks and tests before submitting:\n\n```sh\nbun run devcheck\nbun run test\n```\n\n## License\n\nApache-2.0 — see [LICENSE](./LICENSE) for details.\n",
  "bytes": 18374,
  "sha": "45f23bbbe576b553ad0a59cde3582153a76b601aeef4bde989590277371da959",
  "repo_slug": "cyanheads/musicbrainz-mcp-server",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_cyanheads_musicbrainz_mcp_serv_5181d9c1/readme"
}