io.github.jztan/pdf-mcp MCP Server
io.github.jztan/pdf-mcp
Surgical PDF access for AI agents: hybrid search, selective reading, tables, OCR, and corpus tools without context overflow.
What is the io.github.jztan/pdf-mcp MCP server?
The pdf-mcp MCP server lets Claude and other AI agents search PDFs by meaning or keyword, read only relevant pages, and extract tables, images, and scanned text cleanly. It combines BM25 and semantic search via Reciprocal Rank Fusion, handles multi-column and vertical scripts (Japanese), and caches results in SQLite for fast repeated access.
pdf-mcp solves the problem of large PDFs overwhelming context windows. Instead of loading entire documents, agents can search a single PDF or a whole folder, get ranked hits with excerpts, read only the pages that matter, and extract structured data like tables and charts. It includes OCR for scanned documents, hidden-text detection for security, and corpus-level tools to triage and search across multiple PDFs at once.
How to install io.github.jztan/pdf-mcp
Copy-paste configuration for popular MCP clients.
PDF_MCP_CACHE_DIRDirectory for storing PDF cache (default: ~/.cache/pdf-mcp)
PDF_MCP_CACHE_TTLCache time-to-live in hours (default: 24)
Tools & capabilities
Tools this server exposes to the agent.
pdf_info— Get page count, metadata, table of contents summary, and scanned-page detection; optionally flag hidden or injected text.pdf_search— Hybrid search (BM25 keyword + semantic via RRF) across a single PDF; returns ranked hits with page/section granularity and paragraph excerpts with bounding boxes.pdf_read_pages— Read specific pages or ranges with optional OCR; returns embedded images, tables, and hidden-text flags.pdf_read_all— Read entire document in one call (byte-capped); returns full text with hidden-text detection.pdf_render_pages— Render pages as PNG images for vision models to see diagrams, handwriting, and scans.pdf_extract_chart— Extract chart data as exact (x, y) tables from vector charts; returns rendered image if extraction is unreliable.pdf_get_toc— Fetch full table of contents for documents with >50 bookmarks.pdf_corpus_warm— Warm a folder or list of PDFs into cache (text and optional embeddings) within a time budget.pdf_corpus_overview— Get per-document triage cards for a folder: title, page count, top TOC entries, and text coverage.pdf_corpus_search— Search across a folder of PDFs (keyword, semantic, or hybrid); returns ranked hits with document and page provenance.pdf_cache_stats— Show per-document cache breakdown and total size.pdf_cache_clear— Clear expired or all cache entries.server_info— Report which optional features (column-aware extraction, OCR, semantic search) and configuration are active.
Use cases
- Search a 200-page annual report for risk factors and read only the matching pages, saving 80% of context tokens.
- OCR a scanned multi-page contract and extract specific clauses with keyword and semantic search.
- Triage a folder of 10 PDFs, identify which ones are relevant, and search across all of them in one query.
- Extract exact (x, y) data from vector charts in a technical document without manual transcription.
- Read Japanese PDFs with correct top-to-bottom, right-to-left text order and search unspaced CJK keywords.
io.github.jztan/pdf-mcp MCP server FAQ
pdf-mcp is an MCP server that gives AI agents like Claude surgical access to PDFs: hybrid search (keyword + semantic), selective page reading, OCR for scans, structured extraction of tables and charts, and corpus-level tools to search across folders of PDFs without flooding context.
Yes. pdf-mcp is open-source under the MIT license and available on PyPI. The semantic search model (~67 MB) downloads on first use at no cost.
Run `pip install pdf-mcp`, then add to your `claude_desktop_config.json`: `{"mcpServers": {"pdf-mcp": {"command": "pdf-mcp"}}}`. Restart Claude Desktop.
Install via pip, then use the MCP configuration UI in your editor or add to `.vscode/mcp.json` or `.cursor/mcp.json`: `{"servers": {"pdf-mcp": {"command": "pdf-mcp"}}}`.
No authentication is required for local use (STDIO transport). For HTTP transport (remote/shared server), you generate a token via `openssl rand -hex 32` and configure allow-listed paths in `~/.config/pdf-mcp/config.toml`.
Python 3.10+. For OCR on scanned PDFs, install system Tesseract (via `brew install tesseract` on macOS, `apt install tesseract-ocr` on Ubuntu, or download the Windows installer). For correct multi-column reading order, install the optional `[multicolumn]` extra.
README (reference)
Source of truth, from the repository.
pdf-mcp
Surgical PDF access for AI agents: search, read, and extract without flooding context.
An MCP server that lets Claude Code and other AI agents search a PDF by meaning or keyword, read only the pages that matter, and cleanly pull out tables, images, and scanned text, even from multi-column and Japanese layouts.
mcp-name: io.github.jztan/pdf-mcp
Try it in your browser
Drop in any PDF, or a whole folder of them, and watch an agent triage the corpus, search across every document at once, and read only the pages that matter, using a fraction of the tokens. 100% client-side, no install required.
<p align="center"> <a href="https://pdf-mcp.jztan.com/"><img src="https://raw.githubusercontent.com/jztan/pdf-mcp/develop/docs/images/demo.gif" alt="pdf-mcp browser demo: an AI agent warms a 6-PDF corpus, triages it, searches across all six documents, and reads only the matching page, with 97.5% of the corpus never entering the context window" width="760"></a> </p>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 (BM25 keyword + semantic) |
| Tables | Lost in raw text | Extracted and inlined per page |
| Charts | Trapped in the plot image | Extracted as (x, y) data tables |
| Multi-column PDFs | Columns interleaved in extracted text | Column-aware reading order (pdf-mcp[multicolumn]) |
| Vertical scripts (Japanese) | Columns scrambled / glyph soup | Geometric reorder of vertical text (tategaki / 縦書き); CJK keyword search works on unspaced Japanese/Chinese/Korean text via a char-split FTS index |
| Images | Ignored | Extracted as PNG files |
| Repeated access | Re-parse every time | SQLite cache |
| Scanned PDFs | No text extracted | OCR via Tesseract, parallelized across pages (pdf_read_pages(ocr=True)) |
| Visual content | Must describe in words | Render page as image (pdf_render_pages) |
| Hidden / injected text | Silently ingested as if a human vetted it | Flagged as untrusted: hidden-text detection (content_trust=True) |
| Folders of PDFs | One document at a time | Corpus tools: warm, triage, and search across a whole folder |
| Tool design | Single monolithic tool | 13 specialized tools |
Features
- Hybrid search: find relevant pages with a question, not a page range. Combines BM25 keyword and semantic search via Reciprocal Rank Fusion
- Corpus search: point the server at a folder of PDFs: warm them into the cache, get per-document triage cards, and search across all documents at once with ranked, document-attributed hits
- Paginated reading: fetch only the pages your agent needs; large documents don't blow your context window
- OCR: scanned and image-based PDFs are fully readable and searchable via Tesseract, parallelized across pages for ~2–3x faster extraction on typical scans
- Structured extraction: tables, embedded images, and table of contents returned as structured data, not text soup
- Chart data extraction: pull exact
(x, y)tables from vector charts, read from the plot geometry rather than guessed from the image; declines with a rendered image when a chart can't be read reliably - Vertical-script reading order: Japanese tategaki (縦書き) reconstructed from glyph geometry into correct top-to-bottom, right-to-left order; article segmentation for dense magazine layouts; mojibake filtered
- Persistent cache: SQLite-backed; re-reads are instant and survive server restarts
- Secure URL fetching: HTTPS-only with SSRF protection; local network ranges are blocked
- Content-trust / hidden-text detection: flags text a human reader can't see (invisible render mode, sub-point fonts, transparent or white-on-white fill, off-page) so an agent treats it as untrusted rather than vetted. Flag-only: nothing is stripped
Contents
- Installation
- Quick Start
- Tools
- Example Workflow
- Remote / HTTP transport
- Configuration
- Roadmap
- Contributing
- Contributors
- Security
- License
Installation
pip install pdf-mcp
Semantic search is included by default (hybrid auto search is built on it;
~67 MB embedding model download on first use). The former [semantic] and
[cjk] extras remain as no-op aliases. Platform note: the bundled
onnxruntime has no wheels for Intel macOS on Python 3.14+ or Alpine/musl;
use Python ≤ 3.13 there.
For correct reading order on multi-column PDFs (adds pymupdf4llm, which pulls pymupdf_layout/onnxruntime):
pip install 'pdf-mcp[multicolumn]'
Without it, multi-column pages fall back to positional-sort extraction, which can interleave columns.
Japanese/Chinese/Korean PDFs work out of the box: keyword search uses a char-split FTS index that matches unspaced CJK terms, and semantic CJK search is covered by the default install.
For OCR on scanned PDFs (requires system Tesseract):
# macOS
brew install tesseract
# Ubuntu/Debian
apt install tesseract-ocr
# On 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:
<details open> <summary><strong>Claude Code</strong></summary>claude mcp add pdf-mcp -- pdf-mcp
Or add to ~/.claude.json:
{
"mcpServers": {
"pdf-mcp": {
"command": "pdf-mcp"
}
}
}
</details>
<details>
<summary><strong>Claude Desktop</strong></summary>
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.
</details> <details> <summary><strong>Visual Studio Code</strong></summary>Requires VS Code 1.101+ 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"
}
}
}
</details>
<details>
<summary><strong>Codex CLI</strong></summary>
codex mcp add pdf-mcp -- pdf-mcp
Or configure manually in ~/.codex/config.toml:
[mcp_servers.pdf-mcp]
command = "pdf-mcp"
</details>
<details>
<summary><strong>Kiro</strong></summary>
Create or edit .kiro/settings/mcp.json in your workspace:
{
"mcpServers": {
"pdf-mcp": {
"command": "pdf-mcp",
"args": [],
"disabled": false
}
}
}
Save and restart Kiro.
</details> <details> <summary><strong>Other MCP Clients</strong></summary>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"]
}
}
}
</details>
Verify Installation
pdf-mcp --help
Tools
The typical pattern: call pdf_info first to plan, then pdf_search to locate; its paragraph excerpts are often enough to answer directly. Use pdf_read_pages or pdf_read_all when you need deeper context. For a folder of PDFs, start with pdf_corpus_overview to triage, then pdf_corpus_search to search across documents.
| Tool | What it does |
|---|---|
pdf_info | Page count, metadata, TOC summary, scanned-page detection. Call first. Pass content_trust=True for a content_trust block (suspicious, hidden_text_runs, hidden_chars, injection_in_hidden, pages_flagged, signals); add detail=True for per-span spans. |
pdf_get_toc | Full table of contents for documents with >50 bookmarks |
pdf_corpus_warm | Warm a folder (or list) of PDFs into the cache, text and optional embeddings, within a time budget. Returns per-doc status plus unprocessed/skipped. |
pdf_corpus_overview | Per-document triage cards for a folder: title, page count, top TOC entries, text coverage. Auto-warms within the budget. |
pdf_corpus_search | Search across a folder of PDFs (keyword, semantic, or hybrid), returning ranked hits with document and page provenance, excerpts, and coverage. |
pdf_read_pages | Read specific pages or ranges; OCR-on-demand; embedded images + tables, each with source bbox + clip coordinates. Always returns hidden_text_detected (response level) and per-page hidden_text; hidden_text_detected: true means some returned text was invisible to a human reader and should be treated as especially untrusted. |
pdf_read_all | Read entire document in one call (byte-capped for safety). Always returns hidden_text_detected; hidden_text_detected: true means some returned text was invisible to a human reader and should be treated as especially untrusted. |
pdf_render_pages | Render pages as PNG for vision models: diagrams, handwriting, scans |
pdf_extract_chart | Extract chart data as exact (x, y) tables from vector charts; declines with a rendered image when not reliably extractable |
pdf_search | Hybrid RRF search (keyword + semantic), page or section granularity, optional paragraph excerpts (paragraph hits also carry bbox + clip coordinates, and table_context when the excerpt's numbers need column labels) |
pdf_cache_stats | Per-document cache breakdown + total size |
pdf_cache_clear | Clear expired or all cache entries |
server_info | Which optional features (column-aware, OCR, semantic) and config are active. Call before feature-dependent calls. |
Example prompts:
"Read the PDF at /path/to/document.pdf"
"Which pages discuss supply chain risks?"
"Find sections about the training process"
"Show me what page 5 looks like"
"OCR pages 3-5 of the scanned PDF"
See docs/tool-reference.md for the complete reference: every parameter, response shape, security contract, and example. For semantic-search model selection, see docs/embedding-models.md.
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")
→ Matches with structural paragraph excerpts: each excerpt
is the bullet, paragraph, or heading that matched, not a
fixed-width window. Often enough to answer directly.
3. If excerpts are sufficient → synthesize answer
4. If more context needed:
pdf_read_pages("report.pdf", "89-95")
→ Full page text for deeper reading
Remote / HTTP transport
STDIO remains the default and is what every example above uses. A second entry point serves the same tools over HTTP, but the two transports suit different jobs:
| transport | what it serves | use it for |
|---|---|---|
| STDIO (default) | any local file the agent can name, since agent and server share a filesystem | ad hoc documents on your own machine |
HTTP (pdf-mcp-http) | a curated corpus on the server, plus https:// URLs it can fetch | clients that cannot spawn a process (Anthropic API MCP connector, claude.ai custom connectors), and a warm corpus shared by several clients |
export PDF_MCP_AUTH_TOKEN="$(openssl rand -hex 32)"
pdf-mcp-http
Because paths resolve on the server, an agent connected over HTTP reads what is
already there: files under an allow-listed root, or a URL the server fetches. It
cannot hand over a file from its own machine. Call server_info to discover the
roots a server will open. See
Getting documents to the server.
It is single-tenant and fails closed: without an auth token and a [paths]
allow list, the process exits rather than starting an open endpoint. Before
you deploy it, read docs/remote-access.md for the
trust boundary and the threat model versus stdio, and
docs/configuration.md for
setup, client config, and token rotation.
Docker
./deploy.sh # generates .env with a token, pulls the image, starts, health-checks
cp your.pdf documents/ # the intake path: this folder is the server's /data/pdfs
The image is published to GHCR for amd64 and arm64, so nothing is compiled locally. Everything is baked in (OCR, column-aware extraction, embedding model), so all tools work on the first request. The container runs as a non-root user and publishes to host loopback only; put a TLS proxy in front for public access.
./deploy.sh --help lists the lifecycle commands, including --build to
build locally instead of pulling. For the environment variables (host port,
image tag, auth token) and the deployment guards, see
docs/configuration.md.
Configuration
pdf-mcp works out of the box with no configuration. To restrict which paths and URL hosts the server can access, tune cache and worker settings, or understand what's cached, see docs/configuration.md.
- Access control:
~/.config/pdf-mcp/config.tomlallow/deny rules for paths and URLs, plus response byte caps - Content-trust phrases: extend the hidden-text
injection_in_hiddenhint with your own (including non-English) phrases via[content_trust].injection_phrases - Environment variables: cache directory, TTL, and parallel OCR/render worker count
- HTTP transport setup: token generation, TLS, client config, and token rotation for
pdf-mcp-http - Caching: SQLite-backed persistence, what's cached, and invalidation
Roadmap
See ROADMAP.md for planned features and release history.
Contributing
Contributions are welcome. See docs/contributing.md for setup, checks, the coherence eval harness, and quality-loop guidelines.
Contributors
Thank you to everyone who has helped improve this project through code, reviews, testing, and feature requests:
<!-- contributors:start -->@Summer907 · @ebbsanchez · @VooDisss · @DerDennisOP · @deepdmk
<!-- contributors:end --> <a href="https://github.com/jztan/pdf-mcp/graphs/contributors"> <img src="https://contrib.rocks/image?repo=jztan/pdf-mcp" alt="Contributors" /> </a>Per-release contributor credits are listed in the Changelog.
Security
Found a vulnerability? See SECURITY.md for the threat model, reporting channel, and expected response timeline. Please do not open a public GitHub issue for unpatched security reports.
License
MIT. See LICENSE.
Links
Blog posts
The story behind the releases. Building pdf-mcp keeps surprising me: benchmarks that go the wrong way, formats that break everything, features I had to remove. I write about that thinking in The Dispatch. Come along if that's your kind of thing.
Background, benchmarks, and design notes from building pdf-mcp:
Getting started
- How I Built pdf-mcp: The problem with large PDFs in AI agents and a working solution
- How Claude Code Actually Reads PDFs: How AI agents use pdf-mcp tools to read and navigate PDF documents
- How AI Agents Should Read PDFs: 5 Patterns That Survived Production: Five production-tested patterns for how agents should navigate PDFs at scale
Corpus & multi-document search
- A Knowledge Base Is Just a Folder: Turning a folder of PDFs into an agent knowledge base with the corpus tools, no ingestion pipeline or vector store
- Cross-Document Retrieval for AI Agents Without a Vector Database: Why BM25 scores don't merge across per-document indexes but ranks do, and how two-stage RRF puts a gold document in the top 3 on 89.9% of 89 queries over a 100-PDF corpus
Search & retrieval
- 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
- Section Chunking vs Page Chunking for AI Agents: Why section-aware search delivers full section content in one call while page-mode costs 2–6 extra tool calls per query
- Section-Level RAG: Why BM25 Beat Hybrid Search in My Benchmark: Why pdf-mcp's section-grain search is BM25-only: hybrid RRF caused a 33% lexical regression at section grain, so granularity decides the search technique
- How One Search Change Eliminated an Entire Agent Step: Switching pdf_search from fixed-width snippets to paragraph excerpts turned it from a pivot tool into a terminal tool: 97% vs 80% answer containment across a 30-query benchmark
Engineering & security
- MCP Server Security: 8 Vulnerabilities: What we found when we audited an MCP server for security holes
- Your LLM Is Free QA for Your MCP Server: Four Payload UX bugs in pdf-mcp that schema tests missed but Claude Desktop surfaced during real use
- Why Multi-Column PDFs Scramble Reading Order in RAG: Fixing two-column extraction (0.564 → 0.816 fidelity), the title-page author-grid regression it caused, and the aggregate metric that stayed blind to both
Related MCP servers

io.github.jztan/qt4-doc-mcp-server
Offline MCP server for local Qt 4.8, Qt 5, and Qt 6 documentation with full-text search.
JPL-referenced Human Design charts for AI agents: bodygraph, transit, composite, Penta, DreamRave.

Model Context Protocol server that lets LLMs collaboratively drive tmux

Creates compliant French e-invoices locally: Factur-X PDF/A-3 with EN 16931 CII XML, legal numbering

io.github.kaael1/mcp-power-automate
Inspect, validate, edit, and revert Power Automate flows with AI agents using browser-backed auth and local snapshots.