Skip to main content
Glama
Dthen

listenbrainz-mcp

by Dthen
README.md
# listenbrainz-mcp

Self-hosted [MCP](https://modelcontextprotocol.io/) server exposing the full
public ListenBrainz API as **37 tools** over stdio: 94/94 OpenAPI spec
operations plus 35 documented-but-not-in-spec endpoints (129 total),
enforced by an exact-set coverage gate in the test suite.

Python ≥ 3.11 · `mcp` 2.x SDK · `httpx2`. Tools return JSON envelope strings —
failures are `{"error": ..., "status": ...}` envelopes, never raises. The
`LISTENBRAINZ_TOKEN`, if set, only ever rides the `Authorization` header:
never logged, never echoed into an envelope.

## Install & run

```sh
python -m venv .venv
.venv/bin/pip install -e ".[dev]"    # runtime + pytest
.venv/bin/listenbrainz-mcp           # stdio server; or: python -m listenbrainz_mcp
```

## Configuration

| Variable | Required | Meaning |
|---|---|---|
| `LISTENBRAINZ_TOKEN` | no | Auth token. Omit to run unauthenticated (tighter upstream rate limits). |

Register with any MCP client using the common config shape:

```json
{
  "mcpServers": {
    "listenbrainz": {
      "command": "/absolute/path/to/listenbrainz-mcp/.venv/bin/listenbrainz-mcp",
      "args": [],
      "env": { "LISTENBRAINZ_TOKEN": "<token, or omit>" }
    }
  }
}
```

## Tools

Full per-tool endpoint map lives in the registry itself — import
`listenbrainz_mcp.server` and read `coverage.snapshot()` (or run each tool's
docstring, which carries the authoritative usage notes):

| Domain | Tools |
|---|---|
| core | get_listens, get_listen_summary, submit_listen, manage_listen, search_users, get_similar_users |
| stats | get_user_stats, get_activity_stats, get_sitewide_stats, get_entity_listeners |
| metadata | get_recording_metadata, get_entity_metadata, lookup_metadata, get_manual_mapping, submit_manual_mapping |
| popularity | get_popularity |
| radio | get_recommendations, manage_recommendation_feedback, lb_radio |
| feedback | manage_recording_feedback, manage_pins |
| discovery | get_fresh_releases |
| playlists | list_playlists, get_playlist, manage_playlist, manage_playlist_tracks |
| social | manage_follows, get_feed, manage_timeline |
| art | generate_cover_art |
| system | validate_token, get_dump_info, manage_settings, get_service_status, get_playlist_status, get_donors |

Five spec paths differ live (spec spelling 404s); the real paths are wired
and documented in `coverage.SANCTIONED_SPEC_OVERRIDES`.

## Timeouts & upstream quirks

Default client timeout is 30 s; three slow routes ride per-request overrides,
each asserted at the transport seam by unit tests:

- lb-radio generation routes (`explore/lb-radio`, `lb-radio/artist/{mbid}`,
  `lb-radio/tags`) → **120 s** (generation observed ~28–40 s)
- `GET /1/playlist/search` → **90 s** (upstream answers in ~32–33 s consistently)
- `GET /1/donors/all-flairs` → **60 s** (~25 s observed)

Upstream occasionally resets long-running connections at ~40 s on
`/1/lb-radio/tags` (`RemoteProtocolError` envelope). It is server-side and
intermittent — retry before assuming a code fault.

## Testing

```sh
.venv/bin/pytest tests/ -q
```

Gates: exact-set coverage (every spec op claimed exactly once), domain unit
tests on a scripted transport, and full in-process dispatch tests (real MCP
client → MCPServer → impls → LBClient with a faked socket).
`scripts/live_smoke.py` is a manual seven-call unauthenticated smoke against
the real API.

## License

[0BSD](LICENSE) — Zero-Clause BSD.

TDQS

A4.1/5.0

Scored across 37 tools

Disambiguation4/5

Most tools have clearly distinct purposes (listens, stats, metadata, feedback, pins, playlists, etc.). A few could be confused—e.g., get_user_stats vs get_activity_stats both deal with stats, and manage_recording_feedback vs manage_recommendation_feedback are similar—but the detailed descriptions and parameter sets make each tool's role clear enough to prevent misselection.

Naming Consistency4/5

The naming predominantly follows a consistent get_/manage_/submit_ pattern (get_listens, get_user_stats, manage_playlist, submit_listen). Exceptions like 'lb_radio' and 'generate_cover_art' break the pattern slightly, but they are still readable and self-explanatory. Overall, the conventions are predictable with a few outliers.

Tool Count4/5

At 37 tools, the surface is substantial, but it corresponds to a complete API wrapper for ListenBrainz. Each tool bundles multiple related endpoints (e.g., manage_playlist covers 9 actions), so the functional scope is well-managed. It sits at the high end of 'reasonable' for a full-featured service, not sprawling or redundant.

Completeness5/5

The tool set covers the major ListenBrainz domains: listening data (submit/get/manage), statistics (user/sitewide/activity), metadata lookup and mapping, recommendations and feedback, pins, follows, playlists (create/edit/delete/copy/export/import), feed and timeline events, cover art generation, and settings/status. It includes both read and write operations, and even handles edge cases like token validation and dump info. No obvious dead ends or missing lifecycle steps.

Maintenance

ActivityMaintained
ResponsivenessNo issues