pdfnative-mcp — AI Client Integration Guide
Tracks the latest published
pdfnative-mcp(v1.6.0, built on pdfnative 1.7.0). Full release notes: pdfnative-mcp releases. Live package versions — and thepdfnativeversion each one is built on — are shown at the top of the documentation home.
pdfnative-mcp is an MCP server that exposes the full pdfnative library to any AI client supporting the Model Context Protocol — Claude Desktop, Cursor, Continue, Zed, ChatGPT, and more.
What is MCP? The Model Context Protocol is an open standard (originally developed by Anthropic) that lets AI assistants call external tools in a structured, safe way. An MCP server declares a set of tools with typed inputs and outputs; the AI client invokes those tools on your behalf during a conversation.
With pdfnative-mcp installed, you can say to your AI assistant:
"Generate a Q1 2026 financial report as PDF/A-2b with a QR code pointing to our dashboard, then inspect the result to confirm it's archive-grade."
…and the AI will call the right combination of generate_basic_pdf, add_barcode, add_table, and inspect_pdf tools, returning a ready-to-download PDF.
Installation#
# Run directly with npx — no global install required (recommended)
npx -y pdfnative-mcp
# Or install globally
npm install -g pdfnative-mcp
pdfnative-mcp
Requirements: Node.js ≥ 22.
Configuration by client#
Claude Desktop#
Edit the config file for your OS:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"pdfnative": {
"command": "npx",
"args": ["-y", "pdfnative-mcp"],
"env": {
"PDFNATIVE_MCP_OUTPUT_DIR": "/Users/you/Documents/mcp-pdfs"
}
}
}
}
Restart Claude Desktop after saving. The pdfnative server will appear in the tools panel.
Cursor#
In your project .cursor/mcp.json (or global ~/.cursor/mcp.json):
{
"mcpServers": {
"pdfnative": {
"command": "npx",
"args": ["-y", "pdfnative-mcp"],
"env": {
"PDFNATIVE_MCP_OUTPUT_DIR": "/path/to/pdf-output"
}
}
}
}
Continue#
In your ~/.continue/config.json:
{
"mcpServers": [
{
"name": "pdfnative",
"command": "npx",
"args": ["-y", "pdfnative-mcp"],
"env": {
"PDFNATIVE_MCP_OUTPUT_DIR": "/path/to/pdf-output"
}
}
]
}
Zed#
In your Zed settings.json:
{
"context_servers": {
"pdfnative": {
"command": {
"path": "npx",
"args": ["-y", "pdfnative-mcp"],
"env": {
"PDFNATIVE_MCP_OUTPUT_DIR": "/path/to/pdf-output"
}
}
}
}
}
Environment variables#
| Variable | Purpose |
|---|---|
PDFNATIVE_MCP_OUTPUT_DIR |
Absolute path to the sandbox directory. Required to enable outputMode: "file". When unset, only base64 output is available. (The misspelt PDFNATIVE_MPC_OUTPUT_DIR still works as a deprecated alias.) |
PDFNATIVE_MCP_CACHE_DIR |
Absolute path to enable the persistent SHA-256-keyed result cache (1 h TTL, 256 MiB LRU). When unset, the cache is disabled. |
PDFNATIVE_MCP_PORT |
When set to a valid port (1–65535), starts an HTTP server on http://127.0.0.1:<port>/mcp instead of stdio. |
PDFNATIVE_MCP_HTTP_TOKEN (v1.6.0) |
Opt-in bearer token for the HTTP transport (≥ 16 characters, no whitespace — a weaker value aborts startup). When set, /mcp requires Authorization: Bearer <token>; otherwise requests get 401 + WWW-Authenticate. Without it the loopback endpoint has no authentication. Never logged. |
PDFNATIVE_MCP_MAX_INFLATE_BYTES (v1.6.0) |
Operator override of the engine's 100 MiB per-stream decompression cap (integer ≥ 1024, read once at startup; an invalid value refuses to start with one clean fatal: line). |
PDFNATIVE_MCP_TSA_URL (v1.6.0) |
RFC 3161 timestamp authority endpoint. Required by sign_pdf timestamp: true and timestamp_pdf (TSA_NOT_CONFIGURED otherwise). |
PDFNATIVE_MCP_TSA_AUTH (v1.6.0) |
Optional Authorization header value sent to the TSA. Treated as a secret; never logged. |
PDFNATIVE_MCP_REVOCATION (v1.6.0) |
Revocation sources for add_ltv mode: 'online' — ocsp, crl, or ocsp,crl (REVOCATION_NOT_CONFIGURED otherwise). |
PDFNATIVE_MCP_NETWORK_ALLOWED_HOSTS (v1.6.0) |
Mandatory allow-list of hosts the OCSP/CRL fetcher may contact. A certificate-supplied URL outside the list fails with NETWORK_HOST_NOT_ALLOWED. |
PDFNATIVE_MCP_NETWORK_TIMEOUT_MS (v1.6.0) |
Network timeout for TSA/OCSP/CRL requests (1000–120000 ms, default 10000). |
Network charter (v1.6.0). The server still makes no outbound request by default. The only egress it can ever perform goes to the TSA / OCSP / CRL endpoints the operator configures via the variables above — URLs never come from tool arguments, and certificate-supplied OCSP/CRL URLs pass an SSRF guard (allow-list, http(s) only, no credentials, no redirects, internal address literals rejected, size caps, timeouts).
Tool reference#
pdfnative-mcp exposes 28 tools:
| Tool | Purpose |
|---|---|
generate_basic_pdf |
Multi-page documents from structured blocks — all 13 block kinds since v1.6.0 (headings, paragraphs, lists, tables, images, links, TOC, barcodes, SVG, form fields, charts, page breaks, spacers). Accepts optional pdfA, layout options, build-time encrypt, and print-production fields. |
add_table |
Tabular PDF reports from column headers and data rows. Optional autoFitColumns and clipCells. Accepts pdfA. |
add_barcode |
QR Code, Code 128, EAN-13, Data Matrix, PDF417 — embedded in a single-page PDF. Accepts pdfA. |
add_international_text |
25 lang font codes — the 22 writing systems plus latin, emoji and the explicit math script (Noto Sans Math, on-demand) — with BiDi & OpenType shaping. lang accepts string, string[], or comma-separated. |
add_form |
Interactive AcroForm PDFs with text fields, text areas, checkboxes, radio buttons, dropdowns, and list boxes (v1.6.0). Accepts pdfA. |
embed_image |
Embed a JPEG or PNG image (base64-encoded) into a titled PDF document, with align and alt (v1.6.0). Accepts pdfA. |
prepare_signature_placeholder |
Create a PDF with a /Sig AcroForm placeholder ready to be signed; subFilter, reserveTimestamp and frozen signer metadata (v1.6.0). Accepts pdfA. |
sign_pdf |
PAdES CMS digital signatures — RSA-SHA256/384/512 and ECDSA-SHA256 P-256, profile: 'pades', RFC 3161 timestamp (B-T), certificate chains, named fields and multiple signatures (v1.6.0). |
add_ltv (v1.6.0) |
Embed long-term-validation material (/DSS + /VRI) into a signed PDF — PAdES B-LT — online through the operator-configured revocation provider or offline from caller-supplied material. |
timestamp_pdf (v1.6.0) |
Append a /DocTimeStamp (ETSI.RFC3161) through the operator TSA — PAdES B-LTA — with auto-suffixed field names for periodic re-timestamping. |
inspect_pdf |
Read-only inspection. Returns version, pageCount, encryption, pdfA, signatureCount, info, optional perPage, optional pageLabels[], and — (v1.6.0) — an optional signatures[] inventory, annotations[], page boxes + userUnit, dss / docTimestampCount / trapped, plus checks + checksPassed. |
inspect_layout (v1.6.0) |
Read-only pagination dry run — page count, page geometry and each block's position for a prospective document, with no PDF produced. |
validate_pdf |
Read-only PDF/UA structural validation (valid, errors, warnings). |
verify_pdf |
Real CMS/PKCS#7 signature verification — RSA & ECDSA, message digest, certificate chain. Since v1.6.0, /DocTimeStamp entries are verified as RFC 3161 tokens and ltv: true reports the achieved PAdES level (B-B → B-LTA). |
add_attachment |
Embed files (e.g. Factur-X / ZUGFeRD e-invoice XML) into PDF/A-3b output. |
extract_attachments |
Extract embedded files from an existing PDF (optionally metadata-only). |
extract_text |
Extract text content from an existing PDF via the native parser. |
merge_pdfs |
Concatenate 2–50 PDFs into one document via the page-tree API (drops signatures//AcroForm, keeps URI links). |
split_pdf |
Split one PDF into one document per page range — multi-output { mode, count, totalBytes, parts[] }. In-memory assembly is capped by maxOutputSizeBytes, default 256 MiB; each emitted PDF is separately capped at 50 MiB. |
extract_pages |
Pull an arbitrary, order-preserving page subset (max 5000) into a single PDF. |
annotate_pdf |
Overlay markup annotations (text / highlight / underline / strikeout / squiggly / square / circle / line / freetext) on an existing PDF via incremental update. A visual review layer, not a redaction. |
draft_governance_issue |
Assemble a governance-compliant GitHub-issue draft plus a structured compliance report locally — network-free by construction; never submits. |
add_chart (v1.5.0) |
Render a chart as native PDF vector paths — nine types since v1.6.0 (bar, barH, stackedBar, stackedBarH, line, area, scatter, pie, donut), with a secondary axis, log and time scales, and data labels. Tagged as /Figure with alt text when pdfA is set. |
read_form_fields (v1.5.0) |
List an existing AcroForm's fields with their types, current values and available options — the read half of the fill round-trip. Accepts password. |
fill_form (v1.5.0) |
Fill AcroForm field values and optionally flatten them into static page content. Works on encrypted PDFs via incremental update. |
encrypt_pdf (v1.5.0) |
Re-secure an existing PDF with AES-128 or AES-256 — owner/user passwords and an explicit permission set. |
decrypt_pdf (v1.5.0) |
Remove encryption from a password-protected PDF in-server — RC4, AES-128 and AES-256 sources. |
update_metadata (v1.6.0) |
Rewrite an existing PDF's /Info dictionary (+ XMP) — title, author, subject, keywords, pinned modDate — as a non-destructive incremental update. |
Every tool publishes an outputSchema advertised in tools/list. Since v1.6.0 the server speaks the MCP 2026-07-28 spec (stateless envelope, server/discover, resultType, cache hints) on SDK v2, while 2025-era clients (2025-11-25 / 2025-06-18 / 2025-03-26) keep working through the automatic legacy fallback.
generate_basic_pdf#
Produces a multi-page document from a list of content blocks.
{
"title": "Q1 2026 Report",
"blocks": [
{ "type": "heading", "text": "Executive Summary", "level": 1 },
{ "type": "paragraph", "text": "Revenue grew 24 % year over year." },
{ "type": "list", "style": "bullet", "items": ["Strong APAC", "Stable EU", "Soft NA"] },
{ "type": "pageBreak" },
{ "type": "heading", "text": "Details", "level": 2 }
],
"footerText": "Confidential — Internal use only",
"outputMode": "base64"
}
Block types supported: all 13 engine block kinds since v1.6.0 — heading (levels 1–3), paragraph, list (bullet / numbered, nested items to depth 6), table, image, link (http: / https: / mailto: only), toc (a contents page with internal /GoTo links, pairs with outline: 'auto'), barcode, svg (a safe subset: paths, shapes and text — no XML parser, no external reference ever fetched), formField, chart (same body as add_chart), pageBreak, and spacer (height in points). The table / image / barcode / formField blocks share their body with the dedicated tools, so a standalone artefact and an inline block validate and render identically.
Layout options (v1.6.0), available on all nine document tools: pageSize (A4 default, Letter, Legal, A3, Tabloid), margins (all four sides, 0–200 pt), running headerTemplate / footerTemplate (left / center / right zones with {page} {pages} {title} {date} placeholders — a footerTemplate replaces the default footer, so footerText is then ignored), compress, and debug.
Build-time encrypt (v1.6.0), on generate_basic_pdf, add_table, add_form, add_international_text, embed_image, add_barcode and add_chart: AES-128 (default) or AES-256 with owner/user passwords and permissions — and unlike encrypt_pdf, it keeps the AcroForm, making encrypted fillable forms reachable. Exclusive with pdfA (ISO 19005-1 §6.3.2); never cached.
Print production (v1.6.0), on the nine document tools: print (TrimBox / BleedBox / ArtBox / CropBox, a bleed shorthand, crop + registration marks, /UserUnit), metadata (/Author, /Subject, /Keywords, /Trapped with XMP parity), and outputIntent (custom RGB ICC profile). viewerPreferences gains duplex, pickTrayByPDFSize, printPageRange (1-based), numCopies. Boxes survive merge_pdfs / split_pdf / extract_pages and are reported by inspect_pdf.
Honest PDF/A (v1.6.0): text rendered through the viewer's base-14 Helvetica is not embedded, so a PDF/A claim on such a file is rejected by veraPDF. embedFonts: true embeds Noto Sans Latin for a valid claim; strict: true fails instead of producing a non-conformant file; includeDiagnostics: true echoes the engine's diagnostics (PDFA_NO_FONT_ENTRIES, PDFA_UNEMBEDDED_FORM_FONT, PDFA_DEVICE_CMYK_IMAGE).
Reproducible output (v1.6.0): creationDate (ISO-8601) on all nine document tools pins /Info /CreationDate, the XMP dates and therefore the trailer /ID — byte-identical output on the same host time zone.
add_table#
Generates a tabular report from column headers and rows.
{
"title": "Monthly Sales",
"headers": ["Region", "Units", "Revenue"],
"rows": [
["APAC", "1 200", "$240,000"],
["EMEA", "800", "$160,000"]
],
"infoItems": [{ "label": "Period", "value": "January 2026" }],
"footerText": "Internal use only",
"autoFitColumns": true,
"clipCells": true,
"pdfA": "pdfa2b",
"outputMode": "base64"
}
autoFitColumns and clipCells transparently switch to the document-block backend so cell content fits its column or is clipped at the boundary, leveraging pdfnative's TableBlock props. Optional pdfA produces an archive-grade variant.
Smart-table fields (v1.0.0).
add_tableexposes the six pdfnative 1.2TableBlockfields:wrap('auto'|'always'|'never', default'auto'),repeatHeader(defaulttrue),zebra,caption,minRowHeight,cellPadding. Multi-page tables reprint headers and wrap on overflow by default — agent-driven invoice/report workflows get multi-page-safe output out of the box. See the Smart tables guide for full semantics.
add_barcode#
{
"format": "qr",
"data": "https://pdfnative.dev",
"caption": "Scan to learn more",
"ecLevel": "H",
"outputMode": "file",
"outputPath": "tickets/event-42.pdf"
}
Supported formats: qr, code128, ean13, datamatrix, pdf417.
Error correction levels (QR only): L, M, Q, H.
add_international_text#
{
"title": "مرحبا بالعالم 👋",
"lang": ["ar", "emoji"],
"paragraphs": [
"هذا اختبار للنص العربي مع تشكيل OpenType ومحارف ثنائية الاتجاه.",
"Mixed content: العربية + English + emoji 🚀 ✓"
]
}
Supported lang codes: ar (Arabic), he (Hebrew), th (Thai), ja (Japanese), zh (Chinese Simplified), ko (Korean), el (Greek), hi (Devanagari/Hindi), bn (Bengali), ta (Tamil), te (Telugu), si (Sinhala), bo (Tibetan), km (Khmer), my (Myanmar), am (Ethiopic), ru (Cyrillic/Russian), ka (Georgian), hy (Armenian), tr (Turkish), vi (Vietnamese), pl (Polish), plus latin (Noto Sans VF), emoji (Noto Emoji/COLRv1) and the explicit math symbols font (v1.4.0).
lang accepts string, string[], or a comma-separated value — e.g. "ar,emoji" or ["ar", "emoji"]. When pdfA is set on this tool, the latin font is auto-registered so curly quotes, em-dashes, and ellipses validate cleanly under PDF/A.
add_form#
Creates an interactive AcroForm PDF.
{
"title": "Employee Onboarding",
"fields": [
{ "fieldType": "text", "name": "fullName", "label": "Full Name", "required": true },
{ "fieldType": "dropdown", "name": "dept", "label": "Department", "options": ["Engineering", "Sales", "HR"] },
{ "fieldType": "checkbox", "name": "agree", "label": "I agree to the terms", "checked": false }
],
"outputMode": "base64"
}
Field types: text, textarea, checkbox, radio, dropdown, and listbox (v1.6.0). Text fields accept an optional placeholder (v1.6.0). Since v1.6.0, textarea maps to the engine's true multi-line field (/Ff 4096) — in v1.5.0 it rendered as a single-line widget.
embed_image#
{
"title": "Product Photo",
"imageBase64": "<base64-encoded JPEG bytes>",
"mimeType": "image/jpeg",
"caption": "Front view of Model X",
"width": 400,
"outputMode": "base64"
}
Note: Alpha-channel PNGs (color type 6) are not supported. Pre-process such images to remove the alpha channel before embedding.
Since v1.6.0 the tool also accepts align (horizontal placement) and alt (accessibility text written as /Figure /Alt in tagged output).
prepare_signature_placeholder#
Creates a PDF pre-wired with an AcroForm /Sig field, ready to be signed by sign_pdf.
{
"title": "Service Agreement",
"signerName": "Alice Dupont",
"reason": "Approved",
"location": "Paris, FR",
"blocks": [
{ "type": "paragraph", "text": "By signing below, I accept the terms and conditions." }
],
"outputMode": "base64"
}
Since v1.6.0 the placeholder also takes subFilter, reserveTimestamp (extra /Contents room for a later RFC 3161 token), placeholderBytes, and signingTime — and the signer metadata (signerName, reason, location, contactInfo, signingTime) is baked into the /Sig dictionary at placeholder time, fixing a pre-1.7 engine bug where those values never reached the signed document.
sign_pdf#
Signs any PDF. When the input already contains a /Sig placeholder (e.g. produced by prepare_signature_placeholder) it is signed in place; otherwise the placeholder is auto-injected first (autoInjectPlaceholder defaults to true; set it to false to require an existing placeholder).
{
"pdfBase64": "<base64 PDF bytes>",
"algorithm": "rsa-sha256",
"certDerBase64": "<base64 X.509 certificate in DER format>",
"rsaKeyPkcs1DerBase64":"<base64 PKCS#1 RSAPrivateKey in DER format>",
"signerName": "Alice",
"reason": "Approval",
"location": "Paris, FR",
"signingTime": "2026-01-15T10:30:00Z"
}
For ECDSA P-256: use "algorithm": "ecdsa-sha256" and supply either ecPrivateScalarHex (64 hex chars, raw scalar d) or ecPrivateKeyDerBase64 (SEC1 / PKCS#8 DER, base64 — signed through the constant-time node:crypto path) instead of rsaKeyPkcs1DerBase64. The two EC inputs are mutually exclusive.
PAdES and timestamps (v1.6.0):
algorithmalso acceptsrsa-sha384andrsa-sha512.profile: 'pades'produces an ETSI.CAdES.detached signature (PAdES B-B).timestamp: truerequests an RFC 3161 timestamp from the operator-configured TSA (PDFNATIVE_MCP_TSA_URL) and embeds it in the CMS — PAdES B-T. Fails withTSA_NOT_CONFIGUREDwhen no TSA is set,TSA_REJECTEDwhen the authority declines.certChainDerBase64embeds intermediate certificates alongside the signer certificate.fieldNametargets a specific/Sigfield (SIGNATURE_FIELD_NOT_FOUNDwhen absent,PLACEHOLDER_AMBIGUOUSwhen several placeholders exist and none is named);allowMultiple: trueadds a signature next to existing ones instead of rejecting.- The default placeholder reservation is now
max(16384, estimated CMS size)— plus 8192 bytes whentimestamp: true— instead of a flat 16384 bytes. To pin it exactly, build the placeholder withprepare_signature_placeholderand itsplaceholderBytesinput.
To climb the rest of the PAdES ladder, follow with add_ltv (B-LT) and timestamp_pdf (B-LTA), then check the achieved level with verify_pdf ltv: true. The server's pades_ladder prompt walks through the full recipe.
inspect_pdf#
Read-only PDF inspection over openPdf(). Never modifies the input.
{
"pdfBase64": "<base64 PDF bytes>",
"pages": true,
"check": ["pdfa", "signed"]
}
Inputs:
pdfBase64— base64 PDF bytes (required).password— password for an encrypted PDF (v1.5.0).pages— whentrue, includes per-pageindex,width,height— and, since v1.6.0, the declared page boxes anduserUnit.signatures(v1.6.0) — whentrue, a per-signature inventory:subFilter,isDocTimestamp,isPlaceholder,byteRange,vriKey.annotations(v1.6.0) — whentrue, anannotations[]list (0-basedpage,subtype,rect, and when presentcontents,title,color,quadPoints, linkurl) plusannotationCount.check— array of CI assertions. Allowed values:pdfa,signed,encrypted,placeholder,attachments, and — (v1.6.0) —dss,docTimestamp,trapped,annotations. The response includeschecks(per-assertion result) andchecksPassed(boolean AND). Since v1.6.0,checkscontains only the requested keys.verbosity—'full'(default) or'summary'(token-frugal scalar subset).fields— optional dot-path projection of the result.
Outputs: version, pageCount, encryption ('none' / 'aes-128' / 'aes-256' / 'rc4' / 'unknown'), optional encryptionInfo ({ algorithm, revision, authenticatedAs }, present when the document is encrypted and opened successfully), pdfA (null or the detected claim string), signatureCount, hasSignaturePlaceholder, attachments[] (embedded-file summaries), info (decoded /Info entries), optional perPage[], optional pageLabels[] (when /PageLabels is declared), optional checks + checksPassed — plus, presence-gated since v1.6.0, dss, docTimestampCount and trapped.
Useful in CI as a final assertion step before publishing a PDF artifact:
{ "tool": "inspect_pdf",
"input": { "pdfBase64": "<...>", "check": ["pdfa", "signed"] } }
// → { ..., "checks": { "pdfa": true, "signed": true }, "checksPassed": true }
merge_pdfs#
Concatenates 2–50 PDFs into a single document via pdfnative's page-tree API. Encrypted sources are supported since v1.5.0 via password (a single password applied to every encrypted source), and the output can be re-encrypted via encrypt. Signatures and /AcroForm are dropped because page edits invalidate /ByteRange; self-contained URI /Link annotations are kept.
{
"pdfsBase64": ["<base64 PDF 1>", "<base64 PDF 2>", "<base64 PDF 3>"],
"outputMode": "base64"
}
Errors: PASSWORD_REQUIRED / PASSWORD_INVALID (encrypted source without / with the wrong password), OUTPUT_TOO_LARGE (50 MiB per emitted PDF; assembly capped by maxOutputSizeBytes, default 256 MiB), PDF_PARSE_FAILED.
split_pdf#
Splits one PDF into one document per page range, returning a multi-output shape. Ranges are 0-based and inclusive; end defaults to start (a single page).
{
"pdfBase64": "<base64 PDF bytes>",
"ranges": [{ "start": 0, "end": 2 }, { "start": 3, "end": 9 }],
"outputMode": "base64"
}
// → { "mode": "base64", "count": 2, "totalBytes": 123456, "parts": [ { "index": 0, "sizeBytes": 61000, "base64": "..." }, ... ] }
Each part is capped at 50 MiB; the aggregate output is capped at 200 MiB.
extract_pages#
Pulls an arbitrary, order-preserving page subset (max 5000 pages) into a single PDF.
{
"pdfBase64": "<base64 PDF bytes>",
"pages": [0, 2, 4, 1],
"outputMode": "base64"
}
Page indices are 0-based; the output preserves the order you request.
annotate_pdf#
Overlays markup annotations on an existing PDF via incremental update, so the original bytes — and any existing signature — stay intact.
{
"pdfBase64": "<base64 PDF bytes>",
"annotations": [
{ "type": "highlight", "page": 0, "rect": [72, 700, 520, 715], "color": "#ffe066", "contents": "Review this clause" },
{ "type": "text", "page": 1, "rect": [80, 640, 100, 660], "contents": "Sticky note" }
],
"outputMode": "base64"
}
Types: text, highlight, underline, strikeout, squiggly, square, circle, line, freetext. Each takes a 0-based page, a rect: [x1, y1, x2, y2], and optional color / contents. Encrypted sources → ENCRYPTED_SOURCE; an out-of-range page → a validation error.
Overlay, not redaction.
annotate_pdfis a visual review layer; the underlying bytes remain. It does not remove or obscure content — see the deferredredact_pdfnote under What's new in v1.4.0.
draft_governance_issue#
Assembles a governance-compliant GitHub-issue draft plus a structured compliance report locally and returns them. It never submits and makes no network call — there is no HTTP client, no GitHub SDK, and no fetch anywhere in the server.
{
"title": "SVG <text> word-wrap not supported",
"issueType": "feature",
"summary": "renderSvg() emits <text> on a single line with no automatic wrapping.",
"reproduction": {
"command": "node repro.mjs",
"result": "Long <text> overflows the viewport instead of wrapping."
},
"expectedBehavior": "Optional word-wrap for SVG <text>.",
"targetRepo": "pdfnative",
"affectedPackages": ["pdfnative"],
"duplicateSearchPerformed": true,
"outputMode": "inline"
}
duplicateSearchPerformed must be true. A draft that proposes a runtime dependency, omits a reproduction, or sets it to false is rejected with GOVERNANCE_VIOLATION. Present the returned draftMarkdown + compliance to the user; a human submits it. Read the governance_contract / draft_issue_workflow prompts first.
add_chart (v1.5.0)#
Renders a chart as native PDF vector paths — no rasterisation, no image round-trip. Multi-series bar/line, legends, gridlines and negative values are supported; the chart is tagged /Figure with /Alt (auto-generated when altText is omitted).
{
"chartType": "bar",
"title": "Revenue by quarter",
"series": [{ "label": "2026", "values": [1.2, 1.5, 1.4, 1.9] }],
"intro": "Quarterly revenue in $M.",
"pdfA": "pdfa2b",
"outputMode": "base64"
}
Inputs: chartType (bar / barH / line / pie / donut, plus — (v1.6.0) — stackedBar / stackedBarH / area / scatter) and series (required; pie/donut take exactly one series), plus title, legend ('bottom' / 'none'), markers (line series), colors (hex palette override), altText, intro (paragraph above the chart), pdfA, outputMode / outputPath. For a chart amongst headings/paragraphs/tables, use a chart block inside generate_basic_pdf — both build identical pdfnative blocks.
Charts v2 (v1.6.0): per-series xValues with xAxis.type: 'linear' | 'time' (UTC-deterministic time axes), a secondary right axis (axis2), axis.scale: 'log', dataLabels, and labelStride / labelRotation for crowded x labels. Engine cross-field rules surface as CHART_ERROR with a remedy.
read_form_fields (v1.5.0)#
Read-only enumeration of an existing PDF's AcroForm — the discovery half of the fill round-trip.
{ "pdfBase64": "<base64 PDF bytes>", "verbosity": "full" }
Inputs: pdfBase64 (required), password (encrypted sources), verbosity ('full' default / 'summary' = { fieldCount } only), fields (dot-path projection).
Outputs: fieldCount and fields[] — each with name (fully-qualified), type (text / checkbox / radio / dropdown / listbox / button / signature / unknown), current value, readOnly / required / multiline flags, options[] ({ export, label } for choice fields), maxLen, onState, and widgets[] (0-based page + rect).
fill_form (v1.5.0)#
Fills (and optionally flattens) the AcroForm of an existing PDF via a non-destructive incremental update, so a prior signature stays valid for its revision. Works on encrypted documents via password (appended objects are encrypted under the document's existing scheme).
{
"pdfBase64": "<base64 PDF bytes>",
"values": { "fullName": "Alice Dupont", "agree": true },
"flatten": false,
"outputMode": "base64"
}
Inputs: pdfBase64 (required), values (name → string | boolean | string[]; omit with flatten: true for a pure flatten), flatten (default false), onUnknownField ('throw' default / 'ignore'), nonWinAnsi ('throw' default / 'needAppearances'), password, outputMode / outputPath.
Errors: FORM_FIELD_NOT_FOUND, FORM_VALUE_TYPE_ERROR, FORM_UNSUPPORTED (signature fields cannot be filled/flattened), PASSWORD_REQUIRED / PASSWORD_INVALID.
encrypt_pdf (v1.5.0)#
Re-secures an existing PDF with AES-128 (default, widest compatibility) or AES-256 via the page-tree re-encryption path. An already-encrypted source can be rotated to a new password by supplying its current password.
{
"pdfBase64": "<base64 PDF bytes>",
"ownerPassword": "s3cret-owner",
"userPassword": "open-me",
"algorithm": "aes256",
"permissions": { "print": true, "copy": false, "modify": false, "extractText": false },
"outputMode": "base64"
}
Inputs: pdfBase64 and ownerPassword (required), userPassword (omitted/empty = opens without a prompt), algorithm ('aes128' default / 'aes256'), permissions ({ print?, copy?, modify?, extractText? }, each allowed when omitted), password (current password of an encrypted source), outputMode / outputPath.
Like merge/split/extract, encryption rebuilds the page tree: existing signatures and
/AcroFormare dropped, and only self-contained URI link annotations are kept. Encrypt before signing, not after. Excluded from the response cache.
decrypt_pdf (v1.5.0)#
Opens an encrypted PDF (RC4, AES-128 or AES-256) and emits an unencrypted copy in-server — no external tool needed.
{ "pdfBase64": "<base64 encrypted PDF>", "password": "open-me", "outputMode": "base64" }
Inputs: pdfBase64 (required), password (user or owner; omit only for documents with an empty user password), outputMode / outputPath.
The rebuild drops signatures and
/AcroForm. To read an encrypted PDF without rebuilding it, passpasswordtoinspect_pdf/extract_text/extract_attachmentsinstead. Excluded from the response cache.
add_ltv (v1.6.0)#
Embeds long-term-validation material — a /DSS (Document Security Store) with /VRI entries — into an already-signed PDF via incremental update, taking a PAdES B-T document to B-LT.
{
"pdfBase64": "<base64 signed PDF>",
"mode": "online",
"preferOcsp": true,
"outputMode": "base64"
}
Inputs: pdfBase64 (required), mode ('online' default — fetches OCSP responses / CRLs through the operator-configured revocation provider; 'offline' — embeds parse-validated caller-supplied material), preferOcsp, certificatesDerBase64[] / ocspResponsesDerBase64[] / crlsDerBase64[] (offline material), outputMode / outputPath. The structured result includes a summary of what was embedded.
Errors: LTV_NO_SIGNATURE (nothing to validate), LTV_EMPTY (no material gathered), LTV_MATERIAL_INVALID, LTV_ERROR, REVOCATION_NOT_CONFIGURED (online mode without PDFNATIVE_MCP_REVOCATION), NETWORK_HOST_NOT_ALLOWED, NETWORK_ERROR, ENCRYPTED_SOURCE (decrypting would destroy the signatures — sign before encrypting).
Online mode is the only network-touching path in the server besides the TSA, and it only ever contacts operator-allow-listed hosts. See Environment variables above.
timestamp_pdf (v1.6.0)#
Appends a /DocTimeStamp signature field (ETSI.RFC3161) through the operator-configured TSA, taking a B-LT document to B-LTA. The token's status, message imprint and nonce are checked before embedding; its own CMS signature is verified by verify_pdf.
{
"pdfBase64": "<base64 signed PDF>",
"outputMode": "base64"
}
Inputs: pdfBase64 (required), fieldName (auto-suffixed DocTimeStamp1, DocTimeStamp2, … when omitted — supporting periodic re-timestamping), placeholderBytes (4096–65536), outputMode / outputPath.
Errors: TSA_NOT_CONFIGURED, TSA_REJECTED, NETWORK_ERROR, ENCRYPTED_SOURCE.
update_metadata (v1.6.0)#
Rewrites an existing PDF's /Info dictionary — and its XMP packet when present — via a non-destructive incremental update, so earlier revisions stay a byte-exact prefix.
{
"pdfBase64": "<base64 PDF bytes>",
"title": "Quarterly Report — Final",
"author": "Finance Team",
"keywords": "finance, Q1, 2026",
"modDate": "2026-08-25T09:00:00+02:00",
"outputMode": "base64"
}
Inputs: pdfBase64 (required), title, author, subject, keywords (≤ 1000 characters), modDate (ISO-8601 with offset — pinned for reproducible bytes), outputMode / outputPath.
Errors: ENCRYPTED_SOURCE (use decrypt_pdf → update_metadata → encrypt_pdf), METADATA_ERROR, PDF_PARSE_FAILED.
inspect_layout (v1.6.0)#
A read-only pagination dry run: measures how a prospective document would paginate — without producing a PDF and without consuming output tokens on base64 bytes. The seventh read tool with verbosity / fields.
{
"title": "Q1 2026 Report",
"blocks": [
{ "type": "heading", "text": "Executive Summary", "level": 1 },
{ "type": "paragraph", "text": "Revenue grew 24 % year over year." }
],
"verbosity": "summary"
}
// → { "pageWidth": 595.28, "pageHeight": 841.89, "totalPages": 1, "blockCount": 2 }
Inputs: title and blocks (required), plus every input that moves a block — footerText, pdfA, normalize, embedFonts, pageSize, margins, headerTemplate, footerTemplate — and verbosity / fields.
Outputs (full): pageWidth, pageHeight, margins, totalPages, and pages[].blocks[] with each block's type, page, x, top, width, height (2-decimal points).
Known engine gap: a
tocblock is measured as 0 pt, so a document with a printed contents page may paginate one page later than previewed.
MCP prompts#
Since v1.4.0 the server advertises the MCP prompts capability; v1.6.0 grows it to six prompts — the two governance prompts plus four recipe prompts:
| Prompt | Purpose |
|---|---|
governance_contract |
The full AI-governance / Human-in-the-Loop contract. |
draft_issue_workflow |
The step-by-step recipe for producing a compliant issue draft with draft_governance_issue. |
pades_ladder (v1.6.0) |
The B-B → B-T → B-LT → B-LTA recipe: sign_pdf → add_ltv → timestamp_pdf, verified with verify_pdf ltv: true. |
print_ready (v1.6.0) |
Producing press-ready output: bleed, printer's marks, custom OutputIntent. |
reproducible_output (v1.6.0) |
Byte-stable output via pinned creationDate / signingTime / modDate. |
pdfa_valid (v1.6.0) |
Producing a PDF/A file that veraPDF actually accepts (embedFonts, strict, diagnostics). |
Error codes#
code |
Raised by | Meaning / fix |
|---|---|---|
ENCRYPTED_SOURCE |
annotate_pdf, update_metadata, add_ltv, timestamp_pdf |
The source PDF is encrypted and the tool has no password parameter. Remedies are tool-specific since v1.6.0: for annotate_pdf / update_metadata, decrypt_pdf → edit → encrypt_pdf; for add_ltv / timestamp_pdf, decrypting would destroy the signatures — sign before encrypting. |
PASSWORD_REQUIRED (v1.5.0) |
password-aware tools (inspect_pdf, verify_pdf, extract_text, extract_attachments, read_form_fields, fill_form, page-tree trio, encrypt_pdf, decrypt_pdf) |
The source is encrypted and no password was supplied. |
PASSWORD_INVALID (v1.5.0) |
password-aware tools | The supplied password does not open the document. |
ENCRYPTION_UNSUPPORTED (v1.5.0) |
password-aware tools | The document uses an encryption scheme the reader does not support. |
FORM_FIELD_NOT_FOUND (v1.5.0) |
fill_form |
A values key matches no field (with onUnknownField: 'throw', the default). |
FORM_VALUE_TYPE_ERROR (v1.5.0) |
fill_form |
A value's type does not match the field (e.g. a boolean for a text field). |
FORM_UNSUPPORTED (v1.5.0) |
fill_form |
The field cannot be filled/flattened (e.g. a signature field). |
OUTPUT_TOO_LARGE |
PDF-emitting tools | An emitted PDF exceeds 50 MiB (for split_pdf, also the 200 MiB aggregate), or assembly exceeds maxOutputSizeBytes (default 256 MiB). |
PDF_PARSE_FAILED |
page-tree tools | The source bytes are not a parseable PDF. |
GOVERNANCE_VIOLATION |
draft_governance_issue |
The draft breaks the AI-governance contract (proposes a runtime dependency, omits a reproduction, or duplicateSearchPerformed: false). Fix the draft and retry. |
TSA_NOT_CONFIGURED (v1.6.0) |
sign_pdf (timestamp: true), timestamp_pdf |
No PDFNATIVE_MCP_TSA_URL is set. Configure the TSA in the server environment. |
TSA_REJECTED (v1.6.0) |
sign_pdf, timestamp_pdf |
The timestamp authority declined the request. |
REVOCATION_NOT_CONFIGURED (v1.6.0) |
add_ltv (mode: 'online') |
No PDFNATIVE_MCP_REVOCATION sources are set. Configure them, or use mode: 'offline' with caller-supplied material. |
NETWORK_HOST_NOT_ALLOWED (v1.6.0) |
add_ltv |
A certificate-supplied OCSP/CRL URL points outside PDFNATIVE_MCP_NETWORK_ALLOWED_HOSTS. |
NETWORK_ERROR (v1.6.0) |
TSA/LTV tools | The TSA/OCSP/CRL request failed (timeout, refusal, size cap). |
LTV_NO_SIGNATURE / LTV_EMPTY / LTV_MATERIAL_INVALID / LTV_ERROR (v1.6.0) |
add_ltv |
No signature to validate / no material gathered / supplied DER material does not parse / embedding failed. |
METADATA_ERROR (v1.6.0) |
update_metadata |
The incremental /Info + XMP rewrite failed. |
PRINT_ERROR (v1.6.0) |
document tools with print |
The requested page boxes are inconsistent (e.g. do not fit pageSize). |
CHART_ERROR (v1.6.0) |
add_chart, chart blocks |
An engine cross-field chart rule failed (the message carries the remedy). |
PLACEHOLDER_AMBIGUOUS (v1.6.0) |
sign_pdf |
Several unsigned placeholders exist and no fieldName was given. |
SIGNATURE_FIELD_NOT_FOUND (v1.6.0) |
sign_pdf |
The named fieldName does not exist. |
CMS_PARSE_FAILED (v1.6.0) |
verify_pdf |
A CMS structure is shorter or more malformed than the parser expects. |
Protocol errors (v1.6.0): calling an unknown tool or prompt name is now a JSON-RPC
-32602error ([UNKNOWN_TOOL]/[UNKNOWN_PROMPT]), not anisErrorresult. Likewise, an unknown or misspelt input key — top-level or nested — fails withVALIDATION_ERROR("Unrecognized key") instead of being silently stripped.
The pdfA flag#
Every document tool (generate_basic_pdf, add_table, add_form, embed_image, add_barcode, prepare_signature_placeholder, add_international_text, add_chart (v1.5.0)) accepts an optional pdfA field. add_attachment is the special case: it always produces PDF/A-3b (the only conformance level that allows arbitrary embedded files), so it has no pdfA input.
pdfA value |
PDF version | Notes |
|---|---|---|
"pdfa1b" |
1.4 | Most conservative — no transparency, no AES |
"pdfa2b" |
1.7 | Default archive target |
"pdfa2u" |
1.7 | 2b + Unicode mapping for every glyph |
"pdfa3b" |
1.7 | 2b + arbitrary /EmbeddedFile attachments |
When set on add_international_text, the latin font auto-registers so non-WinAnsi Latin characters validate cleanly. Mutually exclusive with the underlying pdfnative encryption layer (ISO 19005-1 §6.3.2).
Output modes#
Every document-producing tool accepts an outputMode field. The seven read-only
tools (inspect_pdf, inspect_layout, validate_pdf, verify_pdf, extract_text,
extract_attachments, read_form_fields) return JSON only and have no
outputMode:
| Mode | Behaviour |
|---|---|
"base64" (default) |
The PDF bytes are returned inline in the MCP response as a base64 string. Suitable for pipelines that immediately consume or display the bytes. |
"file" |
The PDF is written to the sandbox directory configured via PDFNATIVE_MCP_OUTPUT_DIR. An outputPath (relative, .pdf extension) is required. Disabled unless the environment variable is set. |
Exception:
draft_governance_issueusesoutputMode: "inline" | "file"(notbase64) and writes a.mddraft — not a PDF — whenfilemode is selected.
End-to-end example: signed document#
This workflow uses two tools in sequence:
// Step 1 — create the placeholder
{
"tool": "prepare_signature_placeholder",
"input": {
"title": "Purchase Order #42",
"signerName": "Jane Smith",
"reason": "CFO approval",
"location": "London, UK",
"blocks": [
{ "type": "paragraph", "text": "Total amount: $128,000" }
],
"outputMode": "base64"
}
}
// Step 2 — sign the returned PDF
{
"tool": "sign_pdf",
"input": {
"pdfBase64": "<result from step 1>",
"algorithm": "rsa-sha256",
"certDerBase64": "<your DER certificate>",
"rsaKeyPkcs1DerBase64": "<your PKCS#1 private key>",
"signerName": "Jane Smith",
"reason": "CFO approval",
"location": "London, UK",
"signingTime": "2026-04-26T09:00:00Z",
"outputMode": "base64"
}
}
Security model#
pdfnative-mcp is designed to run safely inside your AI client:
- No network access by default — the server opens no outbound connection unless the operator configures TSA/OCSP/CRL endpoints for the PAdES tools (see the network charter under Environment variables). URLs never come from tool arguments, and certificate-supplied OCSP/CRL URLs pass an SSRF guard.
- Sandboxed file writes —
fileoutput mode is gated byPDFNATIVE_MCP_OUTPUT_DIR. When unset, file writes are rejected with aSecurityError. - Path traversal protection — absolute paths,
..sequences, NUL bytes, and non-.pdfextensions are all rejected. - Output size cap — PDF output is capped at 50 MiB per call.
split_pdfadditionally enforces 50 MiB per part and a 200 MiB aggregate ceiling across its parts, returningOUTPUT_TOO_LARGEwhen exceeded. - HTTP transport hardening — when bound to
PDFNATIVE_MCP_PORT, the HTTP transport is loopback-only and rejects foreignHost/Originheaders with 403 (DNS-rebinding protection). An opt-in bearer token (PDFNATIVE_MCP_HTTP_TOKEN) adds authentication (v1.6.0) — without it, the loopback endpoint has none. - Input validation — every tool validates inputs against strict JSON Schemas and Zod runtime checks at the boundary. Since v1.6.0 every schema is
.strict()at every nesting level, so an unknown or misspelt key is aVALIDATION_ERRORinstead of being silently stripped, and PEM armour where DER base64 is expected fails with the exactopenssl … -outform DERremedy. - Decompression cap — the engine's 100 MiB per-stream inflate cap is operator-tunable via
PDFNATIVE_MCP_MAX_INFLATE_BYTES(v1.6.0).
See SECURITY.md for responsible disclosure.
Fonts come from the local pdfnative install#
pdfnative-mcp bundles no font data of its own: at runtime it resolves the
locally installed pdfnative package and lazily imports the Noto font modules
from that package's fonts/ directory, embedding a font only when a request
actually uses it. Two practical consequences:
- Upgrading the local
pdfnativeupgrades the server's font coverage — nothing to configure, nothing re-downloaded. - The local install is an extension point for agents. A coding agent on the
host can compile a font with
compileFontData(pdfnative/tools) into the resolved local install and the next tool call serves it — the pattern that closed the mathematical-symbols gap locally before Noto Sans Math shipped in the engine. The publishedlangenum is closed and there is no operator-level fonts variable yet (a roadmap candidate), so this is a local code-level pattern — see Agentic workflows → Via the MCP server for the honest, step-by-step version.
Troubleshooting#
The server does not appear in my AI client.
Verify that Node.js ≥ 22 is installed (node --version) and that the config file path is correct for your OS. Restart the client after any config change.
file output mode returns a SecurityError.
Set the PDFNATIVE_MCP_OUTPUT_DIR environment variable to an existing absolute path in the client config.
add_international_text produces blank text.
The Noto fonts ship bundled with pdfnative — no network access is needed. Blank text usually means an unsupported lang code or a corrupted install; check the lang value against the supported list and reinstall pdfnative if needed.
sign_pdf fails with "invalid placeholder".
By default sign_pdf auto-injects a /Sig placeholder when the input has none (autoInjectPlaceholder: true), so this error normally only appears when autoInjectPlaceholder was set to false — either drop that flag or call prepare_signature_placeholder first, and make sure the placeholder PDF was not modified between creation and signing.
Output PDF exceeds 50 MB.
Split the content across multiple tool calls or reduce image/barcode count.
Release history#
The current release is v1.6.0 (28 tools, built on pdfnative 1.7.0 — see the header note). Per-release notes, oldest first:
What's new in v1.0.0#
v1.0.0 is the first stable release, built on pdfnative 1.2.0, and commits to API stability via a per-tool _meta.apiVersion field. New fields are optional and backward-compatible — omitting them produces byte-identical output. Tool count: 12 (was 9).
- Three new tools:
verify_pdf— read-only verification of every PAdES Baseline /adbe.pkcs7.detachedsignature: recomputes theByteRangeSHA-256, validates the CMSmessageDigest, and verifies thesignatureValuewith the embedded signer certificate (RSA-SHA256 + ECDSA-SHA256 P-256). OptionaltrustedRootsDerBase64enables chain trust.add_attachment— generate a PDF/A-3 (ISO 19005-3) document with one or more embedded files. Primary use case: Factur-X / ZUGFeRD electronic invoices (XML payload withrelationship: 'Source'). 8 MiB per-file cap.extract_text— best-effort plain-text extraction from a non-encrypted PDF (operands ofTj/'/"/TJ). Reportsextractable: falsewhen a page yields no text; encrypted PDFs are rejected.
- Smart-table fields on
add_table—wrap,repeatHeader,zebra,caption,minRowHeight,cellPadding(pdfnative 1.2TableBlockprops). Multi-page tables reprint headers and wrap on overflow by default. - Signing ergonomics —
sign_pdfaccepts ECDSA SEC1 / PKCS#8 DER keys andautoInjectPlaceholder: true(default) transparently callsaddSignaturePlaceholder()when the input lacks a/Sigwidget (one-call signing of any PDF). inspect_pdfnow reportshasSignaturePlaceholderand anattachments[]summary; two newcheckvalues:placeholder,attachments. Thesignedcheck is nowsignatureCount > 0 && !hasSignaturePlaceholder.- Opt-in result cache (
PDFNATIVE_MCP_CACHE_DIR) — SHA-256 keyed over canonical JSON of{tool, apiVersion, input}, 1 h TTL, 256 MiB LRU. SkipsoutputMode: 'file'calls. _meta.apiVersion = '1.0.0'and per-tool_meta.exampleson every tool listing for AI-agent discovery — anchored todocs/API_STABILITY.md.pdfAflag on every document tool — valuespdfa1b,pdfa2b,pdfa2u,pdfa3b, mapping to pdfnative'staggedoption (spread straight from pdfnative's exportedPDF_A_CONFORMANCE_TARGETS).- Env-var rename: the canonical name is
PDFNATIVE_MCP_OUTPUT_DIR(was the misspeltPDFNATIVE_MPC_OUTPUT_DIR, which still works as a deprecated alias with a one-shot stderr warning, scheduled for removal in v2.0.0).
Deferred to v1.1: merge_pdfs, split_pdf, redact_pdf — require pdfnative page-tree primitives not yet exported.
What's new in v1.1.0#
v1.1.0 upgrades the server to the pdfnative 1.3.0 engine and adds accessibility-first read tooling:
- New tool:
validate_pdf— read-only PDF/UA structural conformance checks (ISO 14289-1). - Six additional scripts in
add_international_text: Telugu (te), Sinhala (si), Tibetan (bo), Khmer (km), Myanmar (my), Ethiopic (am). - COLRv1 colour emoji support through the upgraded engine.
- Paragraph newline sanitization to avoid malformed text blocks when callers send embedded
\n.
What's new in v1.2.0#
v1.2.0 keeps full backward compatibility and extends MCP ergonomics for AI workflows:
- New tool:
extract_attachments— read embedded files from PDF/A-3 documents (Factur-X / ZUGFeRD round-trip). - Watermark support on
generate_basic_pdfandadd_table(text, opacity, angle, color, position). - Opt-in Unicode normalization (
normalize:NFC/NFD/NFKC/NFKD) on international/document flows. - Token-frugal read modes on read-only tools via
verbosity: 'summary'and selectivefieldsprojection. - Base64 payload deduplication: generated bytes are returned once via
resourcein base64 mode.
What's new in v1.3.0#
v1.3.0 upgrades the engine to pdfnative 1.4.0, adds a page-tree tool trio, and stays fully backward-compatible — tool count rises to 17:
- Three new page-tree tools:
merge_pdfs— concatenate 2–50 source PDFs into one document via pdfnative's page-tree API. Encrypted sources are rejected; signatures and/AcroFormare dropped (page edits invalidate/ByteRange); self-contained URI/Linkannotations are preserved.split_pdf— split one PDF into one document per page range, returning a multi-output shape{ mode, count, totalBytes, parts[] }. Caps: 50 MiB per part, 200 MiB aggregate.extract_pages— pull an arbitrary, order-preserving page subset (max 5000 pages) into a single PDF.
- Enriched authoring options (from pdfnative 1.4.0):
generate_basic_pdfgainsoutline('auto'or an explicit nested tree),pageLabels, nestedlistitems (max depth 6), andviewerPreferences.add_tablegainscellBorders(top/right/bottom/left, color, width, style),cellVAlign('top'/'middle'/'bottom'), andviewerPreferences.add_international_textgainsviewerPreferences.
- Constant-time signing —
sign_pdfnow signs RSA and EC-DER keys through a per-callnode:cryptoprovider (constant-time) with a transparent pure-JS fallback; pure-scalarecPrivateScalarHexremains pure-JS. - HTTP transport hardening — the
PDFNATIVE_MCP_PORTStreamable HTTP transport now rejects foreignHost/Originheaders with 403 (DNS-rebinding protection).serverInfoadvertises a human-readabletitle+description(MCP 2025-11-25 alignment). - New error codes on the page-tree tools:
ENCRYPTED_SOURCE,OUTPUT_TOO_LARGE,PDF_PARSE_FAILED. - Spec alignment — MCP SDK
^1.29(2025-11-25 revision), JSON Schema 2020-12, and_meta.apiVersionbumped to1.3.0on every tool.
What's new in v1.4.0#
v1.4.0 upgrades the engine to pdfnative 1.5.0, brings the pdfnative AI-governance / Human-in-the-Loop (HITL) system to the MCP surface, and adds markup annotations — taking the catalogue to 19 tools at that release. Fully backward-compatible: every v1.3.0 call works unchanged and default responses are byte-identical.
- Two new tools:
draft_governance_issue— an agent drafts a fully compliant GitHub issue locally (a draft.mdplus a machine-readablecompliancereport) and stops. The agent is a draftsman, never an autonomous submitter: a human is the only gate, and — by construction, not just policy — the server makes zero GitHub writes and no outbound network call. A draft that proposes a runtime dependency, omits a reproduction, or setsduplicateSearchPerformed: falseis rejected with the newGOVERNANCE_VIOLATIONerror.annotate_pdf— overlaytext,highlight,underline,strikeout,squiggly,square,circle,line, andfreetextannotations on an existing PDF via incremental update. A visual review layer, not a redaction — the underlying bytes remain. Encrypted sources →ENCRYPTED_SOURCE.
- MCP
promptscapability — the server now advertises two prompts:governance_contract(the full HITL contract) anddraft_issue_workflow(the step-by-step recipe). - Page labels in
inspect_pdf— read-only surfacing of/PageLabelsranges via a new optionalpageLabels[]output field (present only when the PDF declares them). - Math / scientific script —
add_international_textaccepts the explicitmathlang (Noto Sans Math), embedded on demand only (e.g.lang: ['latin', 'math']); there is no global auto-routing. - Engine upgrade — pdfnative
^1.4.0→^1.5.0(additive, no breaking changes);_meta.apiVersionbumped to1.4.0on every tool.
redact_pdfis deferred by design. pdfnative's annotation writer can only overlay content; an overlay-only “redaction” would leave the original bytes intact and create false security. It is intentionally not shipped and is tracked as an upstream true content-removal request — a fitting first use ofdraft_governance_issue.
What's new in v1.5.0#
v1.5.0 upgrades the engine to pdfnative 1.6.0 and takes the catalogue to 24 tools at that release, closing the read/modify loop on documents the assistant did not create.
- Five new tools:
add_chart— bar, horizontal-bar, line, pie and donut charts drawn as native PDF vector paths. No rasterisation and no image round-trip, so the output stays sharp at any zoom and tags as/Figurewith alt text. If you were previously asking an agent to render a chart to PNG and push it throughembed_image, stop — this replaces that.read_form_fields— list an AcroForm's fields with types, current values and options.fill_form— fill values and optionallyflattenthem into static page content. Works on encrypted documents via incremental update.encrypt_pdf— re-secure with AES-128 or AES-256, setting owner/user passwords and a permission set.decrypt_pdf— remove encryption in-server, for RC4, AES-128 and AES-256 sources.
- Passwords on the read tools.
inspect_pdf,verify_pdf,extract_textandextract_attachmentsaccept apassword, as do the page-tree trio. Encrypted sources are no longer rejected outright. - New error codes:
PASSWORD_REQUIRED,PASSWORD_INVALID,ENCRYPTION_UNSUPPORTED,FORM_FIELD_NOT_FOUND,FORM_VALUE_TYPE_ERROR,FORM_UNSUPPORTED. - Generated PDFs are exposed as MCP resources (
pdfnative://output/{path}), so a client can list and re-read them without a second tool call. File-mode results carry aresource_link. - Tool annotations (
readOnlyHint,destructiveHint,idempotentHint,openWorldHint) are published intools/list, letting a client reason about which calls are safe to retry. - The encryption tools are deliberately excluded from the response cache.
What's new in v1.6.0#
v1.6.0 aligns the server with the MCP 2026-07-28 specification and grows the catalogue to 28 tools, exposing the engine's full document model and completing the PAdES baseline ladder. No breaking changes: every v1.5.0 call keeps its inputs, error codes and response shape, and a superset gate against the published 1.5.0 catalogue proves it.
- Four new tools:
add_ltv— embed/DSS+/VRIlong-term-validation material (PAdES B-LT), online through the operator-configured revocation provider or offline from caller-supplied material.timestamp_pdf— append a/DocTimeStampthrough the operator TSA (PAdES B-LTA), with auto-suffixed field names for periodic re-timestamping.update_metadata— rewrite/Info(+ XMP) of an existing PDF as an incremental update.inspect_layout— a read-only pagination dry run: page count and block positions with no PDF produced.
- Full engine coverage —
generate_basic_pdfcomposes all 13 block kinds (table,image,link,toc,barcode,svg,formFieldjoin the six existing ones);pageSize,margins, runningheaderTemplate/footerTemplate,compressanddebugon the nine document tools; build-timeencryptthat keeps the AcroForm; image watermarks withposition. - PAdES signing —
sign_pdfgainsprofile: 'pades',timestamp: true(RFC 3161, B-T), RSA-SHA384/512,certChainDerBase64,fieldName/allowMultiple;verify_pdf ltv: truereports profile, timestamp, embedded revocation status and the achieved level;prepare_signature_placeholdergainssubFilter/reserveTimestampand freezes signer metadata into the/Sigdictionary (fixing a pre-1.7 engine bug where those values were silently dropped). - Network charter — still no outbound request by default; the single permitted egress class is the operator-configured TSA / OCSP / CRL endpoints, never a URL from a tool argument, behind an SSRF guard. Seven new environment variables configure the HTTP bearer token, the inflate cap and the network providers.
- Print production —
printpage boxes,bleed, printer'smarks,/UserUnit,metadatawith/Trapped, customoutputIntent; boxes survive the page-tree tools and are reported byinspect_pdf. - Charts v2 —
stackedBar/stackedBarH/area/scatter, per-seriesxValueswith linear or time axes, secondaryaxis2,axis.scale: 'log',dataLabels,labelStride/labelRotation. - Honest PDF/A —
embedFonts: true(embed Noto Sans Latin for a claim veraPDF accepts),strict: true(fail instead of emitting a non-conformant file),includeDiagnostics: true; two new diagnostics; an advisory veraPDF corpus runs locally and in CI. - MCP 2026-07-28 transport — SDK v2 (
@modelcontextprotocol/server),server/discover,resultType, cache hints, per-resultserverInfo, deterministictools/list; 2025-era clients keep working via the automatic legacy fallback. - Four new recipe prompts —
pades_ladder,print_ready,reproducible_output,pdfa_valid— taking the prompt catalogue to six. - Reproducible output —
creationDateon all nine document tools,signingTimeon the placeholder,modDateonupdate_metadata.
Migrating from v1.5.0 — six behaviour changes, all on error paths or on inputs the published schema already declared invalid:
- Stray keys fail. An unknown or misspelt key, top-level or nested, is now
VALIDATION_ERROR("Unrecognized key") instead of being silently stripped — the schemas always declaredadditionalProperties: false. - Page-index mistakes are
VALIDATION_ERROR. Out-of-range pages / ranges onmerge_pdfs/split_pdf/extract_pageswerePDF_PARSE_FAILED; the message now carries a 0-based hint. validate_pdfon unparsable bytes is an error, not a verdict. v1.5.0 returned{ valid: false }; v1.6.0 returnsisError: true[PDF_PARSE_FAILED]— a parse failure is not a PDF/UA verdict.- Unknown tool or prompt names are protocol errors — JSON-RPC
-32602([UNKNOWN_TOOL]/[UNKNOWN_PROMPT]), noisErrorresult. inspect_pdf.checksholds only the keys you asked for — readchecksPassedor the requested key, never an absent one.add_formtext areas change bytes.fieldType: 'textarea'now produces a real multi-line field (/Ff 4096).
Further reading#
- pdfnative-mcp on GitHub — source, issues, CHANGELOG
- pdfnative-mcp on npm — version history, install stats
- pdfnative Quick Start — pdfnative library directly in Node.js / browser
- Architecture guide — how pdfnative-mcp sits in the ecosystem
- Model Context Protocol specification — MCP standard reference