kavita-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@kavita-mcpTurn this markdown into an EPUB and add it to my Reports collection"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
kavita-mcp
An MCP server that publishes markdown as EPUB
books into a Kavita library, and reads your own
reading state there. Claude writes a report, calls add_document, and a few
minutes later the book is on your e-reader through Kavita's OPDS catalog.
The EPUB is built with pandoc and carries everything Kavita needs: title, author, summary, language, date and collection. Kavita imports it with its folder watcher; nothing else has to happen.
Files are written through File Browser's HTTP API (the upload service next to Kavita), not a shared volume.
Reading tools (on deck, unread, want-to-read, mark read) act as the user whose Kavita key the server has.
No database. No book content is ever returned to the model.
Built for and tested against Kavita 0.9.1 (0.9.1.4) and File Browser v2.63.
Tools
All ten carry title, readOnlyHint, destructiveHint, idempotentHint and
openWorldHint annotations.
Tool | Parameters | What it does |
|
| Builds the EPUB and writes it to |
|
| Rewrites a report's metadata in its EPUB. |
|
| Deletes a report's folder. Needs |
|
| Compact rows across all your libraries, with |
|
| Title search (Kavita doesn't search summaries). |
|
| Author, summary, collections, word count, pages, progress, added date, file path, want-to-read, Kavita link. |
| Collection and library names, to reuse before publishing. | |
|
| Your "read next" queue, which Kavita's OPDS catalog shows. |
|
| What you're reading, what you haven't started, your queue. |
|
| Marks every page read. |
A row is {series_id, title, author, library, added, word_count, pages, read_status, progress_pct}.
Images: only embedded raster images as data: URIs (PNG, JPEG, GIF, WebP;
up to MAX_IMAGE_BYTES each). Any other image (a URL, a path, SVG) becomes its alt
text; pandoc runs with --sandbox, so it never fetches or reads anything else.
Nothing active reaches the book. Kavita's web reader puts book pages into the
page without sanitising them, so the EPUB gets only passive content: raw HTML is
dropped except a few bare tags (<sub>, <sup>, <br>, <b>, <i>, ...), event
handler and style attributes are removed, and links keep their target only for
http(s):, mailto: and # (others keep just their text).
scan_now: asks Kavita to scan the Reports library now (seconds) instead of
waiting for its folder watcher (about 6 minutes). It needs an admin key; with
another key the file is still written and the answer says so.
Deleting the last report also removes its series from Kavita directly (admin
key): Kavita removes nothing in a scan that finds no series at all, so it would
otherwise keep listing it until the next report is published. A hidden
Reports/.kavita-mcp file keeps the folder from being empty, because Kavita aborts
every scan of an empty library folder.
Related MCP server: mdbook
Configuration
Variable | Default | |
| (required) | A Kavita auth key (user settings → Auth keys). The tools act as its user. |
|
| Kavita, as kavita-mcp reaches it |
| unset | Kavita's public URL, for links in answers |
|
| The Reports library's folder as Kavita sees it |
|
| File Browser, with its login method |
|
| The same folder as File Browser sees it |
|
| How long |
|
| Cap on |
|
| Cap on each embedded image |
|
| or |
|
| The endpoint is |
GET /healthz answers {"kavita": …, "library": …, "upload": …} (503 if one is
false).
Deployment
The image is ghcr.io/jprintz/kavita-mcp:<version> (amd64 and arm64). It needs a
writable /tmp (pandoc); everything else can be read-only.
The layout it expects: one folder that File Browser serves, with a Books/ and a
Reports/ folder inside, and Kavita mounting Reports/ (read-only is enough) as
the folder of a Book library:
library/ File Browser's root
Books/ Kavita: /books (your other books)
Reports/ Kavita: /reports (library "Reports", type Book)A Podman Quadlet unit, as the author runs it next to Kavita and File Browser on a network both share:
[Container]
ContainerName=kavita-mcp
Image=ghcr.io/jprintz/kavita-mcp:0.1.0
Network=edge.network
Secret=kavita_mcp_api_key,type=env,target=KAVITA_API_KEY
Environment=KAVITA_PUBLIC_URL=https://kavita.example.com
UserNS=auto
ReadOnly=true
NoNewPrivileges=true
DropCapability=ALLIn Kavita, once:
Server settings → turn on folder watching (it is off by default).
Add a library "Reports": type Book, folder
/reports, folder watching on, manage collections on (collections come from the EPUB).
Authentication: the server has none of its own. Put it behind something that authenticates every request: the author uses Cloudflare Access with Managed OAuth, which is what claude.ai custom connectors expect. Don't expose it unauthenticated: whoever reaches it acts with the Kavita key.
Logs carry the tool name, the slug or series ID, the outcome and the duration; never the key, the markdown, summaries or Kavita's answers.
Verified Kavita behaviour (0.9.1.4)
Checked against the source at the release commit and a live instance; the integration tests pin most of it.
EPUB metadata Kavita reads (Book library):
dc:title→ series name,dc:creator→ writer,dc:description→ summary,dc:language,dc:date→ release year, an EPUB3 collection title (title-typecollection) → a collection owned by the server's first admin, reused by name (only with "manage collections" on).dc:subjectbecomes genres; nothing in an EPUB becomes Kavita tags. So no metadata API call is needed.Finding a new series:
POST /api/Series/v2with{"field": 24, "comparison": 0, "value": "/reports/<slug>"}(the series folder).Library/scan-folderwaits a fixed minute and takes the key only in its body;POST /api/Library/scan?libraryId=starts right away, soscan_nowuses that.Folder watching is off by default (server setting). When on, a new or deleted folder shows up after about 6 minutes (5 minutes' debounce, then a scan 1 minute later). It picks up writes from another container on the same host.
Requests: enums are integers;
Series/v2returns the total only in thePaginationresponse header; collections:Collection/update-for-serieswith id 0 always creates a new collection, so kavita-mcp looks one up by name first;Collection/update-serieswithseriesIdsToRemoveremoves.Permissions: scanning (
Library/scan,Series/scan,scan-folder), series metadata edits and series deletes are admin-only. Reading, search, want-to-read, progress and the user's own collections work with any key. Want-to-read, progress and collections are per user, so use the key of the person who reads.Auth: the key goes in the
x-api-keyheader on every call; no token exchange. A bad key gets 401 with an empty body.Overwriting an EPUB keeps the series ID (same title) and updates it in place. Two catches, both handled:
Kavita re-reads a file only when its mtime, cut to the minute, is later than the book's last scan. A change within the same minute is ignored unless the scan is forced. kavita-mcp forces a re-read of that one book (
Series/scan) onscan_now, and also without it when Kavita scanned the book less than two minutes ago.Kavita drops a library-scan request while a scan of that library is still running, so
scan_nowrepeats the request while it waits.
A changed title makes a new series (progress and collections stay with the old one, which disappears), so
update_documentnever changes titles.Collections work without Kavita+. They are private to their owner unless promoted. A user's own collections show in their OPDS catalog.
Deleting: a series whose folder is gone disappears on the next scan, but only if that scan finds at least one other series; Kavita aborts scans of an empty library folder outright.
DELETE /api/Series/{id}alone, with the file still there, comes back on the next scan.Kavita's web reader inserts book pages with Angular's sanitiser bypassed (
bypassSecurityTrustHtml), so anything active in an EPUB would run in the reader's session; see "Nothing active reaches the book" above.
Also found: Kavita cuts titles with a volume marker ("Report v2 Final" becomes
series "Report", volume 2) and merges every such book into one series. For those
titles the EPUB names its series explicitly (calibre:series = the title, with
Kavita's loose-leaf index), which keeps the title and a single book. Folders loose
in a library's root are ignored, hence one folder per book.
Development
uv sync --all-extras
make test # unit tests; set PANDOC (pandoc 3.11) and EPUBCHECK_JAR for the EPUB ones
make integration # throwaway Kavita + File Browser + this image, then the acceptance tests
make throwaway-downscripts/throwaway.sh starts Kavita 0.9.1 and File Browser laid out like the
deployment above, creates an admin, both libraries, a non-admin user and their
keys (in .throwaway/env, git-ignored). scripts/record_fixtures.py re-records the
Kavita responses the unit tests use. uv run pytest -m watch checks folder
watching on its own (about 7 minutes). In CI the integration workflow runs only when
started by hand.
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Publish Markdown as shareable documents, export PDF and Word, share, and send for e-signature.
Search, read, and safely update Markdown notes in your connected Phasoric knowledge vaults.
Create, validate, edit, export (markdown/svg/png/mermaid), and search JSON Canvas files.
MCP-native open-source Notion alternative: read & write pages, databases and kanban boards.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables writing text content to Markdown files with folder organization and overwrite control, and listing recent Markdown files.-
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to read, write, and manage markdown books with multi-format support and interactive features.MIT
- AlicenseAqualityAmaintenanceGit-native Markdown content management CLI with a built-in MCP server. Lets AI clients (Claude, Cursor) search, read, write, and govern story/paper/note repositories, auto-generate READMEs, validate configs, and export EPUB or SFT training data.920 npmMIT
- AlicenseAqualityCmaintenanceEnables local EPUB reading with AI agents, supporting annotations, parallel editions, and MCP tools.7AGPL 3.0