Skip to main content
Glama
monch1962
by monch1962

Calibre MCP

CI Python MCP Calibre Licence: MIT

A read-only Model Context Protocol server for an existing Calibre ebook library.

Calibre MCP lets MCP-compatible clients search book metadata, query Calibre's full-text index, inspect book details, browse library categories, and discover related books. It uses Calibre's supported calibredb command-line interface rather than reading metadata.db directly.

Features

  • Metadata search using Calibre's search language

  • Full-text search with matching snippets

  • Detailed metadata for individual books

  • Recently added books

  • Authors, tags, series, publishers, and language categories

  • Related-book discovery

  • MCP resources for books, searches, and library status

  • Optional Calibre Content Server links

  • In-memory TTL cache

  • Streamable HTTP transport

  • Podman Quadlet deployment

  • No metadata-changing MCP tools

Related MCP server: calibre-manager

Available tools

Tool

Purpose

server_info

Show server, Calibre, cache, and library configuration

library_status

Show book count and full-text indexing status

search_books

Search Calibre metadata

search_fulltext

Search inside indexed ebooks and return snippets

get_book_metadata

Return all available metadata for one book

list_recent_books

List books most recently added

list_categories

Browse authors, tags, series, publishers, and languages

find_related_books

Find books with overlapping authors, series, or tags

clear_cache

Clear the in-memory read cache

MCP resources

URI

Purpose

calibre://library/status

Library and full-text index status

calibre://book/{book_id}

Detailed metadata for a book

calibre://search/{query}

Metadata search results

Requirements

  • A Calibre library with metadata.db

  • Calibre 9.x

  • Python 3.11 or newer

  • An MCP client supporting Streamable HTTP

  • Podman and systemd for the included Quadlet deployment

The full-text tools require Calibre's full-text index to be enabled and completed.

Quick start with Podman Quadlet

1. Clone the repository

git clone https://github.com/monch1962/calibre-mcp.git
cd calibre-mcp

2. Confirm your Calibre library

The supplied Quadlet assumes:

/tank/media/Books

Confirm that the library database exists:

test -f /tank/media/Books/metadata.db && echo "Calibre library found"

3. Determine the library owner

stat -c 'uid=%u gid=%g owner=%U:%G' /tank/media/Books

Edit quadlet/calibre-mcp.container and set User= to the returned numeric UID and GID:

User=1000:1000

Also change the host library path if yours is different:

Volume=/tank/media/Books:/books

4. Build the image

sudo podman build \
  --build-arg CALIBRE_VERSION=9.11.0 \
  -t localhost/calibre-mcp:1.0.0 .

5. Install the Quadlet

sudo mkdir -p /etc/containers/systemd

sudo cp quadlet/calibre-mcp.container \
  /etc/containers/systemd/calibre-mcp.container

sudo systemctl daemon-reload
sudo systemctl start calibre-mcp.service

Do not run systemctl enable calibre-mcp.service. The generated service is transient; the Quadlet's [Install] section creates the boot dependency.

6. Verify the deployment

sudo systemctl status calibre-mcp.service --no-pager
sudo journalctl -u calibre-mcp.service -n 100 --no-pager
sudo podman ps --filter name=calibre-mcp

Verify Calibre inside the container:

sudo podman exec calibre-mcp \
  calibredb list \
  --with-library /books \
  --for-machine \
  --fields title \
  --limit 1

sudo podman exec calibre-mcp \
  calibredb fts_index status \
  --with-library /books

The default endpoint is:

http://localhost:8008/mcp

Test with MCP Inspector

npx @modelcontextprotocol/inspector

Select Streamable HTTP and connect to:

http://YOUR_SERVER:8008/mcp

Example metadata search:

{
  "query": "author:asimov",
  "limit": 10
}

Example full-text search:

{
  "query": "zero trust architecture",
  "limit": 10
}

Example restricted full-text search:

{
  "query": "encryption",
  "limit": 10,
  "restrict_to": "search:tags:security"
}

Connect an MCP client

Use the Streamable HTTP endpoint exposed by the server:

http://YOUR_SERVER:8008/mcp

Client configuration formats vary. Consult your client's MCP documentation and select Streamable HTTP rather than stdio or legacy SSE.

Calibre search examples

search_books accepts Calibre search expressions:

author:asimov
title:"i robot"
tags:history
series:"Discworld"
publisher:penguin
languages:eng
rating:>=4

An empty query returns all books, subject to the result limit.

Set the URL of your existing Calibre Content Server in the Quadlet:

Environment=CALIBRE_CONTENT_SERVER_URL=http://mini-nas:8083

When configured, metadata results include browser and format-download links.

Configuration

Environment variable

Default

Description

CALIBRE_LIBRARY_PATH

/books

Calibre library inside the container

CALIBREDB

calibredb

Path to the Calibre CLI

CALIBRE_COMMAND_TIMEOUT

120

Command timeout in seconds

CALIBRE_MAX_RESULTS

100

Maximum results returned by a tool

CALIBRE_CACHE_TTL

300

Cache lifetime in seconds; set to 0 to disable

CALIBRE_CACHE_SIZE

256

Maximum cached entries

CALIBRE_MAX_CONCURRENT_COMMANDS

4

Maximum concurrent calibredb subprocesses

CALIBRE_CONTENT_SERVER_URL

unset

Optional Content Server base URL

MCP_HOST

0.0.0.0

MCP HTTP bind address

MCP_PORT

8000

MCP port inside the container

HOME

/tmp/calibre-home

Writable location for Calibre configuration

Why the library mount is writable

Calibre checks whether the library filesystem is case-sensitive by briefly creating and deleting a probe file in the library root. Consequently, the bind mount cannot be mounted read-only.

This server remains functionally read-only because it exposes no tools that call Calibre commands such as:

  • add

  • remove

  • set_metadata

  • add_format

  • remove_format

Run the container as the same unprivileged UID and GID that owns the library. Do not run it as root unless your environment specifically requires that.

Security

  • Keep port 8008 restricted to trusted LAN or Tailscale clients.

  • Do not expose the endpoint directly to the public Internet.

  • Streamable HTTP does not add authentication in this deployment.

  • Place an authenticated reverse proxy in front of the service before broader exposure.

  • Pin release versions rather than using a moving container tag.

  • Review SECURITY.md before reporting a vulnerability.

Red-team hardening (round 1)

Ten adversarial attack vectors were proven with failing tests and then fixed. Each TestAttack_* test in tests/attack_round1_test.py is a permanent regression fixture for its vector.

#

Attack vector

Entry point

Defence

1

Unbounded cache key — a multi-megabyte query is retained in memory per cache entry

search_books / search_fulltext

Keys over 512 bytes are SHA-256 hashed (_cache_key)

2

Unbounded cache value — large calibredb output (comments, snippets) retained per entry

_run

Values over 1 MiB bypass the cache (_cache_put)

3

server_info subprocess hang — calibredb --version ran without a timeout

server_info

Timeout applied; TimeoutExpiredToolError

4

Unhandled JSONDecodeError on invalid calibredb output → raw internal error

_list_books / search_fulltext

_loads_json wrapper → ToolError

5

Unhandled ValueError on a non-numeric book-id key → raw internal error

_normalise_books

Wrapped → ToolError

6

Search-syntax injection via library metadata — quotes/backslashes in authors, series, or tags break out of the generated query

find_related_books

_exact_match_clause strips " and \ from clause values

7

Unbounded query length — MB-scale queries reach calibredb and the cache

search_books / search_fulltext

Queries over 8192 chars rejected with ToolError

8

Unbounded transient calibredb stdout capture under concurrent floods

_run

Residual risk — bounded by CALIBRE_COMMAND_TIMEOUT; documented

9

Unauthenticated endpoint on 0.0.0.0

deployment

Accepted posture — documented in SECURITY.md

10

Information disclosure — library path, Calibre version

server_info / library_status

Accepted for a read-only knowledge server; documented

Known-safe surfaces verified in this round: shell injection (list argv, no shell=True), option-value injection (--sort-by/--categories/--restrict-to reject leading-dash values in Calibre's parser), resource URI path traversal (non-numeric ids rejected), result-limit clamping (_limit), and cache race conditions (lock-guarded).

Red-team hardening (round 2)

Six input-shape validation vectors proven and fixed; fixtures in tests/attack_round2_test.py.

#

Attack vector

Entry point

Defence

11

Unbounded book_id magnitude — id:{huge} query built internally bypasses the round-1 query cap and reaches calibredb as an MB-scale argv entry

get_book_metadata / book_resource / find_related_books

_validate_book_id bounds ids to 1..2³¹−1 (_book)

12

Unbounded categories string → MB-scale argv

list_categories

1024-char cap → ToolError

13

Unbounded restrict_to string → MB-scale argv

search_fulltext

2048-char cap → ToolError

14

Unbounded sort_by string → MB-scale argv

search_books

128-char cap → ToolError

15

Non-iterable formats metadata → TypeError → raw 500

_content_links

Non-list/tuple formats ignored; details link still returned

16

Format-extension injection in generated download links (.., x;rm -rf)

_content_links

Extension whitelist [a-z0-9]{1,10} — non-matching formats skipped

Red-team hardening (round 3)

Three error-path robustness vectors proven and fixed; fixtures in tests/attack_round3_test.py.

#

Attack vector

Entry point

Defence

17

Oversized CSV field (over the 128 KiB csv field-size limit) → raw csv.Error → 500

list_categories

Iteration wrapped → ToolError

18

calibredb list output as an array of non-dict items → AttributeError in search_books → 500

_normalise_books

Non-dict array items rejected → ToolError

19

fts_search dict payload with an unexpected list-valued key passes through uncapped → response amplification

search_fulltext

Every list-valued key is sliced to the result limit

Red-team hardening (round 4)

Two concurrency/process-flood vectors proven and fixed; fixtures in tests/attack_round4_test.py.

#

Attack vector

Entry point

Defence

20

Concurrent calibredb process flood — N parallel tool calls spawn N subprocesses (CPU/memory exhaustion, Calibre DB contention)

_run

threading.Semaphore caps in-flight commands at CALIBRE_MAX_CONCURRENT_COMMANDS (default 4); oversubscribed calls → ToolError

21

server_info version subprocess flood — one uncached subprocess per call

server_info

Version call routed through the same semaphore (_run_version)

Red-team hardening (round 5 — terminal verification)

Zero new vulnerabilities. A coverage-gap audit added 11 verification tests (tests/attack_round5_test.py) exercising every entry point not yet covered by rounds 1–4 — search_resource, book_resource (non-numeric, traversal-like, in-range), status_resource, library_status, list_recent_books, clear_cache, search_fulltext list payloads, zero/negative limits, TTL-zero cache disable, and whitespace queries. All passed immediately, confirming the rounds 1–4 defences hold across the full tool/resource surface.

Two deployment-posture documentation findings were recorded in SECURITY.md (no code change): the Containerfile has no USER directive (runs as root when built outside the Quadlet, which sets User=1000:1000), and the Quadlet sets SecurityLabelDisable=true (SELinux label separation is off).

Local development

Create a virtual environment:

python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e .
python -m pip install pytest ruff

Run the tests:

pytest

Run lint checks:

ruff check .

Start the server locally:

export CALIBRE_LIBRARY_PATH="/path/to/Calibre Library"
python server.py

Project status

Version 1.0.0 is suitable for personal and trusted-network deployments. The public API may gain additional tools and resources in future minor releases, while existing tool names and argument shapes will be kept stable where practical.

Contributing

Issues and pull requests are welcome. See CONTRIBUTING.md.

Licence

Released under the MIT Licence.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    An MCP server that enables querying and managing Calibre libraries via chat by interacting with the Calibre content server over HTTP. It allows users to search for books, update metadata, manage authors and tags, and handle book file uploads or conversions.
    3
    BSD 3-Clause
  • A
    license
    A
    quality
    D
    maintenance
    An MCP server to manage and organize a Calibre ebook library, enabling metadata editing, search, conversion, and more through AI assistants.
    17
    5
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    A local stdio MCP server that enables AI tools to search a self-hosted Calibre library over SSH, supporting metadata queries, full-text search, and book details.
    7
    MIT