Handing a paper to a language model usually starts with “convert it to Markdown first.” It looks like a format conversion, but in practice it is four problems stacked on top of each other: layout, formulas, tables, and timeouts. pdf-capture-mcp is a multi-stage capture pipeline exposed as an MCP server that hangs off an Agent. A single line of natural language is enough to produce a tidy Obsidian vault bundle.
- I
Extract
PyMuPDF for plain layouts; Marker for complex pages; MinerU for multi-column and InDesign files.
- II
Clean
Hyphenation, control characters, image paths, math delimiters — only deterministic, information-preserving fixes.
- III
Audit
Beyond statistical gates, a content audit that grew out of one 75-page paper that fooled every check.
- IV
Repair
Cross-channel comparison against the PDF text layer; report failures rather than guess at numbers.
- V
Vault
Main text, figures, chunks, and the QC record in one directory; drag the folder straight into Obsidian.
Three engines, on demand
Three engines are layered by need. The base install is ~80 MB and works out of the box. Complex layouts pull in Marker; multi-column layouts add MinerU in its own virtualenv so the main install stays clean. Vision models are optional — Tongyi, Zhipu, Moonshot, OpenAI, or a local Ollama — used to fill in tables and figure text. API keys live only on disk with chmod 600; they never echo back in responses.
A folder both Agents and Obsidian can read
Each conversion writes a folder by default. An Agent reads the entry README.md to know where to start; a human can drag the whole folder straight into an Obsidian vault. Identity is content-addressed: doc_id is a hash of the PDF, so re-running the same file idempotently overwrites the previous output rather than following filenames.
<slug>/
├── <slug>.md Main text, named after the folder
├── README.md Entry point: abstract, file table, chunk schema
├── images/ Extracted figures
├── tables/ Tables as standalone CSV
└── data/
├── chunks.jsonl Semantic chunks with heading paths
├── metadata.json
└── qc_report.json Audit and repair record
mode="auto" is the default: under 15 pages returns in place; longer PDFs return a job_id and an ETA immediately, with get_job_status walking through classify → extracting → table_extraction → qc → done. Results are always written to disk first, so a disconnected client never loses the run. Scanned PDFs are forced through OCR; failed page-windows are listed in missing_segments — loss is visible, never silent.
QC rules against silent text loss
QC runs in two tiers. The first inspects character density, headings, formulas, and table coverage to catch gross failures. The second grew out of one real 75-page audit: every actual defect in that paper had slipped past the statistical gates.
| Rule | What it catches | Auto-repair |
|---|---|---|
| MD-102 | Control characters inside cell wraps splitting Enlightenment apart | Re-stitch |
| MD-104 | Scientific notation splitting across cells, lost decimal point | Geometric recovery; digits untouched |
| MD-105 | Header row fused with the first data row | Token-geometric reconstruction, token conservation |
| MD-108 | Brackets in citation links being read as math delimiters by KaTeX | Strip escapes only inside the link |
| MD-201 | De-hyphenated comparison against the PDF text layer, catching body text loss | Inject only when the gate passes |
Text structural defects are handled through repair-or-report: we attempt a fix against the PDF text layer, and every step has a machine-verifiable gate. MD-104’s recovered value, after stripping the decimal point, must concatenate back to the original fragment — we recover the lost dot, never alter digits. When a gate fails, the candidate stays in qc_report.repairs with its proposal; guesses never enter the body. Merged cells that geometry cannot resolve are escalated to the configured vision model; if Markdown cannot express a rowspan, we fall back to HTML. Any critical finding downgrades PASS to WARN.
Talking to your Agent
With the MCP configured, plain natural language is enough:
Convert
~/Downloads/paper.pdfto Markdown.
Extract every table from this report.
Is this PDF a scan?
For Cursor, Claude Desktop, and similar clients, attach it through mcp.json:
{
"mcpServers": {
"pdf-capture": {
"command": "uvx",
"args": ["pdf-capture-mcp[marker]"]
}
}
}
On networks where Hugging Face downloads are slow or restricted, prime the model cache first: ask the Agent to run download_models, and set HF_ENDPOINT=https://hf-mirror.com if needed. The full tool list, environment variables, and troubleshooting live in the project README.
MIT. Python 3.11+. MinerU runs as a separate subprocess and is not pulled into this package.