Skip to content

pdf-reader-mcp — Tools Reference

INFO

Auto-generated from the tools/list handshake of v0.15.0 (19 tools, 2026-09-04). Do not edit by hand — regenerate with node scripts/generate-reference.mjs.

This page is the generated reference — every tool's parameters, types, defaults and returns, transcribed from the server's tools/list (the source of truth is the server itself). For the server's responsibilities, boundaries and how to use it, see the guide page.

Tools

ToolSummary
get_page_countGet the total number of pages in a PDF document.
get_metadataExtract metadata from a PDF document including title, author, creation date, page count, PDF version, and structural information.
read_textExtract text content from a PDF document with Y-coordinate-based reading order preservation.
search_textSearch for text within a PDF document.
read_imagesExtract embedded images from a PDF document as PNG or JPEG files.
read_urlFetch a PDF from a URL and extract its text content.
render_pageRasterise pages of a PDF to PNG or JPEG images, returned as MCP image content blocks.
summarizeGenerate a quick overview report of a PDF document.
inspect_structureExamine PDF internal object structure including catalog entries, page tree, and object statistics.
inspect_tagsAnalyze the Tagged PDF structure tree for accessibility assessment.
inspect_fontsList all fonts used in a PDF document with their properties.
inspect_annotationsExtract and categorize all annotations in a PDF document.
inspect_signaturesExamine digital signature fields in a PDF document.
extract_tablesExtract every <Table> subtree from a Tagged PDF as a structured row/cell list, optionally rendered as Markdown tables.
extract_structured_textExtract a tagged PDF's text in logical content order, with each piece labelled by its structure type.
locate_objectsReport where the given objects sit on the page.
validate_tagged[DEPRECATED — will be removed in the next major version]
validate_metadata[DEPRECATED — will be removed in the next major version]
compare_structureCompare the internal structures of two PDF documents and identify differences.

get_page_count

Get PDF Page Count

Get the total number of pages in a PDF document.

This is a lightweight operation that only reads the PDF header, not the full content.

Parameters

ParameterTypeRequiredDefaultDescription
file_pathstring (minLength 1)yesAbsolute path to a local PDF file (e.g., "/path/to/document.pdf")

Returns

Page count as a number.

Examples:

  • Quick check before deciding which pages to extract
  • Validate a PDF file is readable
Worked example — "Just the page count"
  • Measured: v0.15.0
  • Specimen: docs/specimens/publish-demo.pdf (pass an absolute path)

Lightweight: header only.

Parameters

jsonc
{
  "file_path": "/absolute/path/to/docs/specimens/publish-demo.pdf"
}

Returned value

jsonc
1

get_metadata

Get PDF Metadata

Extract metadata from a PDF document including title, author, creation date, page count, PDF version, and structural information.

Parameters

ParameterTypeRequiredDefaultDescription
file_pathstring (minLength 1)yesAbsolute path to a local PDF file (e.g., "/path/to/document.pdf")
response_format"markdown" | "json"no"markdown"Output format: "markdown" for human-readable, "json" for structured data

Returns

Metadata including: title, author, subject, keywords, creator, producer, creation/modification dates, page count, PDF version, linearized/encrypted/tagged/signature flags, file size.

Examples:

  • Get document properties for cataloging
  • Check if a PDF is tagged (accessibility)
  • Verify PDF version compatibility
Worked example — "Title, and is it tagged?"
  • Measured: v0.15.0
  • Specimen: docs/specimens/publish-demo.pdf (pass an absolute path)
  • response_format: "json"

Parameters

jsonc
{
  "file_path": "/absolute/path/to/docs/specimens/publish-demo.pdf",
  "response_format": "json"
}

Returned JSON

jsonc
{
  "title": "請求書(サンプル)— pdf-publish デモ",
  "author": "PDF Agent Stack",
  "pageCount": 1,
  "pdfVersion": "1.7",
  "isEncrypted": false,
  "isTagged": true,
  "hasSignatures": false,
  "fileSize": 88943
}

isTagged is an observation. It is not a PDF/UA verdict.

read_text

Read PDF Text

Extract text content from a PDF document with Y-coordinate-based reading order preservation.

Text is extracted page by page, sorted by vertical position (top to bottom) then horizontal position (left to right), providing natural reading order.

/ActualText replacements (ISO 32000-2 §14.9.4) are resolved, on both of the paths that clause defines: the /ActualText of a structure element, and the one in a Span marked-content property list — the latter occurs in untagged documents too. So a word carried as ActualText (ligature substitutes, hyphenation fixes) reads here the way a person viewing the page sees it, not in its glyph form.

For tagged PDFs, extract_structured_text is still the better tool when order matters: it returns text in logical content order (ISO 32000-2 §14.8.2.5), which this tool does not — read_text sorts by coordinate. Tables in tagged PDFs are best read with extract_tables.

For untagged multi-column PDFs (e.g. older 新旧対照表 PDFs that lack a structure tree), pass split_columns: 2 or 3 to bucket items by X-coordinate left-to-right.

For Japanese form-style PDFs (帳票・様式) where U+3000 fullwidth spaces are used as visual indentation, pass compact_whitespace: true to collapse runs of whitespace to a single ASCII space. Cuts 20–40% of token consumption without losing content.

Parameters

ParameterTypeRequiredDefaultDescription
file_pathstring (minLength 1)yesAbsolute path to a local PDF file (e.g., "/path/to/document.pdf")
pagesstringnoPage range to process. Format: "1-5", "3", or "1,3,5-7". Omit for all pages.
response_format"markdown" | "json"no"markdown"Output format: "markdown" for human-readable, "json" for structured data
split_columnsinteger (1–3)noNumber of columns to use when reordering text. 1 (default) = existing Y-sort. 2 or 3 = bucket by X-coordinate left-to-right. Use for untagged 新旧対照表 / two-column PDFs where Y-sort would interleave columns. Tagged PDFs with proper <Table> markup should use extract_tables instead.
compact_whitespacebooleannoWhen true, collapse runs of whitespace (incl. fullwidth space U+3000) to a single ASCII space and trim each line. Reduces token consumption on Japanese form-style PDFs. Default: false (no whitespace normalization).

Returns

{ scope, pages }. pages is the extracted text organized by page number, preceded by the extractability tally. With split_columns >= 2, columns are separated by a blank line so a downstream LLM can tell them apart.

Examples:

  • Extract all text: { file_path: "/path/to/doc.pdf" }
  • Untagged 新旧対照表: { file_path: "/path/to/older-shinkyu.pdf", split_columns: 2 }
  • Japanese form template: { file_path: "/path/to/form.pdf", compact_whitespace: true }
Worked example — "The text of page 1"
  • Measured: v0.15.0
  • Specimen: docs/specimens/publish-demo.pdf (pass an absolute path)
  • pages: "1"
  • response_format: "json"

This specimen is tagged, so when order matters call extract_structured_text first.

Parameters

jsonc
{
  "file_path": "/absolute/path/to/docs/specimens/publish-demo.pdf",
  "pages": "1",
  "response_format": "json"
}

Returned JSON

jsonc
{
  "scope": {
    "textExtraction": { "status": "read" },
    "extractabilityObservation": { "status": "read" }
  },
  "pages": [
    {
      "page": 1,
      "text": "請求書(サンプル)— pdf-publish デモ\n請求書(サンプル)\n株式会社サンプル商事 御中\n…",
      "extractability": {
        "page": 1,
        "unmappableFonts": [],
        "state": "extracted"
      }
    }
  ]
}

If extractability.state is no_text_layer or not_extractable, the next call is render_page.

search_text

Search PDF Text

Search for text within a PDF document. Returns matching locations with surrounding context.

Case-insensitive search across all or specified pages. Each match includes the page number, the matched text, and configurable surrounding context.

The search runs over the same text read_text returns, so /ActualText replacements (ISO 32000-2 §14.9.4) match: a word carried as ActualText (ligature substitutes, hyphenation fixes) is found under the spelling a viewer shows, not under its glyph form. Rarely, a page's marked content cannot be aligned with the extracted text and the replacement is left unresolved; when that happens on a search with no hits, the result carries a note naming those pages.

Parameters

ParameterTypeRequiredDefaultDescription
file_pathstring (minLength 1)yesAbsolute path to a local PDF file (e.g., "/path/to/document.pdf")
querystring (minLength 1)yesText to search for (case-insensitive)
pagesstringnoPage range to process. Format: "1-5", "3", or "1,3,5-7". Omit for all pages.
context_charsinteger (0–500)no80Number of characters to show before and after each match
max_resultsinteger (1–100)no20Maximum number of matches to return
response_format"markdown" | "json"no"markdown"Output format: "markdown" for human-readable, "json" for structured data

Returns

Search matches with page number, matched text, and surrounding context, plus scope — which of the two readings behind the answer were done: searching the characters on the page, and observing whether those characters have a route to Unicode (ISO 32000-2 §9.10.1). When the search itself could not run, totalMatches and matches are null rather than 0 and []: "could not search" and "searched and found nothing" are different answers.

Examples:

  • Search entire PDF: { file_path: "/path/to/doc.pdf", query: "digital signature" }
  • Search specific pages: { file_path: "/path/to/doc.pdf", query: "error", pages: "1-10" }
Worked example — "Which page is 『請求明細』 on?"
  • Measured: v0.15.0
  • Specimen: docs/specimens/publish-demo.pdf (pass an absolute path)
  • query: "請求明細"
  • response_format: "json"

The search runs over the same text read_text returns.

Parameters

jsonc
{
  "file_path": "/absolute/path/to/docs/specimens/publish-demo.pdf",
  "query": "請求明細",
  "max_results": 3,
  "response_format": "json"
}

Returned JSON

jsonc
{
  "scope": {
    "textExtraction": { "status": "read" },
    "extractabilityObservation": { "status": "read" }
  },
  "query": "請求明細",
  "totalMatches": 1,
  "matches": [
    {
      "page": 1,
      "lineIndex": 5,
      "text": "請求明細",
      "contextBefore": "",
      "contextAfter": ""
    }
  ],
  "truncated": false
}

read_images

Read PDF Images

Extract embedded images from a PDF document as PNG or JPEG files.

Each image is returned as an MCP image content block, so a vision-capable model can look at it directly. A text block lists the metadata for all of them (page, index, size in the file, size returned, colour space, encoded bytes).

These are the image XObjects the page draws, not a picture of the page. A page whose content is vector drawing, or whose text is what you want to see, is not covered by this tool.

Response size is bounded: at most 4 MB of encoded image data per call. Images beyond the budget are named in the text block with the reason and are not returned — nothing is dropped silently. A 200 dpi A4 scan is ~11.6 MB of pixels on its own, so pass pages, max_width or max_height when working with scans.

Parameters

ParameterTypeRequiredDefaultDescription
file_pathstring (minLength 1)yesAbsolute path to a local PDF file (e.g., "/path/to/document.pdf")
pagesstringnoPage range to process. Format: "1-5", "3", or "1,3,5-7". Omit for all pages.
format"png" | "jpeg"noEncoding of the returned images. png (default) is lossless; jpeg is smaller and drops alpha (composited over white).
qualityinteger (1–100)noJPEG quality 1-100 (default 80). Ignored when format is png.
max_widthinteger (1–10000)noDownscale images wider than this, averaging over the source pixels. Images are never enlarged. Omit to return each image at its own size.
max_heightinteger (1–10000)noDownscale images taller than this. Never enlarges.

Returns

A text block with the metadata table and any omissions, then one image content block per returned image.

Examples:

  • Extract all images: { file_path: "/path/to/doc.pdf" }
  • A scanned page, small enough to look at: { file_path: "/path/to/scan.pdf", pages: "1", max_width: 1200, format: "jpeg" }

read_url

Read PDF from URL

Fetch a PDF from a URL and extract its text content. Text is ALL this tool returns — see the scope note below.

Downloads the PDF from the specified URL, then extracts text with Y-coordinate-based reading order. Supports HTTP and HTTPS. Maximum file size: 50MB. Timeout: 30 seconds.

Scope (#25): the fetched bytes are discarded after extraction; this tool deliberately does not save them. Every other tool of this server takes a file_path, so to use search_text, inspect_structure, extract_tables, render_page or anything else on a URL's PDF, download the file to local disk FIRST (with whatever fetch capability the calling environment has) and pass its path. This keeps every tool of this server read-only with respect to the file system — writing files is not a reader's job. read_url exists for the one-shot case: "what does the document at this URL say?"

Like read_text, accepts split_columns: 2 | 3 for untagged multi-column PDFs and compact_whitespace: true to collapse U+3000 / ASCII whitespace runs. Tagged PDFs should use extract_tables instead.

Parameters

ParameterTypeRequiredDefaultDescription
urlstringyesURL pointing to a PDF file (HTTP or HTTPS)
pagesstringnoPage range to process. Format: "1-5", "3", or "1,3,5-7". Omit for all pages.
response_format"markdown" | "json"no"markdown"Output format: "markdown" for human-readable, "json" for structured data
split_columnsinteger (1–3)noNumber of columns to use when reordering text. 1 (default) = existing Y-sort. 2 or 3 = bucket by X-coordinate left-to-right. Use for untagged 新旧対照表 / two-column PDFs where Y-sort would interleave columns. Tagged PDFs with proper <Table> markup should use extract_tables instead.
compact_whitespacebooleannoWhen true, collapse runs of whitespace (incl. fullwidth space U+3000) to a single ASCII space and trim each line. Reduces token consumption on Japanese form-style PDFs. Default: false (no whitespace normalization).

Returns

{ scope, pages }, the same shape as read_text: pages is the extracted text by page number, and scope says which of the two readings behind it were done — taking the characters off the page, and observing whether those characters have a route to Unicode (ISO 32000-2 §9.10.1). Either can fail on its own; only when neither could be done is this an error, and it then names both reasons.

Examples:

  • Read remote PDF: { url: "https://example.com/document.pdf" }
  • Untagged 2-column PDF: { url: "https://...", split_columns: 2 }
  • Japanese form: { url: "https://...", compact_whitespace: true }

render_page

Render PDF Page

Rasterise pages of a PDF to PNG or JPEG images, returned as MCP image content blocks.

This is the tool for documents whose text cannot be read as text: pages read_text reports as no_text_layer or not_extractable, vector drawings, forms, handwriting, stamps. It draws the PAGE — everything on it — where read_images only extracts the image XObjects a page happens to embed.

Rendering uses PDFium compiled to WebAssembly (optional dependency @hyzyla/pdfium). Note this is a different engine from the pdf.js this server reads text with; where their behaviour on a damaged file differs, neither output is evidence about the other. If the dependency is not installed, this tool says so and every other tool works normally.

pages is required — rendering is the most expensive operation here, and "all pages" of a large scan should be a decision, not a default. The response carries at most 4 MB of encoded images; pages past the budget are named with the reason, not dropped.

Parameters

ParameterTypeRequiredDefaultDescription
file_pathstring (minLength 1)yesAbsolute path to a local PDF file (e.g., "/path/to/document.pdf")
pagesstring (minLength 1)yesPage range to render. Required: rendering all pages is never implicit.
dpiinteger (36–600)noRasterisation density (default 150). PDF points are 1/72 inch.
max_widthinteger (1–10000)noCap on the rendered width in pixels; wins over dpi when smaller.
format"png" | "jpeg"nopng (default, lossless) or jpeg (smaller — usually right for scans).
qualityinteger (1–100)noJPEG quality 1-100 (default 80). Ignored for png.

Returns

A text block with per-page metadata (point size, pixel size, effective dpi, bytes) and any omissions, then one image content block per rendered page.

Rasterising a page can take unbounded time — a tiling pattern (ISO 32000-2 §8.7.3.1) whose /XStep or /YStep is a near-zero magnitude asks for an astronomical number of tiles, and the clause forbids only zero. Each page therefore gets 20 seconds (PDF_READER_RENDER_TIMEOUT_MS overrides it); the rendering runs off the main thread, so a page that does not finish is stopped and named in the omissions rather than taking the server down with it. The pages rendered before it are still returned, and the pages after it are reported separately as not attempted — "could not be rendered" and "never started" are different answers.

Examples:

  • A scanned page: { file_path: "/path/to/scan.pdf", pages: "1", format: "jpeg" }
  • A diagram at high detail: { file_path: "/path/to/doc.pdf", pages: "3", dpi: 300 }

summarize

Summarize PDF

Generate a quick overview report of a PDF document.

Combines metadata, text presence check, image count, and a text preview from the first page into a single summary. Useful as a first step before deciding which detailed tools to use.

Parameters

ParameterTypeRequiredDefaultDescription
file_pathstring (minLength 1)yesAbsolute path to a local PDF file (e.g., "/path/to/document.pdf")
response_format"markdown" | "json"no"markdown"Output format: "markdown" for human-readable, "json" for structured data

Returns

Summary including: page count, PDF version, file size, tagged/encrypted/signature flags, text presence, per-document text extractability, the pages that are not fully extractable, image count, a text preview from page 1, and the next suggestions.

Four separate readings produce that summary — the document information, the text of page 1, the image count, and the extractability observation — and any of them can fail on its own. scope says which were done. A field whose reading did not happen is null, never 0, false or "": "not read" and "read and found nothing" are different answers, and next stays silent about any premise that was not observed.

Examples:

  • Quick overview: { file_path: "/path/to/doc.pdf" }
  • Machine-readable: { file_path: "/path/to/doc.pdf", response_format: "json" }
Worked example — "Give me an overview of this PDF"
  • Measured: v0.15.0
  • Specimen: docs/specimens/publish-demo.pdf (pass an absolute path)
  • response_format: "json"

Parameters

jsonc
{
  "file_path": "/absolute/path/to/docs/specimens/publish-demo.pdf",
  "response_format": "json"
}

Returned JSON (textPreview omitted)

jsonc
{
  "scope": {
    "metadata": { "status": "read" },
    "textPreview": { "status": "read" },
    "imageCount": { "status": "read" },
    "extractabilityObservation": { "status": "read" }
  },
  "metadata": {
    "title": "請求書(サンプル)— pdf-publish デモ",
    "pageCount": 1,
    "pdfVersion": "1.7",
    "isEncrypted": false,
    "isTagged": true,
    "hasSignatures": false,
    "fileSize": 88943
  },
  "imageCount": 0,
  "hasText": true,
  "textExtractability": "extracted",
  "unreadablePages": [],
  "next": [
    "isTagged is true: extract_structured_text returns the body in logical content order …"
  ]
}

isTagged: true and textExtractability: "extracted", so the next call is extract_structured_text. next is a suggestion, not an order.

inspect_structure

Inspect PDF Structure

Examine PDF internal object structure including catalog entries, page tree, and object statistics.

Parameters

ParameterTypeRequiredDefaultDescription
file_pathstring (minLength 1)yesAbsolute path to a local PDF file (e.g., "/path/to/document.pdf")
response_format"markdown" | "json"no"markdown"Output format: "markdown" for human-readable, "json" for structured data

Returns

Catalog entries (keys and COS types), page tree info (page count, MediaBox samples), object statistics, and encryption status.

Object statistics report three separate counts:

  • byType: the COS type of each indirect object (ISO 32000-2 §7.3) — one of dict, stream, array, name, string, integer, real, boolean, null, ref
  • byDocType: the /Type of each dictionary (Catalog, Pages, Page, Font, ObjStm, XRef, ...)
  • unreadable: objects the cross-reference table names but that could not be read. This is counted apart from totalObjects: 0 means "every object was read", not "nothing was checked".

Examples:

  • Examine document catalog for structural features
  • Count PDF objects and streams
  • Check page dimensions across the document
Worked example — "What is in the catalog?"
  • Measured: v0.15.0
  • Specimen: docs/specimens/publish-demo.pdf (pass an absolute path)
  • response_format: "json"

catalog trimmed.

Parameters

jsonc
{
  "file_path": "/absolute/path/to/docs/specimens/publish-demo.pdf",
  "response_format": "json"
}

Returned JSON

jsonc
{
  "catalog": [
    { "key": "Type", "type": "name", "value": "Catalog" },
    { "key": "Pages", "type": "ref", "value": "ref(1)" },
    { "key": "StructTreeRoot", "type": "ref", "value": "ref(5)" },
    { "key": "Lang", "type": "string", "value": "ja" }
  ],
  "pageTree": {
    "totalPages": 1,
    "mediaBoxSamples": [{ "page": 1, "width": 595.28, "height": 841.89 }]
  },
  "objectStats": {
    "totalObjects": 52,
    "unreadable": 0
  },
  "isEncrypted": false,
  "pdfVersion": "1.7"
}

unreadable: 0 means "every object was read", not "nothing was checked".

inspect_tags

Inspect Tagged PDF Structure

Analyze the Tagged PDF structure tree for accessibility assessment.

Parameters

ParameterTypeRequiredDefaultDescription
file_pathstring (minLength 1)yesAbsolute path to a local PDF file (e.g., "/path/to/document.pdf")
response_format"markdown" | "json"no"markdown"Output format: "markdown" for human-readable, "json" for structured data

Returns

Whether the PDF is tagged, the structure tree hierarchy with roles, max nesting depth, total element count, and role distribution (e.g., Document, P, H1, Table, Figure).

Examples:

  • Check if a PDF is tagged for accessibility (PDF/UA)
  • Inspect the tag hierarchy and role distribution
  • Assess document structure quality
Worked example — "Observe the tag structure"
  • Measured: v0.15.0
  • Specimen: docs/specimens/publish-demo.pdf (pass an absolute path)
  • response_format: "json"

The full tree is omitted; counts only.

Parameters

jsonc
{
  "file_path": "/absolute/path/to/docs/specimens/publish-demo.pdf",
  "response_format": "json"
}

Returned JSON

jsonc
{
  "isTagged": true,
  "maxDepth": 5,
  "totalElements": 29,
  "roleCounts": {
    "Document": 1,
    "H1": 2,
    "P": 3,
    "H2": 2,
    "Table": 1,
    "TR": 4,
    "TH": 4,
    "TD": 12
  }
}

No pass/fail. PDF/UA judgment is pdf-verify-mcp's validate_conformance.

inspect_fonts

Inspect PDF Fonts

List all fonts used in a PDF document with their properties.

Parameters

ParameterTypeRequiredDefaultDescription
file_pathstring (minLength 1)yesAbsolute path to a local PDF file (e.g., "/path/to/document.pdf")
response_format"markdown" | "json"no"markdown"Output format: "markdown" for human-readable, "json" for structured data

Returns

Font name, type (TrueType, Type1, CIDFont, etc.), encoding, embedded/subset status, and pages where each font is used.

Examples:

  • Check if all fonts are embedded (required for PDF/A, PDF/X)
  • Identify font types and encodings
  • Find which pages use specific fonts
Worked example — "Which fonts are embedded?"
  • Measured: v0.15.0
  • Specimen: docs/specimens/publish-demo.pdf (pass an absolute path)
  • response_format: "json"

Parameters

jsonc
{
  "file_path": "/absolute/path/to/docs/specimens/publish-demo.pdf",
  "response_format": "json"
}

Returned JSON

jsonc
{
  "fonts": [
    {
      "name": "OAFEEB+NotoSansJP-Regular",
      "type": "Type0",
      "encoding": "Identity-H",
      "isEmbedded": true,
      "isSubset": true,
      "pagesUsed": [1]
    }
  ],
  "totalFontCount": 1,
  "embeddedCount": 1,
  "subsetCount": 1,
  "pagesScanned": 1
}

inspect_annotations

Inspect PDF Annotations

Extract and categorize all annotations in a PDF document.

Parameters

ParameterTypeRequiredDefaultDescription
file_pathstring (minLength 1)yesAbsolute path to a local PDF file (e.g., "/path/to/document.pdf")
pagesstringnoPage range to process. Format: "1-5", "3", or "1,3,5-7". Omit for all pages.
response_format"markdown" | "json"no"markdown"Output format: "markdown" for human-readable, "json" for structured data

Returns

Total annotation count, breakdown by subtype (Link, Widget, Highlight, Text, etc.) and by page, flags for links/forms/markup presence, and individual annotation details.

Examples:

  • Check for form fields (Widget annotations)
  • Find all links in a document
  • Inventory markup annotations (highlights, comments)
Worked example — "Are there any annotations?"
  • Measured: v0.15.0
  • Specimen: docs/specimens/publish-demo.pdf (pass an absolute path)
  • response_format: "json"

Zero means "read and found none", not "could not read".

Parameters

jsonc
{
  "file_path": "/absolute/path/to/docs/specimens/publish-demo.pdf",
  "response_format": "json"
}

Returned JSON

jsonc
{
  "totalAnnotations": 0,
  "bySubtype": {},
  "byPage": { "1": 0 },
  "annotations": [],
  "hasLinks": false,
  "hasForms": false,
  "hasMarkup": false
}

inspect_signatures

Inspect PDF Digital Signatures

Examine digital signature fields in a PDF document.

Parameters

ParameterTypeRequiredDefaultDescription
file_pathstring (minLength 1)yesAbsolute path to a local PDF file (e.g., "/path/to/document.pdf")
response_format"markdown" | "json"no"markdown"Output format: "markdown" for human-readable, "json" for structured data

Returns

Total signature field count, signed/unsigned breakdown, and details for each field (signer name, reason, location, signing time, filter/subFilter).

Note: This tool inspects signature field structure only. Cryptographic signature verification is not performed.

Examples:

  • Check if a PDF has been digitally signed
  • Inspect signer information and signing dates
  • Verify signature field structure
Worked example — "Show me the signature-field structure"
  • Measured: v0.15.0
  • Specimen: docs/specimens/selfmade-pades-lta.pdf (pass an absolute path)
  • response_format: "json"

No cryptographic verification is performed.

Parameters

jsonc
{
  "file_path": "/absolute/path/to/docs/specimens/selfmade-pades-lta.pdf",
  "response_format": "json"
}

Returned JSON

jsonc
{
  "totalFields": 2,
  "signedCount": 2,
  "unsignedCount": 0,
  "fields": [
    {
      "fieldName": "Sig1",
      "isSigned": true,
      "reason": "known-good specimen",
      "signingTime": "D:20260811163237+09'00'",
      "filter": "Adobe.PPKLite",
      "subFilter": "ETSI.CAdES.detached"
    },
    {
      "fieldName": "Timestamp-938ea022-8782-4761-b0a2-a5ba44d069e4",
      "isSigned": true,
      "filter": "Adobe.PPKLite",
      "subFilter": "ETSI.RFC3161"
    }
  ],
  "note": "Cryptographic signature verification is not performed. Only field structure is inspected."
}

Whether a signature is mathematically valid is pdf-verify-mcp's verify_signatures / verify_integrity.

extract_tables

Extract Tables (Tagged PDF)

Extract every <Table> subtree from a Tagged PDF as a structured row/cell list, optionally rendered as Markdown tables.

How it works: walks the document's StructTreeRoot depth-first (the same walker as extract_structured_text / inspect_tags) and pulls cell text for each <TR><TH>/<TD>, then collapses kerning whitespace (e.g. "消 費 税 法" → "消費税法"). This sidesteps reading-order extraction's failure mode on multi-column tables (typical of 新旧対照表 PDFs).

A Table that continues across a page break is ONE table (ISO 32000-2 §14.8.2.5 NOTE 2) — pages is an array, and a table touching the requested page range is returned whole. Cell text honours /ActualText replacements (§14.9.4).

Parameters

ParameterTypeRequiredDefaultDescription
file_pathstring (minLength 1)yesAbsolute path to a local PDF file (e.g., "/path/to/document.pdf")
pagesstringnoPage range to process. Format: "1-5", "3", or "1,3,5-7". Omit for all pages.
response_format"markdown" | "json"no"markdown"Output format: "markdown" for human-readable, "json" for structured data

Returns

Markdown — # Extracted Tables summary block followed by one ## Table N — Page(s) … section per table with a GFM table.

JSON — { isTagged, tables: [{ pages, index, headerRows, bodyRows, footerRows }], totalTables, pagesScanned, note? }. index is the table's 1-based position in logical content order, document-wide.

Limitations:

  • Untagged PDFs return an empty result and a note.
  • colspan/rowspan are not honoured (cells are listed in source order).
  • Nested tables are not emitted separately (their text appears in the outer cell).

Examples:

  • Pull 新旧対照表 from a kaisei tsutatsu PDF for diffing
  • Convert 帳票 (form template) tables into structured data
Worked example — "Give me this table as structured data"
  • Measured: v0.15.0
  • Specimen: docs/specimens/publish-demo.pdf (pass an absolute path)
  • response_format: "json"

Parameters

jsonc
{
  "file_path": "/absolute/path/to/docs/specimens/publish-demo.pdf",
  "response_format": "json"
}

Returned JSON

jsonc
{
  "isTagged": true,
  "tables": [
    {
      "pages": [1],
      "index": 1,
      "headerRows": [],
      "bodyRows": [
        {
          "cells": [
            { "text": "品目", "isHeader": true },
            { "text": "数量", "isHeader": true },
            { "text": "単価", "isHeader": true },
            { "text": "金額", "isHeader": true }
          ]
        },
        {
          "cells": [
            { "text": "PDF監査サービス", "isHeader": false },
            { "text": "1", "isHeader": false },
            { "text": "50,000", "isHeader": false },
            { "text": "50,000", "isHeader": false }
          ]
        }
        // … rows for タグ付きPDF生成 and 合計 follow
      ],
      "footerRows": []
    }
  ],
  "totalTables": 1,
  "pagesScanned": 1
}

On an untagged PDF the tables array is empty and a note is attached.

extract_structured_text

Extract Structured Text

Extract a tagged PDF's text in logical content order, with each piece labelled by its structure type.

This answers "what is the text of the H1?" — which read_text (flat, coordinate order), inspect_tags (structure, no text) and extract_tables (text, tables only) each cannot.

Parameters

ParameterTypeRequiredDefaultDescription
file_pathstring (minLength 1)yesAbsolute path to a local PDF file (e.g., "/path/to/document.pdf")
pagesstringnoPage range to process. Format: "1-5", "3", or "1,3,5-7". Omit for all pages. An element that touches the range is returned whole, even if it continues outside it.
rolesstring[]noStructure types to include, e.g. ["H1","H2"] to extract an outline. Omit for all roles.
include_bboxbooleannofalseAlso report where each element is drawn, as boxes: one rectangle per page in PDF default user space (origin bottom-left, pt, normalised) — the form pdf-writer-mcp add_annotation takes. Each carries a basis: "layout-attribute-bbox" (the /BBox the file declares) or "text-extent" (measured from the element's text; images and vector art contribute nothing). Elements with no rectangle carry boxNote saying why. Off by default: it costs a second pass over every page.
response_format"markdown" | "json"no"markdown"Output format: "markdown" for human-readable, "json" for structured data

Returns

isTagged, the document language, and a flat list of elements in logical content order. Each element has: role, depth (nesting; top level is 0), text, pages, and optionally alt / label / rows / boxes / boxNote.

scope says which of the two readings behind that answer were done — reading the structure tree, and observing whether the characters under it have a route to Unicode (ISO 32000-2 §9.10.1). When the structure tree could not be read, isTagged and elements are null rather than false and []: "not read" and "read and found no tags" are different answers.

The list is flat with a depth field rather than nested — a depth-first pre-order plus depth encodes the tree exactly, so nothing is lost. Table is the exception and carries rows, because a table is two-dimensional and depth cannot express "row 2, column 3".

Key properties:

  • Order is a depth-first traversal of the document's structure tree, which is how ISO 32000-2 §14.8.2.5 defines logical content order.
  • An element that spans pages stays ONE element (pages is an array). A paragraph split across a page break is returned as one paragraph, not two.
  • ActualText replaces the glyphs when present (§14.9.4: "a replacement, not a description"). Alt is reported separately in alt and never as text — it describes content that has no text (§14.9.3), so it must not leak into the body.
  • Lbl (a list bullet or number) is reported in label, not mixed into text.
  • Artifacts (page numbers, running heads) are excluded: §14.8.2.5 NOTE 3 puts them outside the logical content order.

With include_bbox (answers "where is this paragraph?", so an annotation can be placed on it): Each element gains boxes — ONE RECTANGLE PER PAGE, because an element that spans pages has no single rectangle. Each is { page, rect: {x1,y1,x2,y2}, basis } in PDF default user space (origin bottom-left, pt, already normalised), which is exactly what pdf-writer-mcp add_annotation takes: no coordinate system has to be reinterpreted in between. /Rotate and a shifted /CropBox do not affect it.

basis says how strong the claim is, and the two are not the same kind of claim:

  • layout-attribute-bbox — the /BBox the file DECLARES for the element (ISO 32000-2 Table 379). A statement by the producer about its own geometry, reported as-is. This is the only source for content that has no text.
  • text-extent — MEASURED from the text the element owns: baseline origin plus the font's ascent/descent. That is the line box, not the glyph outlines. Images and vector drawings contribute nothing to it.

When a declared /BBox does not cover the text measured inside it, that disagreement is reported in boxNote rather than smoothed over.

An element with no rectangle has no boxes and carries boxNote saying why — a Figure holding one image is the usual case (§14.8.4.8.5: such an element "should have a BBox attribute"). Absent is not zero-sized, and neither is guessed at.

Untagged PDFs return isTagged: false with a reason and no elements. Nothing is guessed from coordinates — §14.8.2.5 NOTE 1 is explicit that page order need not match logical order, so a guess could not be trusted. To add a structure scaffold, use pdf-writer-mcp ensure_tagged and retry.

Examples:

  • Extract a document outline: { file_path: "/doc.pdf", roles: ["H1","H2","H3"] }
  • Get content for reflow / conversion, structure preserved: { file_path: "/doc.pdf" }
  • Read the text of a specific section's pages: { file_path: "/doc.pdf", pages: "4-6" }
  • Find where to put an annotation: { file_path: "/doc.pdf", roles: ["P"], include_bbox: true } → hand a box straight to pdf-writer-mcp add_annotation. To go the other way, from an object number a diff reported to a rectangle, use locate_objects.
Worked example — "Headings and paragraphs in logical order, with positions"
  • Measured: v0.15.0
  • Specimen: docs/specimens/publish-demo.pdf (pass an absolute path)
  • pages: "1"
  • include_bbox: true
  • response_format: "json"

elements trimmed to the first two items.

Parameters

jsonc
{
  "file_path": "/absolute/path/to/docs/specimens/publish-demo.pdf",
  "pages": "1",
  "include_bbox": true,
  "response_format": "json"
}

Returned JSON

jsonc
{
  "scope": {
    "textExtraction": { "status": "read" },
    "extractabilityObservation": { "status": "read" }
  },
  "isTagged": true,
  "lang": "ja",
  "elements": [
    {
      "role": "H1",
      "depth": 1,
      "text": "請求書(サンプル)— pdf-publish デモ",
      "pages": [1],
      "boxes": [
        {
          "page": 1,
          "rect": { "x1": 56, "y1": 766.306, "x2": 375.194, "y2": 792.37 },
          "basis": "text-extent"
        }
      ]
    },
    {
      "role": "P",
      "depth": 1,
      "text": "株式会社サンプル商事 御中",
      "pages": [1],
      "boxes": [
        {
          "page": 1,
          "rect": { "x1": 56, "y1": 699.322, "x2": 190.464, "y2": 715.25 },
          "basis": "text-extent"
        }
      ]
    }
    // … H2 "請求明細", Table, remarks follow
  ]
}

rect is PDF user space (origin bottom-left, pt). pdf-writer-mcp's add_annotation takes it as-is. basis is text-extent, so it is measured from the text.

locate_objects

Locate PDF Objects (object number → page and rectangle)

Report where the given objects sit on the page.

Bridges "which object" to "which coordinates": pdf-verify-mcp's verify_integrity names the objects an incremental update changed, and pdf-writer-mcp's add_annotation wants a page number and a rectangle. The rectangle is returned in PDF user space (origin bottom-left, pt, x1 < x2 and y1 < y2 — ISO 32000-1 §7.9.5 normalised form), which is exactly what add_annotation takes.

Parameters

ParameterTypeRequiredDefaultDescription
file_pathstring (minLength 1)yesAbsolute path to a local PDF file (e.g., "/path/to/document.pdf")
object_numbersinteger (1–9007199254740991)[]yesObject numbers to locate, e.g. [25, 27]. Typically the objects pdf-verify-mcp's verify_integrity reported as changed.
response_format"markdown" | "json"no"markdown"Output format: "markdown" for human-readable, "json" for structured data

Returns

Per object: whether it exists, its /Type and /Subtype, and the places it occupies, each with the basis the coordinates rest on:

  • annotation-rect — the object's own /Rect. Exact.
  • page-box — the object is a page; the rectangle is its crop/media box.
  • page-content-stream — the object draws the page; the rectangle is the WHOLE page, not the part that changed.
  • page-resource — a font, image or colour space used by the page. No rectangle exists for it.

Limits (observations, not judgements):

  • Narrowing a content stream to the paragraph that moved needs a content-stream walk with graphics state; this tool does not do it and says so rather than inventing a rectangle.
  • An object that does not exist (freed by a later revision) is returned with found: false — not as "no coordinates".
  • In an encrypted document, coordinates and types are still reliable (numbers and names are not encrypted, ISO 32000-1 §7.6.2) but field names are reported as null instead of mojibake.

Examples:

  • Turn verify_integrity's "obj 27 was added after signing" into a page and rectangle
  • Find which page a changed form field widget is on before annotating it
Worked example — "Where are objects 7, 9 and 4?"
  • Measured: v0.15.0
  • Specimen: docs/specimens/publish-demo.pdf (pass an absolute path)
  • object_numbers: [7, 9, 4]
  • response_format: "json"

Typical input is the object numbers pdf-verify-mcp's verify_integrity returned.

Parameters

jsonc
{
  "file_path": "/absolute/path/to/docs/specimens/publish-demo.pdf",
  "object_numbers": [7, 9, 4],
  "response_format": "json"
}

Returned JSON

jsonc
{
  "objects": [
    {
      "objectNumber": 7,
      "found": true,
      "type": "Page",
      "locations": [
        {
          "page": 1,
          "rect": { "x1": 0, "y1": 0, "x2": 595.28, "y2": 841.89 },
          "basis": "page-box"
        }
      ]
    },
    {
      "objectNumber": 9,
      "found": true,
      "type": null,
      "locations": [
        {
          "page": 1,
          "rect": { "x1": 0, "y1": 0, "x2": 595.28, "y2": 841.89 },
          "basis": "page-content-stream"
        }
      ],
      "reason": "This is the page's content stream, so the rectangle is the whole page. …"
    },
    {
      "objectNumber": 4,
      "found": true,
      "type": "Font",
      "subtype": "Type0",
      "locations": [{ "page": 1, "rect": null, "basis": "page-resource" }],
      "reason": "A resource is used by the page but has no rectangle of its own; …"
    }
  ],
  "isEncrypted": false
}

A page-content-stream rectangle is the whole page. To point at a paragraph, use extract_structured_text with include_bbox.

validate_tagged

Validate Tagged PDF (deprecated)

Deprecated

[DEPRECATED — will be removed in the next major version]

Prefer pdf-verify-mcp's validate_conformance with flavour: "pdfua-1" (or "pdfua-2"). It supersedes this tool rather than merely replacing it: it verifies the actual /Alt and /ActualText values of Figure tags (this tool only counts Figures), checks Link /Contents, inspects StructTreeRoot from the catalog directly (this tool synthesises it per page), cites ISO 14289 clauses, and delegates to veraPDF when available.

Reason: the family boundary is "pass/fail against an ISO standard belongs to pdf-verify-mcp; reporting observations belongs to pdf-reader-mcp". This tool predates pdf-verify-mcp and was the exception. Use inspect_tags here for structure-tree facts — that tool is NOT deprecated.

This tool remains a quick preflight and still works. Only the checks below are performed; a pass here does not imply PDF/UA conformance.

Validate PDF/UA tagged structure requirements.

Parameters

ParameterTypeRequiredDefaultDescription
file_pathstring (minLength 1)yesAbsolute path to a local PDF file (e.g., "/path/to/document.pdf")
response_format"markdown" | "json"no"markdown"Output format: "markdown" for human-readable, "json" for structured data

Returns

Validation results including: whether the PDF is tagged, total checks performed, pass/fail counts, detailed issues with severity levels (error/warning/info), and a summary.

totalChecks counts the checks that were actually decided. A check whose premise could not be observed is not counted there and appears in notChecked with the reason — TAG-005 judges Figure tags against the number of images the page draws, so without that number it would have to assume zero images and would report a pass for something nobody looked at.

Checks performed:

  • Document marked as tagged
  • Structure tree root existence
  • Document root tag presence
  • Heading hierarchy (H1-H6) sequential order
  • Figure tags for images
  • Paragraph tag presence
  • Structure element count
  • Table tag structure (TR/TH/TD)

Examples:

  • Check if a PDF meets PDF/UA accessibility requirements
  • Identify missing or incorrect tag structure
  • Assess document accessibility quality

validate_metadata

Validate PDF Metadata (deprecated)

Deprecated

[DEPRECATED — will be removed in the next major version]

For standards conformance, prefer pdf-verify-mcp's validate_conformance (flavour: "pdfua-1" / "pdfa-*"), which judges against the ISO text and delegates to veraPDF when available. Use get_metadata here if you just want to read metadata fields.

Reason: the family boundary is "pass/fail against an ISO standard belongs to pdf-verify-mcp; reporting observations belongs to pdf-reader-mcp". This tool predates pdf-verify-mcp.

Known limitation (not being fixed — superseded): the checks read the document information dictionary only. PDF/UA-1 §7.1 requires dc:title in the XMP metadata stream and states a conforming reader "shall ignore" the Info dictionary; it also requires ViewerPreferences/DisplayDocTitle = true and Suspects = false, none of which are checked here. ISO 32000-2 §14.3.3 deprecates the Info dictionary except CreationDate/ModDate. Treat the results below as general best-practice hints, not PDF/UA or PDF/A grounds.

Validate PDF metadata conformance against best practices and specification requirements.

Parameters

ParameterTypeRequiredDefaultDescription
file_pathstring (minLength 1)yesAbsolute path to a local PDF file (e.g., "/path/to/document.pdf")
response_format"markdown" | "json"no"markdown"Output format: "markdown" for human-readable, "json" for structured data

Returns

Validation results including: total checks, pass/fail counts, detailed issues with severity, metadata field presence summary, and an overall summary.

Checks performed (all against the Info dictionary — see the limitation above):

  • Title presence (best practice; NOT the PDF/UA basis, which is XMP dc:title)
  • Author presence
  • Creation date format validation
  • Modification date presence
  • Producer identification
  • PDF version detection
  • Tagged flag status
  • Subject and Keywords presence
  • Encryption and accessibility impact

Examples:

  • Quick check of document metadata completeness for publishing standards
  • (For PDF/A archival or PDF/UA compliance, use pdf-verify-mcp validate_conformance instead)

compare_structure

Compare PDF Structures

Compare the internal structures of two PDF documents and identify differences.

Parameters

ParameterTypeRequiredDefaultDescription
file_path_1string (minLength 1)yesAbsolute path to the first PDF file for comparison
file_path_2string (minLength 1)yesAbsolute path to the second PDF file for comparison
response_format"markdown" | "json"no"markdown"Output format: "markdown" for human-readable, "json" for structured data

Returns

Structural comparison including: property-by-property diff (page count, PDF version, encryption, tagged status, object counts, page dimensions, file size, catalog entries, signatures), font comparison (fonts unique to each file and shared fonts), and a summary.

The comparison needs both files, so an unreadable file is an error — but the error names which of the two could not be read, and says that the other one was fine.

Examples:

  • Compare two versions of the same document
  • Verify structural consistency across PDF exports
  • Identify differences in PDF generation pipelines

MIT Licensed