PDF SPEC MCP Server
[CI]
[npm version]
日本語版 README はこちら
An MCP (Model Context Protocol) server that provides structured access to ISO 32000 (PDF) specification documents. Enables LLMs to navigate, search, and analyze PDF specifications through well-defined tools.
[!IMPORTANT]
This is a specification reference, not a rule engine.
It retrieves and structures the text of ISO 32000 — clauses, tables, definitions, and
shall/should/may requirements. It does not examine a PDF file, and it cannot tell
you whether a document conforms to anything. Conformance verdicts come from
pdf-verify-mcp
(validate_conformance / evaluate_policy).
The distinction matters because three different things get conflated:
declaration — a label the file wrote about itself ("I am PDF/A" in the metadata). Writing it is not evidence /
conformance — whether the file actually meets the standard. There is no way to prove it in full; you can only find where it breaks the rules /
validation — what a validator (veraPDF and the like) reports against the checks it implements. A pass means "this inspection did not fail", not "the file conforms to the standard".
Reading a shall here tells you what the standard requires — not whether your file meets it.
A search that returns nothing means "cannot answer", not "no such requirement."
ISO 19005 (PDF/A) and ETSI PAdES are outside this corpus; see list_specs → coverage.gaps.
What each PDF family server does — and does not do
[!IMPORTANT]
PDF specification files are NOT included in this package.
You must obtain the PDF specification documents separately and place them in a local directory.
Download from: PDF Association — Sponsored Standards
See "Setup" for details.
Features
- Multi-spec support — Auto-discovers and manages up to 17 PDF-related documents (ISO 32000-2, PDF/UA, Tagged PDF guides, etc.)
- Structured content extraction — Headings, paragraphs, lists, tables, and notes from any section
- Full-text search — Keyword search with section-aware context snippets
- Requirements extraction — Extracts normative language (shall / must / may) per ISO conventions
- Definitions lookup — Term definitions from the "Terms and definitions" clause (clause 3 of ISO 32000-2, clause 4 of ISO 32000-1)
- Table extraction — Multi-page table detection with header merging
- Version comparison — Diff PDF 1.7 vs PDF 2.0 section structures
- Bounded-concurrency processing — Parallel page processing for large documents
- On-disk index cache — The search index and the full requirements scan are built once per PDF and reused by every later process (ISO 32000-2: ~6 s → ~0.2 s)
Architecture
Layer Overview
Setup
1. Obtain PDF Specification Files
[!WARNING]
PDF specifications are copyrighted documents and are not included in this package.
Download them from the sources below and place them in a local directory.
All 17 files below are supported. You do not need all of them — place only the specs you need (at minimum, ISO 32000-2 is recommended).
2. Install
This package ships a CLI binary (pdf-spec-mcp) intended to be launched by an MCP client.
You do not need to install it manually — just point your MCP client to npx @shuji-bonji/pdf-spec-mcp@latest as shown in the next step.
If you want to run it directly from the shell (e.g. for debugging):
Or install it globally (optional):
3. Configure MCP Client
Environment Variable
Claude Desktop
Add to claude_desktop_config.json:
[!IMPORTANT]
Use @latest (or pin a version). npx -y <pkg> without a version keeps running whatever
it cached the first time — -y only skips the install prompt, it does not check for updates.
A bare specifier will happily run a months-old release. @latest makes npx check the registry
on each start; pin @0.4.0 instead if you want reproducibility.
To clear a stale cache: rm -rf ~/.npm/_npx.
Cursor / VS Code
Add to .cursor/mcp.json or VS Code MCP settings:
Installed as a Claude Code plugin
The plugin manifest (.claude-plugin/plugin.json) declares "PDF_SPEC_DIR": "${PDF_SPEC_DIR}"
and relies on the host to substitute the reference. Not every host does:
When the reference is passed through verbatim, every tool answers with REGISTRY_ERROR whose
message starts with PDF_SPEC_DIR is the unexpanded text "${PDF_SPEC_DIR}" and whose first
next_actions entry is set_absolute_path_in_host_config. That is a host-configuration problem,
not a missing directory — write the absolute path into the host's own configuration and restart.
Host-by-host steps: https://shuji-bonji.github.io/pdf-agent-stack/reference/env-vars.
Index cache
Two operations walk every page of a specification: the first search_spec on a spec builds
its full-text index (ISO 32000-2, 1023 pages: about 6 s on a laptop), and get_requirements
without a section scans every section (about 11 s). Everything else opens only the pages it
needs and answers in well under a second.
Since 0.5.0 those two results are written to disk after the first build and read back by every
later process — an MCP client that starts one server per session no longer pays the build each
time. The second process answers the same search_spec in about 0.2 s and the full
requirements scan in about 0.02 s, from byte-for-byte the same index.
- Location:
${PDF_SPEC_CACHE_DIR:-${XDG_CACHE_HOME:-~/.cache}/pdf-spec-mcp}/v1/<version>/<spec>.<kind>.<sha256[0:16]>.json.
The whole 17-spec corpus is about 18 MB per package version.
- Key: package version,
pdfjs-dist version, spec id, and the SHA-256 of the PDF. A
replaced PDF, an upgraded server, or an upgraded pdfjs all miss and rebuild. Entries of
older versions are left in place (another install may still use them); --clear-cache
removes everything.
- Failure is a miss, never an error: an unreadable, truncated, or foreign file is rebuilt;
an unwritable directory is reported once on stderr and the server carries on without a cache.
- It is derived from your copy of the PDFs and stays on your machine. It is not part of
the package and must not be redistributed — the specifications are copyrighted.
Nothing about searching changes: the same in-memory structure is searched by the same code.
Only where it comes from (built vs. read) does.
Pre-building the cache
The cache fills lazily, one spec at a time as tools touch it. To warm every spec up front — after
installing, after upgrading, or from cron — run the CLI (it uses the same code path as the tools,
processes specs sequentially, and exits):
A full build of the 17-spec corpus takes about a minute on a laptop.
Available Tools
All tools accept an optional spec parameter to target a specific specification (default: iso32000-2).
list_specs — Discover Specifications
List all available specification documents. Use the returned IDs as the spec parameter in other tools.
get_structure — Table of Contents
Get the section hierarchy (TOC tree) of a specification.
get_section — Section Content
Get structured content (headings, paragraphs, lists, tables, notes) of a specific section.
A parent section returns its entire subtree (its preamble followed by all subsections, in document order). Top-level clauses can be very large — prefer the most specific section number.
Every answer carries pageRange and boundary. boundary says how each end of the content was cut:
Anything other than heading / heading, heading / page or heading / document means the cut fell on a page edge: read the neighbouring section too before relying on the content. For a parent section, start is the parent's own and end is its last descendant's.
search_spec — Full-text Search
Search across a specification with section-aware context snippets. The first call on a spec
builds its index (a few seconds); the index is then cached on disk (see Index cache).
get_requirements — Normative Requirements
Extract normative requirements (shall / must / may) per ISO conventions.
sectionsCutByPage lists the sections in scope whose content was cut at a page edge (see boundary under get_section); their requirements may include a neighbour's sentences or miss the tail. It is empty when every section in scope was cut at headings.
get_definitions — Term Definitions
Look up term definitions from the "Terms and definitions" clause — clause 3 of ISO 32000-2 (71 terms), clause 4 of ISO 32000-1 / pdf17 (48 terms). section in each result is the number printed in the document (3.12, 4.11).
get_tables — Table Extraction
Extract table structures (headers, rows, captions) from a section. Multi-page tables are automatically merged.
The answer carries pageRange (the pages the tables were read from) and boundary (see get_section). totalTables: 0 means no Table element was found in those pages — with a page-cut boundary, the pages themselves may not be the whole section.
compare_versions — Version Comparison
Compare section structures between PDF 1.7 (ISO 32000-1) and PDF 2.0 (ISO 32000-2). Uses title-based automatic matching to detect matched, added, and removed sections.
[!NOTE]
This tool requires both PDF 1.7 (PDF32000_2008.pdf) and PDF 2.0 files in PDF_SPEC_DIR.
Supported Specifications
The server auto-discovers PDF files in PDF_SPEC_DIR by filename pattern matching:
Directory Structure
Development
License
MIT