Readwise MCP Server
A lightweight Python-based server that enables users to manage and retrieve their Readwise highlights and daily reviews through a token-efficient interface. It supports features like document importing, historical backfilling, and highlight searching with built-in deduplication and state management.
README
Readwise MCP Server
Minimal Python MCP server for Readwise integration - token-efficient, single-file implementation using FastMCP.
Features
- Token-efficient: 8 essential tools (vs 13+ in Node.js version)
- Single-file architecture: ~350 lines of code
- Proven logic: Reuses battle-tested deduplication and pagination from backfill script
- State compatibility: Preserves existing state file format
- Smart optimization: Uses synced ranges to skip unnecessary API calls
Installation
1. Clone repository
git clone https://github.com/ngpestelos/readwise-mcp-server.git
cd readwise-mcp-server
2. Create virtual environment
python3 -m venv venv
source venv/bin/activate
3. Install dependencies
pip install -r requirements.txt
4. Configure environment variables
Set these in your .mcp.json file:
READWISE_TOKEN: Your Readwise API tokenVAULT_PATH: Path to your PARA vault (e.g.,/path/to/your/vault)
Configuration
Update .mcp.json
Add or update the readwise entry in your vault's .mcp.json:
{
"mcpServers": {
"readwise": {
"command": "/absolute/path/to/readwise-mcp-server/venv/bin/python",
"args": ["/absolute/path/to/readwise-mcp-server/server.py"],
"env": {
"READWISE_TOKEN": "your_token_here",
"VAULT_PATH": "/absolute/path/to/your/vault"
}
}
}
}
Replace the paths with your actual installation locations.
Tools Reference
1. readwise_daily_review()
Fetch today's highlights and save to Daily Reviews directory.
Parameters: None
Returns:
{
"status": "success",
"count": 42,
"file": "/path/to/daily-review.md"
}
Example:
Call readwise_daily_review()
2. readwise_import_recent(category="tweet", limit=20)
Import recent documents since last import with automatic deduplication.
Parameters:
category(string, optional): Document category (default: "tweet")limit(int, optional): Maximum documents to fetch (default: 20)
Returns:
{
"status": "success",
"imported": 5,
"skipped": 15,
"total_analyzed": 20
}
Example:
Call readwise_import_recent(category="article", limit=50)
3. readwise_backfill(target_date, category="tweet")
Paginate backwards to target date with synced range optimization.
Parameters:
target_date(string, required): Target date in YYYY-MM-DD formatcategory(string, optional): Document category (default: "tweet")
Returns:
{
"status": "success",
"imported": 67,
"skipped": 433,
"pages": 10,
"reached_target": true
}
Example:
Call readwise_backfill(target_date="2026-01-01")
4. readwise_book_highlights(title=None, book_id=None)
Get highlights for a specific book.
Parameters:
title(string, optional): Book title to search forbook_id(string, optional): Specific book ID
Returns:
{
"status": "success",
"count": 15,
"highlights": [...]
}
Example:
Call readwise_book_highlights(title="Atomic Habits")
5. readwise_search_highlights(query, limit=50)
Search highlights by text query.
Parameters:
query(string, required): Search querylimit(int, optional): Maximum results (default: 50)
Returns:
{
"status": "success",
"count": 8,
"highlights": [...]
}
Example:
Call readwise_search_highlights(query="productivity")
6. readwise_state_info()
Show current import state and synced ranges.
Parameters: None
Returns:
{
"status": "success",
"last_import": "2026-01-22T04:29:12Z",
"oldest_imported": "2026-01-01",
"synced_ranges": [...],
"backfill_in_progress": false,
"documents_on_disk": 1044,
"documents_with_ids": 614
}
Example:
Call readwise_state_info()
7. readwise_init_ranges()
Scan filesystem to build synced_ranges from existing documents.
Parameters: None
Returns:
{
"status": "success",
"range": {
"start": "2026-01-01T00:00:00Z",
"end": "2026-01-21T00:00:00Z",
"doc_count": 614
},
"documents_analyzed": 614
}
Example:
Call readwise_init_ranges()
8. readwise_reset_state(clear_ranges=False)
Clear state file (optionally preserve synced_ranges).
Parameters:
clear_ranges(bool, optional): Whether to clear synced ranges (default: False)
Returns:
{
"status": "success",
"message": "State reset",
"cleared_ranges": false
}
Example:
Call readwise_reset_state(clear_ranges=True)
State File Format
The server maintains state at .claude/state/readwise-import.json:
{
"last_import_timestamp": "2026-01-22T04:29:12.864733Z",
"oldest_imported_date": "2026-01-01",
"synced_ranges": [
{
"start": "2026-01-01T06:17:43.693000+00:00",
"end": "2026-01-21T02:33:27.975000+00:00",
"doc_count": 614,
"verified_at": "2026-01-21T10:43:56.626549Z"
}
],
"backfill_in_progress": false
}
Testing
Run the test suite:
source venv/bin/activate
pytest test_server.py -v
Test Categories
Unit Tests:
- State file reading/writing
- Synced range optimization logic
- Filename sanitization
- ID extraction from URLs
- Document scanning for deduplication
Integration Tests:
- API calls with mocked responses
- Markdown formatting
- Document saving with collision handling
Troubleshooting
Connection Issues
-
Check MCP connection:
/mcpShould show "Connected to readwise"
-
Verify environment variables are set correctly in
.mcp.json -
Check logs in stderr output
State Issues
If state file appears corrupted:
-
View current state:
Call readwise_state_info() -
Reset state (preserve ranges):
Call readwise_reset_state() -
Rebuild ranges from filesystem:
Call readwise_init_ranges()
Deduplication Issues
If documents are being imported multiple times:
-
Rebuild synced ranges:
Call readwise_init_ranges() -
Check filesystem for duplicate filenames manually
-
Verify readwise_url frontmatter is present in existing documents
Architecture
Single-File Design
The server is intentionally kept to a single file (~350 lines) for:
- Simplicity and maintainability
- Easy deployment and updates
- Minimal dependencies
- Clear code organization
Reused Logic
Key functions reused from .claude/scripts/readwise-backfill.py:
load_state()/write_state()- State managementoptimize_backfill()- Synced range optimizationscan_existing_documents()- Filesystem deduplicationsanitize_filename()- Safe filename generationextract_id_from_url()- ID extraction
Token Efficiency
Optimizations for reduced token usage:
- 8 tools vs 13+ in Node.js version (38% reduction)
- Combined operations (fetch + dedupe + save in one call)
- Smart defaults minimize required parameters
- Tool descriptions limited to 20-30 words
- Returns structured summaries, not full markdown dumps
Estimated token overhead reduction: ~60%
Comparison with Node.js Version
| Feature | Python MCP | Node.js MCP |
|---|---|---|
| Lines of code | ~350 | ~6,749 |
| Tool count | 8 | 13+ |
| Dependencies | 4 | 10+ |
| State compatibility | ✓ | ✓ |
| Token efficiency | High | Medium |
| Maintenance | Simple | Complex |
Development
Running locally for development
source venv/bin/activate
python server.py
Adding new tools
- Add tool function using
@mcp.tool()decorator - Follow existing patterns for error handling and logging
- Return structured dict with
statusfield - Add tests to
test_server.py - Update README.md with tool documentation
License
MIT
Credits
- Built with FastMCP by Anthropic
- Based on proven logic from
.claude/scripts/readwise-backfill.py - Designed for token efficiency and simplicity
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
E2B
Using MCP to run code via e2b.