关于 PDF Verify (PDF Agent Stack)
PDF Verify 是用于验证 PDF 真实性与规范符合性的 MCP 服务,面向需要检查签名、篡改和文档标准的工作流。它结合密码学验证、修订变更分析与策略评估,并区分无法判定与通过。
主要功能
- 01验证数字签名及信任链
- 02检测签署后的文档变更
- 03识别 PAdES 基线等级
- 04验证 PDF/A 与 PDF/UA 符合性
- 05按规则评估信任策略
适用场景
- 01核验合同或财务 PDF 签名
- 02检查签署后是否发生未授权更改
- 03验证归档文件的 PDF/A 符合性
- 04按领域策略筛查文档风险
MCP 相关
PDF Verify 提供 MCP 工具,用于签名与完整性验证、PAdES 检测、符合性识别和验证、条款检查及策略评估。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
7收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| password | string | 否 | 加密 PDF 的密码。对于权限加密的 PDF 可省略(会自动尝试使用空用户密码)。 |
| file_path | string | 是 | 本地 PDF 文件的绝对路径(例如,"/path/to/document.pdf") |
| trust_anchors | array | 否 | 信任锚证书的绝对路径(PEM 或 DER)。与 PDF_VERIFY_TRUST_ANCHORS 环境变量合并使用(指向包含 *.pem/*.crt/*.cer/*.der 文件的目录)。省略且未设置环境变量时,信任状态报告为 not_evaluated。 |
| response_format | enum | 否 | 输出格式:"markdown" 表示便于阅读,"json" 表示结构化数据markdown | json |
| check_revocation | enum | 否 | 吊销检查:“none”、“embedded”(PDF/CMS 中的 OCSP/CRL 数据,默认)或“online”(还会通过 HTTP 查询 OCSP 响应程序和 CRL 分发点)。none | embedded | online |
| revocation_freshness | integer | 否 | 验证时间之前 CRL / OCSP 响应可签发的秒数(thisUpdate),且仍可支持“good”。默认值为 86400(24 h);0 表示仅接受在验证时间或之后签发的数据。较早的数据会返回“unknown”。 |
| trusted_ocsp_responders | array | 否 | 本地信任的 OCSP 响应者证书(PEM 或 DER)的绝对路径(RFC 6960 §4.2.2.2)。即使响应者不是签发 CA 或其委托方,由其中一个响应者签名的响应也会被接受。 |
详细介绍
pdf-verify-mcp
用于 PDF 真实性与合规性验证的 MCP 服务——包括加密数字签名验证、篡改检测、PAdES 基线级别检测,以及 PDF/A(ISO 19005)/ PDF/UA(ISO 14289)验证。
与 pdf-reader-mcp(结构分析)和 pdf-spec-mcp(规范参考)同属 PDF 系列。pdf-reader-mcp 告诉你 PDF 包含什么,而 pdf-verify-mcp 告诉你它是否真实有效。
工具
| 工具 | 用途 |
|---|---|
verify_signatures | 加密验证、根据信任锚评估信任链、吊销检查(嵌入式 OCSP/CRL 或在线检查)、RFC 3161 时间戳验证 |
verify_integrity | 篡改检测:增量更新、签名后的更改、按 P 值评估 DocMDP 认证违规(见下文),以及修订链的对象级差异(每次更新添加、重写或释放了哪些对象)。增量更新是合法的,因此差异用于指出需要审查的内容——不会据此作出任何判定。要了解这些对象在页面上的位置,请使用 pdf-reader-mcp 的 locate_objects |
detect_pades_level | PAdES 基线级别(B-B / B-T / B-LT / B-LTA),包含经过内容验证的 LTV 数据 |
identify_conformance | 从 XMP 元数据识别声明的 PDF/A / PDF/UA 合规性 |
validate_conformance | PDF/A(ISO 19005)和 PDF/UA(ISO 14289)验证:安装了 veraPDF 时使用 veraPDF,否则使用内置规则子集 |
validate_clauses | 映射自 ISO 32000-1/-2 条款的约束——即规范正文本身,而 PDF/A 和 PDF/UA 配置文件并未涵盖这部分内容 |
evaluate_policy | 根据固定规则表中的验证事实,给出确定性的 4 值信任判定(trust_and_use / use_with_caution / human_review_required / reject),并支持领域配置文件(合同、金融、法律、医疗、政府)。判定逻辑由代码实现;叙述说明由大模型生成 |
判定结果
| 判定结果 | 含义 |
|---|---|
valid | ByteRange 摘要匹配,且 CMS 签名在加密层面有效 |
invalid | 摘要不匹配或签名验证失败——可能遭到篡改 |
indeterminate | 格式不受支持,或无法完成验证 |
DocMDP 认证权限(v0.14)
认证签名的 P 值说明允许哪些类型的更改
(ISO 32000-2 表 257)。verify_integrity 会在对象级别对签名后的更改进行分类,并将其与 P 值允许的更改进行比较。
| P | 允许的更改 | 添加注释是否违规? |
|---|---|---|
| 1 | 无(DSS / 文档时间戳增量更新是 §12.8.2.2 中的例外) | 是 |
| 2 | 填写表单、实例化页面模板、签名 | 是——注释从 3 开始 |
| 3 | 与 2 相同,另允许创建、删除和修改注释 | 否 |
允许的更改必然会连带修改的对象——/Annots 增多的页面、目录、/Info、XMP 流——会被归类为 housekeeping,不计为违规。如果将它们计入,就会导致每个经过认证的文档都违规,因为合法的 P=3 注释添加会连带修改这四个对象。
violationAssessment 为三值
| 值 | 含义 |
|---|---|
permitted | 签署后的所有变更均属于 P 允许的类型 |
violated | 至少有一项变更不属于允许范围 |
indeterminate | 无法确定(无法遍历 xref 链,或无法读取已变更对象的类型) |
⚠️
indeterminate不代表通过。 此服务会推翻结论,而「无法推翻」与「没有问题」是不同的说法——这与validate_clauses返回needs_external_fact,而不是默认将检查判为通过,遵循的是同一原则。为保持兼容,
violatedByLaterChanges(布尔值)会将indeterminate归并为false。凡是「无法判断」不能被误认为「没有问题」的场景,请使用violationAssessment。evaluate_policy也会将indeterminate提升为human_review_required。
没有 /Type 的裸流会被视为未确定,而不是 content:相同的字节可能是表单字段的外观流(P=2 允许),也可能是页面内容流(任何 P 都不允许)。
响应大小(v0.29.0)
JSON 响应不会因长度而截断。列表会设上限,且每处截断都会在列表旁报告 { returned, total }:signatures / levels 上限为 32(verify_signatures 不会验证超出上限的字段;evaluate_policy 会验证全部字段,但仅限制 facts.signatures),revisions 上限为 32,violations / results 上限为 200(compliant、failedRules、violations、notDecided 等计数涵盖所有条目)。Markdown 响应会在 50,000 个字符处截断,并显示标记。
evaluate_policy 中的内容清单(v0.30.0)
evaluate_policy 还会统计文档中可执行或尚未完成的内容,并在 facts.content 下报告——即 ISO 32000-2 §12.8.7 表 264(法律内容证明)中的计数:JavaScriptActions、LaunchActions、URIActions、GoToRemoteActions、HideAnnotationActions、MovieActions、SoundActions,以及 SubmitFormActions、URI 主机、是否存在 /OpenAction 或文档级 /AA、批注数量,以及文件中仍存在的 /Subtype /Redact 批注数量。会遍历嵌套的(直接)动作字典,因此链接上的内联 /A 也会计入。两条规则会读取该清单:
POL-CAUTION-REDACT-PENDING— 仍存在涂黑批注(ISO 32000-2 §12.5.6.23)。已应用的涂黑不会留下批注,因此其标记的内容尚未从文件中移除。use_with_caution。POL-CAUTION-ACTIVE-CONTENT— 存在 ECMAScript、Launch、远程跳转或 SubmitForm/ImportData 动作;原因会说明/OpenAction//AA是否会使其中某些动作在打开时运行。use_with_caution。URI 动作不会改变判定结果;其主机名会列入提示信息。
清单是观测结果,而非判定结果:若结果为 null(无法获取),会报告为「未计数」,绝不会报告为 0;无法读取的对象会使计数成为下限。表 264 中未计数的键:AlternateImages、ExternalStreams、TrueTypeFonts、ExternalRefXobjects、ExternalOPIdicts、NonEmbeddedFonts、DevDepGS_*。
信任与撤销(v0.2)
传入 trust_anchors(PEM/DER 文件路径),或设置 PDF_VERIFY_TRUST_ANCHORS 环境变量(证书目录),以评估签名者的证书链:结果为 trusted / untrusted / not_evaluated,并附带证书路径。验证时间依次取已验证的签名时间戳、覆盖该签名的最早文档时间戳,或当前时间(v0.27.0);CMS signingTime 属性由签名者写入,不会用于验证。所选时间会以 validationTime 报告。
check_revocation 控制撤销检查:embedded(默认值——DSS、CMS SignedData.crls 或 CMS 签名属性 adbe-revocationInfoArchival 中的 OCSP/CRL 数据;以 revocation.origin 报告)、online(还会通过 HTTP 查询 OCSP 响应器和 CRL 分发点),或 none(revocation.status: not_checked)。如果 CRL 和 OCSP 响应的签名无法验证、其 nextUpdate 早于验证时间,或其 thisUpdate 比验证时间早超过 revocation_freshness 秒(默认值为 86400),结果将为 unknown(v0.28.0)。传入 trusted_ocsp_responders 可信任由其他 CA 签发的 OCSP 响应器。每个中间 CA 的结果都会在 trust.chainRevocation 中报告。如果时间戳能证明签名早于撤销时间,则已撤销的签名者证书会得到 revoked_after_validation_time(判定结果不变);否则结果为 revoked,判定为 indeterminate。在 online 模式下,会通过 AIA caIssuers 获取缺失的颁发者证书以补全证书链(v0.4)。提供信任锚时,也会评估 RFC 3161 时间戳的 TSA 证书链(tsaTrust)。
| 步骤 | 检查内容 | 网络 |
|---|---|---|
| 完整性 | /ByteRange 哈希与 CMS messageDigest 匹配 | 无 |
| 签名值 | 使用签名者证书的公钥验证签名值 | 无 |
| 证书链 | 链接至信任锚 | 仅 online:通过 AIA caIssuers 获取颁发者证书 |
| 撤销 | 签名者证书的 OCSP / CRL | embedded 模式下无;online 模式下,嵌入数据无法给出结果时会通过 HTTP 查询 |
| 时间戳 | RFC 3161 令牌和 TSA 签名 | 无 |
verdict: valid 表示完整性和签名值检查均通过(revoked 会使其变为 indeterminate)。online 结果反映 CA 在查询时的状态,之后可能发生变化。有关每个步骤及如何选择模式,请参阅站点指南。
未提供信任锚时,
trust保持为not_evaluated;valid判定表示密码学完整性有效,而非签名者身份有效。
权限加密的 PDF(用户密码为空)会自动解密;对于阅读器密码保护的 PDF,请传入 password。支持:RC4(R2–R4)、AES-128、AES-256(R6)。解密会还原字符串元数据(字段名称、/M、/Reason、/Location)和 XMP——签名的 /Contents 不加密,因此验证不会依赖它。
支持的 SubFilter:ETSI.CAdES.detached(PAdES)、adbe.pkcs7.detached、ETSI.RFC3161(文档时间戳)。RFC 3161 签名时间戳会经过完整验证(摘要值 + TSA 签名)。旧版 MD5/SHA-1 签名通过 node:crypto 验证,并标记为弱签名。
PDF/A 验证(v0.3)
validate_conformance 使用混合引擎。安装了 veraPDF 时(通过 PDF_VERIFY_VERAPDF 环境变量指定或位于 PATH 中),验证会交由它执行,以获得权威结果。否则,内置的约 15 条高价值 ISO 19005 规则会在本地运行(加密、尾部 /ID、LZW、字体嵌入、JavaScript/禁止的操作、OutputIntent、A-1 的透明度、XFA 等),并逐条报告对应的条款编号。
本地结果会如实说明其局限:发现违规意味着确定不符合要求;全部通过则表示“已检查的子集中未发现违规”,绝不等同于认证。
接受 PDF/A-4(pdfa-4、pdfa-4e、pdfa-4f)。请注意,PDF/A-4 没有符合性级别——不存在 pdfa-4b;e 和 f 是变体。本地规则依据 ISO 19005-1/-2 编写,尚未根据 ISO 19005-4 检查,因此 PDF/A-4 报告会明确说明本地判定的权威性低于 veraPDF。请使用 veraPDF 验证第 4 部分。
XMP 扩展架构和 TDMRep(v0.31.0)
ISO 19005 仅在 XMP 属性的架构为预定义架构(XMP Specification、pdfaid),或通过数据包中的 pdfaExtension:schemas 描述时,才接受该属性(19005-1 6.7.8、19005-2/-3/-4 6.6.2.3)。veraPDF 会将违规报告为“XMP property is either not predefined, or is not defined in any XMP extension schema”,并仅在检查上下文中指出命名空间。从 v0.31.0 起,validate_conformance 会将该名称写入违规项的 detail——例如 http://www.w3.org/ns/tdmrep/ (tdm:reservation, tdm:policy) — W3C TDM Reservation Protocol (TDMRep): …——并说明二者如何共存:只要数据包描述了 TDMRep 架构(reservation 为 Integer,policy 为 URL),PDF/A 与 TDMRep 就兼容。identify_conformance 会列出数据包声明的所有命名空间,并标记为预定义 / 已描述 / 已知;声明了 PDF/A 时,还会指出未描述的命名空间。evaluate_policy 会在建议中重述具体原因。使用 veraPDF 1.28.2 测得:包含 TDMRep 且没有扩展架构的 PDF/A-3B 数据包,其两个属性在 6.6.2.3.1 test1 和 test2 中均未通过;同一数据包若包含架构描述,则不会触发 XMP 失败。
ISO 32000 条款约束(v0.9)
validate_clauses 检查的是另一部分:PDF 规范正文,而非 PDF/A 或 PDF/UA 配置文件。文件即使被 veraPDF 判定为 COMPLIANT,仍可能违反 ISO 32000——例如在 /FontFile2 下嵌入 CFF 字体程序(表 124),这是一个真实案例,只在查看器警告中显现。
条款与结构条件之间的映射及其评估由 @shuji-bonji/pdf-constraints 提供。每份报告都会注明作出判定的版本,因为规则会随着约束的增加而变化。
每项约束会得到以下四种状态之一:
| 状态 | 含义 |
|---|---|
pass | 无法证伪此约束中的任何内容 |
fail | 已证伪,并以事实及其测量值作为证据 |
not_applicable | 此条款不适用于该文档 |
needs_external_fact | 未提供文件之外的事实,因此尚未判定此约束 |
报告会刻意区分两件事:
needs_external_fact不代表通过。 只有创建字体的人才知道它是否为子集——PDF 并未说明。可通过given: { isSubset: true }提供此信息;若不提供,约束会降低判定结果,而不会默认静默通过。- 有些失败是痕迹,而非违规。 如果条款针对的是 PDF 处理器(写入文件的行为),文件最多只能表明有人违反了条款——§14.3.4 明确允许保留已有的不一致,因此最后写入文件的人不一定有错。
这些属于 T1 条款,因此可以直接陈述失败,并引用条款 ID——使用 pdf-spec-mcp 的 get_requirements 获取条款原文。
PDF/UA 验证(v0.6)
传入 flavour: "pdfua-1"(或 "pdfua-2"),即可根据 ISO 14289 验证无障碍合规性。若已安装 veraPDF,则会通过 --flavour ua1 调用它;否则将运行 12 条原生规则:MarkInfo/Marked、StructTreeRoot、pdfuaid 声明、/Lang、DisplayDocTitle、文档标题、Figure /Alt、图像标记、标题层级、表格 TH/TR、Link /Contents 和加密屏障。标签通过 /RoleMap 解析。
PDF/UA 原生违规项带有 severity:只有 error 规则才能证明不合规,而 warning 规则会标记需要人工审核的内容。无障碍性无法完全通过机器判定——是否存在替代文本可以检查,是否有意义则无法检查。
原生子集的能力止于 pdf-lib 所及之处。需要分析内容流的规则——7.1-3(内容标记为 artifact 或带标签)、7.2-34(页面内容的语言)、7.18.1-1(嵌套在 Annot 标签中的注释)、7.18.3-1(/Tabs)——交由 veraPDF 处理,而不做近似判断。重视无障碍性时,请安装 veraPDF。
如果未显式指定 flavour,则仅当文档声明为 PDF/UA 且未声明为 PDF/A 时,才会选择 PDF/UA。使用 pdf-reader-mcp 的
inspect_tags检查结构树本身;合规性判断在此完成。
安装
作为插件安装(通过 shuji-bonji/claude-plugins 市场,推荐):
/plugin marketplace add shuji-bonji/claude-plugins
/plugin install pdf-verify-mcp@shuji-bonji
或直接添加到 MCP 配置中:
{
"mcpServers": {
"pdf-verify": {
"command": "npx",
"args": ["-y", "@shuji-bonji/pdf-verify-mcp@latest"]
}
}
}
请使用
@latest(或固定版本)。 不带版本号运行npx -y <pkg>时,会一直运行首次缓存的版本——-y仅跳过安装提示,不会检查更新。要清除过期缓存:rm -rf ~/.npm/_npx。
使用示例
- “验证 /path/to/contract.pdf 中的签名——签名后是否被篡改?”
- “这份经过认证的 PDF 在认证后是否被修改?”
- “此签名是否启用了 LTV(B-LT 或 B-LTA)?”
- “此文档是否声明符合 PDF/A-2b?”
开发
npm install
npm test # vitest (fixtures are generated in-memory)
npm run build
npm run check # biome lint + format
npm run test:fixtures # write sample signed/tampered PDFs to tests/fixtures/generated/
测试夹具(自签名证书 + 已签名 PDF)通过 pkijs + WebCrypto 以编程方式生成——仓库中不含二进制资源。
许可证
MIT © shuji-bonji
依赖项:pkijs / asn1js(BSD-3-Clause)、pdf-lib(MIT)、normativepdf(MIT)、@shuji-bonji/pdf-constraints(MIT)、@modelcontextprotocol/sdk(MIT)、zod(MIT)。
交叉引用章节由 normativepdf 读取。这是一个以条款为驱动的 PDF 库,每种行为都与 ISO 32000 中的条款绑定。它很严格——无法读取的章节在其中会被视为错误。损坏文件的恢复策略(回退到较早的 startxref、限制循环 /Prev 链的长度、将线性化文件的两个章节合并回一次保存)保留在此服务中,因为这属于取证判断,而非对规范的解读。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。