A Model Context Protocol server for Listenarr, the automated audiobook collection manager. It lets an AI assistant browse your library, search the Audible catalogue, grab releases from your indexers, and diagnose downloads that got stuck — through Listenarr's own REST API.
Built in Go, following the architecture of jellyfin-mcp.
Note. There is a dependency on a local docker registry if you're deploying this to a local k8s node. You'll need to change this for your own k8s setup. See https://github.com/leachuk/listenarr-mcp/blob/main/k8s/plain/deployment.yaml
15 tools across 7 toolsets:
| Toolset | Tools | What it covers |
|---|---|---|
library | listenarr_library, listenarr_library_add, listenarr_library_edit, listenarr_library_delete | Browse and filter the collection, add books by ASIN, edit monitoring and metadata, rename/move files, delete entries |
search | listenarr_search, listenarr_metadata | Audible catalogue lookups (with fuzzy ranking), indexer release searches, author and series catalogues |
downloads | listenarr_downloads, listenarr_download_grab, listenarr_download_manage | Watch transfers, grab releases, retry blocked imports, cancel and purge |
monitoring | listenarr_monitoring | Standing subscriptions to an author or a series |
indexers | listenarr_indexers | Inspect, test, toggle, and remove indexers |
config | listenarr_config | Root folders, quality profiles, download clients, settings (credentials masked) |
system | listenarr_system, listenarr_history | Connectivity, health, storage, logs, and the activity audit trail |
Plus 13 resources (library, wanted list, active downloads, queue, health,
root folders, indexers, recent history, per-book detail, and four reference
guides) and 6 prompts for guided workflows: find-and-add,
whats-downloading, finish-series, library-health, follow-author, and
diagnose-stuck-download.
Three things all look like "search", and they are not interchangeable:
listenarr_library action=list → what you ALREADY HAVE
listenarr_search action=metadata → the Audible CATALOGUE → yields an ASIN
listenarr_search action=indexers → downloadable RELEASES → yields a download_reference
The server's instructions spell this out to the model, because getting it wrong is the most common way an assistant wastes calls on this kind of API.
| Variable | Required | Default | Notes |
|---|---|---|---|
LISTENARR_API_KEY | yes | — | Listenarr → Settings → General |
LISTENARR_URL | no | http://listenarr:4545 | In-cluster Service DNS name when running in Kubernetes |
LISTENARR_API_VERSION | no | v1 | URL segment; only change if Listenarr ships a v2 |
LISTENARR_TIMEOUT_SECONDS | no | 60 | Raise if indexer searches time out |
The server authenticates to Listenarr with the X-Api-Key header.
--toolsets library,search,system register only these tool groups
--read-only register only tools that cannot change anything
--disable-destructive allow writes, but no deletes or cancellations
--http serve streamable HTTP instead of stdio
--addr 0.0.0.0:8080 HTTP listen address
--http-token <secret> bearer token; REQUIRED for non-localhost binds
--read-only and --disable-destructive are enforced at registration time: a
tool that is never registered cannot be called, whatever the model attempts.
cp .env.example .env && $EDITOR .env
make run
Or point an MCP client at the binary:
{
"mcpServers": {
"listenarr": {
"command": "/path/to/listenarr-mcp",
"env": {
"LISTENARR_URL": "http://192.168.0.129:30545",
"LISTENARR_API_KEY": "your-api-key"
}
}
}
}
MCP servers here run under ToolHive in
the mcp namespace, alongside mcp-jellyfin and mcp-arr. ToolHive runs the
container over stdio and fronts it with its own streamable-HTTP proxy.
# 1. Build and publish the image to the local registry
make docker-build docker-push
# 2. Create the API key secret
make k8s-secret
# 3. Create the MCPServer (and optional NodePort on 30802)
make k8s-apply
# 4. Check it came up
make k8s-status
The endpoint is then:
http://mcp-mcp-listenarr-proxy.mcp.svc.cluster.local:8080/mcp
Manifests live in k8s/. See k8s/README.md for the
details, including a plain-Kubernetes alternative in k8s/plain/ for running
without ToolHive.
make docker-push uses skopeo, not docker push. Two reasons, both
specific to this setup:
192.168.0.129:30050 serves plain HTTP, and Docker refuses
that without an insecure-registries entry;skopeo runs natively on the host and --dest-tls-verify=false handles the HTTP.
brew install skopeo # one-time
make docker-build docker-push
make registry-tags
Three layers, because an assistant driving a download client can do real damage:
--read-only / --disable-destructive /
--toolsets decide what exists at all.confirm=true, and uses MCP
elicitation to ask the user directly where the client supports it.Credentials are masked on the way out: indexer and download-client API keys,
and anything credential-shaped in the settings blob, are truncated to
abcd...wxyz before the model ever sees them.
make check # gofmt check, go vet, go test
make build # binary into build/
make test
Layout follows jellyfin-mcp:
main.go CLI entrypoint (cobra)
internal/listenarr/ API client, helpers, extractors, input/output types
internal/server/ transports, middleware, subscriptions, completions
internal/server/tools/ the 15 tools, grouped by toolset
internal/server/resources/ resources and reference guides
internal/server/prompts/ guided workflow prompts
k8s/ ToolHive MCPServer + plain-Kubernetes manifests
MIT
1 commits
Hacker News (1)
Go
97.1%
Makefile
1.8%
Dockerfile
1.1%
A Model Context Protocol server for Listenarr, the automated audiobook collection manager. It lets an AI assistant browse your library, search the Audible catalogue, grab releases from your indexers, and diagnose downloads that got stuck — through Listenarr's own REST API.
Built in Go, following the architecture of jellyfin-mcp.
Note. There is a dependency on a local docker registry if you're deploying this to a local k8s node. You'll need to change this for your own k8s setup. See https://github.com/leachuk/listenarr-mcp/blob/main/k8s/plain/deployment.yaml
15 tools across 7 toolsets:
| Toolset | Tools | What it covers |
|---|---|---|
library | listenarr_library, listenarr_library_add, listenarr_library_edit, listenarr_library_delete | Browse and filter the collection, add books by ASIN, edit monitoring and metadata, rename/move files, delete entries |
search | listenarr_search, listenarr_metadata | Audible catalogue lookups (with fuzzy ranking), indexer release searches, author and series catalogues |
downloads | listenarr_downloads, listenarr_download_grab, listenarr_download_manage | Watch transfers, grab releases, retry blocked imports, cancel and purge |
monitoring | listenarr_monitoring | Standing subscriptions to an author or a series |
indexers | listenarr_indexers | Inspect, test, toggle, and remove indexers |
config | listenarr_config | Root folders, quality profiles, download clients, settings (credentials masked) |
system | listenarr_system, listenarr_history | Connectivity, health, storage, logs, and the activity audit trail |
Plus 13 resources (library, wanted list, active downloads, queue, health,
root folders, indexers, recent history, per-book detail, and four reference
guides) and 6 prompts for guided workflows: find-and-add,
whats-downloading, finish-series, library-health, follow-author, and
diagnose-stuck-download.
Three things all look like "search", and they are not interchangeable:
listenarr_library action=list → what you ALREADY HAVE
listenarr_search action=metadata → the Audible CATALOGUE → yields an ASIN
listenarr_search action=indexers → downloadable RELEASES → yields a download_reference
The server's instructions spell this out to the model, because getting it wrong is the most common way an assistant wastes calls on this kind of API.
| Variable | Required | Default | Notes |
|---|---|---|---|
LISTENARR_API_KEY | yes | — | Listenarr → Settings → General |
LISTENARR_URL | no | http://listenarr:4545 | In-cluster Service DNS name when running in Kubernetes |
LISTENARR_API_VERSION | no | v1 | URL segment; only change if Listenarr ships a v2 |
LISTENARR_TIMEOUT_SECONDS | no | 60 | Raise if indexer searches time out |
The server authenticates to Listenarr with the X-Api-Key header.
--toolsets library,search,system register only these tool groups
--read-only register only tools that cannot change anything
--disable-destructive allow writes, but no deletes or cancellations
--http serve streamable HTTP instead of stdio
--addr 0.0.0.0:8080 HTTP listen address
--http-token <secret> bearer token; REQUIRED for non-localhost binds
--read-only and --disable-destructive are enforced at registration time: a
tool that is never registered cannot be called, whatever the model attempts.
cp .env.example .env && $EDITOR .env
make run
Or point an MCP client at the binary:
{
"mcpServers": {
"listenarr": {
"command": "/path/to/listenarr-mcp",
"env": {
"LISTENARR_URL": "http://192.168.0.129:30545",
"LISTENARR_API_KEY": "your-api-key"
}
}
}
}
MCP servers here run under ToolHive in
the mcp namespace, alongside mcp-jellyfin and mcp-arr. ToolHive runs the
container over stdio and fronts it with its own streamable-HTTP proxy.
# 1. Build and publish the image to the local registry
make docker-build docker-push
# 2. Create the API key secret
make k8s-secret
# 3. Create the MCPServer (and optional NodePort on 30802)
make k8s-apply
# 4. Check it came up
make k8s-status
The endpoint is then:
http://mcp-mcp-listenarr-proxy.mcp.svc.cluster.local:8080/mcp
Manifests live in k8s/. See k8s/README.md for the
details, including a plain-Kubernetes alternative in k8s/plain/ for running
without ToolHive.
make docker-push uses skopeo, not docker push. Two reasons, both
specific to this setup:
192.168.0.129:30050 serves plain HTTP, and Docker refuses
that without an insecure-registries entry;skopeo runs natively on the host and --dest-tls-verify=false handles the HTTP.
brew install skopeo # one-time
make docker-build docker-push
make registry-tags
Three layers, because an assistant driving a download client can do real damage:
--read-only / --disable-destructive /
--toolsets decide what exists at all.confirm=true, and uses MCP
elicitation to ask the user directly where the client supports it.Credentials are masked on the way out: indexer and download-client API keys,
and anything credential-shaped in the settings blob, are truncated to
abcd...wxyz before the model ever sees them.
make check # gofmt check, go vet, go test
make build # binary into build/
make test
Layout follows jellyfin-mcp:
main.go CLI entrypoint (cobra)
internal/listenarr/ API client, helpers, extractors, input/output types
internal/server/ transports, middleware, subscriptions, completions
internal/server/tools/ the 15 tools, grouped by toolset
internal/server/resources/ resources and reference guides
internal/server/prompts/ guided workflow prompts
k8s/ ToolHive MCPServer + plain-Kubernetes manifests
MIT
Hacker News (1)
1 commits
Go
97.1%
Makefile
1.8%
Dockerfile
1.1%