关于 Pdf Reader Mcp Server
Pdf Reader Mcp Server 是用于读取和分析 PDF 内部结构的 MCP 服务,面向需要检查文档内容、结构与可提取性的场景。它提供只读工具,并可检查标签结构、对象、签名字段及结构差异。
主要功能
- 01提取文本并报告逐页可提取性
- 02检查目录、对象树、标签和字体
- 03读取图像并渲染页面
- 04提取逻辑顺序文本与表格
- 05检查注释和签名字段结构
- 06比较两个 PDF 的结构
适用场景
- 01定位扫描件或不可提取文本的页面
- 02检查带标签 PDF 的逻辑阅读顺序
- 03比较 PDF 版本之间的结构变化
MCP 相关
向 MCP 客户端提供 18 个只读 PDF 工具,涵盖文本、图像、结构检查和比较。远程 URL 读取仅返回文本;其他操作需使用本地文件路径。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
19收录的工具列表 · 更新于 3 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| file_path | string | 是 | 本地 PDF 文件的绝对路径(例如,"/path/to/document.pdf") |
详细介绍
PDF Reader MCP 服务
English | 日本語
专门用于解析 PDF 内部结构的 MCP(模型上下文协议)服务。
常见的 PDF MCP 服务只是文本提取的薄封装,而本项目专注于读取和分析 PDF 文档的内部结构。搭配 pdf-spec-mcp 使用,可进行了解规范的结构分析与验证。
PDF 系列
| 服务 | 作用 |
|---|---|
| pdf-spec-mcp | PDF 规范知识(ISO 32000、PDF/A、PDF/UA) |
| pdf-reader-mcp(本项目) | 读取和检查 PDF 内部结构——PDF 包含什么 |
| pdf-verify-mcp | 真实性验证——是否真实可信:加密签名验证、防篡改检测、PAdES 级别、PDF/A 验证、加密 PDF 解密 |
pdf-reader-mcp 检查签名结构(inspect_signatures);若要进行加密签名验证、信任/吊销评估和 PDF/A 一致性验证,请使用 pdf-verify-mcp。
功能
18 个工具分为三个层级:
第 1 层:基本操作
| 工具 | 描述 |
|---|---|
get_page_count | 轻量级获取页数 |
get_metadata | 提取完整元数据(标题、作者、PDF 版本……) |
read_text | 按 Y 坐标顺序提取文本(可选启用 split_columns: 2 | 3,用于未标记的多栏 PDF;compact_whitespace 适用于日文表单)。解析 /ActualText 替换内容(§14.9.4,涵盖结构元素路径和 Span 标记内容路径)。按页报告文本可提取性(§9.10.1),因此不会把空结果误认为空白页面。对于带标签 PDF 的逻辑顺序,优先使用 extract_structured_text |
search_text | 全文搜索并提供上下文。搜索内容与 read_text 返回的文本相同,也包括 /ActualText,因此搜索命中对应读者实际看到的内容(若有标记内容无法对齐,note 会指出相关页面) |
read_images | 将嵌入的图像 XObject 作为 PNG 或 JPEG 文件返回,以 MCP 图像内容块的形式提供,供视觉模型读取。max_width / max_height 按面积平均值缩小图像;响应有字节数限制,并会列出未返回的图像 |
read_url | 获取远程 PDF 并提取其中的文本——仅此而已。不会保存文件字节;若要对 URL 对应的 PDF 使用其他 17 个工具,请先下载文件并传入本地路径(参见“read_url 与只读边界”) |
render_page | 通过 PDFium-WASM(可选依赖 @hyzyla/pdfium)将页面栅格化为 PNG/JPEG。当文本可提取性显示 no_text_layer / not_extractable 时可使用此工具——绘制完整页面,包括矢量图和表单 |
summarize | 快速概览报告(元数据 + 文本 + 图像数量 + 文档级文本可提取性) |
第 2 层:结构检查
| 工具 | 描述 |
|---|---|
inspect_structure | 对象树和目录字典分析 |
inspect_catalog | 根据目录检查文档级清单(ISO 32000-2 §7.7.2):书签(/Outlines)、图层(/OCProperties)、附件(/EmbeddedFiles,含 /AFRelationship)、包含已声明 pdfaid / pdfuaid / W3C TDMRep(tdm:reservation)值的 XMP 数据包,以及打开文档时会执行的内容(/OpenAction、/AA、文档级 JavaScript)。每项都标记为 absent 或 unreadable,绝不混为一谈。报告中按原样呈现声明,不作验证 |
inspect_tags | 带标签 PDF 结构树可视化 |
inspect_fonts | 字体清单(嵌入/子集/类型检测) |
inspect_annotations | 注释列表(按子类型分类) |
inspect_signatures | 数字签名字段结构分析 |
extract_structured_text | 按逻辑内容顺序提取带标签 PDF 文本(ISO 32000-2 §14.8.2.5),并为每段标注结构类型(H1 / P / Table …)。解析 /ActualText,分离 /Alt 和列表标签,并保持跨页元素完整。include_bbox: true 会补充每个元素的绘制位置——每页一个矩形,格式与 pdf-writer-mcp 的 add_annotation 所需格式相同 |
extract_tables | 将带标签 PDF 的 <Table> 子树转换为 Markdown 表格(保留列)。跨越分页符的表格仍作为一个表格(pages 数组) |
locate_objects | 将对象编号映射到页面和矩形,使用 pdf-writer-mcp 的 add_annotation 所需的坐标格式。将 pdf-verify-mcp 的 verify_integrity 所报告的“哪些对象发生了变化”对应到“它们位于何处”。每个位置都会注明其 basis:注释自身的 /Rect 是精确位置,内容流只能指出“整页” |
第 3 层:比较
| 工具 | 描述 |
|---|---|
compare_structure | 两个 PDF 之间的结构差异(属性 + 字体) |
validate_tagged 和 validate_metadata 已移除(自 0.7.0 起弃用)。此服务只报告 PDF 包含的内容,不会根据标准返回通过/失败结果。要验证 PDF/UA 或 PDF/A 合规性,请使用 pdf-verify-mcp 的 validate_conformance(flavour: "pdfua-1"、"pdfa-*" 等)。要查看这些工具过去会检查的信息,请使用这里的 inspect_tags(结构树)和 get_metadata(元数据字段)。
read_url 与只读边界(#25)
read_url 只返回文本。现在明确说明这一设计决定,而不再只是暗示:提取后会丢弃获取到的字节,因为保存这些字节会让读取工具写入文件系统,而此服务中的每个工具都是只读的(readOnlyHint: true——共 18 个)。
若要对位于 URL 的 PDF 使用 search_text、inspect_structure、extract_tables、render_page 或其他工具,请先下载文件——使用调用环境具备的任何获取能力——再传入本地路径。获取文件由调用方负责,这是有意为之:Agent 环境总有办法下载文件,而会写入文件的读取器便不再是纯粹的观察者。
对于一次性询问“这份 URL 上的文档写了什么?”,read_url 仍是合适的工具。
文本无法读取时仍可渲染页面 (#23)
以前,summarize 报告 hasText: false 就意味着无法继续处理:此服务中的其他功能都无法再读取该文档。render_page 解决了这个问题——它会将页面栅格化为 PNG 或 JPEG,并以 MCP 图像内容块的形式返回,让视觉模型能够读取扫描件、图表、填写好的表单或手写内容。
render_page({ file_path: "/path/to/scan.pdf", pages: "1-3", format: "jpeg" })
必须指定 pages:渲染是此处开销最大的操作,因此渲染 500 页扫描件的“所有页面”应该由用户决定,而不是默认行为。此功能也受与 read_images 相同的 4 MB 响应大小限制,并会列出未包含的内容。
渲染由编译为 WebAssembly 的 PDFium(@hyzyla/pdfium,可选依赖项)执行。WASM 二进制文件在所有平台上都是相同的字节,因此发布的程序包无论通过 npx 在何处运行,行为都一致——这就是此处不使用原生插件的原因。若未安装该依赖项,render_page 会说明需要安装什么,其他工具仍可正常工作。PDFium(BSD-3-Clause)与此服务用于读取文本的 pdf.js 是不同的引擎;工具描述会说明这一点,因为引擎之间的渲染差异不应归咎于文件。
选择该方案前进行过测试:pdf.js +
@napi-rs/canvas(1.0.7 和 0.1.80)在绘制图像的页面上会导致整个进程发生段错误——而这恰好是此工具要处理的页面;如果未配置standardFontDataUrl,还会将页面渲染为空白。
图像以图像文件的形式返回 (#22)
以前,read_images 会对 imgData.data 进行 base64 编码——它是 pdfjs 的解码后像素。一张 8×8 的 RGB 图像只有 192 字节,其中完全没有 PNG 或 JPEG 签名,因此任何查看器都无法打开结果,视觉模型也无法读取;而让视觉模型读取图像,正是提取图像的初衷。
现在,图像会先编码(默认使用无损的 PNG;需要更小文件时可使用 format: "jpeg" 和 quality),再以 MCP image 内容块的形式返回,元数据则一并放在文本块中。此处使用的两种编码器都是纯代码实现——无需原生插件,也无需针对各平台的二进制文件。
编码后的图像数据响应大小上限为 4 MB。一张 200 dpi 的 A4 扫描件仅像素数据就约为 11.6 MB,因此超出上限的图像会标明名称及原因,而不会被直接丢弃:
read_images({ file_path: "/path/to/scan.pdf", pages: "1", max_width: 1200, format: "jpeg" })
read_images 会返回页面绘制的图像 XObject。它不是页面截图——矢量图和文本不在其处理范围内。
文本可提取性——三种状态,而非两种 (#21)
以前,read_text 要么返回文本,要么什么都不返回,而“什么都不返回”可能有三种不同含义。ISO 32000-2 §9.10.1 对这些情况作了区分,因此此服务也会区分。每个返回文本的工具——read_text、read_url、search_text、extract_structured_text、summarize——都会按页面报告:
| 状态 | 条件 | 后续操作 |
|---|---|---|
extracted | 使用的每种字体都能根据 §9.10.2 映射到 Unicode | 使用提取出的文本 |
no_text_layer | 没有文本显示运算符(Tj TJ ' "),但存在图像内容 | 页面内容是像素。需要 OCR 或渲染后的图像;此服务两者都不提供 |
not_extractable | 使用的某种字体没有 /ToUnicode、标准编码或已知的 CID 字符集 | 文本缺失或有误。报告会列出字体和相关条款 |
not_observed | 文件已加密,或无法读取内容流 | 未进行任何测量。这不等于“其中什么都没有” |
not_extractable 会按字体报告,因此页面同时使用可读取字体和不可读取字体时,会明确说明这是部分缺失,而不会误报为完整提取。
此项检查直接从文件中进行,而非依据 pdf.js 的输出:pdf.js 会为加载的每种字体合成 toUnicode 映射,因此询问它某种字体是否有 /ToUnicode CMap 时,即使字体字典中没有,也会得到肯定答复。
安装
npx(推荐)
npx @shuji-bonji/pdf-reader-mcp@latest
Claude Desktop
添加到 claude_desktop_config.json:
{
"mcpServers": {
"pdf-reader-mcp": {
"command": "npx",
"args": ["-y", "@shuji-bonji/pdf-reader-mcp@latest"]
}
}
}
使用
@latest。npx -y <pkg>中的-y标志只会跳过安装提示, 不会检查更新。如果不使用@latest,npx 会继续运行它首次缓存的版本, 因此新版本永远不会送达。如果怀疑当前使用的是过时版本,请运行rm -rf ~/.npm/_npx并重启客户端。
Claude Code
claude mcp add pdf-reader-mcp -- npx -y @shuji-bonji/pdf-reader-mcp@latest
从源代码构建
git clone https://github.com/shuji-bonji/pdf-reader-mcp.git
cd pdf-reader-mcp
npm install
npm run build
使用示例
获取页数
get_page_count({ file_path: "/path/to/document.pdf" })
→ 42
搜索文本
search_text({
file_path: "/path/to/spec.pdf",
query: "digital signature",
pages: "1-20",
max_results: 10
})
→ Found 5 matches (page 3, 7, 12, 15, 18)
摘要
summarize({ file_path: "/path/to/document.pdf" })
→ | Pages | 42 |
| PDF Version | 2.0 |
| Tagged | Yes |
| Signatures | No |
| Images | 15 |
比较结构
compare_structure({
file_path_1: "/path/to/v1.pdf",
file_path_2: "/path/to/v2.pdf"
})
→ | Page Count | 10 | 12 | ❌ |
| PDF Version | 1.7 | 2.0 | ❌ |
| Tagged | true | true | ✅ |
提取结构化文本(带标签的 PDF,逻辑顺序)
extract_structured_text({ file_path: "/path/to/report.pdf", pages: "1-2" })
→ # Structured Text
- **Tagged**: Yes / **Language**: en-US / **Elements**: 7
## Logical Content Order
- **Document** (pages 1–2)
- **H1** (page 1) — Quarterly Report
- **P** (pages 1–2) — This paragraph begins on page one and continues on page two.
- **Table** (page 1)
| Item | Amount |
|---|---|
| Sales | 100 |
- **Figure** (page 2) — *alt:* A bar chart of sales
这可以回答“H1 的文本是什么?”——而 read_text(扁平的坐标顺序)无法做到。顺序是对结构树进行深度优先遍历的结果(ISO 32000-2 §14.8.2.5)。/ActualText 会替换字形(§14.9.4),/Alt 不会包含在正文文本中(§14.9.3),列表标签会单独报告,跨越分页符的元素仍算作一个元素。使用 roles: ["H1", "H2"] 提取大纲。未带标签的 PDF 会返回 isTagged: false 和原因——不会根据坐标进行猜测。
include_bbox:元素的绘制位置
extract_structured_text({ file_path: "/doc.pdf", roles: ["P"], include_bbox: true })
→ - **P** (page 1) — Measured paragraph
- *bbox* p1 `(50.0, 297.5, 161.4, 308.6)` — text-extent
- **Figure** (page 1)
- *bbox* p1 `(50.0, 150.0, 110.0, 190.0)` — layout-attribute-bbox
矩形采用 PDF 默认用户空间(左下角为原点,单位为 pt,已归一化)——这正是 pdf-writer-mcp 的 add_annotation 所需的格式,因此“为这段文字添加注释”无需进行坐标转换。/Rotate 和偏移的 /CropBox 不会改变矩形的位置。
basis 表示该结论的可靠程度,两种依据的性质不同:
basis | 含义 |
|---|---|
layout-attribute-bbox | 文件声明的 /BBox(ISO 32000-2 表 379),通过 /A 或 /C + /ClassMap 读取。这是制作者给出的声明,原样报告——也是没有文本的内容唯一可用的来源 |
text-extent | 根据元素文本测量得出:基线原点加上字体的上升部/下降部。这是行框,不是字形轮廓。图像和矢量图不计入 |
跨页元素会在每页各有一个矩形——将它们合并会导致内容被标到它实际所在页面之外。没有矩形的元素会通过 boxNote 说明原因,而不会返回零尺寸矩形。
声明会原样报告,并进行交叉核验。 文件中的声明可能毫无意义:Well-Tagged PDF 1.0 和 Tagged PDF Best Practice Guide 两者封面上的 Figure 都声明了 /BBox [-32768 -32768 32767 32767]——这是本应为矩形的地方出现的 int16 哨兵值;而 PDF32000_2008 的 545 个声明中,有 131 个超出了页面边缘。由于此输出旨在直接用于 add_annotation,因此会根据页面框(§7.7.3.3)和元素自身文本对声明进行核验;若有任一矛盾,会在 boxNote 中报告,同时仍原样返回该矩形。
与独立的真实数据对比测量:在 Well-Tagged PDF (WTPDF) 1.0 中,将 166 个 Link 结构元素与制作者为相同链接设置的 173 个 Link 注释 /Rect 值进行比较——IoU 中位数为 0.972,没有任何一个互不相交。
提取表格(带标签的 PDF)
extract_tables({ file_path: "/path/to/kaisei-tsutatsu.pdf", pages: "1" })
→ # Extracted Tables
- **Tagged**: Yes / **Pages Scanned**: 1 / **Tables Found**: 1
## Table 1 — Page 1
| 改正後 | 改正前 |
| --- | --- |
| …第2条第 16 項《定義》… | …第2条第 15 項《定義》… |
跨越分页符的表格会作为一个表格报告——pages 是一个数组(例如 ## Table 3 — Pages 5–7),只要表格与请求的 pages 范围有交集,就会返回完整表格。单元格文本会遵循 /ActualText 替换规则(与自 #18 起的 read_text 和 search_text 一样)。未带标签的 PDF 会返回空结果,并在 note 中建议使用下方的按列回退方案。
读取未带标签的多栏 PDF
read_text({ file_path: "/path/to/older-shinkyu.pdf", split_columns: 2 })
→ // Plain Y-sort would interleave columns:
// "改正後セル1 改正前セル1\n 改正後セル2 改正前セル2..."
//
// With split_columns: 2 the left column is emitted first, then the right:
// "改正後セル1\n改正後セル2\n…\n\n改正前セル1\n改正前セル2\n…"
对于未带标签的多栏 PDF,使用 split_columns: 2 | 3。对于带有正确 <Table> 标记的带标签 PDF,优先使用上方的 extract_tables。
紧凑空白(日本表格)
read_text({ file_path: "/path/to/form.pdf", compact_whitespace: true })
→ // Original PDF uses U+3000 fullwidth space as visual indentation:
// " ( ) 自 年 月 日 法 有 ( 年 月 日) 有 有"
//
// With compact_whitespace: true:
// "( ) 自 年 月 日 法 有 ( 年 月 日) 有 有"
//
// Empirically reduces character count by ~40% on form PDFs.
compact_whitespace 与 split_columns 相互独立——两者可以结合使用。
技术栈
- TypeScript + MCP TypeScript SDK
- pdfjs-dist(Mozilla)——文本/图像提取、标签树、注释
- normativepdf + @normativepdf/recover —— COS 对象访问,读取交叉引用表不符合 ISO 32000-2 §7.5 的文件
- Vitest ——单元测试 + E2E 测试(483 项测试)
- Biome ——代码检查 + 格式化
- Zod ——输入验证
测试
npm test # Run all tests (unit + E2E: 483 tests)
npm run test:e2e # E2E tests only (283 tests)
npm run test:watch # Watch mode
架构
pdf-reader-mcp/
├── src/
│ ├── index.ts # MCP Server entry point
│ ├── constants.ts # Shared constants
│ ├── types.ts # Type definitions
│ ├── tools/
│ │ ├── tier1/ # Basic tools (7)
│ │ ├── tier2/ # Structure inspection (6)
│ │ ├── tier3/ # Validation & analysis (3)
│ │ └── index.ts # Tool registration
│ ├── services/
│ │ ├── pdfjs-service.ts # pdfjs-dist wrapper (parallel page processing)
│ │ ├── recover-service.ts # @normativepdf/recover — opening a document,
│ │ │ # and how far it could be read (DocumentScope)
│ │ ├── structure-service.ts # Catalog, page tree, object statistics
│ │ ├── font-service.ts # Fonts named in each page's /Resources
│ │ ├── signature-service.ts # Signature fields of the AcroForm (structure only)
│ │ ├── content-stream-service.ts # Marked-content operators, text-showing tally
│ │ ├── struct-tree-service.ts # Logical structure (tags, structured text)
│ │ ├── validation-service.ts # Validation & comparison logic
│ │ └── url-fetcher.ts # URL fetching
│ ├── schemas/ # Zod validation schemas
│ └── utils/
│ ├── pdf-helpers.ts # PDF utilities (page range parsing, file I/O)
│ ├── batch-processor.ts # Batch processing for large PDFs
│ ├── formatter.ts # Output formatting
│ └── error-handler.ts # Error handling
└── tests/
├── tier1/ # Unit tests
└── e2e/ # E2E tests (16 suites, 283 tests)
错误契约(houki-hub 系列)
自 v0.6.0 起,此 MCP 会返回遵循 houki-hub 系列错误契约的结构化错误,在整个系列中使用统一的 code 词汇表。与 houki-egov-mcp / houki-nta-mcp 结合使用时,大模型或 Skill 层可以通过一致的逻辑解读错误。
docs/ERROR-CODES.md——错误代码词汇表(houki-research-skill)docs/ERROR-HANDLING.md——处理策略 / next_actions 模板
实现相互独立——不依赖 houki-abbreviations 或其他系列软件包。参考实现为 houki-egov-mcp/src/errors.ts;pdf-reader-mcp 的本地定义位于 src/errors.ts。
发生错误时,每个工具都会返回 isError: true,并在 content[0].text 中返回 JSON 序列化的 LawServiceError:
{
"error": "The file does not appear to be a valid PDF.",
"code": "INVALID_PDF",
"hint": "ファイルが破損していないか確認してください。",
"next_actions": [
{
"action": "inspect_structure",
"reason": "PDF が壊れている可能性があります。Catalog / Pages 等の構造を確認してください"
}
],
"detail": { "cause": "Invalid PDF structure" }
}
pdf-reader-mcp 使用的代码
| code | 用途 |
|---|---|
INVALID_ARGUMENT | 客户端参数无效,例如路径、URL、页面范围 |
DOC_NOT_FOUND | 文件不存在 (ENOENT) |
INVALID_PDF | PDF 无效或已损坏 |
ENCRYPTED_PDF | PDF 已加密(目前不支持) |
UNSUPPORTED_PDF_FEATURE | 不支持的 PDF 功能 |
FILE_TOO_LARGE | 超过 50MB 上限(pdf-reader 特有) |
SOURCE_API_ERROR | URL 获取时发生 HTTP 错误 (4xx/5xx) |
SOURCE_TIMEOUT | 远程获取超时 |
SOURCE_UNAVAILABLE | DNS / 连接失败 |
INTERNAL_ERROR | 包括权限拒绝在内的其他错误 |
迁移说明(v0.5.x → v0.6.0)
旧版 v0.5.x 会在 content[0].text 中填入 Error: ...\n\nSuggestion: ... 这种人类可读字符串。v0.6.0 会在同一位置填入 JSON 字符串。如果大模型侧依赖文本解读,请改用 JSON.parse(content[0].text) 进行解析。可以通过 isError: true 标记判断是否为结构化错误。
与 pdf-spec-mcp 配合使用
pdf-spec-mcp 提供 PDF 规范知识(ISO 32000-2 等)。同时启用这两个服务后,大模型可以执行了解规范的工作流:
summarize——获取 PDF 概览inspect_tags——检查标签结构- pdf-spec-mcp
get_requirements——获取 PDF/UA 要求 - pdf-verify-mcp
validate_conformance——检查符合性(flavour: "pdfua-1") compare_structure——比较修复前后的差异
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。