The problem
Most PDF tools hand an agent a wall of text. The agent then guesses — the page
number, the table grid, the region behind the claim. And a confidently wrong
answer costs more than "I can't tell."
The difference
| A text dump says | Citra returns |
Revenue was about $12M.
|
page 1 · table p1-table-1 · 3 cols / 9 cells
bbox 72,151 → 454,79 · confidence 0.92
continuation → page 2 (same column count)
|
Locators in, citations out. A human can check the claim.
One call. Locators included.
{
"status": "ok",
"route": { "engine": "rust-core", "path": "rust-read-pdf-v1" },
"source": { "hash": "99d313eb…", "path": "…/selectable-table-v1.pdf" },
"results": [{
"data": {
"table_info": [{
"page": 1,
"bounding_box": { "left": 72, "top": 151, "right": 454.8, "bottom": 79 },
"colCount": 3,
"cellCount": 9,
"confidence": 0.92,
"provenance": { "engine": "pdf-reader-core", "source": "selectable_text" },
"continuation": {
"role": "starts",
"groupId": "table-continuation-p1-table-1-p2-table-1",
"signals": ["same_column_count", "repeated_header_candidate"]
},
"quality": {
"completeness": 0.79,
"cellBoundingBoxCoverage": 0.89,
"signals": ["missing_cells", "merged_cell_candidates"]
}
}]
}
}],
"gaps": []
}
Excerpt of a real read_pdf response against test/fixtures/differential/v3014-selectable-table-v1.pdf
(paths shortened). The table is detected and linked to its continuation on page 2 — and when
Citra cannot prove something, it says so in gaps instead of guessing.
Predictable defaults
Citra keeps the default path cheap and explicit. fast reads the embedded text
layer and structure. quality explicitly enables OCR, rendering, and richer
crops. research is not hidden inside PDF reading. Expensive work is requested,
never silently triggered.
Install in 30 seconds
npx -y @sylphx/citra
No Docker. No API key. No global install. That starts a stdio MCP server your agent
can use immediately.
| Any agent / CLI | npx -y @sylphx/citra |
| Claude Code | claude mcp add citra -- npx -y @sylphx/citra |
| Claude Desktop / Cursor / VS Code / Codex | "command": "npx", "args": ["-y", "@sylphx/citra"] |
| Global CLI | npm i -g @sylphx/citra → citra |
Claude Desktop / Cursor / VS Code — full mcpServers snippet
{
"mcpServers": {
"citra": {
"command": "npx",
"args": ["-y", "@sylphx/citra"]
}
}
}
Why teams pick Citra
- Zero-config. A real
npx MCP server — not a 20-step bootstrap.
- Evidence, not vibes. Page, geometry, table cells, provenance. Citations a human can check.
- Local-first. PDFs stay on the machine. No required cloud vision API, no document upload.
- Fail closed. No matching native binary? The process refuses to start. Never a silent engine fallback.
- Native and small. A Rust PDF engine behind a thin launcher — not PDF.js plus a large JS tree.
What you get
Four tools. One surface. Few, powerful, obvious.
read_pdf | The main read: markdown, tables with cells and geometry, structure, optional OCR, citation-ready chunks |
search_pdf | Cheap literal retrieval first: page and bounding-box locators before a deep read |
pdf_compare | Compare two local PDFs at page and term level |
pdf_evidence | Focused verification: inspect, render_page, extract_regions, ocr_pages, analyze_regions |
Full option and result reference: docs/api
Proof, method-bounded
| ≥ 10.4× | median warm read_pdf latency vs the TypeScript engine — same host (linux-x64), 8 required fixture classes, median of class speedups ~15.4× |
| ~3.4× smaller | clean install — 82.3 MiB → 24.4 MiB of node_modules vs TS 3.0.14 |
| 20 files | on disk vs 4,101 — one native binary per platform, zero production JS dependencies |
| 5 platforms | macOS arm64/x64 · Linux x64/arm64 · Windows x64 |
Warm-cache figure is method-bounded: long-lived MCP server, repeated identical local
read_pdf after warm-up, measured on the sole-Rust lineage (4.1.0) against TS 3.0.14. The first
request in a process pays full parse cost. No multi-host extrapolation.
See the performance report and
claims policy.
Platforms
One optional native package is selected for your host only:
| macOS arm64 | @sylphx/citra-darwin-arm64 |
| macOS x64 | @sylphx/citra-darwin-x64 |
| Linux x64 | @sylphx/citra-linux-x64-gnu |
| Linux arm64 | @sylphx/citra-linux-arm64-gnu |
| Windows x64 | @sylphx/citra-win32-x64-msvc |
Security & trust
- Local-first — no required cloud provider; the PDF is not uploaded.
- Fail closed — a missing native binary stops the process; there is no silent TypeScript fallback.
- Panic-unwind — a malformed document (e.g. a broken ToUnicode CMap) fails the request, never the process (#608).
- Remote URL policy — a
url source is a pinned, redirect-revalidated http(s) fetch, not a browser; private addresses are rejected unless MCP_PDF_ALLOW_PRIVATE_IPS=true is explicitly set (policy).
- HTTP transport is opt-in and hardened — loopback by default,
MCP_API_KEY enforced before binding elsewhere, and --allow-dir restricts filesystem reach. Details: security docs · report privately per SECURITY.md.
Companion MCP tools
| Iris | Image facts and pixel evidence |
| Cue | Video timelines and timestamp evidence |
| Spine | Repository architecture and impact |
| Locus | Exact code-chunk retrieval |
| Lookout | Web research with source excerpts |
Each product is independent. Install only the tools your agent needs.
Documentation
Stop PDF hallucinations. Give agents proof.
npx -y @sylphx/citra
⭐ Star this repo if Citra made your agent tell the truth.