Citra (SylphxAI/pdf-reader-mcp): a local-first PDF evidence server for agents
Give your AI agent eyes for PDFs — structured text, tables, OCR, visual evidence, and page-level citations via MCP. Native Rust, local-first.
At a glance
- What is it?
- Citra is an MCP server that hands AI agents structured PDF text, tables, OCR output and page-level citations, backed by a native Rust engine and a Node launcher. It is MIT licensed and installs with a single npx command, but it fails closed when the platform native package is missing.
- Who is it for?
- Adopt Citra if your agent needs to quote a PDF and show the page, table cell or bounding box behind the quote, and you are running macOS arm64, macOS x64, Linux x64, Linux arm64 or Windows x64. Do not adopt it if you need a hosted multi-tenant PDF service, a GUI reader, or a platform outside that list, because the README states a missing native package makes the server fail closed rather than fall back to a TypeScript engine.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem Citra targets: agents that quote PDFs they cannot prove
A language model asked about a financial report will happily produce "revenue was about $12M" with no page number, no table reference and no way for a human to check. The README frames this as the core pain: page numbers are invented or missing, tables are flattened into soup, and scanned PDFs become noise. Citra's answer is an "Agent Document Twin", a structured representation of the document that the agent can cite rather than paraphrase.
The audience is narrow and specific. This is for developers building agents on the Model Context Protocol who need document grounding with provenance: RAG pipelines over contracts and filings, research assistants that must quote a paper, and workflows over scanned archives where OCR output has to stay tied to a page. It is not aimed at end users who want to read a PDF. The repository ships no reader UI, and the README's comparison table is entirely about what an agent receives, not what a person sees.
The evidence-first framing shows up in the repo layout too. There is a docs/EVIDENCE_CONTRACT.md described as "Evidence = result contract", plus docs/TOOL_SURFACE.md covering a "few clear tools" policy. That is a deliberate constraint: three tools, not thirty.
How the Rust engine, the Node launcher and the three tools fit together
The architecture is a thin Node launcher in front of a native Rust engine. package.json declares "type": "module" and a single bin entry, citra, pointing at ./dist/runtime-entry.js. The Cargo workspace lists four members: crates/pdf-reader-core, crates/pdf-reader-cli, crates/pdf-reader-mcp-server, and a vendored vendor/adobe-cmap-parser. The README describes the current production shape as "a native Rust engine on supported platforms via a thin Node launcher".
Platform selection is per-host. One optional native package is installed for your machine and only yours: @sylphx/citra-darwin-arm64, @sylphx/citra-darwin-x64, @sylphx/citra-linux-x64-gnu, @sylphx/citra-linux-arm64-gnu, or @sylphx/citra-win32-x64-msvc. If that package is absent, the README says the server fails closed, with no silent TypeScript PDF engine behind it. That is the most consequential design decision in the project. It means a broken install surfaces as an error instead of as quietly degraded extraction, which is the right call for evidence work and the wrong call if you wanted a pure-JavaScript fallback for an unsupported host.
Agents see three tools. read_pdf is the default path and returns markdown, tables, structure, OCR and citations. search_pdf finds page and snippet matches before a deep read. pdf_evidence handles crops, renders, inspection and focused evidence operations. The minimal call passes a sources array with an absolute path.
{
"sources": [{ "path": "/absolute/path/to/report.pdf" }]
}The Cargo.toml also documents a real bug fix rather than a feature: pdf-extract depends on adobe-cmap-parser 0.4.1, which the comment says panics on malformed CMap destinations (issue #608), so a patched copy is vendored via [patch.crates-io] so that "arbitrary PDFs can never abort the server from a CMap parse panic". The release profile uses lto = "thin", codegen-units = 1 and strip = "symbols".
Installing Citra and running a first read against a real PDF
The README's install path is one command with no Docker, no API key and no global install. Run it and it spawns a stdio MCP server.
npx -y @sylphx/citraFor Claude Code the README gives a dedicated registration command:
claude mcp add citra -- npx -y @sylphx/citraFor Claude Desktop, Cursor, VS Code and Codex, the documented configuration is a JSON block with the same command and args. Note the key is mcpServers, and no environment variables appear in this snippet.
{
"mcpServers": {
"citra": {
"command": "npx",
"args": ["-y", "@sylphx/citra"]
}
}
}If you want the binary on your PATH instead, the README offers npm i -g @sylphx/citra and then the citra command. To check what the CLI exposes before wiring anything up, run the help flag:
npx -y @sylphx/citra --helpAfter the server is registered, the first real use is a read_pdf call with an absolute path, as shown in the previous section. The README's example files directory contains read-pdf-basic.json, read-pdf-options.json, search-then-verify.json, ocr-scanned.json, evidence-crop.json and agent-document-twin.json, so you can compare your own call shape against a working one. There is also an SDK surface: @sylphx/citra/sdk exports Citra with read, search and evidence, and @sylphx/citra/pure-rust exposes low-level client helpers. The README states the SDK requires the same platform native package as the MCP server.
Where Citra breaks down: unsupported hosts, cold starts and the cache boundary
The fail-closed policy is a limitation as much as a virtue. The supported list is macOS arm64, macOS x64, Linux x64, Linux arm64 and Windows x64. Anything else, including musl-based Linux images, has no listed native package, and the README does not document a fallback. If you are deploying into an Alpine container, verify that @sylphx/citra-linux-x64-gnu actually resolves there before you build around it. The package name carries the gnu suffix, which is the detail to check.
The performance claims carry their own boundary, and the README states it plainly. The measured result is "at least around 10x" median latency improvement in persistent_warm mode, on a same-host linux-x64 comparison against @sylphx/[email protected], across eight required fixture classes. The README calls this "Not a multi-host guarantee" and points to a claims policy document. Two things follow. First, the warm-path number depends on a process-local cache for identical local path plus options, so the first request in any process still pays full parse cost. A workload that spawns a fresh server per document sees the startup_inclusive mode, not the warm one. Second, a benchmark against a historical 3.0.14 lineage is a comparison with the project's own past, not with other PDF tools.
The install footprint table is more concrete. On a clean linux-x64 install, the main package is about 77 KB on disk versus about 403 KB historically, full node_modules is about 24.4 MiB versus about 82.3 MiB, and installed files drop from 4,101 to 20. Production npm dependencies are listed as an empty set plus one platform native. The README is honest that the native binary is multi-megabyte because it is the PDF engine, and asks readers to compare full clean installs rather than a JS wrapper tarball against a native executable. That framing is fair, and the numbers are still a real reduction.
One more gap: the README does not document rollback or how to pin a specific native package version, and package.json declares an engines field for bun >=1.4.0 with no node field. If your runtime is Node, that constraint is worth resolving before you commit.
Citra versus extraction libraries and hosted document APIs
The obvious alternative is a PDF extraction library called directly from your own code, such as PDF.js, which the README lists as a production dependency of the historical 3.0.14 TypeScript lineage. The difference in approach is where the tool boundary sits. A library gives you text and layout primitives and leaves citation structure, page geometry and table cell coordinates to you. Citra exposes three MCP tools and treats the result object as an evidence contract, so page numbers, geometry and provenance come back as part of the response rather than as something you assemble. The cost of that trade is flexibility: you get the tool surface the project chose, and docs/TOOL_SURFACE.md describes a deliberate "few clear tools" policy. If you need a custom extraction pipeline with your own heuristics, a library is the better fit.
The second alternative is a hosted document-intelligence API, typically with cloud vision for OCR. Citra's stated position is local-first: PDFs stay on the machine and no cloud vision API is required. For regulated documents, or for anyone who cannot ship page images to a third party, that removes a review step. The trade is that you own the runtime, the native package resolution and the platform matrix yourself. The README also mentions an "instrument family" including Iris for images, Cue for video, Spine, Lookout and Locus, so Citra is positioned as one piece of a larger toolset rather than a standalone product.
Worth noting: the repository is named pdf-reader-mcp, the npm package is @sylphx/citra, the bin is citra, and the MCP identifier is io.github.SylphxAI/citra. Anyone searching for the old name should expect that mismatch.
Maintenance cadence, licence and what an upgrade actually costs
The last push to the default branch was on 2026-09-07, and the repository is not archived. Recent releases are v4.1.3 on 2026-08-03, v4.1.2 on 2026-07-28 and v4.1.1 on 2026-07-24. The README, however, advertises "live 5.0.0" and package.json reports version 5.0.1, while the Cargo workspace package version is 3.1.1. Those three numbers do not agree, which matters when you are pinning a version in a lockfile or trying to match a changelog entry to a binary. Treat the npm version as the one that governs what npx resolves, and do not assume the Rust crate version tracks it.
Upgrade cost is dominated by the native package, not the JavaScript. The main package is small and the dependency tree is close to empty, so npm install is cheap. The risk sits in the optional platform binary: a version bump that publishes a new main package without a matching @sylphx/citra-<platform> artifact will fail closed on the host, which is loud but disruptive. Pin both the main package and the native package in CI and upgrade them together. The engines field requiring bun >=1.4.0 is another thing to re-check on each bump if you run the launcher under Node.
The licence is MIT, declared in package.json and in the Cargo workspace metadata. MIT permits commercial use and modification with the copyright notice retained. Two things are outside that grant: the vendored vendor/adobe-cmap-parser directory is a patched copy of a third-party crate whose own licence governs it, and the repository contains corpus/ and benchmark-artifacts/ directories whose contents may carry separate terms. Check those before redistributing anything from them. This is a description of what the repository states, not legal advice.
Editorial conclusion
Adopt Citra if your agent needs to quote a PDF and show the page, table cell or bounding box behind the quote, and you are running macOS arm64, macOS x64, Linux x64, Linux arm64 or Windows x64. Do not adopt it if you need a hosted multi-tenant PDF service, a GUI reader, or a platform outside that list, because the README states a missing native package makes the server fail closed rather than fall back to a TypeScript engine. Before wiring it into anything that matters, run npx -y @sylphx/citra --help on the exact host and confirm the matching @sylphx/citra-* native package resolves, then read docs/EVIDENCE_CONTRACT.md so you know what a citation object actually contains.
Frequently asked questions
Do I really need a PDF reader app?
Citra is not a reader app. It is an MCP server that returns structured text, tables, OCR output and page-level citations to an AI agent, and the repository ships no reader interface for a person to open a document in.
What is MCP in Adobe?
The MCP Citra implements is the Model Context Protocol, not anything from Adobe. The README describes it as a stdio MCP server that agents connect to, with the identifier io.github.SylphxAI/citra.
Is the PDF reader app safe?
Citra is local-first: the README states PDFs stay on the machine and no cloud vision API is required, and the code is MIT licensed. The README does not document a security audit, so review SECURITY.md in the repository before relying on that.
What is the best free PDF reader for my computer?
That question is about GUI readers, which Citra is not. Citra is a free, MIT-licensed MCP server for agents, and its supported hosts are macOS arm64, macOS x64, Linux x64, Linux arm64 and Windows x64.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sylphxai-pdf-reader-mcp)