Skip to main content
Glama
CaseyRo

Readwise MCP HTTP Server

by CaseyRo

mcp-readwise

MCP server for Readwise and Readwise Reader, built on FastMCP. Engagement-aware reads, the usual write tools, and one thing you can't easily do anywhere else: turn a markdown blob into a real, brand-styled EPUB and have it land in your Reader Library a minute later.

16 tools. Python 3.12. Deployed via Docker.

Why this exists

The most common source of "long markdown that needs a reading home" in 2026 is research output from Claude, OpenAI, and other agent loops. Deep-research mode produces 3000–8000 word briefs in a single tool turn. They arrive as markdown in a chat window, and they shouldn't stay there — they want chapter navigation, a TOC sidebar, downloadable export to a Kobo / Boox / Kindle, and most often the Readwise iOS and iPad apps, which are quietly the best long-form reading and highlighting clients on those devices.

This MCP server is the transporter for that markdown into Reader. An agent calls save_markdown_as_epub, the EPUB appears in your Library a minute later, and the chat window is no longer your reading place.

Related MCP server: Kiseki-Labs-Readwise-MCP

The niche-but-nice part: markdown → real EPUB in Reader

EPUB rendered by save_markdown_as_epub, opened in Readwise Reader. The CDIT brand stylesheet survives Reader's renderer: Strong Blue H1 underlines, Inter typography, the mint-rail "Note" preface block from the frontmatter note: field, and Reader's own TOC sidebar listing the auto-generated chapters.

Readwise's Reader API has no file-upload endpoint — I verified this against their v3 docs and their own official CLI, which doesn't expose one either. The closest thing the API offers is "save URL" or "save HTML." Neither produces a true EPUB in Reader, which is the format with proper chapter nav, TOC, and downloadable export to Kobo / Boox / Kindle.

The one path that does produce a real EPUB is the email-to-library mechanism: every Reader account has a <custom>@library.readwise.io address that accepts EPUB attachments and ingests them properly. So save_markdown_as_epub automates that path:

markdown blob → pandoc renders to EPUB 3 (with CDIT brand styling)
             → aiosmtplib delivers via Resend SMTP
             → email lands at <custom>@library.readwise.io
             → Readwise ingest pipeline picks it up (1–5 min)
             → real EPUB appears in Library

The tool returns immediately after SMTP delivery — the ingest is async by nature. A companion tool verify_epub_received polls Reader to confirm the document landed, with time-aware retry guidance baked into the response so an LLM caller knows when to retry vs. when to surface a failure.

Setup

Three environment variables, all required (the server still boots without them — only save_markdown_as_epub refuses to run; every other tool, including verify_epub_received, works normally):

# Your custom Readwise Library email
# Find at: read.readwise.io → Account → Personalize email addresses
# Bearer credential — rotate via Readwise if it leaks.
READWISE_LIBRARY_EMAIL=casey-personal@library.readwise.io

# Resend API key, used as SMTP password (username is literal "resend")
RESEND_API_KEY=re_…

# Verified sender registered in Resend
EPUB_FROM_ADDRESS=mcp-readwise@cdit-dev.de

Calling it

# From any MCP client (Claude, agents, scripts):
result = save_markdown_as_epub(
    markdown="""---
    title: Q2 Planning Brief
    author: Casey
    tags: [planning, brief]
    note: Context for the team — read this before Thursday's call.
    ---
    # Background
    ...
    """,
    idempotency_key="brief-2026-q2-v1",  # optional — collapses retry duplicates
)
# → EpubSendResult(title=..., accepted_at=..., recipient=...,
#                  identifier_scheme="x-mcp-readwise-idempotency", ...)

# Wait 1–2 minutes, then:
verify = verify_epub_received(title=result.title, since=result.accepted_at)
# → VerifyResult(found=True, document=ReaderDocument(...),
#                note="Found in Reader Library.")

The docstring leads with the async contract loudly so LLM agents reading the schema know not to tell the human "done" until verify_epub_received confirms.

Brand stylesheet (CDIT)

EPUB output is styled by a hand-tuned CSS at mcp_readwise/assets/epub/cdit-style.css, inheriting the palette from cdit-works.de:

  • Carbon #272f38 (body text), Cloud Dancer #f0eee9 (page background)

  • Strong Blue #1f5da0 (links, H1 underline), Mint #5cc6c3 (blockquote rail, Note preface)

  • Inter weights 400 / 700 / 800 embedded as static woff2 subsets (latin + latin-ext, ~170KB)

  • Headings deliberately diverge from the website's display face: chapter heads use Inter weight 800 with tracking -0.02em, not League Gothic — condensed display fonts fatigue across long-form chapter breaks

  • body { line-height: 1.7; text-align: left; hyphens: auto; } tuned for sustained reading

To customize, fork the CSS file. It's a first-class editable asset, not generated from Python.

Frontmatter

Both save_markdown and save_markdown_as_epub accept YAML frontmatter for self-describing markdown:

---
title: My Note
author: Casey
summary: A brief description.
tags: [research, draft]
note: Context for the reader.
published_date: 2026-05-11
image_url: https://example.com/cover.jpg
---
# Body starts here

Content with **markdown** features — tables, footnotes, fenced code, smart quotes
all render properly through the `extra` + `sane_lists` + `smarty` extensions.

Title resolution (first non-empty wins): explicit title= param → frontmatter title: → first # H1 in body → "Untitled". Same precedence for other fields (without the H1 fallback).

Limits

  • EPUB ceiling: 20 MiB raw binary (Readwise's email ingest caps at 30 MB; base64 inflates ~33%, MIME adds ~1 MB, so 20 MiB fits with margin). Larger EPUBs raise EpubTooLargeError before SMTP.

  • Pandoc binary in the Docker image: ~150 MB. Accepted cost.

  • Inline images in markdown must use absolute HTTPS URLs — pandoc fetches them at build time; relative paths don't resolve.

The other 14 tools

Read (engagement-aware)

These two collapsed an earlier 7-tool read surface into intent-shaped calls. They're built on a per-source engagement score that joins Readwise v2 books with their Reader v3 documents, so books and articles, finished and saved, recent and legacy, all sit on one comparable axis.

Tool

Description

reading_status

Single-call snapshot — recent activity, evergreen top, current attention, junk drawer, signal density. Accepts window_days (default 7) and week_offset (default 0).

writing_material

Bundle highlights for drafting. Source-first (book_id / document_id / title_search) or topic-first (topic). Filters by min_engagement floor (default 0.7).

Read (direct Reader lookup)

These bypass the engagement cache to browse or look up the full Reader library (e.g. archived docs the cache doesn't surface).

Tool

Description

reader_list_documents

Cursor-paginated list of Reader documents, filterable by location / category / updated_after.

reader_get_by_url

Look up a single Reader document by its source URL.

Write

Tool

Description

save_url

Save a URL to Reader; Readwise fetches and parses. Synchronous.

save_markdown

Save a markdown blob to Reader as rendered HTML with category="epub" UI hint. Synchronous, returns ReaderDocument. Use this for lightweight notes you don't need as a real EPUB.

save_markdown_as_epub

The real-EPUB-via-email path described above. Async, returns EpubSendResult.

verify_epub_received

Confirm a save_markdown_as_epub send has landed. Time-aware retry guidance in the response note.

update_progress

Update reading progress (0.0–1.0).

create_highlight / update_highlight / delete_highlight

Highlight CRUD with note and tags.

Tags

Tool

Description

list_tags

List user-created custom tags.

create_tag / delete_tag

Tag CRUD.

tag_highlight

Add or remove a tag on a highlight.

Three ways to save your own content into Reader

You want

Use

Sync / Async

Fidelity

Setup

Save a URL (Readwise fetches & parses)

save_url

sync

HTML article

none

Save markdown as HTML with epub-UX hint

save_markdown

sync

HTML with category="epub"

none

Save markdown as a real EPUB book

save_markdown_as_epub

async (1–5 min)

true EPUB 3 with TOC, chapter nav, brand styling

three env vars

Installation

uv sync

Pandoc is required for save_markdown_as_epub. It's baked into the Docker image; for local dev install it via brew install pandoc. Other tools work without it.

Configuration

Variable

Required

Default

Description

READWISE_TOKEN

Yes

Readwise API access token (get one)

MCP_API_KEY

When TRANSPORT=http

Bearer token for the MCP Portal auth

TRANSPORT

No

stdio

stdio or http

HOST

No

127.0.0.1

HTTP server host

PORT

No

8000

HTTP server port

READWISE_BASE_URL

No

https://readwise.io

Readwise API base URL

ENGAGEMENT_INDEX_TTL_SECONDS

No

1800

TTL for the engagement index cache

ENGAGEMENT_TAG_DENYLIST

No

(built-in)

Tags excluded from the annotation bonus

EPUB sender (optional, but all three required together)

READWISE_LIBRARY_EMAIL

Only for save_markdown_as_epub

Your <custom>@library.readwise.io

RESEND_API_KEY

Only for save_markdown_as_epub

Resend SMTP password

EPUB_FROM_ADDRESS

Only for save_markdown_as_epub

Verified Resend sender address

SMTP_HOST

No

smtp.resend.com

Override to use Postmark, SES, etc.

SMTP_PORT

No

587

EPUB_LANG

No

en

EPUB OPF dc:language metadata

EPUB_MAX_BYTES

No

20971520

20 MiB ceiling before send

Usage

# Local stdio mode (default — for direct MCP client use)
READWISE_TOKEN=… uv run mcp-readwise

# HTTP mode (for MCP Portal / Cloudflare deployment)
READWISE_TOKEN=… MCP_API_KEY=… TRANSPORT=http uv run mcp-readwise

# Docker
cp .env.example .env  # fill in values
docker compose up -d

Health endpoint

GET /health

Returns build identifier, git commit, uptime, registered tool count, engagement index status, and the epub_sender configured flags (without ever exposing the API key or library email in plaintext).

{
  "status": "healthy",
  "version": "0.7.0",
  "build": "0.7.0+6ec5b6b",
  "tools": 16,
  "engagement_index": { "built": true, "source_count": 152, "age_seconds": 312 },
  "epub_sender": {
    "configured": true,
    "smtp_host": "smtp.resend.com",
    "smtp_port": 587,
    "from_address": "mcp-readwise@cdit-dev.de",
    "library_email_set": true
  }
}

How the engagement score works

Every source — book or article, Reader-imported or legacy Kindle — gets a vector engagement score with four components:

  • raw ranks "current attention" (recency-weighted overall score)

  • intensity ranks "evergreen interests" (recency removed; pure depth)

  • recency small modifier from how recently the source was last highlighted

  • return_strength captures multi-year highlight clusters and recent Reader re-opens

Computed from a layered sum of:

  1. Base layerlegacy (v2-only Kindle/iBooks book), highlighted, finished_no_hl, reading, saved_warm, saved_cold

  2. Density — non-discarded highlight count

  3. Recency — last-highlight age, banded (30d / 1y / 5y)

  4. Annotation — does any highlight carry a user note, a non-structural tag, or is_favorite?

  5. Return signal — multi-year highlight clusters AND/OR Reader-era reopens

See openspec/changes/archive/2026-05-11-workflow-shaped-tools/design.md for the full formula and decision rationale.

Project structure

mcp_readwise/
  server.py             # FastMCP app, tool registration, /health
  config.py             # pydantic-settings configuration
  client.py             # Centralized httpx client (auth, retries, rate limits)
  auth.py               # Bearer token verifier for MCP Portal
  engagement.py         # The engagement index + scoring formula
  markdown_render.py    # Frontmatter parser + Markdown → HTML helper
  epub_render.py        # Pandoc wrapper + EPUB metadata builder
  smtp_client.py        # Async SMTP transport (aiosmtplib)
  assets/epub/          # CDIT brand stylesheet + embedded Inter woff2 subsets
  models/               # Pydantic response models
  tools/
    status.py, writing.py             # Engagement-aware reads
    markdown.py                       # save_markdown (HTML path)
    epub_sender.py, epub_verifier.py  # Real-EPUB-via-email path
    reader.py, highlights.py, tags.py # Standard write tools

Deployment

Deployed via Komodo to ubuntu-smurf-mini, accessible through the Cloudflare MCP Portal at mcp-readwise.cdit-dev.de. Auto-deploys on push to main via GitHub webhook → Komodo listener.

License

MIT

Install Server
A
license - permissive license
A
quality
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/CaseyRo/mcp-readwise'

If you have feedback or need assistance with the MCP directory API, please join our Discord server