
pdfnative
โ 1from Nizoka
Local-first PDF engine for AI agents. Zero-dep TypeScript, PDF/A, signatures, 800+ pages/sec.
pdfnative-mcp
Model Context Protocol (MCP) server that bridges the pdfnative library โ a zero-dependency, ISO 32000-1 compliant PDF engine โ to any MCP-compatible AI client (Claude Desktop, Cursor, Continue, ChatGPT, Zed, โฆ).
โจ Features
pdfnative-mcp exposes 19 production-grade tools to any MCP host:
| Tool | Purpose |
|---|---|
generate_basic_pdf | Multi-page A4 documents from structured blocks (headings, paragraphs, lists, page breaks). Embedded newlines auto-split into paragraphs. Optional pdfA. |
add_barcode | QR Code, Code 128, EAN-13, Data Matrix, PDF417 โ embedded in a single-page PDF. |
add_international_text | 24 scripts (incl. Latin & COLRv1 colour emoji) with BiDi & OpenType shaping; multi-lang per document. |
add_table | Tabular reports with smart fields (wrap, repeatHeader, zebra, caption, minRowHeight, cellPadding). |
add_form | Interactive AcroForm PDFs with text fields, checkboxes, radio buttons, dropdowns. |
embed_image | Embed a JPEG or PNG image (base64) into a titled PDF document. |
prepare_signature_placeholder | Step 1 of the two-step sign workflow โ create a PDF with a /Sig AcroForm placeholder. |
sign_pdf | Apply a PAdES-compatible CMS signature (RSA-SHA256 / ECDSA-SHA256 P-256). Auto-injects a placeholder when needed. |
verify_pdf | Verify every PAdES signature in a PDF (integrity + signature value + optional chain trust). |
validate_pdf (new in v1.1.0) | Validate a Tagged PDF for PDF/UA (ISO 14289-1) structural conformance (read-only). |
add_attachment | Generate a PDF/A-3 document with embedded files (Factur-X / ZUGFeRD invoices). |
extract_attachments | Read-only extraction of embedded files (Factur-X / ZUGFeRD XML round-trip) with byte-for-byte payloads. |
extract_text | Best-effort plain-text extraction from a non-encrypted PDF. |
inspect_pdf | Read-only inspection: PDF version, page count, encryption, PDF/A claim, signatures, attachments, placeholder state. |
merge_pdfs (new in v1.3.0) | Concatenate 2โ50 PDFs into one via pdfnative's page-tree API. |
split_pdf (new in v1.3.0) | Split one PDF into one document per page range (multi-output). |
extract_pages (new in v1.3.0) | Pull an arbitrary page subset into a single PDF. |
annotate_pdf (new in v1.4.0) | Add markup annotations (highlight, note, square/circle, line, freetext) as a visual overlay โ not a redaction. |
draft_governance_issue (new in v1.4.0) | Draft a governance-compliant GitHub issue locally for human review; never submits, no network. |
New in v1.4.0:
- ๐ค AI governance + human-in-the-loop โ
draft_governance_issuelets an agent draft a fully compliant GitHub issue locally (draft.md+ machine-readable compliance report). The agent is a draftsman, never an autonomous submitter: a human is the only gate, and the server makes zero GitHub writes and no outbound network calls. Backed by thegovernance_contractanddraft_issue_workflowMCP prompts. - โ๏ธ Markup annotations โ
annotate_pdfoverlays highlight, sticky-note, underline, strikeout, squiggly, square, circle, line, and freetext annotations on an existing PDF via incremental update. It is a visual review layer, not a redaction โ underlying bytes remain. - ๐ข Page labels in
inspect_pdfโ read-only surfacing of/PageLabelsranges (roman, decimal, prefixed). - โ Math / scientific script โ
add_international_textacceptslang: 'math'(explicit, likeemoji) to embed the Noto Sans Math face on demand. - ๐งฉ MCP prompts โ the server now advertises the
promptscapability withgovernance_contractanddraft_issue_workflow. - โฌ Engine upgrade โ pdfnative v1.5.0.
New in v1.3.0:
-
๐ Three page-tree tools โ
merge_pdfs,split_pdf,extract_pages(built on pdfnative v1.4.0's page-tree API; encrypted sources are rejected). -
๐ Bookmarks, page labels & nested lists โ
generate_basic_pdfgainsoutline('auto'or explicit tree),pageLabels, multi-levellistitems, andviewerPreferences. -
๐ Table cell borders & alignment โ
add_tablegainscellBorders,cellVAlign, andviewerPreferences;add_international_textgainsviewerPreferences. -
๐ Constant-time signing โ
sign_pdfsigns RSA and EC-DER keys through anode:cryptoprovider with a transparent pure-JS fallback; signatures stay interoperable. -
โฌ Engine upgrade โ pdfnative v1.4.0.
-
๐ Tool
extract_attachmentsโ read embedded files back out of a PDF (completes the Factur-X / ZUGFeRD round-trip) with byte-for-byte payloads, afilenamefilter, and anincludeData: falsemetadata-only probe. -
๐ง Watermarks โ
generate_basic_pdfandadd_tableaccept an optionalwatermark(text, opacity, angle, colour, position) rendered on every page. -
๐ Unicode
normalizeโ opt-inNFC/NFD/NFKC/NFKDongenerate_basic_pdfandadd_international_text. -
๐ช Token-frugal reads โ the read-only tools (
inspect_pdf,verify_pdf,validate_pdf,extract_text,extract_attachments) accept optionalverbosity: 'summary'andfields: [โฆ]inputs for ~90% smaller responses on large results, with no loss of the fields agents branch on. Defaults are unchanged. -
๐ช No base64 duplication โ generated PDFs (base64 mode) are returned once as an embedded
resourcecontent block instead of also being copied intostructuredContent. -
๐ง MCP registry publish fix โ
mcpNamenow uses the canonical GitHub login casing (io.github.Nizoka/pdfnative-mcp) so the registry's case-sensitive validation accepts the npm package. -
โฌ Dependency โ upgraded to zod 4.
New in v1.1.0:
- ๐ Tool
validate_pdfโ read-only PDF/UA (ISO 14289-1) structural conformance check. - ๐ Six new scripts โ Telugu, Sinhala, Tibetan, Khmer, Myanmar, Ethiopic (24 scripts total).
- ๐ COLRv1 colour emoji โ native colour emoji with monochrome fallback.
- ๐ Newline sanitizer โ embedded
\nin paragraphs auto-splits into separate paragraphs (Safe PDF/A). - ๐ Automatic NFC normalisation for
add_international_text. - ๐ Engine upgrade โ pdfnative v1.3.0: the Euro sign / CP-1252 symbols now extract correctly, and wrapped table cells get unique per-line MCIDs (PDF/UA-safe).
New in v1.0.0:
- ๐ Three new tools:
verify_pdf,add_attachment(Factur-X / ZUGFeRD),extract_text. - ๐ Smart-table fields:
wrap,repeatHeader,zebra,caption,minRowHeight,cellPadding. - ๐
inspect_pdfnow reportshasSignaturePlaceholderand per-attachment summary; newcheckvalues'placeholder'and'attachments'. - ๐ Signing ergonomics:
sign_pdfaccepts ECDSA SEC1 / PKCS#8 DER keys and auto-injects a/Sigplaceholder when missing (one-call signing of any PDF). - ๐ Opt-in cache (
PDFNATIVE_MCP_CACHE_DIR): SHA-256 keyed, 1ย h TTL, 256ย MiB LRU. - ๐
_meta.apiVersionand per-tool_meta.examplesfor AI-agent discovery โ seedocs/API_STABILITY.md. - ๐ AI agent guide:
docs/AI_GUIDE.mdโ decision tree + common pitfalls. See also the rootAGENTS.mdoperations manual. - ๐ PDF/A authoring guide:
docs/guides/PDFA.md. - ๐ Env-var rename:
PDFNATIVE_MCP_OUTPUT_DIR(wasPDFNATIVE_MPC_OUTPUT_DIR; old name still works with a one-shot deprecation warning). - โ
Now shipped:
merge_pdfs,split_pdf,extract_pages(v1.3.0) andannotate_pdf(v1.4.0).redact_pdfstays deferred โ pdfnative can only overlay content, and an overlay-only "redaction" would create false security, so it is intentionally not shipped (tracked as an upstream content-removal request).
All tools support two output modes:
base64(default) โ the generated PDF is returned once as an embeddedresourcecontent block (adata:application/pdf;base64,โฆURI);structuredContentcarries only{ mode, sizeBytes }.fileโ the PDF is written to a sandboxed directory configured viaPDFNATIVE_MCP_OUTPUT_DIR. File output is disabled unless this variable is set; absolute paths, path traversal, non-.pdfextensions, and NUL bytes are all rejected.
Upgrading from v1.1.0: the only behaviour change is that base64-mode bytes are no longer duplicated into
structuredContent.base64. Read them from the embeddedresourceblock instead:- const base64 = response.structuredContent.base64; // v1.1.0 + const block = response.content.find((c) => c.type === 'resource'); + const base64 = block.resource.blob; // v1.2.0
Token-frugal reads (v1.2.0). The four read-only tools accept two optional inputs:
verbosity: 'summary'โ returns a compact scalar-only verdict (drops the heavy arrays / full text). E.g.verify_pdfโ{ signatureCount, allValid, invalid, summary }.fields: ['a', 'b.c']โ projects the structured result to named dot-paths; composes afterverbosity. Unknown paths are omitted leniently.
Smallest โis this PDF signed and valid?โ probe: { "pdfBase64": "โฆ", "verbosity": "summary", "fields": ["allValid"] }.
Why pdfnative?
pdfnative-mcp inherits every guarantee of the underlying engine:
- Zero runtime dependencies โ pure JavaScript, no native bindings.
- ISO 32000-1 (PDF 1.7) compliant output.
- PDF/A-1b/2b/3b, AES-128/256 encryption, AcroForm, digital signatures.
- 16 Unicode scripts with built-in BiDi reordering, Arabic positional shaping, Thai/Devanagari/Bengali/Tamil OpenType shaping.
- Tree-shakeable ESM build.
๐ Tool reference
generate_basic_pdf
{
"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"
}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.
add_international_text
{
"title": "ู
ุฑุญุจุง ุจุงูุนุงูู
",
"lang": "ar",
"paragraphs": [
"ูุฐุง ุงุฎุชุจุงุฑ ูููุต ุงูุนุฑุจู ู
ุน ุชุดููู OpenType ูู
ุญุงุฑู ุซูุงุฆูุฉ ุงูุงุชุฌุงู.",
"Mixed content: ุงูุนุฑุจูุฉ + English โ"
]
}Supported lang codes: ar, he, th, ja, zh, ko, el, hi, bn, ta, ru, ka, hy, tr, vi, pl, latin, emoji, math.
Multi-script documents โ pass an array or comma-separated list:
{
"title": "Mixed Script",
"lang": ["ar", "emoji"],
"paragraphs": ["ุงูุนุฑุจูุฉ ู
ุน ุฑู
ูุฒ ๐๐"],
"pdfA": "pdfa2u"
}sign_pdf
As of v1.0.0, sign_pdf auto-injects a /Sig placeholder when missing โ you can sign any PDF in one call:
{
"pdfBase64": "<any base64 PDF>",
"algorithm": "rsa-sha256",
"certDerBase64": "<base64 X.509 cert in DER>",
"rsaKeyPkcs1DerBase64": "<base64 PKCS#1 RSAPrivateKey DER>",
"signerName": "Alice",
"reason": "Approval",
"location": "Paris, FR",
"signingTime": "2026-01-15T10:30:00Z"
}For ECDSA P-256: use algorithm: "ecdsa-sha256" and supply either ecPrivateKeyDerBase64 (SEC1 or PKCS#8 DER) or ecPrivateScalarHex (64 hex chars).
PEM โ DER conversion:
openssl x509 -in cert.pem -outform DER | base64 -w0 # cert
openssl rsa -in key.pem -outform DER -traditional | base64 -w0 # RSA PKCS#1
openssl pkey -in key.pem -outform DER | base64 -w0 # ECDSAUse
prepare_signature_placeholderonly when you need to customize the placeholder (e.g. largerplaceholderBytesfor >4096-bit RSA keys). Otherwise callsign_pdfdirectly.
add_table
{
"title": "Monthly Sales",
"headers": ["Region", "Units", "Revenue"],
"rows": [
["APAC", "1200", "$240,000"],
["EMEA", "800", "$160,000"]
],
"infoItems": [{ "label": "Period", "value": "January 2025" }],
"footerText": "Internal use only",
"outputMode": "base64"
}add_form
{
"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"
}embed_image
{
"title": "Product Photo",
"imageBase64": "<base64-encoded JPEG bytes>",
"mimeType": "image/jpeg",
"caption": "Front view of Model X",
"width": 400,
"outputMode": "base64"
}Note: pdfnative does not support alpha-channel PNGs (color type 6). Pre-process such images to remove the alpha channel before embedding.
prepare_signature_placeholder
{
"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"
}Pass the returned PDF bytes to sign_pdf to complete the signing workflow.
inspect_pdf
Read-only structural and security inspection โ useful for downstream verification, CI assertions, and AI agents that need to reason about a PDF before acting on it.
{
"pdfBase64": "<base64 PDF>",
"pages": true,
"check": ["pdfa", "signed", "attachments"]
}Returns:
{
"version": "1.7",
"pageCount": 3,
"encryption": "none", // 'none' | 'aes-128' | 'aes-256' | 'rc4' | 'unknown'
"pdfA": "3B", // null when no PDF/A claim is present
"signatureCount": 1,
"hasSignaturePlaceholder": false,
"attachments": [{ "filename": "factur-x.xml", "mimeType": "application/xml", "sizeBytes": 1234, "relationship": "Source" }],
"info": { "Producer": "pdfnative", "Title": "Invoice INV-2025-001" },
"perPage": [{ "index": 0, "width": 595, "height": 842 }],
"checks": { "pdfa": true, "signed": true, "attachments": true },
"checksPassed": true
}check[] accepts any of 'pdfa', 'signed', 'encrypted', 'placeholder', 'attachments'. checksPassed is the AND of all requested checks.
validate_pdf
Read-only PDF/UA (ISO 14289-1) structural conformance check for a Tagged PDF. Generate an accessible document with any tool using pdfA (e.g. pdfA: 'pdfa2u'), then validate the result:
{ "pdfBase64": "<tagged-pdf-base64>" }Returns:
{
"standard": "pdf-ua-1",
"valid": true,
"errors": [], // blocking structural violations (empty when valid)
"warnings": [], // non-blocking best-practice recommendations
"summary": "PDF/UA structural prerequisites hold."
}It verifies catalog /MarkInfo /Marked true, /StructTreeRoot (+ /ParentTree), /Metadata (XMP), /Lang, and per-page MCID uniqueness. This is a fast developer-time gate โ not a substitute for a full reference validator (veraPDF), which additionally checks fonts, colour, and rendering.
annotate_pdf
Overlay markup annotations on an existing PDF via incremental update. This is a visual review layer, not a redaction โ the underlying content is untouched.
{
"pdfBase64": "<base64 PDF>",
"annotations": [
{ "type": "highlight", "page": 0, "rect": [72, 700, 520, 715], "color": [1, 1, 0], "contents": "Check this figure" },
{ "type": "text", "page": 0, "rect": [540, 700, 560, 720], "contents": "Reviewer note" }
]
}Types: text, highlight, underline, strikeout, squiggly, square, circle, line, freetext. Encrypted sources are rejected (ENCRYPTED_SOURCE).
draft_governance_issue
Draft a governance-compliant GitHub issue locally for a human to review and submit. The server never contacts GitHub and makes no outbound network calls; it returns the draft Markdown plus a machine-readable compliance report.
{
"title": "add_table drops the caption on the second page",
"issueType": "bug",
"summary": "The table caption is only rendered on page 1 when repeatHeader is true.",
"reproduction": { "command": "node examples/run.mjs add-table-caption.json", "result": "Page 2 has no caption row." },
"expectedBehavior": "The caption repeats with the header on every page.",
"duplicateSearchPerformed": true
}A draft that proposes a runtime dependency, omits a reproduction, or sets duplicateSearchPerformed: false is rejected with GOVERNANCE_VIOLATION. See docs/guides/AI_GOVERNANCE.md for the full human-in-the-loop contract.
verify_pdf, add_attachment, extract_text
See the dedicated sections in docs/AI_GUIDE.md and the reference in docs/KNOWLEDGE_BASE.md. Ready-to-run examples live under examples/.
๐ Security model
pdfnative-mcp runs inside the host process and exposes a stdio MCP server. It does not open network sockets and does not perform any I/O outside the configured sandbox.
- File writes are gated by
PDFNATIVE_MCP_OUTPUT_DIR. When unset, thefileoutput mode is rejected with aSecurityError. - Path resolution rejects absolute paths, traversal sequences (
..), NUL bytes, and any extension other than.pdf. - Output size is capped at 50 MB per call.
- Inputs are validated against strict JSON Schemas + Zod runtime checks at the boundary of every tool.
See SECURITY.md for the responsible disclosure process.
๐งช Local development
git clone https://github.com/Nizoka/pdfnative-mcp.git
cd pdfnative-mcp
npm install
npm run typecheck
npm run lint
npm test
npm run buildSmoke-test the server over stdio:
node dist/cli.js
# In another terminal, send a JSON-RPC initialize request via stdin (e.g. with mcp-inspector).Contributors: see docs/guides/LOCAL_TESTING.md for the full local-verification workflow โ the quality gate, examples-as-tests, validating that generated PDFs are structurally correct (
assertValidPdf,inspect_pdf,validate_pdf,verify_pdf), opening output in a viewer, external PDF/A checking with veraPDF, and the MCP Inspector.
๐ฃ Release process
pdfnative-mcp follows the same release formalism as pdfnative:
- One release note file per tag in
release-notes/vX.Y.Z.md CHANGELOG.mdmirrors each release bullet list- GitHub Release body is copied from
release-notes/vX.Y.Z.md - npm publication is handled by GitHub Actions Trusted Publishing (OIDC), without
NPM_TOKEN
See release-notes/TEMPLATE.md for the canonical structure and publication checklist.
๐ Project structure
src/
โโโ cli.ts # stdio entrypoint (#!/usr/bin/env node)
โโโ index.ts # public library exports
โโโ server.ts # McpServer factory + tool registry
โโโ output.ts # sandboxed file writer / base64 emitter (single + multi)
โโโ text.ts # newline sanitizer (Safe PDF/A)
โโโ doc-features.ts # nested lists, outline, page labels, viewer prefs
โโโ pagetree.ts # page-tree error mapping (merge/split/extract)
โโโ crypto-provider.ts # node:crypto constant-time signature provider
โโโ errors.ts # ToolError, SecurityError
โโโ tools/
โโโ generate-basic-pdf.ts
โโโ add-barcode.ts
โโโ sign-pdf.ts
โโโ add-international-text.ts
โโโ add-table.ts
โโโ add-form.ts
โโโ embed-image.ts
โโโ inspect-pdf.ts
โโโ verify-pdf.ts
โโโ validate-pdf.ts
โโโ add-attachment.ts
โโโ extract-attachments.ts
โโโ extract-text.ts
โโโ merge-pdfs.ts
โโโ split-pdf.ts
โโโ extract-pages.ts
โโโ prepare-signature-placeholder.ts
tests/ # vitest suites๐บ Roadmap
v1.3.0 is shipped. The full plan โ released milestones, in-progress work, and long-term direction โ lives in ROADMAP.md.
Blocked upstream (page-tree manipulation):
redact_pdfand an encrypted-PDF round-trip โ pdfnative does not yet export the content-redaction / decryption primitives required to build these safely. They remain on the roadmap, blocked on an upstream API. (merge_pdfs,split_pdfandextract_pagesshipped in v1.3.0.)
Have a feature idea? Open an issue or PR.
โญ Star the project
If pdfnative-mcp is useful to you, please โญ this repository โ and consider also starring the underlying engine Nizoka/pdfnative. Stars help others discover the project and motivate continued development.
๐ค Contributing
Contributions are very welcome. Please read CONTRIBUTING.md, check the open issues, and follow the code of conduct.
๐ License
MIT ยฉ 2026 Nizoka
pdfnative-mcp is built on top of pdfnative and the Model Context Protocol TypeScript SDK.
# Run directly with npx (recommended for MCP clients)
npx -y pdfnative-mcp
# Or install globally
npm install -g pdfnative-mcp
pdfnative-mcpBefore it works, you'll need: PDFNATIVE_MCP_OUTPUT_DIR
๐ Installation
# Run directly with npx (recommended for MCP clients)
npx -y pdfnative-mcp
# Or install globally
npm install -g pdfnative-mcp
pdfnative-mcpRequirements: Node.js โฅ 22.
โ๏ธ Configuration
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"pdfnative": {
"command": "npx",
"args": ["-y", "pdfnative-mcp"],
"env": {
"PDFNATIVE_MCP_OUTPUT_DIR": "/Users/you/Documents/mcp-pdfs"
}
}
}
}Cursor / Continue / Zed / Windsurf / Cline / Roo Code
Any MCP-compatible client that supports stdio servers will work. Use the same command + args + env triple. Example for Cursor (~/.cursor/mcp.json):
{
"mcpServers": {
"pdfnative": {
"command": "npx",
"args": ["-y", "pdfnative-mcp"],
"env": { "PDFNATIVE_MCP_OUTPUT_DIR": "/Users/you/Documents/mcp-pdfs" }
}
}
}Windsurf / Cline / Roo Code use the same shape inside their respective MCP config files.
Environment variables
| Variable | Purpose |
|---|---|
PDFNATIVE_MCP_OUTPUT_DIR | Absolute path to the sandbox directory. Required to enable outputMode: 'file'. |
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. Binds loopback only and enables DNS-rebinding protection (foreign Host/Origin โ 403). |
No common issues documented yet. If you hit a problem, the repository's GitHub Issues page is the best place to look.
Licensed under MITโ you can use, modify, and redistribute it under that license's terms.
View the full license file on GitHub โ