
PDF Reader (PDF Agent Stack)
io.github.shuji-bonji由 GitHub 帳號 shuji-bonji 發布v0.22.0更新於 Nov 7, 2026
Read PDF internals: text, structure, fonts, signatures and tags, with what was not read declared.
概覽
PDF Reader (PDF Agent Stack) 的本機 MCP 服務;無需登入;STDIO。
暫無 AI 概覽:以下資訊來自註冊中心條目和服務本身。
安裝
在 SourceWeft 中
- 開啟 儀表板中的 PDF Reader (PDF Agent Stack),將其新增到工作區。
- 為需要使用其工具的對話啟用該服務。
Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。
其他 MCP 客戶端
參照 儲存庫 中的啟動說明。
README
PDF Reader MCP Server
[npm version] [License: MIT] [Built with Claude Code]
English | 日本語
An MCP (Model Context Protocol) server specialized in deciphering PDF internal structures.
While typical PDF MCP servers are thin wrappers for text extraction, this project focuses on reading and analyzing the internal structure of PDF documents. Pair it with pdf-spec-mcp for specification-aware structural analysis and validation.
PDF family
pdf-reader-mcp inspects signature structure (inspect_signatures); for cryptographic signature verification, trust/revocation evaluation, and PDF/A conformance validation, use pdf-verify-mcp.
Features
18 tools organized into three tiers:
Tier 1: Basic Operations
Tier 2: Structure Inspection
Tier 3: Comparison
validate_tagged and validate_metadata were removed (deprecated since 0.7.0). This server
reports what a PDF contains and does not return pass/fail against a standard. For PDF/UA or
PDF/A conformance, use pdf-verify-mcp
validate_conformance (flavour: "pdfua-1", "pdfa-*", …). For the facts those tools used
to look at, use inspect_tags (structure tree) and get_metadata (metadata fields) here.
read_url and the read-only boundary (#25)
read_url returns text, and only text. This is a decision, now stated rather than implied:
the fetched bytes are discarded after extraction, because saving them would make a reader
tool write to the file system, and every tool of this server is read-only
(readOnlyHint: true — all 18 of them).
To run search_text, inspect_structure, extract_tables, render_page or anything else
against a PDF that lives at a URL, download the file first — with whatever fetch capability
the calling environment has — and pass the local path. Fetching is the caller's
responsibility, deliberately: an agent environment always has a way to download a file, and a
reader that also writes files has stopped being a pure observer.
read_url remains the right tool for the one-shot question: what does the document at this
URL say?
Pages can be rendered when text cannot be read (#23)
summarize reporting hasText: false used to be a dead end: nothing in this server could
read the document any further. render_page closes that — it rasterises pages to PNG or JPEG
and returns them as MCP image content blocks, so a vision model can read a scan, a diagram, a
filled form, or handwriting.
pages is required: rendering is the most expensive operation here, and "all pages" of a
500-page scan should be a decision, not a default. The same 4 MB response budget as
read_images applies, with omissions named.
Rendering runs on PDFium compiled to WebAssembly (@hyzyla/pdfium, an optional
dependency). A WASM binary is the same bytes on every platform, so the published package still
behaves identically wherever npx runs it — the reason native addons are not used here.
Without the dependency installed, render_page reports what to install and every other tool
works normally. PDFium (BSD-3-Clause) is a different engine from the pdf.js this server reads
text with; the tool description says so, because a rendering difference between engines must
not be attributed to the file.
Measured before choosing this: pdf.js +
@napi-rs/canvas(1.0.7 and 0.1.80) segfaults the whole process on pages that draw images — exactly the pages this tool exists for — and renders blank pages whenstandardFontDataUrlis not configured.
Images come back as image files (#22)
read_images used to base64 imgData.data — pdfjs's decoded pixels. An 8×8 RGB image was
192 bytes with no PNG or JPEG signature anywhere in it, so the result could not be opened by
any viewer and could not be read by a vision model, which is the reason to extract an image in
the first place.
Images are now encoded (PNG by default, lossless; format: "jpeg" with quality when smaller
matters) and returned as MCP image content blocks, with the metadata alongside in a text
block. Both encoders are written out here — no native addon, no per-platform binary.
The response is bounded at 4 MB of encoded image data. A 200 dpi A4 scan is ~11.6 MB of pixels on its own, so images past the budget are named with the reason rather than dropped:
read_images returns the image XObjects a page draws. It is not a picture of the page — vector
drawings and text are not covered by it.
Text extractability — three states, not two (#21)
read_text used to answer with text or with nothing, and nothing meant three different things.
ISO 32000-2 §9.10.1 separates them, so this server does too. Every text-returning tool —
read_text, read_url, search_text, extract_structured_text, summarize — reports, per
page:
not_extractable is reported per font, so a page that mixes a readable font with an
unreadable one is a partial loss and says so, rather than passing as complete.
The observation is made from the file, not from pdf.js's output: pdf.js synthesises a
toUnicode map for every font it loads, so asking it whether a font has a /ToUnicode CMap
answers yes for fonts whose dictionary has none.
Installation
npx (recommended)
Claude Desktop
Add to your claude_desktop_config.json:
Use
@latest. The-yflag innpx -y <pkg>only skips the install prompt — it does not check for updates. Without@latest, npx keeps running whichever version it cached the first time, so new releases never reach you. If you suspect you are on a stale version, runrm -rf ~/.npm/_npxand restart your client.
Claude Code
From Source
Usage Examples
Get Page Count
declaredCount is the /Count the page tree declares (ISO 32000-2 §7.7.3.2) and pagesReached
is how many pages walking the tree actually reached. They differ only when the writer got
/Count wrong; both are reported and neither is judged.
Metadata from both sources
get_metadata reads the eight document-level fields (title, author, subject, keywords, creator,
producer, creation and modification dates) from the document information dictionary (§14.3.3)
and the XMP metadata stream (§14.3.2). When both hold a value, PDF 2.0 and later use XMP
(§14.3.3 deprecates the dictionary for this in PDF 2.0) and earlier versions use the dictionary.
The two dates are the exception: they use the dictionary in every version, because §14.3.3 does
not deprecate CreationDate and ModDate, and §14.5 compares PieceInfo's LastModified with
the dictionary's ModDate.
metadataSources names the source of each field, and metadataConflicts lists the fields whose
two values differ (dates compared as instants). Values are returned as written — a dictionary
date looks like D:20260915100000+09'00', an XMP date like 2026-09-15T10:00:00+09:00.
Search Text
Summarize
Compare Structure
Extract Structured Text (Tagged PDF, logical order)
This answers "what is the text of the H1?" — which read_text (flat,
coordinate order) cannot. Order is a depth-first traversal of the structure
tree (ISO 32000-2 §14.8.2.5). /ActualText replaces the glyphs (§14.9.4),
/Alt stays out of the body text (§14.9.3), list labels are reported
separately, a list nested in an item comes back as its own L / LI
elements after the item (not folded into the item's text), and an element
spanning a page break stays ONE element. Use
roles: ["H1", "H2"] to pull an outline. Untagged PDFs return
isTagged: false with a reason — nothing is guessed from coordinates.
include_bbox: where the element is drawn
Rectangles are in PDF default user space (origin bottom-left, pt, normalised) —
exactly what pdf-writer-mcp
add_annotation takes, so "annotate this paragraph" needs no coordinate
conversion in between. /Rotate and a shifted /CropBox do not move them.
basis says how strong the claim is, and the two are different in kind:
An element spanning pages gets ONE RECTANGLE PER PAGE — merging them would put
content on a page it is not on. An element with no rectangle says why in
boxNote rather than returning a zero-sized one.
A declaration is reported as-is, and cross-checked. Files state nonsense:
the cover Figure of both Well-Tagged PDF 1.0 and the Tagged PDF Best Practice
Guide declares /BBox [-32768 -32768 32767 32767] — int16 sentinels where a
rectangle should be — and PDF32000_2008 has 131 of its 545 declarations reaching
past the page edge. Since this output is meant to go straight into
add_annotation, a declaration is checked against the page box (§7.7.3.3) and
against the element's own text; either contradiction is reported in boxNote,
with the rectangle still returned unaltered.
Measured against independent ground truth: on Well-Tagged PDF (WTPDF) 1.0, the
166 Link structure elements were compared with the 173 Link annotation
/Rect values the producer placed for the same links — median IoU 0.972,
none disjoint.
Extract Tables (Tagged PDF)
A table that continues across a page break is reported as ONE table —
pages is an array (e.g. ## Table 3 — Pages 5–7), and a table touching
the requested pages range is returned whole. Cell text honours
/ActualText replacements (as do read_text and search_text since #18).
Untagged PDFs return an empty result with a
note recommending the column-aware fallback below.
Read Untagged Multi-Column PDF
Use split_columns: 2 | 3 for untagged multi-column PDFs. For Tagged
PDFs with proper <Table> markup, extract_tables (above) is preferred.
Compact Whitespace (Japanese Forms)
compact_whitespace is orthogonal to split_columns — both can be combined.
Tech Stack
- TypeScript + MCP TypeScript SDK
- pdfjs-dist (Mozilla) — text/image extraction, tag tree, annotations
- normativepdf + @normativepdf/recover — COS object access, read from files whose cross-reference table does not follow ISO 32000-2 §7.5
- Vitest — unit + E2E testing (483 tests)
- Biome — linting + formatting
- Zod — input validation
Testing
Architecture
Error Contract (houki-hub family)
Since v0.6.0, this MCP returns structured errors that follow the houki-hub family error contract, sharing a unified code vocabulary across the family. Combined with houki-egov-mcp / houki-nta-mcp, an LLM or Skill layer can interpret errors with consistent logic.
docs/ERROR-CODES.md— error code vocabulary (houki-research-skill)docs/ERROR-HANDLING.md— handling policy / next_actions templates
Implementation is independent — no dependency on houki-abbreviations or other family packages. The reference implementation is houki-egov-mcp/src/errors.ts; pdf-reader-mcp's local definition is in src/errors.ts.
On error, every tool returns isError: true and the JSON-stringified LawServiceError in content[0].text:
Every error carries error (an English sentence), code and retryable; hint, next_actions and detail are optional, and nothing else appears at the top level (PDF family contract, pdf-agent-stack docs/specs/06 §2.3, since v0.19.0). retryable is fixed per code: it says whether changing the arguments, flags or environment could make the same reading of the same document succeed. The values of next_actions[].action are stable — a Skill may branch on them.
Codes used by pdf-reader-mcp
Migration note (v0.5.x → v0.6.0)
旧 v0.5.x までは content[0].text に Error: ...\n\nSuggestion: ... という人間可読文字列を入れていました。v0.6.0 では同じ場所に JSON 文字列 が入ります。LLM 側でテキスト解釈に依存していた場合は、JSON.parse(content[0].text) での解釈に切り替えてください。isError: true フラグで構造化エラーかどうかを判定できます。
Pairing with pdf-spec-mcp
pdf-spec-mcp provides PDF specification knowledge (ISO 32000-2, etc.). With both servers enabled, an LLM can perform specification-aware workflows:
summarize— get a PDF overviewinspect_tags— examine the tag structure- pdf-spec-mcp
get_requirements— fetch PDF/UA requirements - pdf-verify-mcp
validate_conformance— check conformance (flavour: "pdfua-1") compare_structure— diff before/after fixes
License
MIT
來源:README.md,提交 ebffee0
工具
0版本歷史
9- v0.22.0最新Nov 7, 2026
- v0.21.0Nov 6, 2026
- v0.20.1Nov 5, 2026
- v0.19.0Nov 5, 2026
- v0.18.1Nov 3, 2026
- v0.17.0Oct 15, 2026
- v0.16.1Oct 15, 2026
- v0.16.0Oct 12, 2026
- v0.15.5Sep 19, 2026
評分
已滿足適用檢查項的 50%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
- 近 30 天內可連通25 分
請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。
- 已宣告認證方式10 分
請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。
- 已列出工具15 分
請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。
- 描述必需15 分
- 圖示10 分
- 文件15 分
- 授權條款10 分