Choosing your surface
Write application code → the library. Drive a shell, Makefile or CI job →
pdfnative-cli. Give a conversational AI assistant tool access →pdfnative-mcp. Author documents inside a React 19 app →pdfnative-react. All four surfaces sit on the same zero-dependency engine and produce the same ISO 32000-1 / PDF/A-conformant bytes, so the choice is about who is calling, not about what comes out — and you can switch later without re-authoring your documents.
The decision, in prose#
You are writing application code — a Node.js, Deno, Bun or browser
service, a worker, a script with logic around the PDF. Use the library
(npm install pdfnative). It is the full surface: synchronous builders, the
parser, signatures with long-term validation, streaming, Web Worker support.
Everything the other three surfaces do, they do by calling this package.
You are driving a shell, a CI pipeline, a container, or a build tool in
another language. Use the CLI (pdfnative-cli, binary pdfnative) —
21 commands over stdin/stdout pipelines, with an agent-native automation
contract: a --json envelope, stable E_* error codes, --dry-run, and
compact --summary / --fields output projection. No JavaScript required.
You are (or you are building) a conversational assistant with tool
access — Claude Desktop, Cursor, Continue, Zed, or any Model Context
Protocol client. Use the MCP server (pdfnative-mcp, npx -y pdfnative-mcp) — 28 tools with strict JSON Schemas, read-back tools for
self-verification, and no outbound network access except the
operator-configured TSA/OCSP/CRL endpoints.
Your host application is React 19. Use the React renderer
(pdfnative-react) — declarative JSX compiled on-device to pdfnative blocks
by a custom reconciler (no DOM, no headless browser), with live-preview hooks
and the token-frugal DocSpec for agent authoring. React 19 is a peer
dependency of this package only; the engine stays zero-dependency.
Capability × surface#
The same facts in machine-readable form live in
docs/data/surfaces.json; tool, command and export
names are verified against
docs/assets/ecosystem.json and the engine's
export surface by the documentation CI. Version annotations name the release
of that surface's own package which introduced the capability; an em-dash
means the surface does not offer it.
| Capability | Library (pdfnative) |
CLI (pdfnative-cli) |
MCP (pdfnative-mcp) |
React (pdfnative-react) |
|---|---|---|---|---|
| Generate documents | buildDocumentPDFBytes / buildPDFBytes |
render |
generate_basic_pdf (+ the dedicated document tools) |
renderToBytes and friends, <Document> tree |
| Smart tables | table block |
render (table block, or --variant table) |
add_table |
<Table> |
| Native vector charts | chart block (v1.6.0) |
render (chart block) (v1.3.0) |
add_chart (v1.5.0) |
<Chart> (v1.1.0; charts v2 — nine kinds, secondary axis, log/time scales, data labels — since v1.2.0) |
| Digital signatures (PAdES CMS) | addSignaturePlaceholder (v1.2.0) + signPdfBytes |
sign |
sign_pdf (+ prepare_signature_placeholder) |
— |
| LTV ladder (B-T → B-LTA) | signPdfBytesWithTimestamp, addValidationInfo, addDocumentTimestamp (v1.7.0) |
sign --timestamp, ltv, doc-timestamp (v1.4.0) |
add_ltv, timestamp_pdf (v1.6.0) |
— |
| Encrypt / decrypt | build-time encryption layout option; existing PDFs via the page-tree encrypt option and openPdf with a password |
encrypt / decrypt (v1.3.0) |
encrypt_pdf / decrypt_pdf (v1.5.0) |
build-time only, via the layout render option |
| Fill / flatten forms | readFormFields, fillForm, flattenForm (v1.6.0) |
fill (v1.3.0) |
read_form_fields, fill_form (v1.5.0) |
— |
| Extract text | extractText (v1.6.0) |
extract-text (v1.3.0) |
extract_text |
— |
| Merge / split / extract pages | mergePdfs, splitPdf, extractPages (v1.4.0) |
merge / split / extract (v1.2.0) |
merge_pdfs / split_pdf / extract_pages (v1.3.0) |
— |
| Markup annotations | buildAnnotation + PdfModifier.addAnnotation (v1.5.0) |
annotate (v1.2.0) |
annotate_pdf (v1.4.0) |
— |
| Inspect layout (pagination dry run) | inspectDocumentLayout (v1.5.0) |
render --inspect-layout (v1.2.0) |
inspect_layout (v1.6.0) |
inspectDocument |
| Validate PDF/UA | validatePdfUA (v1.3.0) |
inspect --pdfua (v1.1.0) |
validate_pdf (v1.1.0) |
— (lintDocument checks the authoring model before rendering, not the emitted PDF) |
| Update metadata (signature-safe) | PdfModifier.updateMetadata (v1.7.0) |
metadata (v1.4.0) |
update_metadata (v1.6.0) |
— |
| Compare two PDFs (text + structure) | — (compose openPdf / extractText / the readers) |
compare (v1.4.0) |
— | — |
| Runtime font registration | registerFont / registerFonts (v1.5.0, any loader — incl. fonts you compile with compileFontData) |
render --font (bundled fonts only) (v1.1.0) |
— (fixed lang enum; fonts resolved from the local pdfnative install) |
registerFont / registerFonts (re-exported) |
Honest notes#
- LTV transport differs by surface on purpose. The engine opens no socket:
in the library, the RFC 3161 / OCSP / CRL transport is injected by your
code (
setTimestampProvider/setRevocationProvider). On MCP, the transport is operator-configured through environment variables (PDFNATIVE_MCP_TSA_URL,PDFNATIVE_MCP_REVOCATION, an allow-list) — never from tool arguments. On the CLI (since v1.4.0, which completes the write-side ladder), every network touch is an explicit per-invocation opt-in —sign --timestamp <url>,ltv --online,doc-timestamp --url— behind an SSRF guard, andltv collect/ltv embedsplit the ladder across an air gap: evidence is gathered as replayable JSON on a connected machine and embedded fully offline. - The engine ships no cryptographic signature verifier.
listSignaturesis an inventory; full verification (digest, CMS, chain, trust, timestamps, revocation) lives inpdfnative-cli verifyand the MCPverify_pdftool. - React is an authoring surface. It generates documents (including charts,
barcodes, SVG, form widgets and build-time encryption via the
layoutrender option) but does not operate on existing PDFs — no fill, extract, merge or signing. When a React app needs those, call the library directly: it is already installed as the renderer's peer dependency. - The MCP tool names, CLI commands and library exports in the table are the complete story for these capabilities, not a sample — where a cell is an em-dash, the surface genuinely lacks the capability today rather than hiding it under another name.
You can switch later#
All four surfaces call the same engine, so the artefacts are interchangeable:
a PDF rendered by the React reconciler can be signed by the CLI, inspected by
an MCP tool, and have its text extracted by the library. Document inputs
travel too — the CLI's render consumes the same DocumentParams JSON the
library takes, the MCP generate_basic_pdf blocks mirror the engine's block
kinds, and pdfnative-react compiles JSX (or a DocSpec) into that same
model. Starting on the "wrong" surface costs a call-site migration, not a
document rewrite.
Further reading#
- Onboarding — the 90-second install-and-first-call for each surface.
- Ecosystem use cases — four production architectures that compose these surfaces, with diagrams.
- Self-verifying generation — the generate → inspect → assert → correct loop on every surface.
- Architecture — how the four packages relate.
- CLI guide · MCP guide · React guide — the complete per-surface references.
- Agent brief — the same decision tree in paste-into-context form for AI agents.