Converts PDF files into navigable tree structures for AI agents. Agents fetch the table of contents, pick a section, read its content, and request images individually — without consuming the entire document.
The crates/pdf crate is the Functional Core (pure, no I/O). The shell lives in crates/mcptools/src/pdf/ (CLI) and crates/mcptools/src/mcp/tools/pdf.rs (MCP).
Key modules:
parser/backend.rs— lopdf wrapper,PdfBackendtraitparser/layout.rs— text extraction, font-size-based heading detectionparser/table.rs— spatial alignment table detectiontree.rs— stack-based nesting algorithm, buildsDocumentTreeimages.rs— image extraction, format detection, raw-to-PNG re-encoding, CCITT fax decodingrender/markdown.rs— section content to Markdownrender/cleanup.rs— text normalization (ligatures, hyphenation, CJK)lib.rs— public API:ParsedDocument,parse(),read_section(),peek_section(),list_section_images(),get_image(),info(),extract_window()
mcptools pdf toc document.pdfReturns the full document tree as JSON with section IDs, headings, content previews, image counts, and page ranges.
# Read a specific section
mcptools pdf read document.pdf s-1-0
# Read the whole document
mcptools pdf read document.pdfReturns the section content as rendered Markdown with image references. Section IDs come from the pdf toc output (format: s-{depth}-{index}). Omit the section ID to read the entire document.
# Peek at beginning of a section (default)
mcptools pdf peek document.pdf s-1-0
# Peek at middle of whole document with custom limit
mcptools pdf peek document.pdf --position middle --limit 300
# Random sample from a section
mcptools pdf peek document.pdf s-1-0 --position random --limit 200Samples a text snippet from a section without reading the full content. Returns the snippet, the position it was taken from, and total character count. Useful for quickly assessing content before committing to a full read.
Options:
--position/-p: Where to sample from —beginning(default),middle,ending,random--limit/-l: Maximum characters to return (default: 500)
# List all images in the document
mcptools pdf images document.pdf
# List images in a specific section
mcptools pdf images document.pdf s-1-0Returns image IDs, formats, section IDs, section titles, and page numbers for all images in the specified scope.
# Save to file by ID
mcptools pdf image document.pdf Im1 --output photo.jpg
# Print base64 to stdout
mcptools pdf image document.pdf Im1
# Random image from the document
mcptools pdf image document.pdf --random
# Random image from a specific section
mcptools pdf image document.pdf --random --section s-1-0Image IDs are XObject names from the PDF (visible in section image references and pdf images output). All image formats are supported for export: JPEG and JPEG2000 images are extracted as-is, while FlateDecode (raw pixel data) and CCITTFaxDecode (fax) images are automatically re-encoded as PNG.
Options:
--output/-o: Save to file instead of printing base64--section/-s: Scope image selection to a section (used with--random)--random/-r: Pick a random image (cannot be used with an image ID)
mcptools pdf info document.pdfReturns title, author, page count, and creator.
-
Get document structure:
mcptools pdf toc document.pdf
-
Peek at sections to assess content:
mcptools pdf peek document.pdf s-1-0
-
Read specific sections of interest:
mcptools pdf read document.pdf s-1-0 -
List and extract images:
mcptools pdf images document.pdf s-1-0 mcptools pdf image document.pdf Im1 --output image.jpg
PDFs frequently reuse decorative images (logos, backgrounds, page headers) across many pages. When pdf_images returns results, the same image ID appearing on multiple pages is almost certainly decorative. To find meaningful content images (screenshots, diagrams, charts):
- List images for a section — scope with a
sectionIdto reduce noise. - Identify recurring IDs — image IDs that appear on nearly every page (e.g., a company logo or page background) are decorative. Ignore these.
- Extract unique IDs — images that appear only within the target section are the actual content. These are the screenshots, diagrams, and figures worth extracting.
Example: a section spanning pages 27-29 returns 9 images (3 per page). Two IDs repeat on every page (logo + background) — skip those. The 3 unique IDs are the actual screenshots.
{
"method": "tools/call",
"params": {
"name": "pdf_toc",
"arguments": { "path": "/absolute/path/to/document.pdf" }
}
}Arguments:
path(required): Absolute path to the PDF file
{
"method": "tools/call",
"params": {
"name": "pdf_read",
"arguments": {
"path": "/absolute/path/to/document.pdf",
"sectionId": "s-1-0"
}
}
}Arguments:
path(required): Absolute path to the PDF filesectionId(optional): Section ID frompdf_toc(e.g.,s-1-0). Omit for whole document.
{
"method": "tools/call",
"params": {
"name": "pdf_peek",
"arguments": {
"path": "/absolute/path/to/document.pdf",
"sectionId": "s-1-0",
"position": "middle",
"limit": 300
}
}
}Arguments:
path(required): Absolute path to the PDF filesectionId(optional): Section ID frompdf_toc. Omit for whole document.position(optional): Where to sample —beginning(default),middle,ending,randomlimit(optional): Maximum characters to return (default: 500)
{
"method": "tools/call",
"params": {
"name": "pdf_images",
"arguments": {
"path": "/absolute/path/to/document.pdf",
"sectionId": "s-1-0"
}
}
}Arguments:
path(required): Absolute path to the PDF filesectionId(optional): Section ID frompdf_toc. Omit for all images.
{
"method": "tools/call",
"params": {
"name": "pdf_image",
"arguments": {
"path": "/absolute/path/to/document.pdf",
"imageId": "Im1"
}
}
}Arguments:
path(required): Absolute path to the PDF fileimageId(optional): Image ID (XObject name). Required unlessrandomis true.sectionId(optional): Section ID to scope image selection (used withrandom)random(optional): Pick a random image. Cannot be used withimageId.
Returns base64-encoded image data with format and size.
{
"method": "tools/call",
"params": {
"name": "pdf_info",
"arguments": { "path": "/absolute/path/to/document.pdf" }
}
}Arguments:
path(required): Absolute path to the PDF file
SectionId— validated formats-{depth}-{index}(e.g.,s-1-0,s-2-3)HeadingLevel— 1 through 6ImageId— XObject name stringImageFormat— Jpeg, Png, Jpeg2000, Gif, Tiff, Bmp, WebP, UnknownEnrichedImageRef— image ID, format, section ID, section title, page number (returned bylist_section_images)PeekPosition— Beginning, Middle, Ending, RandomPeekContent— snippet with position, total chars, section infoDocumentTree— nested sections with metadata and flat indexSectionContent— rendered Markdown text with image referencesParsedDocument— holds intermediate state for efficient repeated queries
Headings are detected by font size analysis:
- Build a histogram of font sizes across all text spans
- The most frequent size is the body baseline
- Text larger than baseline + 1.5pt is classified as a heading
- Heading levels are assigned by size rank (largest = H1)
If no headings are found, sections are created per-page as a fallback.