Skip to main content
Glama
EthanCheung-gh

PDF Inspector MCP

PDF Inspector MCP

把 pdf-inspector 的 PDF 结构解析与导出能力,以 Model Context Protocol 工具的形式提供给任意 MCP 客户端(Claude Desktop、IDE 插件、自研 Agent 等)。

服务本身没有界面:客户端传入 PDF 路径,就能拿到与上游 Inspector 一致的对象级数据——字符、单词、矩形、线段、曲线、图片、表格——以及字体统计和 JSON/CSV 导出。

与上游的关系

本项目的解析与导出逻辑是 pdf-inspector v0.2.0 的内联移植,不是重新实现:

本仓库文件

上游来源

改动

src/pdf_inspector_mcp/extractor.py

sidecar/extractor.py

逐行移植;额外加了 page_count() 与 extract_pages() 两个方法(工具层需要,侧车原本由请求循环完成)

src/pdf_inspector_mcp/objects.py

src/renderer/types/pdf-objects.ts

TypeScript 类型改为 TypedDict / Literal,字段名与顺序保持一致

src/pdf_inspector_mcp/export.py

src/renderer/services/export-data.ts

逐行移植;JSON 结构、CSV 分段、文件名规则与上游一致

移植时做的行为收紧(上游是给渲染进程用的,这里要直接面对任意客户端输入,坏输入不能变成崩溃或错误数据):

场景

上游

本仓库

metadata.version

直接读 pdfminer 的 pdf_version 属性(新版 pdfplumber 已移除该属性,上游路径会拿到空值)

真实版本号:pdfplumber 属性 → 文件头 %PDF-x.y(在前 1024 字节内搜索,容忍头部前的垃圾字节)→ unknown(仅当文件头确实不可读)

extract_page(0) / 负数页

当作负索引,返回末页等错误数据

抛 ValueError(Invalid page number: 0 (document has N pages))

get_image_data('p9_img_0')(页越界)

抛 IndexError

返回 {'error': 'Invalid stream_id: ...'}

解析失败(打开与提取两阶段)

侧车对任何异常都回传 str(e)(sidecar/main.py),客户端能读到原始原因

已知的 pdfminer/pdfplumber 解析异常同样转成带原因的用户错误:打开阶段 Cannot open '<path>': ...,提取阶段 Failed to read '<path>': <原因>;只有未预期的内部错误才退回通用提示(traceback 留在日志)

src/pdf_inspector_mcp/document.py(文档会话缓存)与 src/pdf_inspector_mcp/server.py(MCP 工具层)是本项目新增的部分——上游是 Electron/IPC,这里换成 MCP 的 stdio 传输。

由于是内联移植,上游的字段命名、坐标语义、错误文案与导出格式就是本项目的兼容基线;两者不会各自漂移。

未移植:上游的页级缓存

上游侧车的 sidecar/cache.py 有一个页级 LRU 缓存(缓存已提取的页面数据),本项目没有移植:

  • pdf_extract_page 对同一页重复调用会重新执行版面分析(pdfplumber 的 char/word 提取),不会命中页级缓存;

  • 跨页、跨工具复用靠的是本项目的 DocumentManager:它按 (绝对路径, password, mtime_ns, size) 缓存已打开的 PDF 句柄(不是页数据),因此重复调用省掉的是"打开/解析文档结构"这一步,而不是"提取页对象"这一步;

  • 唯一的内置记忆化是整档 pdf_analyze_fonts 的字体统计结果(按文档缓存,随文件变化而失效)。

需要反复读取同一批页面的客户端,应把结果自行缓存,或一次性用 pdf_export 导出。

Related MCP server: MCP PDF

环境要求

  • Python ≥ 3.10

  • 依赖:mcp ≥ 2.3、pdfplumber ≥ 0.11、Pillow ≥ 10(见 pyproject.toml)

  • 运行测试另需 pytest、reportlab

安装

python3 -m venv .venv
.venv/bin/pip install -e .

# 运行测试时
.venv/bin/pip install -e ".[dev]"

Windows 下把 .venv/bin/python 换成 .venv\Scripts\python,pdf-inspector-mcp 对应 .venv\Scripts\pdf-inspector-mcp.exe。

安装后会得到两个等价的入口:

.venv/bin/python -m pdf_inspector_mcp.server   # 以模块方式启动
.venv/bin/pdf-inspector-mcp                    # console script

两者都通过 stdio 传输与客户端通信,启动后不打印任何内容,只等待 JSON-RPC 消息。

在 MCP 客户端中配置

本服务是 stdio 服务器:客户端负责启动子进程,并通过 stdin/stdout 收发消息。把下面的配置加入 MCP 客户端的配置文件(例如 Claude Desktop 的 claude_desktop_config.json),把路径换成你自己的绝对路径:

{
  "mcpServers": {
    "pdf-inspector": {
      "command": "/absolute/path/to/pdf-inspector-mcp/.venv/bin/python",
      "args": ["-m", "pdf_inspector_mcp.server"]
    }
  }
}

也可以直接指向 console script(此时 args 留空):

{
  "mcpServers": {
    "pdf-inspector": {
      "command": "/absolute/path/to/pdf-inspector-mcp/.venv/bin/pdf-inspector-mcp",
      "args": []
    }
  }
}

注意事项:

  • command 必须是该虚拟环境里的解释器(或 console script)。用系统 python 会因为找不到 pdf_inspector_mcp 而启动失败。

  • 相对路径参数以服务器进程的工作目录为基准解析;多数客户端不保证工作目录,建议传绝对路径。

  • 想限制缓存文档数,可在配置里加 "env": {"PDF_INSPECTOR_MCP_MAX_DOCS": "4"}(默认 8,非法值回退 8)。

工具一览

六个工具全部是同步工具,返回 JSON 可序列化的对象;password 均为可选参数,仅加密文档需要。page、first_page、last_page、start_page、end_page 都是 1-based。

参数、路径或文档本身不可用时,工具以 isError=true 的结果返回一条可读的错误信息(例如 Invalid format: 'xml' (expected one of 'json', 'csv')),不会把 traceback 塞进回复;调用方可以据此修正参数后重试。四类常见失败的错误文案:

情况

消息特征

参数/路径非法

Invalid format: ...、Invalid PDF path: '<path>' (... does not exist)、Invalid page number: ...

加密文档缺少或输错口令

含 password required or incorrect (pass the 'password' argument for encrypted PDFs),并以 Underlying error: PDFPasswordIncorrect 结尾

不是 PDF / 文件损坏(打开失败)

含 not a valid PDF,并以原始解析错误结尾(如 No /Root object! - Is this really a PDF?)

结构可读、但页内容流损坏(提取失败)

以 Failed to read '<path>': 开头,带上原始解析原因(如 Non-Ascii85 digit found: ...),并说明 the document opened

口令失败、"文件损坏"与"页内容损坏"是三种不同的消息:客户端据此可以决定是补 password、换文件,还是只换一页/缩小范围再试。未预期的内部错误(真正的程序 bug)不会伪装成用户错误:客户端只看到通用失败提示,堆栈留在服务器日志里。

工具

参数

返回

pdf_get_metadata

path、password=None

{pages, version, encrypted, page_w, page_h, title, author, producer, fonts}

pdf_extract_page

path、page、password=None

单页 PageData:{width, height, chars, words, rects, lines, curves, images, tables}

pdf_extract_document

path、first_page=None、last_page=None、password=None

{metadata, pageCount, pages},pageCount 是实际导出的页数

pdf_analyze_fonts

path、start_page=None、end_page=None、password=None

{fonts: [{fontname, pages, charCount, sizeMin, sizeMax, colors}]},按 charCount 降序

pdf_get_image

path、stream_id、password=None

成功 {base64, format, mime_type, byte_length};失败 {error}。唯一不受 1 MB 内联阈值约束的工具

pdf_export

path、format='json'、scope='document'、page=None、first_page=None、last_page=None、object_types=None、output_path=None、password=None

见下方「导出」章节

典型调用顺序

  1. pdf_get_metadata 拿到页数、版本与页面尺寸;

  2. 用 pdf_extract_page 单页细看,或用 pdf_analyze_fonts 找主要字体;

  3. 需要大量数据时用 pdf_export 导出到文件,而不是把大对象塞进对话。

stream_id 与图片

pdf_extract_page/pdf_extract_document 返回的 image 对象带 stream_id,格式固定为 p<page>_img_<index>(页内索引从 0 开始,如 p1_img_0)。把它交给 pdf_get_image 就能取回图片:

  • 浏览器可直接显示的格式(png/jpeg/gif/webp)原样返回;

  • 其它格式(JPEG2000、JBIG2 等)会尽力转成 PNG;

  • 无法解码时返回 {"error": "..."}。

base64 永远是完整数据,不会截断;mime_type 由 format 映射,未知格式为 application/octet-stream。

体量例外:pdf_get_image 是唯一不检查内联阈值的工具——图片预览本身就是它的用途,因此 20 MB 的 JPEG 会返回约 27 MB 的 base64 文本。其余工具统一遵守 MAX_INLINE_BYTES = 1_000_000。

导出:pdf_export

参数语义

参数

说明

format

json(默认)或 csv,大小写不敏感

scope

document(默认,整档或页范围)或 page(单页,必须给 page)

page

scope='page' 时必填的 1-based 页码;仅在 scope='page' 时有效,scope='document' 下被忽略(限定范围请用 first_page/last_page)

first_page / last_page

scope='document' 时的页范围,均省略即整份文档

object_types

char/word/rect/line/curve/image/table 的子集;省略 = 全部;未知值忽略、重复去重、顺序规范化为固定顺序;显式空列表表示「不带任何对象数组」

output_path

落盘路径,父目录必须已存在(不会自动创建);写入时不做换行转换,磁盘字节与序列化结果完全一致

返回结构

落盘(给了 output_path):

{
  "path": "/data/exports/report-all.json",
  "bytes": 18234,
  "format": "json",
  "scope": "document",
  "object_types": ["char", "word", "rect", "line", "curve", "image", "table"],
  "suggested_filename": "report-all.json"
}

内联(未给 output_path 且结果不超过 MAX_INLINE_BYTES = 1_000_000 字节):

{
  "content": "{\n  \"metadata\": {...}",
  "bytes": 18234,
  "format": "json",
  "scope": "document",
  "object_types": ["char", "word", "rect", "line", "curve", "image", "table"],
  "suggested_filename": "report-all.json",
  "inline": true
}

超限(未给 output_path 且结果超过 1 MB):改为写入系统临时目录,文件名沿用 suggested_filename 并加随机前缀,返回 path、inline: false 和一条 note,不返回 content:

{
  "path": "/tmp/pdf-inspector-4f2c9a1b-report-all.json",
  "bytes": 4120357,
  "format": "json",
  "scope": "document",
  "object_types": ["char", "word", "rect", "line", "curve", "image", "table"],
  "suggested_filename": "report-all.json",
  "inline": false,
  "note": "The export is 4120357 bytes, over the 1000000-byte inline limit, so it was written to /tmp/... "
}

pdf_extract_page 与 pdf_extract_document 有同一套体量保护,但它们不会落盘:超过 1 MB 直接返回错误,提示改用 pdf_export(配 output_path)或缩小页范围。

示例:JSON(整档 + 类型过滤)

// 调用
{"path": "/tmp/report.pdf", "format": "json", "object_types": ["word", "table"]}

// 返回的 content(节选,reportlab 生成的单页示例)
{
  "metadata": {
    "pages": 1,
    "version": "1.3",
    "encrypted": false,
    "page_w": 612.0,
    "page_h": 792.0,
    "title": "untitled",
    "author": "anonymous",
    "producer": "ReportLab PDF Library - (opensource)",
    "fonts": ["Helvetica"]
  },
  "exportedAt": "2025-05-01T09:41:07.512Z",
  "pageCount": 1,
  "objectTypes": ["word", "table"],
  "pages": [
    {
      "width": 612.0,
      "height": 792.0,
      "chars": [],
      "words": [
        {"type": "word", "x0": 72.0, "y0": 697.723, "x1": 96.464, "y1": 708.723,
         "text": "page", "fontname": "Helvetica", "size": 11.0, "page": 1}
      ],
      "rects": [],
      "lines": [],
      "curves": [],
      "images": [],
      "tables": []
    }
  ]
}

示例:CSV(单页)

// 调用
{"path": "/tmp/table.pdf", "format": "csv", "scope": "page", "page": 1,
 "object_types": ["char", "table"], "output_path": "/tmp/table-p1.csv"}

写出的文件(# <type> 分段的纯文本 CSV,首个字节是 UTF-8 BOM):

# chars
page,x0,y0,x1,y1,text,fontname,size,color
1,60,697.93,67.22,707.93,H,Helvetica,10,0
1,67.22,697.93,72.78,707.93,e,Helvetica,10,0
…
# tables
page,table_index,row,col,text
1,0,0,0,Header A
1,0,0,1,Header B
1,0,1,0,
1,0,1,1,Row B1

输出格式说明

JSON

  • 顶层字段固定且有序:metadata、exportedAt、pageCount、objectTypes、pages。

  • 两空格缩进、null 表示缺失的元数据、非 ASCII 字符不转义(CJK 文本保持可读)。

  • objectTypes 是规范化后的类型列表,按 char, word, rect, line, curve, image, table 的固定顺序;未选中的类型对应的页面数组为空数组(不是缺失字段)。

  • 单页导出与整档导出共用同一结构,单页时 pages 只有一个元素。

CSV

  • 文件以 UTF-8 BOM(\uFEFF)开头,保证 Excel 正确识别编码。

  • 按对象类型分段,每段以 # <type> 行开头,紧跟一行表头;段顺序同样是规范顺序。段内无数据时只保留标题与表头。

  • # tables 每行一个单元格:page,table_index,row,col,text;table_index 在页内从 0 重新计数。

  • 单元格按 JavaScript String() 语义格式化:布尔为 true/false,整数值浮点去掉 .0(12.0 → 12)。

  • 数组值按 JavaScript Array.prototype.toString(等价于 join(','))渲染,而不是 Python 的 repr:char/rect 的 color 从 pdfplumber 拿到的可能是元组,(0,) → 0,(0.1, 0.2, 0.3) → 0.1,0.2,0.3,空数组 → 空字段(无内容)。因此 CSV 里的颜色列是 0 而不是 "(0,)"。

  • 仅含逗号、双引号、CR、LF 的单元格才会加引号(RFC 4180);单元格内部的换行保持 \n(写入磁盘时不做 CRLF 转换)。

  • suggest_filename 的 URL 分支按 WHATWG URL 规范化处理:./.. 段会被折叠,非法百分号转义(如 %E0%A4%A)会使该 URL 段不可用并回退到本地文件名。

已知偏差(相对上游 JS 实现)

内联移植无法做到字节级一致,以下两点是有意保留的差异:

  1. 数字表示:JSON.stringify 把整数输出为 612,Python 的 json.dumps 输出 612.0——坐标与字号来自 pdfplumber,本来就是浮点。除尾随 .0 外,键顺序、缩进、null 与转义行为一致。

  2. exportedAt:形如 2025-05-01T09:41:07.512Z,UTC、毫秒精度、带 Z 后缀(对应 Date.toISOString());Python 默认的 +00:00 时区写法不会出现。

文件缓存与并发

  • 打开过的文档按 (解析后的绝对路径, password, st_mtime_ns, st_size) 缓存在内存里;文件被改写(mtime/大小变化)或口令不同都会重新打开,不会读到旧内容。

  • 缓存的是已打开的 PDF 句柄,不是页数据:重复调用省掉"打开/解析文档结构",同一页的版面分析仍会重跑(上游 sidecar/cache.py 的页级 LRU 未移植,见「未移植:上游的页级缓存」)。

  • 缓存上限默认 8 份(PDF_INSPECTOR_MCP_MAX_DOCS 可改)。超出时按 LRU 淘汰,淘汰时先 close() 再丢弃文件句柄。

  • 同一个文档的多个调用会串行执行:pdfplumber 的 page/extractor 不是线程安全的,每个会话有一把锁,session() 在 with 期间持有它。不同文档之间互不阻塞。

  • 正在被其他线程使用的会话本轮不参与淘汰,宁可短暂超出上限,也不会把别人手里的文件句柄关掉。

  • 路径校验:~ 会展开、符号链接会解析,必须是存在的普通文件,否则报错并说明收到的是什么路径、为什么不接受。不强制 .pdf 后缀。

开发与测试

# 全仓测试
.venv/bin/python -m pytest -q

# 只跑工具层
.venv/bin/python -m pytest tests/test_server.py -q

# 手动验证 stdio 启动(无输出、等待 stdin 即为正常)
printf '' | .venv/bin/python -m pdf_inspector_mcp.server

tests/test_server.py 里有一个真实端到端用例:用 stdio_client 以子进程方式启动服务,依次完成 initialize() → list_tools()(断言六个工具齐全)→ call_tool():正常元数据、非法参数、加密文档缺口令、越界页码、以及「能打开但页内容流损坏」五类调用都会校验返回文本。若运行环境无法启动子进程,该用例会显式 skip 并给出原因,不会静默通过。

「页内容流损坏」的样本不用桩:测试先让 reportlab 生成正常 PDF,再把该页内容流的载荷整段替换为等长的 0xFF 字节(/Length 与所有 xref 偏移保持不变),于是文档仍能打开、页树与字体可读,但解析该页时 pdfminer 抛出真实异常(Non-Ascii85 digit found)。另有两条 monkeypatch 用例分别覆盖"未包装的 PDFSyntaxError"与"提取阶段的 AttributeError 必须保持为崩溃"。

测试用 PDF 由 tests/pdf_fixtures.py 在 tmp_path 里现场生成(reportlab + 手写 PDF),不依赖仓库里的二进制样本。

安全提示

  • 没有认证与访问控制:任何能连上该 stdio 通道的客户端都可以调用全部工具。

  • 可读本地任意文件:path 参数就是文件系统路径,工具会按进程权限打开它(解析失败才报错)。只应把本服务交给可信客户端使用,不要在多人共享或可被注入的 Agent 环境里随意启用。

  • 调用方决定落盘位置:pdf_export 的 output_path 会把文件写到进程有权限的任何位置;临时文件(超限导出)不会被自动清理。

  • 服务只监听 stdio,不开放网络端口;不要把 mcp.run() 改成对外监听而不加防护。

项目结构

src/pdf_inspector_mcp/
  __init__.py     版本号;保持无副作用导入
  objects.py      对象类型契约(TypedDict / Literal)
  extractor.py    pdfplumber 封装:页面、字体、图片(移植自 sidecar/extractor.py)
  export.py       JSON/CSV 序列化与文件名(移植自 export-data.ts)
  document.py     文档会话缓存、LRU、每文档锁
  server.py       MCP 工具层与 stdio 入口
tests/
  pdf_fixtures.py 测试 PDF 生成器
  conftest.py     公共 fixture
  test_extractor.py / test_export.py / test_document.py / test_server.py

已知限制

  • 一次只能处理一个工具调用内的单份文档;跨文档的比较需要客户端多次调用。

  • 加密 PDF 只支持「把口令作为 password 传进来」这一条路径,不支持证书解锁、权限位校验或交互式提示。

  • 字体分析需要逐字符遍历;超大文档的整档分析耗时与页数成正比(没有上游那种分批进度反馈)。整档 pdf_analyze_fonts 的结果会在会话内缓存直到文档变化。

  • 表格检测使用 pdfplumber 的默认策略,只对有框线的表格稳定;无线表格不会被识别。

  • metadata.version 依赖文件头是否可读:pdfplumber 的版本属性已移除,因此实际取到的是文件头 %PDF-x.y(在前 1024 字节内搜索);只有连文件头都读不到时才返回 "unknown"。

  • 图片解码依赖 Pillow 的编解码器;缺少 JPEG2000 等支持时对应图片返回 {"error": ...} 而不是转码结果。

  • 体量策略的两处例外:pdf_get_image 永远返回完整 base64(不受 1 MB 限制);其余工具(pdf_extract_page、pdf_extract_document、pdf_export 的内联分支)在超过 MAX_INLINE_BYTES = 1_000_000 字节时或报错、或改为落盘。客户端应预期单张图片的响应可能远大于其他工具。

  • 未预期的内部错误(程序 bug,如 AttributeError/TypeError/KeyError)不会转成可读消息:客户端只看到通用失败提示,堆栈留在服务器日志里。已知的 pdfminer/pdfplumber 解析异常在打开与提取两个阶段都会转成带原因的用户错误(Cannot open ... / Failed to read ...);同样可见的还有路径非法、口令缺失/错误与非法参数。

  • 超限导出写入系统临时目录的文件没有自动清理机制。

许可证

本项目以 MIT 协议发布,版权归 EthanCheung 所有;解析与导出逻辑移植自同一作者的 pdf-inspector,同样在此协议下使用。

Available Tools

6 tools
pdf_analyze_fontsA

Per-font usage statistics: character count, pages, size range and colours.

Omit both bounds to analyse the whole document. Returns {'fonts': [...]} sorted by charCount descending; each entry has fontname, pages, charCount, sizeMin, sizeMax and colors. Use it to find which text is prominent without extracting whole pages.

NameRequiredDescriptionDefault
pathYesPath to the PDF: absolute, or relative to the server process working directory. Any readable regular file is accepted; the .pdf suffix is not required.
end_pageNoLast page to analyse (1-based, inclusive). Defaults to the last page.
passwordNoPassword for an encrypted PDF. Omit or pass null for unprotected documents.
start_pageNoFirst page to analyse (1-based, inclusive). Defaults to page 1.

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses the return envelope, sort order (charCount descending) and every entry field, plus the default page-range behaviour. It stops short of stating read-only/non-mutating nature or what happens on bad page bounds or a wrong password, which the schema only partly covers.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads what the tool produces, then the scoping rule, then the return shape — a sensible order with little waste. The enumerated return fields are justified by the absence of an output schema, though the RST-style backticks add visual noise.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and no annotations, the description correctly carries the return contract (field names, ordering) while the schema covers all parameters, so an agent can call it correctly. Missing only edge-case behavior such as invalid page ranges or encrypted-file failures.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so path, start_page, end_page and password are already fully documented, including 1-based inclusivity and defaults. The description only adds the 'omit both bounds' shorthand, adding little beyond the schema; baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('per-font usage statistics' with character count, pages, size range and colours), which is concrete and unambiguous. It implicitly separates itself from pdf_extract_page by contrasting prominence analysis with page extraction, though it never names a sibling tool explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear operating context: 'Omit both bounds to analyse the whole document' and 'Use it to find which text is prominent without extracting whole pages' tell the agent both how to scope the call and when this tool beats extraction. No explicit 'do not use for X' exclusion is stated, which keeps it short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pdf_exportA

Export extracted PDF data as JSON or CSV, either inline or to a file.

This is the tool for anything bigger than a single page: JSON keeps the same shapes as the extract tools (metadata, page count, object types, pages) and CSV lays out one # <type> section per type. Choose scope='page' with page for one page, or scope='document' with optional first_page/last_page. object_types trims the payload and output_path writes the file to disk; otherwise the content comes back inline unless it exceeds the inline limit, in which case it is written to a file in the OS temp directory and that path is returned instead.

NameRequiredDescriptionDefault
pageNo1-based page number; only used when scope='page' (required there). Ignored when scope='document' - use first_page/last_page to bound a range.
pathYesPath to the PDF: absolute, or relative to the server process working directory. Any readable regular file is accepted; the .pdf suffix is not required.
scopeNo'document' (default) exports a page range; 'page' exports the single 'page'.document
formatNoOutput format: 'json' (default) or 'csv'.json
passwordNoPassword for an encrypted PDF. Omit or pass null for unprotected documents.
last_pageNoLast page of a document-scope export (1-based, inclusive). Defaults to the last page.
first_pageNoFirst page of a document-scope export (1-based, inclusive). Defaults to page 1.
output_pathNoFile to write the export to. The parent directory must already exist. Omit to receive the content inline (large results are written to the temp directory instead).
object_typesNoObject types to include: any of char, word, rect, line, curve, image, table. Omit for all types; unknown names are ignored and the output uses the canonical order.

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the burden and does disclose key behavior: the inline size limit silently writes to a temp file and returns the path, and output_path requires an existing parent directory. It omits overwrite semantics for an existing output_path and failure behavior for wrong passwords.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the one-line purpose, then the format semantics, then the scope rule, then trimming and output routing. Slightly verbose with some restatement of scope rules already in the schema, but every sentence conveys usable information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 9-parameter tool with no output schema and no annotations, it covers format shapes, scope selection, payload trimming, and the inline/disk fallback. Missing pieces are overwrite behavior and error cases (bad password, missing directory), which leave a modest gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents each parameter and the baseline applies. The description adds conditional coupling (page only meaningful under scope='page') and what the output shapes look like per format, but most per-parameter meaning is already in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb+resource ('Export extracted PDF data as JSON or CSV') and scopes it against the extract tools ('anything bigger than a single page'). It does not name the specific siblings (pdf_extract_page / pdf_extract_document) it is contrasted with, so the differentiation is suggestive rather than explicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear decision rules: scope='page' with page for one page, scope='document' with first_page/last_page for ranges, and a stated volume threshold ('bigger than a single page'). It stops short of naming which extraction sibling to use for the single-page case.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pdf_extract_documentA

Extract every object of every page in a range: the whole document, or first_page..last_page.

Omit both bounds for the whole document. Returns {'metadata', 'pageCount', 'pages'} where pages holds one PageData object per extracted page and pageCount is the number of pages actually returned, not the document's total. Large documents should be read in ranges, or exported with pdf_export.

NameRequiredDescriptionDefault
pathYesPath to the PDF: absolute, or relative to the server process working directory. Any readable regular file is accepted; the .pdf suffix is not required.
passwordNoPassword for an encrypted PDF. Omit or pass null for unprotected documents.
last_pageNoLast page to extract (1-based, inclusive). Defaults to the last page.
first_pageNoFirst page to extract (1-based, inclusive). Defaults to page 1.

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does add real value: it discloses the return dict shape and clarifies that pageCount is the number actually returned, not the document total — a non-obvious behavior. It also warns that large documents should be chunked, implying resource constraints, though it does not state read-only safety explicitly or describe failure behavior for missing/encrypted files.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Scoping and default behavior are front-loaded in the first two sentences, followed by return-shape info. Slightly dense prose with parenthetical keys, but every sentence carries information an agent needs.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description must explain returns — it does, naming 'metadata', 'pageCount', and 'pages' plus PageData per page, and clarifies the pageCount semantic. It stops short of describing what PageData fields contain, which for a nested return type would be the last missing piece.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description restates the range semantics ('first_page..last_page', omit both for whole document) in a useful summarized form, but adds no format detail beyond what the schema's parameter descriptions already say.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb and resource ('Extract every object of every page in a range') and states the scope precisely, distinguishing full-document from ranged extraction. It never names pdf_extract_page, the closest sibling for single-page extraction, so the differentiation is implicit rather than explicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives concrete operating guidance: 'Omit both bounds for the whole document' and 'Large documents should be read in ranges, or exported with pdf_export', naming an alternative and the condition that selects it. What's missing is any routing to pdf_extract_page for single-page reads or pdf_get_metadata when only document info is needed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pdf_extract_pageA

Extract every object of one page: characters, words, rectangles, lines, curves, images and tables.

page is 1-based and must be within the document. The result is one PageData object (width, height and one array per object type), suitable for inspecting layout, text runs and detected tables. Very large pages are rejected: use pdf_export with output_path for those.

NameRequiredDescriptionDefault
pageYes1-based page number.
pathYesPath to the PDF: absolute, or relative to the server process working directory. Any readable regular file is accepted; the .pdf suffix is not required.
passwordNoPassword for an encrypted PDF. Omit or pass null for unprotected documents.

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations exist, so the description carries the burden, and it does disclose the 1-based indexing, the in-document bounds constraint, the rejection behavior for oversized pages, and the shape of the return value. Missing only operational details like whether the extraction is read-only/stateless or how reserved resources behave.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, each earning its place: the action and scope first, then the page constraint, then the return shape and the fallback for large pages. No repetition or padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no annotations and no output schema, the description supplies the return structure (a PageData with width, height, and per-type arrays) and states the large-page failure mode with a workaround. Nothing an agent needs in order to invoke or interpret it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so all three parameters including password and path semantics are already documented. The description's only added detail is the 1-based page rule, which the schema also states, so this is baseline territory.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Extract every object of one page') and enumerates exactly what comes back (characters, words, rectangles, lines, curves, images, tables). The 'one page' scope implicitly but clearly separates it from the sibling pdf_extract_document.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a concrete exclusion and an alternative: very large pages are rejected, so route those to pdf_export with output_path. It does not say when to prefer this over pdf_extract_document, but the per-page vs whole-document distinction is implied by the name and scope.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pdf_get_imageA

Fetch one embedded image's bytes as base64, addressed by the stream_id reported for that image.

Call pdf_extract_page/pdf_extract_document first and take stream_id from an object of type image (p<page>_img_<index>, index is 0-based within the page). On success returns {'base64', 'format', 'mime_type', 'byte_length'} with the full, untruncated payload; images in non-web formats are transcoded to PNG. On failure returns {'error': '...'} unchanged.

The complete base64 payload is always returned inline: this tool is the one deliberate exception to the 1 MB inline limit the other tools enforce, because the image is the whole point of the call. Large images therefore produce a large response.

NameRequiredDescriptionDefault
pathYesPath to the PDF: absolute, or relative to the server process working directory. Any readable regular file is accepted; the .pdf suffix is not required.
passwordNoPassword for an encrypted PDF. Omit or pass null for unprotected documents.
stream_idYesImage identifier from a page's images array, in the form 'p<page>_img_<index>'.

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does so well: it discloses the return shape, PNG transcoding of non-web formats, error passthrough, and the deliberate exemption from the 1 MB inline limit with the consequence of large responses. That size caveat is exactly the kind of trait annotations would otherwise miss.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action and prerequisite, but the final paragraph restates the inline-payload point at length (two sentences on the same 1 MB exception). Useful, yet it could be compressed to one line without losing information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, yet the description fully specifies the return keys, error form, transcoding behavior, and the size implication. An agent has everything needed to call it and interpret the result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% so the baseline is 3, but the description adds detail the schema omits, notably that the index is 0-based within the page. Combined with the stream_id format it gives the agent extra confidence in constructing the argument.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (fetch) and resource (one embedded image's bytes as base64) plus the addressing key (stream_id). It explicitly distinguishes itself from siblings by requiring pdf_extract_page/pdf_extract_document first, so an agent can tell exactly what this does versus those tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit prerequisite workflow: extract page/document first, then take stream_id from an object of type image. This makes the when-to-use clear relative to siblings, though it doesn't state when not to use it or what to do if the image is already cached.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pdf_get_metadataA

Document-level facts: page count, PDF version, encryption flag, first-page size, title/author/producer and the font names the pages reference.

Use this first to learn how many pages a document has before calling the page tools. Returns the extractor's metadata dictionary unchanged (pages, version, encrypted, page_w, page_h, title, author, producer, fonts).

NameRequiredDescriptionDefault
pathYesPath to the PDF: absolute, or relative to the server process working directory. Any readable regular file is accepted; the .pdf suffix is not required.
passwordNoPassword for an encrypted PDF. Omit or pass null for unprotected documents.

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations present, the description carries the burden and does reasonably well: it discloses that the raw extractor metadata dictionary is returned unchanged and lists its keys, and the presence of a password parameter implies the encrypted-document path. It does not state error behavior (wrong/missing password on an encrypted file) or whether the whole file is parsed, which is the remaining gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the substance and the usage rule in the first two sentences. The final sentence partially repeats the field list from the first sentence, which is mild redundancy but also serves as the de facto return-value specification absent an output schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, and the description compensates by enumerating the returned keys. Combined with the 100%-covered input schema and the explicit 'use first' routing, an agent has what it needs; only failure modes for encrypted PDFs are unaddressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%: path and password are fully documented in the schema, including the note that the .pdf suffix is not required. The description adds no syntax or format detail beyond that, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific resource (document-level metadata) and enumerates exactly what is returned: page count, version, encryption flag, page size, title/author/producer, font names. It also distinguishes itself from the page-extraction siblings by positioning itself as the first call to discover page count.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

"Use this first to learn how many pages a document has before calling the page tools" gives an explicit ordering and routing rule relative to siblings. It stops short of naming when NOT to use it (e.g., when you already need full text, use pdf_extract_document), so it is clear but not exhaustive.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 6 tool updatesv0.1.0
    • First observedpdf_analyze_fonts
    • First observedpdf_export
    • First observedpdf_extract_document
    • First observedpdf_extract_page
    • First observedpdf_get_image
    • First observedpdf_get_metadata

TDQS

A4.1/5.0

Scored across 6 tools

Disambiguation4/5

Each tool targets a distinct concern (metadata, page extraction, document extraction, image bytes, font stats, export), but pdf_extract_document and pdf_export with scope='document' produce largely the same payload, as do pdf_extract_page and pdf_export with scope='page'. The descriptions do explain the split (export is the large-document escape hatch), so an agent can choose, but the boundary is not razor-sharp.

Naming Consistency5/5

All six tools use a uniform pdf_ prefix plus a verb_noun pattern (get_image, get_metadata, extract_page, extract_document, analyze_fonts, export). Verb choice is predictable and no casing or style drift appears.

Tool Count5/5

Six tools is tightly scoped to the PDF-inspection domain, and each one earns its place: a metadata probe, two extraction granularities, a binary-image fetch, a font analytics shortcut, and an export/overflow path. Nothing feels padded or missing at the count level.

Completeness4/5

The surface covers the core lifecycle: inspect metadata, extract page or document objects, pull image bytes, analyse fonts, and export to JSON/CSV. Gaps are minor but real — no text search/find, no outline/bookmark or annotation/form-field access, and no page rendering to an image — though agents can approximate search by extracting and filtering.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    An MCP server that provides comprehensive PDF processing capabilities including text extraction, image extraction, table detection, annotation extraction, metadata retrieval, page rendering, and document structure analysis.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI-powered extraction and analysis of PDF documents with 40+ specialized tools for text, tables, images, layout analysis, security assessment, and document intelligence. Supports both text-based and scanned PDFs with OCR capabilities.
    271 PyPI
    11
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    Enables PDF processing and analysis including text extraction, metadata retrieval, search, page manipulation, splitting/merging, conversion to images, and form handling.
    10
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables AI agents to extract structured data from PDFs with confidence scores and provenance, and to search, review, and correct documents via MCP tools, resources, and prompts.
    -