Production-ready MCP server for PDF processing with intelligent caching. Extract text, search, and analyze PDFs with AI agents.
Project description
pdf-mcp
A Model Context Protocol (MCP) server that enables AI agents to read, search, and extract content from PDF files. Built with Python and PyMuPDF, with SQLite-based caching for persistence across server restarts.
mcp-name: io.github.jztan/pdf-mcp
Features
- 8 specialized tools for different PDF operations
- SQLite caching — persistent cache survives server restarts (essential for STDIO transport)
- Paginated reading — read large PDFs in manageable chunks
- Hybrid search — combines BM25 keyword (FTS5) and semantic (local embeddings) via Reciprocal Rank Fusion; falls back to keyword-only without
pdf-mcp[semantic] - Image extraction — per-page images returned as PNG file paths alongside text
- Table extraction — per-page tables with header and row data, detected via visible borders
- Page rendering — render any page as a PNG image for vision-capable models (
pdf_render_pages) - OCR — extract text from scanned pages via Tesseract; OCR'd text is automatically searchable (
pdf_read_pages(ocr=True)) - URL support — read PDFs from HTTPS URLs with SSRF protection (explicit CIDR block list, HTTPS-only)
Installation
pip install pdf-mcp
For semantic search (adds fastembed and numpy, ~67 MB model download on first use):
pip install 'pdf-mcp[semantic]'
For OCR on scanned PDFs (requires system Tesseract):
# macOS
brew install tesseract
# Ubuntu/Debian
apt install tesseract-ocr
# Windows — download the installer from:
# https://github.com/UB-Mannheim/tesseract/wiki
# Then add the install directory to your PATH.
Quick Start
Choose your MCP client below to get started:
Claude Code
claude mcp add pdf-mcp -- pdf-mcp
Or add to ~/.claude.json:
{
"mcpServers": {
"pdf-mcp": {
"command": "pdf-mcp"
}
}
}
Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"pdf-mcp": {
"command": "pdf-mcp"
}
}
}
Config file location:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Restart Claude Desktop after updating the config.
Visual Studio Code
Requires VS Code 1.102+ with GitHub Copilot.
CLI:
code --add-mcp '{"name":"pdf-mcp","command":"pdf-mcp"}'
Command Palette:
- Open Command Palette (
Cmd/Ctrl+Shift+P) - Run
MCP: Open User Configuration(global) orMCP: Open Workspace Folder Configuration(project-specific) - Add the configuration:
{ "servers": { "pdf-mcp": { "command": "pdf-mcp" } } }
- Save. VS Code will automatically load the server.
Manual: Create .vscode/mcp.json in your workspace:
{
"servers": {
"pdf-mcp": {
"command": "pdf-mcp"
}
}
}
Codex CLI
codex mcp add pdf-mcp -- pdf-mcp
Or configure manually in ~/.codex/config.toml:
[mcp_servers.pdf-mcp]
command = "pdf-mcp"
Kiro
Create or edit .kiro/settings/mcp.json in your workspace:
{
"mcpServers": {
"pdf-mcp": {
"command": "pdf-mcp",
"args": [],
"disabled": false
}
}
}
Save and restart Kiro.
Other MCP Clients
Most MCP clients use a standard configuration format:
{
"mcpServers": {
"pdf-mcp": {
"command": "pdf-mcp"
}
}
}
With uvx (for isolated environments):
{
"mcpServers": {
"pdf-mcp": {
"command": "uvx",
"args": ["pdf-mcp"]
}
}
}
Verify Installation
pdf-mcp --help
Tools
pdf_info — Get Document Information
Returns page count, metadata, file size, estimated token count, and text_coverage — a per-page list of {page, text_chars, raster_images} that lets agents identify OCR candidates without reading content. Call this first to understand a document. Includes toc_entry_count and inline TOC entries when the document has ≤50 bookmarks; larger TOCs return toc_truncated: true — use pdf_get_toc to retrieve the full outline.
"Read the PDF at /path/to/document.pdf"
pdf_read_pages — Read Specific Pages
Read selected pages to manage context size. Each page dict includes text, images/image_count, and tables/table_count. Tables are extracted as structured data (header + rows) and inlined directly in the page response — no separate tool call needed.
Optional parameters:
ocr=True/ocr_lang="eng"— run Tesseract OCR on pages with no extractable text; requires system Tesseract (brew install tesseract); capped at 20 pages per callrender_dpi=200— attach a rendered PNG path alongside text for each page (shares cache withpdf_render_pages)
"Read pages 1-10 of the PDF"
"Read pages 15, 20, and 25-30"
"OCR pages 3-5 of the scanned PDF"
pdf_render_pages — Render Pages as Images
Render PDF pages as PNG images for vision-capable models. Use when you need to see page content — diagrams, handwriting, scanned pages, or any page where text extraction is insufficient. Returns MCP image content blocks that vision models can process natively. Up to 5 pages per call; DPI clamped to 72–400.
For extracting text from scanned pages, use pdf_read_pages(ocr=True) instead — the two tools are orthogonal.
"Show me what page 5 looks like"
"Render the diagram on page 12"
pdf_read_all — Read Entire Document
Read a complete document in one call. Does not include images or tables — use pdf_read_pages if you need those.
Optional parameters:
max_pages=50— safety cap on pages read (default 50, max 500)
"Read the entire PDF (it's only 10 pages)"
pdf_search — Search Within PDF
Find relevant pages before loading content. The default mode is hybrid — Reciprocal Rank Fusion (RRF) merges BM25 keyword results and semantic embedding results into a single ranked list. This consistently outperforms either method alone: keyword search finds exact terms that embeddings miss; semantic search finds conceptual matches that keyword search misses; RRF fusion captures both.
Three modes are available:
mode="auto"(default) — Hybrid RRF whenpdf-mcp[semantic]is installed; keyword-only fallback otherwise.mode="keyword"— BM25/FTS5 only. Best for exact identifiers, product codes, precise terms.mode="semantic"— Semantic only (requirespdf-mcp[semantic]). Best for conceptual queries.
Response includes search_mode: "hybrid" | "keyword" | "semantic" indicating which path ran.
The first call on a new document embeds all pages (one-time cost, ~291ms for 200 pages); subsequent calls are instant.
"Search for 'quarterly revenue' in the PDF"
"Find pages about revenue growth in the PDF"
"Which pages discuss supply chain risks?"
pdf_get_toc — Get Table of Contents
"Show me the table of contents"
pdf_cache_stats — View Cache Statistics
"Show PDF cache statistics"
pdf_cache_clear — Clear Cache
"Clear expired PDF cache entries"
Example Workflow
For a large document (e.g., a 200-page annual report):
User: "Summarize the risk factors in this annual report"
Agent workflow:
1. pdf_info("report.pdf")
→ 200 pages, TOC shows "Risk Factors" on page 89
2. pdf_search("report.pdf", "risk factors")
→ Relevant pages: 89-110
3. pdf_read_pages("report.pdf", "89-100")
→ First batch
4. pdf_read_pages("report.pdf", "101-110")
→ Second batch
5. Synthesize answer from chunks
Caching
The server uses SQLite for persistent caching. This is necessary because MCP servers using STDIO transport are spawned as a new process for each conversation.
Cache location: ~/.cache/pdf-mcp/cache.db
What's cached:
| Data | Benefit |
|---|---|
| Metadata + text coverage | Avoid re-parsing document info |
| Page text | Skip re-extraction |
| Images | Skip re-encoding |
| Tables | Skip re-detection |
| TOC | Skip re-parsing |
| FTS5 index | O(log N) search with BM25 ranking after first query |
| Embeddings | Instant semantic search after first indexing run |
| Rendered PNGs | Skip re-rendering; shared between pdf_render_pages and pdf_read_pages(render_dpi=…) |
Cache invalidation:
- Automatic when file modification time changes
- Manual via the
pdf_cache_cleartool - TTL: 24 hours (configurable)
Configuration
Access control (optional)
Create ~/.config/pdf-mcp/config.toml to restrict which local paths and URL hosts the server will access. The file is optional — if absent, the server is permissive within the built-in SSRF floor (HTTPS-only, blocked private IP ranges).
[paths]
allow = ["~/Documents/**", "/data/pdfs/**"]
deny = ["~/.ssh/**", "~/.aws/**"]
[urls]
allow = ["*.internal.example.com"]
deny = ["untrusted.example.com"]
Rules use shell-glob patterns (* matches across path separators). deny wins when both match. Path matching operates on the resolved path after symlink expansion. A malformed config file prevents the server from starting — it never silently falls back to permissive.
Environment variables
# Cache directory (default: ~/.cache/pdf-mcp)
PDF_MCP_CACHE_DIR=/path/to/cache
# Cache TTL in hours (default: 24)
PDF_MCP_CACHE_TTL=48
Development
git clone https://github.com/jztan/pdf-mcp.git
cd pdf-mcp
# Install with dev dependencies
pip install -e ".[dev]"
# Run tests
pytest tests/ -v
# Type checking
mypy src/
# Linting
flake8 src/ tests/
# Formatting
black src/ tests/
Why pdf-mcp?
| Without pdf-mcp | With pdf-mcp | |
|---|---|---|
| Large PDFs | Context overflow | Chunked reading |
| Token budgeting | Guess and overflow | Estimated tokens before reading |
| Finding content | Load everything | Hybrid search — RRF fusion of BM25 keyword (FTS5) + semantic embeddings; never misses what either alone would |
| Tables | Lost in raw text | Extracted and inlined per page |
| Images | Ignored | Extracted as PNG files |
| Repeated access | Re-parse every time | SQLite cache |
| Scanned PDFs | No text extracted | OCR via Tesseract (pdf_read_pages(ocr=True)) |
| Visual content | Must describe in words | Render page as image (pdf_render_pages) |
| Tool design | Single monolithic tool | 8 specialized tools |
Roadmap
See ROADMAP.md for planned features and release history.
Contributing
Contributions are welcome. Please submit a pull request.
License
MIT — see LICENSE.
Links
- pdf-mcp on PyPI
- pdf-mcp on GitHub
- How I Built pdf-mcp — The problem with large PDFs in AI agents and a working solution
- MCP Server Security: 8 Vulnerabilities — What we found when we audited an MCP server for security holes
- How Claude Code Actually Reads PDFs — How AI agents use pdf-mcp tools to read and navigate PDF documents
- Semantic vs Keyword Search for AI Agents — Benchmarks and a dual-search routing pattern: FTS5 for exact identifiers, embeddings for natural language
- Hybrid Search vs Query Routing for AI Agents — Why pdf-mcp uses hybrid RRF instead of query routing: benchmarks showing RRF wins across query types
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pdf_mcp-1.9.0.tar.gz.
File metadata
- Download URL: pdf_mcp-1.9.0.tar.gz
- Upload date:
- Size: 279.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d31465630c33d9bb8b1e685302aabaddc32906c07a05d246e5c9318b5b93e009
|
|
| MD5 |
669493113e872d147f3a4c17dbcb25e4
|
|
| BLAKE2b-256 |
7c6953c72938d78ee620dd706959902105b24ae7071e6c2d1b4962bb3c620741
|
File details
Details for the file pdf_mcp-1.9.0-py3-none-any.whl.
File metadata
- Download URL: pdf_mcp-1.9.0-py3-none-any.whl
- Upload date:
- Size: 36.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
20bbf86627e454848062328b5ebfa04e8bda1aca4d59dd984174227e6e618598
|
|
| MD5 |
84b3fa702e384f32921e98128b102ee0
|
|
| BLAKE2b-256 |
95baa6dcc4cee380d6f65ba3e8422d37f1af246571930fdc4866a2421d0fe09f
|