opds-mcp
# opds-mcp
[](https://github.com/pastukhov/opds-mcp/actions/workflows/ci.yml)
[](https://github.com/pastukhov/opds-mcp/actions/workflows/coverage-badge.yml)
An [MCP](https://modelcontextprotocol.io) server that lets an LLM browse, search and download
books from [OPDS](https://ru.wikipedia.org/wiki/OPDS) catalogs (Open Publication Distribution
System) — the Atom/JSON-based feed format used by digital libraries such as Project Gutenberg,
Standard Ebooks, Feedbooks, and many self-hosted book servers (Calibre-Web, COPS, KOReader sync
targets, etc).
Supports both OPDS 1.x (Atom + XML) and OPDS 2.0 (JSON) catalogs, OpenSearch-based full-text
search, and downloading acquisition links to disk.
## Tools
- **`opds_browse`** — fetch a catalog/feed document and return its navigation links (search,
next/prev, subsections, facets) plus entries (title, authors, summary, categories, acquisition
links, cover images).
- **`opds_search`** — search a catalog. Give it either the catalog's feed URL (its
`rel="search"` link is discovered automatically) or an OpenSearch description URL directly,
plus a free-text query.
- **`opds_get_entry`** — fetch a single entry/publication document (e.g. an entry's
`rel="alternate"` link) for full details.
- **`opds_download`** — download the file behind an acquisition link to a local directory and
return the saved file path.
All tools accept optional `username`/`password` for catalogs that require HTTP Basic Auth. The
`url` argument of `opds_browse`/`opds_search`/`opds_get_entry` is optional if the server is
configured with a default catalog via `OPDS_BASE_URL` (see below) — pass `url` explicitly to
browse a different catalog for that one call.
## Install
```bash
npm install
npm run build
```
## Configure in an MCP client
### Via npx (no local checkout required)
The package hasn't been published to the npm registry, but `npx` can install and run it straight
from this GitHub repo — it clones the repo, runs `npm install` (which triggers the `prepare`
script to build `dist/`), then executes the `opds-mcp` bin:
```json
{
"mcpServers": {
"opds": {
"command": "npx",
"args": ["-y", "github:pastukhov/opds-mcp"],
"env": {
"OPDS_BASE_URL": "https://example.com/opds/root.xml",
"OPDS_USERNAME": "optional-default-username",
"OPDS_PASSWORD": "optional-default-password",
"OPDS_DOWNLOAD_DIR": "/absolute/path/to/save/books"
}
}
}
}
```
To pin a specific branch or commit, append it: `"github:pastukhov/opds-mcp#branch-or-sha"`.
### Via a local checkout
Example for Claude Desktop / Claude Code (`claude_desktop_config.json` or `.mcp.json`):
```json
{
"mcpServers": {
"opds": {
"command": "npx",
"args": ["-y", "/absolute/path/to/opds-mcp"],
"env": {
"OPDS_BASE_URL": "https://example.com/opds/root.xml",
"OPDS_USERNAME": "optional-default-username",
"OPDS_PASSWORD": "optional-default-password",
"OPDS_DOWNLOAD_DIR": "/absolute/path/to/save/books"
}
}
}
}
```
`npx -y /absolute/path` installs dependencies and builds on first run, same as the GitHub form
above. Alternatively, run `npm install && npm run build` yourself and point `command`/`args`
directly at `node` and `dist/index.js`.
- **`OPDS_BASE_URL`** — the catalog `opds_browse`/`opds_search`/`opds_get_entry` use when a tool
call doesn't pass its own `url`. This is the recommended way to point the server at a specific
library (e.g. `https://your-library.example/opds`) without relying on the model to know or
guess the address; omit it to require an explicit `url` on every call instead.
- **`OPDS_USERNAME`/`OPDS_PASSWORD`** — used as a fallback whenever a tool call doesn't pass its
own credentials, which is convenient when the server is dedicated to a single authenticated
catalog.
- **`OPDS_DOWNLOAD_DIR`** — where `opds_download` saves files; defaults to a directory under the
OS temp folder.
## Example flow
1. `opds_browse` with the catalog's root URL to see navigation links and/or a first page of
entries.
2. `opds_search` with that same root URL and a query to find a specific book.
3. Pick an entry's `acquisitions[].href` from the result and pass it to `opds_download` to save
the file locally (or use `opds_get_entry` first if you need more detail than the search
result already includes).
## Development
```bash
npm run dev # run the server directly with tsx
npm run typecheck # tsc --noEmit
npm test # vitest, using fixture OPDS documents under fixtures/
npm run test:coverage # vitest with a coverage report under coverage/
npm run build # compile to dist/
```
Every pull request runs `typecheck`, `build` and `test:coverage` via
[`.github/workflows/ci.yml`](.github/workflows/ci.yml). On every push to `main`,
[`.github/workflows/coverage-badge.yml`](.github/workflows/coverage-badge.yml) recomputes coverage
and commits `.github/badges/coverage.json`, which the badge at the top of this file reads via
[shields.io's endpoint badge](https://shields.io/badges/endpoint-badge).
## Notes
- Only `http:`/`https:` URLs are accepted; other schemes are rejected before any request is made.
- Downloaded files are capped at 200MB by default and written under a sanitized filename inside
the configured download directory.
- Because feed documents can be arbitrarily large, `opds_browse`/`opds_search` return whatever a
single page contains; use the `navigation.next` link from the response to page through the
rest of the catalog.
TDQS
Scored across 4 tools
Each tool has a distinct purpose: opds_browse for navigation, opds_search for queries, opds_get_entry for detailed entry info, and opds_download for file retrieval. There is no overlap or ambiguity.
All tool names follow a consistent opds_verb_noun pattern (opds_browse, opds_download, opds_get_entry, opds_search), making the set predictable and easy to understand.
With exactly 4 tools, the server covers the essential operations for an OPDS client (browse, search, get entry, download) without being excessive or insufficient.
The tool set provides complete coverage for interacting with an OPDS catalog: navigating feeds, searching, retrieving detailed entry information, and downloading files. No obvious gaps for client-side operations.