Skip to content

pdf-reader-mcp

The server that reports what a PDF says and where on the page it says it. It extracts text, tables, the structure tree, fonts, annotations and signature fields, and returns the coordinates at which each of them is drawn. Everything it returns is an observed fact; it never judges whether that fact is correct.

  • npm: @shuji-bonji/pdf-reader-mcp / current v0.15.0 / GitHub
  • This page is the guide — responsibilities and boundaries. For every tool's parameters and returns, see the tools reference (generated from tools/list)
  • Works with no environment variables

What this one server gives you

If all you need is to read PDFs, this server alone is enough. "Summarize this PDF", "turn this table into CSV", "which fonts are embedded?" — all of it finishes here.

INFO

Unlike plain text extraction, this MCP server can read a tagged PDF in logical reading order, so multi-column layouts and tables do not come out scrambled.

Beyond "what is inside", it also reports where it is drawn on the page. Rectangles come back in the coordinate space pdf-writer-mcp's add_annotation uses directly (PDF default user space, origin bottom-left, pt, normalised), so no coordinate system has to be reinterpreted in between.

QuestionTool
"Where is object 27?"locate_objects
"Where is this paragraph / this heading?"extract_structured_text with include_bbox

What it gives you together with a Skill

This MCP server sits in the fact layer of the four (fact = what was observed): it returns observations and nothing else. How far to read, and how to report what could not be read, is a Skill's job.

Shapes carry meaning (→ legend).

SkillWhat this server does thereRequired?
pdf-readThe foundation. The Skill picks the reading path and makes the server report what it could not readRequired (v0.14.0+ recommended)
pdf-publishThe read-back stage of write → read-back → verifyRecommended
pdf-trustObservation of signature-field structure, tags and metadata; locating changed objectsOptional

Pointing at a tampered region with an annotation connects three servers in sequence. verify_integrity returns the object numbers; locate_objects turns them into pages and rectangles; pdf-writer's add_annotation takes those rectangles as-is.

What it cannot do

  • It cannot say whether a signature is valid. inspect_signatures reads the structure of signature fields; no cryptographic verification happens here
  • It cannot say whether a file conforms. Verdicts belong to pdf-verify
  • It does not OCR. Characters drawn as pixels are not readable. A page whose text cannot be extracted comes back not as an empty string but with the reason (no text layer / a font with no path to Unicode / could not be read)
  • It never guesses logical reading order from coordinates. For an untagged PDF extract_structured_text returns isTagged: false and nothing more. If you need logical reading order, add tags first with pdf-writer's ensure_tagged
  • An encrypted document whose key cannot be derived does not open. Counts such as the page count come back as null, and tools that return a list raise instead

What it does not do

  • Cryptographic verification (→ pdf-verify's verify_signatures)
  • Conformance judgment (the validate_* tools are deprecated → pdf-verify's validate_conformance)
  • Incremental-update history (→ pdf-verify's verify_integrity)

Installation

jsonc
{
  "mcpServers": {
    "pdf-reader": {
      "command": "npx",
      "args": ["-y", "@shuji-bonji/pdf-reader-mcp@latest"]
    }
  }
}

Common parameters

Almost every tool accepts these.

ParameterTypeDescription
file_path requiredstringAbsolute path to a local PDF
response_formatmarkdown / jsonOutput format. Default markdown
pagesstringPage range "1-5" / "3" / "1,3,5-7". All pages when omitted (where supported)

Tools

Parameters, types and defaults are in the tools reference (generated from tools/list).

TierToolOne-liner
1read_textReading-order text extraction (resolves /ActualText)
1read_urlRead a PDF straight from a URL
1read_imagesImage extraction (base64)
1search_textCase-insensitive search
1get_metadataMetadata
1get_page_countPage count (lightweight)
1summarizeOverview report
1render_pageRasterise a page to PNG / JPEG (for documents that cannot be read as text)
2extract_structured_textText in logical content order (tagged PDFs)
2extract_tablesStructured extraction of <Table> subtrees
2inspect_structureInternal object structure
2inspect_tagsObservation of the structure tree
2inspect_fontsFonts and embedding status
2inspect_annotationsAnnotation classification and inventory
2inspect_signaturesStructural observation of signature fields
2locate_objectsObject number → page + rectangle
3compare_structureStructural comparison of two PDFs
3validate_metadatadeprecated
3validate_taggeddeprecated

How to use it

Decide which tool reads the body only after calling summarize. If all you need is the page count, get_page_count is lighter. Prompt → parameters → returned JSON for each tool is at the end of that tool on the tools reference.

Decide which tool to read with

summarize returns metadata.isTagged and textExtractability. Those two fields pick the body-text path. next is a suggestion from the observations, not an instruction you must follow.

Position on the page is a separate path from the body text. The tool depends on whether you are pointing at a paragraph or at an object number.

Get the body text

DocumentToolWhy
Tagged PDFextract_structured_textLogical content order (the depth-first traversal of ISO 32000-2 §14.8.2.5). The only tool that can answer "what is the text of the H1?"
Tables in a tagged PDFextract_tables<TR><TH>/<TD> structure, with kerning whitespace removed (「消 費 税 法」→「消費税法」)
Untaggedread_textY-coordinate reading order. Multi-column documents split by X coordinate with split_columns: 2 / 3

Three things to know about read_text:

  • It resolves /ActualText replacements (ISO 32000-2 §14.9.4) on both paths the clause defines: structure elements and Span marked content. Ligature-substituted and hyphenation-fixed words come back spelled the way a viewer shows them
  • search_text runs over that same text, so hits use the post-replacement spelling
  • Japanese forms indented with fullwidth spaces benefit from compact_whitespace: true, which cuts tokens by 20–40%

Pages that cannot be read as text go to render_page instead. That covers pages where read_text reports no_text_layer (a scan) or not_extractable, and equally vector art, forms, handwriting and seal impressions.

read_images only pulls out the image XObjects a page embeds. render_page draws everything on the page. Rendering uses PDFium compiled to WebAssembly, a different engine from the pdf.js this server reads text with. When the two behave differently on a broken file, neither output is evidence for the other. pages is required. Rendering every page never happens implicitly.

Four things to know about extract_structured_text output:

  • Elements form a flat list with role / depth / text / pages. Pre-order plus depth encodes the tree exactly
  • An element spanning pages stays one element; paragraphs are not split
  • alt is returned separately rather than mixed into text (§14.9.3), and Lbl (list bullets) goes to label
  • Artifacts (page numbers, running heads) are excluded

Get a location on the page

locate_objects and extract_structured_text (include_bbox: true) attach a basis to every rectangle. Each looks like {x1, y1, x2, y2}, but they are not the same kind of evidence. Do not treat a text measurement, a /BBox the file declared, and a box that only names the whole page as equally precise coordinates. The distinction is in each rectangle's basis.

basis in extract_structured_text:

basisWhat it is
layout-attribute-bboxThe /BBox the file declares (ISO 32000-2 Table 379). Not a measurement. The only possible basis for content with no text (an image-only Figure, say)
text-extentMeasured from the element's own text: baseline origin plus the font's ascent/descent — the line box, not glyph outlines. Images and vector art contribute nothing

basis in locate_objects:

basisMeaning
annotation-rectThe object's own /Rect. Exact
page-boxThe object is a page; its crop / media box
page-content-streamThe object draws the page. The rectangle is the whole page, not the changed part
page-resourceA font, image or other resource. No rectangle exists (rect: null)
  • An element spanning pages gets one rectangle per page. Merging them into one would put a rectangle on a page where the element does not exist
  • Declared values are returned as-is, then cross-checked against the page box (§7.7.3.3) and the element's own text; disagreements are reported in boxNote. A declared value does not always agree with measurement: the cover Figure of Well-Tagged PDF 1.0 declares /BBox [-32768 -32768 32767 32767] (an int16 sentinel where a rectangle should be)
  • An element with no derivable rectangle never gets a zero-size box — it carries a boxNote explaining why
  • A nonexistent object number returns found: false (not "coordinates unknown"). Freed numbers arrive here from diffs, and mixing the two would read as "exists but position unknown"
  • In an encrypted document, coordinates and types are returned but /T is null (numbers and names are unencrypted per §7.6.2; strings stay ciphertext)

Checked against an independent ground truth

The 166 measured rectangles of Well-Tagged PDF 1.0's Link structure elements were compared with the 173 /Rect values the producer put on the same links as Link annotations: median IoU 0.972, zero complete misses.

"Where is this paragraph?" and "where is object 27?" are different paths

For content streams, locate_objects can only say "the whole page". To point at a paragraph or heading, use extract_structured_text with include_bbox.

Check the signature fields

inspect_signatures returns the signature-field count, signed/unsigned breakdown and each field's details (signer name, reason, location, signing time, filter/subFilter). It does not verify cryptography. Whether a signature is mathematically valid is pdf-verify-mcp's verify_signatures / verify_integrity.

Two tools not to use

validate_metadata and validate_tagged will be removed in the next major version. pdf-verify-mcp's validate_conformance supersedes both. If you need structure-tree facts, use inspect_tags (which is not deprecated); to just read metadata, use get_metadata.

MIT Licensed