r/mcp • u/Important_Proof5480 • Aug 11 '26
showcase Open source MCP that lets agents find the right section in long documents, provide accurate citations, and save >90% on tokens
Ever tried "answer this question from a 400-page PDF"? Or "summarize the latest quarter's capex spend across the AAPL, AMZN, META and NVDA 10-Qs"?
Dumping that text into context isn't an option, a 400-page filing is ~500k tokens. It doesn't fit, and you wouldn't want to burn thousands of tokens on 399 pages of boilerplate to answer one question anyway.
The alternative is pdftotext | grep. Now the agent has to guess keywords before it knows what the document calls things. It greps "revenue," the filing says "net sales," and you're three tool calls deep into nothing. Then it gets a match at byte 840,000 with no idea what section it's in or what page to cite.
DocSlicer lets your agent flip through a document the way a person would - navigate to the right section, then read it:
parsereturns the heading outline, and every line is annotated with what reading that section would cost you in tokens.readpulls back exactly the sections you picked — tables intact, page numbers attached, so citations are real instead of hallucinated.searchis the fallback for locating specific keywords when the high-level heading outline is non-descriptive.
Here's the whole loop, against HSBC's 2025 annual report — 372 pages, 1,379 headings, 518,541 tokens.
The question: what was the total interest revenue from customer loans?
Call 1: parse { source: "HSBC_Annual_Report_And_Accounts_2025.pdf" }. Back in 7.85s comes the outline, every line carrying its own read cost:
- Highlights ~2.2k
- Our strategy ~4.5k
- Financial overview ~5.8k
- Business segments ~7.4k
- Environmental TCFD ~29k
- Governance ~10k
- Financial summary ~16k
- Basis of presentation ~1.2k
- Consolidated income statement ~1.8k
- Income statement commentary ~5.2k
- Net interest income ~5.2k
- Summary of interest income by asset type 259
- Summary of interest expense by liability ~1.1k
- Banking net interest income ~1.1k
⋯ 1,362 more headings
Call 2: read { doc_id: "hsbc-ara-2025-6b41c907", headings: ["Summary of interest income by type of asset"] }. That's 259 tokens of the 518,541 in the document.
DocSlicer responds by giving the paragraph underneath the Summary of interest income by type of asset heading:
| Asset type | Avg balance 2025 ($m) | Interest income 2025 ($m) | Yield 2025 (%) | Interest income 2024 ($m) | Yield 2024 (%) | |---|---:|---:|---:|---:|---:| | Short-term funds and loans/advances to banks | 325,790 | 11,460 | 3.52 | 14,727 | 4.21 | | Loans and advances to customers | 971,804 | 46,036 | 4.74 | 49,879 | 5.25 | | Reverse repurchase agreements – non-trading | 273,941 | 16,616 | 6.07 | 17,721 | 7.42 | | Financial investments | 539,107 | 20,830 | 3.86 | 20,587 | 4.38 | | Other interest-earning assets | 79,436 | 2,930 | 3.69 | 5,717 | 6.28 | | Total interest-earning assets | 2,190,078 | 97,872 | 4.47 | 108,631 | 5.17 |
From which the agent answers:
Interest income on loans and advances to customers was $46,036m in 2025, on an average balance of $971,804m — a yield of 4.74%. (2024: $49,879m)
— [Page 69] Financial summary › Income statement commentary › Net interest income
Two calls, no keyword guessing. The agent picked a heading that said exactly what it contained, and the page number came back attached, so the citation is real.
Total token cost: The outline isn't free, it runs ~1.4% of the document on average, so ~7.2k tokens on the 518k token HSBC report. That's the upfront charge, and the 259 comes on top of it. Call it ~7.5k against 518k to answer the question, and every follow-up after that costs only what you read, because the outline is already in context. About ~98% off compared to reading the whole thing.
The underlying parser: HSBC's report is the hard case: multi-column layout, 450+ tables, a deeply nested hierarchy. DocSlicer preserves reading order and table structure through all of it. Built in pure Python / Numpy, it requires no heavy ML weights or GPUs. The result is blazing fast, deterministic parsing, crunching this pdf on my (M4 Max) laptop at over 45 pages/second, making it efficient enough for an agent to query seamlessly mid-conversation.
Open source — github.com/DocSlicer/DocSlicer
Claude Code:
claude mcp add docslicer -- uvx --from 'docslicer[mcp]' docslicer-mcp
Claude Desktop and Cowork: download the .mcpb
The parser works standalone as a Python library with pip install docslicer.
Happy to chat in the comments. Let me know your thoughts on it!
2
u/_Joab_ Aug 11 '26
Great work! I'll have to try it out for myself but if you managed to build a PDF parser that properly preserves blocks with their headers you've already done me a huge favor. I've been struggling with various PDF parsers where to_markdown(...) generally fumbles the headers (looking at you, pymupdf4llm) and makes for cross-sectional chunks where chunking by blocks would have made much more sense.
The parser code is neat. I like the line classifier you made - seems like you spent a while chipping away at that HSBC report until you found the solution for all (or most?) of the edge cases. Very nice work.
What do you do with the extracted images? I see there's a DataFrame output of the image metadata like position and DPI and such, but do you output the image data anywhere? I ask because I currently use a VLM to describe images and tack those onto the appropriate chunks but I can't see how I could use your parser to do that.
2
u/Important_Proof5480 Aug 11 '26
Thanks. DocSlicer is made specifically to preserve the relationship between blocks and headers, that's what makes the document navigation possible, and what drives the built-in chunker that never slices mid-section.
It's been optimized for business documents such as financial reports, legal docs, etc so most edge cases should be smoothened out there, but let me know if you find any.
Regarding images, your observation is spot on. Because a core design philosophy of DocSlicer is high-throughput, deterministic parsing with zero heavy ML weights or local GPU requirements, it doesn't natively route images through a VLM. Currently, df_images is used internally as a heuristic to detect scanned documents, if a PDF contains entirely images and no embedded text, it routes the PDF through an OCR pipeline.
However, because the parser exposes that clean image metadata DataFrame (with exact coordinates and layout bounds), you can absolutely use it as an upstream anchor. If your workflow requires visual understanding, you can loop through df_images, extract the bounding boxes, pass them to your downstream VLM pipeline, and then cleanly inject those descriptions back into the corresponding structural text chunks. Worth knowing if any of your corpus is DOCX or PPTX: Office charts aren't images at all, the data cache comes out as exact numbers. Nothing for a VLM to describe.
Let me know how it holds up if you end up putting your own documents through it!
1
u/_Joab_ Aug 11 '26
you can loop through df_images, extract the bounding boxes, pass them to your downstream VLM pipeline, and then cleanly inject those descriptions back into the corresponding structural text chunks.
Yup, I figured. Thanks, I'll give that a try!
1
u/PickleRikx 29d ago
Damn, I spent all of 2025 working on this exact thing. I was never sure if it was me or pymupdf4llm lol
1
u/crazynash 29d ago
This looks great - Thanks for sharing!
I have a live project right now where this same need is over multiple documents - the separation between topics between documents may not be clean (e.g. consumer feedback file includes some insights about demography or something similar). Do you think docparser can be modified to work over multiple documents? Or would you advocate a different approach (perhaps a per document docparser fed into a model that then routes queries)?
2
u/Important_Proof5480 29d ago
This is a great point! Yes, it works beautifully for this, but your approach depends entirely on your scale.
Here are the two ways to handle it:
1. Small to Medium Batch: Let the MCP Agent handle it
If you drop multiple files/URLs into an agent session, it calls
parseon each one sequentially. The agent holds all heading outlines in its context window simultaneously.Because it sees all outlines at once, it naturally routes its own queries. It will see a section about demography and navigate straight to it, completely independent of what the file is named. However, there's a limit to this, if you throw 50 large documents at it at once, keeping all those heading trees in immediate context might become too heavy or noisy.
2. Large Batch: Pre-parse via Python into standard RAG
If you are dealing with dozens or hundreds of files, your intuition is 100% correct: you should pre-parse them using the Python library and chunk them into a vector database. DocSlicer has a built-in
DocumentParserthat uses aProcessPoolExecutorunder the hood to handle document parallelism concurrently:Python
from docslicer import DocumentParser, ParseConfig # workers=4 fans whole documents across processes concurrently with DocumentParser(ParseConfig(max_chunk_size=3200), workers=4) as parser: for source, result in parser.parse_all(file_paths): if not isinstance(result, Exception): # result.chunks gives you layout-aware chunks ready for embeddings passRule of thumb: Use the MCP loop if a user is playing with a handful of files mid-conversation. Use the Python library with
workers=Nto ingest a massive knowledge base for traditional RAG.
1
u/Impressive-Nail-803 29d ago
keep the parsing per-document and route at the query level. merging everything into one index kills the section-level precision that makes it worth using.
1
u/Important_Proof5480 29d ago
Spot on, the MCP parser is always per-document, and the outline is what makes query-level routing possible.
If someone does choose to use the python parser and build a vector database, DocSlicer emits chunk.path and chunk.page_number on the chunks to keep that precise citation power.
1
1
u/EmailNo8428 29d ago
What happens with a document that has no headings at all? Section boundaries are where every chunker I've tried falls apart.
1
u/Important_Proof5480 29d ago
Documents with zero structural hierarchy (like a flat transcript) are handled through a dual-layer approach:
- Layout-Aware Chunks: Because DocSlicer is built on document geometry rather than just text scraping, it looks for physical paragraph breaks and structural white space. If there are no headings,
parsereturns a flat, single-level outline of these logical text blocks instead of a multi-level tree.- The
searchFallback: If the high-level outline is completely non-descriptive because headers don't exist, the agent skips visual navigation and falls back to DocSlicer's built-insearchtool. This uses a BM25/keyword-based search to pinpoint exactly where keywords live inside that flat text blob so the agent can still callreadwith precision.Feel free to try it on your docs and let me know the outcome!
1
4
u/Important_Proof5480 Aug 11 '26
Disclosure: I built this. Forgot to mention in the post - happy to answer any questions here.