Gives Claude surgical access to PDFs through eight specialized tools built on PyMuPDF. The workflow is efficient: call pdf_info to get page count and TOC, use pdf_search for hybrid BM25+semantic search with paragraph excerpts, then pdf_read_pages for targeted reads or pdf_render_pages to feed diagrams to vision models. Includes OCR via Tesseract for scanned documents, structured table and image extraction, and SQLite caching that persists text, embeddings, and rendered pages across server restarts. Reach for this when you need to interrogate large documents without dumping entire PDFs into context. HTTPS-only URL fetching with SSRF protection blocks local network access.
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
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.
| 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 |
(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 reliablypip 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.
Choose your MCP client below to get started:
claude mcp add pdf-mcp -- pdf-mcp
Or add to ~/.claude.json:
{
"mcpServers": {
"pdf-mcp": {
"command": "pdf-mcp"
}
}
}
Add to your claude_desktop_config.json:
{
"mcpServers": {
"pdf-mcp": {
"command": "pdf-mcp"
}
}
}
Config file location:
~/Library/Application Support/Claude/claude_desktop_config.json%APPDATA%\Claude\claude_desktop_config.jsonRestart Claude Desktop after updating the config.
Requires VS Code 1.101+ with GitHub Copilot.
CLI:
code --add-mcp '{"name":"pdf-mcp","command":"pdf-mcp"}'
Command Palette:
Cmd/Ctrl+Shift+P)MCP: Open User Configuration (global) or MCP: Open Workspace Folder Configuration (project-specific){
"servers": {
"pdf-mcp": {
"command": "pdf-mcp"
}
}
}
Manual: Create .vscode/mcp.json in your workspace:
{
"servers": {
"pdf-mcp": {
"command": "pdf-mcp"
}
}
}
codex mcp add pdf-mcp -- pdf-mcp
Or configure manually in ~/.codex/config.toml:
[mcp_servers.pdf-mcp]
command = "pdf-mcp"
Create or edit .kiro/settings/mcp.json in your workspace:
{
"mcpServers": {
"pdf-mcp": {
"command": "pdf-mcp",
"args": [],
"disabled": false
}
}
}
Save and restart Kiro.
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"]
}
}
}
pdf-mcp --help
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) |
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.
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
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, the threat model versus stdio, and the token revocation
runbook.
./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.
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.
~/.config/pdf-mcp/config.toml allow/deny rules for paths and URLs, plus response byte capsinjection_in_hidden hint with your own (including non-English) phrases via [content_trust].injection_phrasesSee ROADMAP.md for planned features and release history.
Contributions are welcome. See docs/contributing.md for setup, checks, the coherence eval harness, and quality-loop guidelines.
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.
MIT. See LICENSE.
Background, benchmarks, and design notes from building pdf-mcp:
Getting started
Search & retrieval
Engineering & security
PDF_MCP_CACHE_DIRDirectory for storing PDF cache (default: ~/.cache/pdf-mcp)
PDF_MCP_CACHE_TTLCache time-to-live in hours (default: 24)
csoai-org/pdf-document-mcp
xt765/mcp-document-converter
io.github.xjtlumedia/markdown-formatter
io.github.ai-aviate/better-notion
suekou/mcp-notion-server
meterlong/mcp-doc