listenbrainz-mcp
# 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
Scored across 37 tools
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.
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.
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.
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.