Skip to main content
Glama
shuji-bonji

pdf-verify-mcp

by shuji-bonji

Verify PDF Integrity (tamper detection)

verify_integrity
Read-onlyIdempotent

Analyze a signed PDF for post-signing modifications, verify DocMDP certification permissions, and report object-level changes in incremental updates.

Instructions

Analyze a PDF for modifications after signing.

Reports: number of revisions (incremental updates), whether bytes were added after each signature's signed range, whether the last signature covers the entire file, DocMDP certification permissions and violations, and DSS presence.

DocMDP is assessed against what the P value actually permits (ISO 32000-2 Table 257): P=1 permits nothing, P=2 form fill-in and signing, P=3 additionally annotation creation/deletion/modification. The later changes are classified from the object-level diff (changeClass), so adding an annotation to a P=2 document is reported as a violation while the same change to a P=3 document is not. Objects a permitted change necessarily drags along — the page whose /Annots grew, the catalog, /Info, the XMP stream — are classified as housekeeping and do not by themselves constitute a violation. Per ISO 32000-2 §12.8.2.2, DSS/document-timestamp incremental updates after a P=1 certification are NOT violations (flagged as laterChangesAppearLtvOnly).

violationAssessment is three-valued: "permitted" / "violated" / "indeterminate". "indeterminate" is not a pass — it means the chain could not be walked or a changed object's kind could not be read, so nothing could be disproved. The boolean violatedByLaterChanges collapses indeterminate to false for backward compatibility; read violationAssessment when "could not tell" must not be mistaken for "fine".

Also reports an object-level diff of the incremental-update chain: for each revision, which objects were added, rewritten or freed, with the object's /Type and a plain-language role (annotation, form field widget, page object, content stream, …), plus the shortlist of objects written after the last signed range (objectChangesAfterLastSignature). Cross-reference and object streams are flagged as bookkeeping. Where the objects sit on the page is pdf-reader-mcp's answer, not this server's.

Limits of the diff — it is an observation, never a verdict:

  • Incremental updates are legal in PDF (ISO 32000-2 §7.5.6). A rewritten object says what to review, not that the file was tampered with. No verdict moves because of it.

  • revisions: null means the cross-reference chain could not be walked (revisionChain.status: 'unwalkable') — "not determined", NOT "nothing changed".

  • A non-null revisions list is not necessarily the whole history. revisionChain.status says which it is: 'complete' (walked from the newest cross-reference section back to the original revision), 'partial' (a list came back but revisionChain.missing names the end that is absent), or 'unwalkable' (the case above). revisionChain.missing holds 'oldest' when the chain ended before the original revision, and 'newest' when the last startxref did not point at a parseable section so an older entry point was used and the last append is not listed; both can be absent at once.

  • Only 'complete' makes "no such change appears in the list" mean "that change was not made." With a cut chain the surviving revision is reported as the original one — changeCount: 0, changes: null, objectChangesAfterLastSignature empty — so every other field reads as "nothing was appended". revisions are listed oldest first. Why the chain was cut (a damaged or cyclic /Prev, the revision cap, an unparseable section) stays in notes.

  • Objects stored inside an object stream are listed with inObjectStream: true and no type.

  • revisionCount counts "startxref" keywords; revisions lists the cross-reference sections the chain reached. The two differ lawfully, so revisionCountAgreement says whether the difference is explained: 'agree', 'accounted' (causes names 'linearised' and/or 'chain-incomplete'), or 'unaccounted' — the file holds a startxref the walked chain does not reach, which is the case worth looking at.

  • Linearised files (ISO 32000-2 Annex F) carry two cross-reference sections for one save; they are merged back into one revision rather than reported as an update, so revisionCount is one higher than the number of saves.

Args:

  • file_path (string): Absolute path to a local PDF file

  • response_format ('markdown' | 'json'): Output format (default: 'markdown')

Returns: Every report begins with a "scope" object - how far the reading got, not a verdict: whether the cross-reference chain could be walked to the end (chainStop), whether this tool had to rebuild the cross-reference table itself (reconstructed - when true, the table is this tool's reconstruction and not the one the file carries), how many objects and sections were read, and whether an encrypted document could be opened. Read it before the verdict: "no violations" over a rebuilt table is not the same statement as "no violations" over the file's own table.

Size (v0.29.0): revisions lists at most 32 revisions (newest first) and 25 changes per revision; revisionsTruncated / changesTruncated say when a list was cut. revisionCount and revisionChain cover the whole walk. JSON is never cut by length.

Integrity report, including revisionChain: { status, missing } — read it before treating the revision list as the file's whole history — and revisionCountAgreement: { status, causes } — read it before quoting revisionCount as the number of times the file was saved. Note that incremental updates after signing are legal in PDF (adding signatures, DSS/LTV data) — findings indicate what to review, not automatically tampering.

Examples:

  • Check whether a signed document was modified after signing

  • Verify a certified (DocMDP) document respects its declared permissions

  • Find out which objects an incremental update touched after a signature was made

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
file_pathYesAbsolute path to a local PDF file (e.g., "/path/to/document.pdf")
response_formatNoOutput format: "markdown" for human-readable, "json" for structured datamarkdown

Schema Changelog

Changes observed during successful MCP inspections.

  1. First observedv0.29.1

TDQS

A4.5/5.0
Behavior5/5

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

The description goes well beyond the readOnly/idempotent annotations by explaining non-obvious behavior: 'indeterminate' is not a pass, a non-null revisions list may be incomplete, 'unwalkable' means undetermined rather than unchanged, reconstructed cross-reference tables weaken conclusions, and lists can be truncated. It also clarifies that incremental updates are legal and findings are observations, not verdicts. This is exactly the kind of behavioral nuance an agent needs beyond structured annotations.

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?

Though long, the description is dense and well organized: purpose, key outputs, classification rules, limits, argument list, return-scope guidance, and truncation policy. Every section earns its place given the complexity of the tool, and the most decision-critical caveats are emphasized with bold text. It is structured for efficient scanning rather than padded.

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 output schema, the description must carry the burden of explaining return semantics, and it does: scope object, revisionChain status values, revisionCountAgreement, violationAssessment values, truncation behavior, and meaningful examples. Given the tool's complexity and the absence of an output schema, nothing critical 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 description coverage is 100%, so the schema already documents both file_path and response_format, including the enum and default. The Args section in the description duplicates that information without adding new semantic constraints. Baseline 3 is appropriate because the description does not need to compensate for schema gaps here.

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 opens with a specific verb and resource: "Analyze a PDF for modifications after signing." It clearly distinguishes this tool from signature-validity or conformance checking by focusing on post-signing modifications, revision history, and tamper-relevant observations. The title reinforces this as tamper detection, and the scope is unambiguous.

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?

The description gives clear context for when the tool is appropriate: checking whether a signed document was modified after signing, and reviewing integrity findings rather than treating them as automatic tampering verdicts. It also explicitly offloads page-geometry questions to pdf-reader-mcp, which helps an agent decide what this tool is not for. It does not explicitly name sibling tools or state 'use X instead' conditions, so it stops just 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.