pdfnative

Motor de PDF local para agentes de IA. TypeScript sin dependencias, PDF/A, firmas, 800+ páginas/segundo.

Documentación

pdfnative-mcp

Servidor MCP para generación de PDF, archivo PDF/A, intercambio de impresión PDF/X-4, tipografía fina, firma PAdES con validación a largo plazo, AcroForms, fusionar/dividir, cifrado y vista previa de diseño — 28 herramientas y 7 indicaciones en el motor pdfnative (sin dependencias, compatible con ISO 32000-1), para Claude Desktop, Cursor, ChatGPT y cualquier cliente del Model Context Protocol.

npm version npm downloads Node version License: MIT CI MCP pdfnative TypeScript OpenSSF Scorecard CodeQL


✨ Características

pdfnative-mcp expone 28 herramientas de nivel de producción a cualquier host MCP:

HerramientaPropósito
generate_basic_pdfDocumentos de varias páginas a partir de 13 tipos de bloques — heading, paragraph, list, table, image (JPEG/PNG), link, toc (índice impreso), barcode, svg, formField, chart, pageBreak, spacer — cada DocumentBlock que ofrece el motor. Los saltos de línea incrustados se dividen automáticamente en párrafos. Opcional: pdfA, pdfx (nuevo en v1.7.0), print, metadata, embedFonts, watermark, outline, opciones de diseño (pageSize, margins, headerTemplate / footerTemplate, compress, debug, encrypt), typography (nuevo en v1.7.0) y colores CMYK (nuevo en v1.7.0).
inspect_layout (nuevo en v1.6.0)Simulación de paginación de solo lectura del mismo blocks (+ title, footerText, pdfA, normalize, embedFonts, pageSize, margins, headerTemplate, footerTemplate, typography): número de páginas y dónde aterriza cada bloque, sin producir PDF.
add_barcodeCódigo QR, Code 128, EAN-13, Data Matrix, PDF417 — incrustados en un PDF de una sola página.
add_international_text27 escrituras Unicode — Lao, Tai Tham, New Tai Lue, Tai Le y Cham (nuevo en v1.7.0) — además de latín, matemáticas y emojis de color COLRv1 (secuencias de bandera / ZWJ, modificadores de tono de piel), con conformado BiDi y OpenType; multilingüe por documento.
add_tableInformes tabulares con campos inteligentes (wrap, repeatHeader, zebra, caption, minRowHeight, cellPadding).
add_formCrear un PDF de AcroForm interactivo nuevo con campos de texto, áreas de texto, casillas de verificación, botones de opción, listas desplegables, cuadros de lista (+ texto de sugerencia placeholder).
read_form_fieldsEnumeración de solo lectura del árbol de campos de un AcroForm existente (nombres, tipos, valores, widgets).
fill_formRellenar y/o aplanar un AcroForm existente (actualización incremental no destructiva).
add_chartGráficos vectoriales nativos v2 — bar / barH / stackedBar / stackedBarH / line / area / scatter / pie / donut, eje secundario, escalas logarítmicas y de tiempo, etiquetas de datos (operadores de ruta PDF puros, seguros para PDF/A).
embed_imageIncrustar una imagen JPEG o PNG (base64) en un documento PDF con título (texto align, alt para salida etiquetada).
prepare_signature_placeholderPaso 1 opcional del flujo de firma — crear un PDF con un marcador de posición /Sig (metadatos del firmante, subFilter, reserveTimestamp integrados).
sign_pdfFirma CMS PAdES B-B / B-T (RSA-SHA256/384/512, ECDSA-SHA256 P-256; profile: 'pades', timestamp, certChainDerBase64, múltiples firmas, signingTime fijable). Inyecta automáticamente un marcador de posición cuando es necesario.
add_ltv (nuevo en v1.6.0)PAdES B-LT — incrustar un /DSS con certificados + material OCSP/CRL (proveedor configurado por el operador, o material sin conexión proporcionado por el llamante).
timestamp_pdf (nuevo en v1.6.0)PAdES B-LTA — añadir un /DocTimeStamp RFC 3161 desde la TSA configurada por el operador; volver a ejecutar para extender la cadena de archivo.
verify_pdfVerificar cada firma PAdES y sello de tiempo de documento (integridad + valor de firma + confianza de cadena opcional; un /DocTimeStamp cuenta en allValid como cualquier firma); ltv: true informa el nivel B-B…B-LTA.
validate_pdfValidar un PDF etiquetado para conformidad estructural PDF/UA (ISO 14289-1), o con standard: 'pdf-x-4' (nuevo en v1.7.0) los requisitos estructurales de PDF/X-4 (ISO 15930-7) — solo lectura, no es una verificación previa certificada.
add_attachmentGenerar un documento PDF/A-3 con archivos incrustados (facturas Factur-X / ZUGFeRD).
extract_attachmentsExtracción de solo lectura de archivos incrustados (ida y vuelta XML Factur-X / ZUGFeRD) con cargas útiles byte a byte.
extract_textExtracción de texto Unicode (resuelve /ToUnicode) con ejecuciones posicionadas opcionales; abre PDFs cifrados mediante password.
inspect_pdfInspección de solo lectura: versión de PDF, número de páginas, cifrado (+ encryptionInfo preciso), declaración PDF/A, declaración PDF/X (pdfX, nuevo en v1.7.0), firmas (+ inventario, /DSS, sellos de tiempo de documento), cuadros de página, /Trapped, adjuntos, estado del marcador de posición, inventario annotations: true de anotaciones de página existentes.
update_metadata (nuevo en v1.6.0)Reescribir /Info título / autor / asunto / palabras clave (+ XMP, fechas incluidas) de un PDF existente como actualización incremental; fijar modDate para bytes reproducibles.
encrypt_pdfReasegurar un PDF con AES-128 / AES-256 (contraseñas de propietario/usuario, permisos, rotación de contraseñas).
decrypt_pdfEmitir una copia sin cifrar de un documento RC4 / AES-128 / AES-256.
merge_pdfsConcatenar de 2 a 50 PDFs en uno mediante la API de árbol de páginas de pdfnative (los cuadros de página se conservan).
split_pdfDividir un PDF en un documento por rango de páginas (salida múltiple).
extract_pagesExtraer un subconjunto arbitrario de páginas en un solo PDF.
annotate_pdfAñadir anotaciones de marcado (resaltado, nota, cuadrado/círculo, línea, texto libre) como superposición visual — no es una redacción.
draft_governance_issueRedactar localmente un problema de GitHub conforme a gobernanza para revisión humana; nunca envía, sin red.

Nuevo en v1.7.0:

  • 🔤 Tipografía fina — un objeto typography opcional en las nueve herramientas de documentos y en inspect_layout: splitParagraphs con orphans / widows, keepHeadingsWithNext, unitBinding, bindShortWords, punctuationSpacing ('fr', 'fr-CA' o reglas explícitas), opticalMargins, metrics: 'exact', fontFeatures (11 etiquetas OpenType), kerning, hyphenationLanguage. Los bloques de párrafo ganan align (left / right / center / justify), keepWithNext y splittable; los bloques de encabezado ganan keepWithNext. Límites honestos: kerning, fontFeatures y el 'fr' espacio estrecho sin separación necesitan embedFonts: true (Helvetica base-14 degrada 'fr' a 'fr-CA'); tnum / lnum no cambian nada en el Noto Sans incluido (TYPOGRAPHY_FEATURE_INEFFECTIVE de diagnóstico); no hay diccionario de separación silábica instalado, por lo que hyphenationLanguage no tiene efecto aquí — los guiones suaves (U+00AD) se respetan. Ver docs/guides/TYPOGRAPHY.md.
  • 🎨 CMYK en cualquier lugar donde se acepte un color — cadenas de operandos 'c m y k' (0–1) y tuplas de porcentaje [c, m, y, k] (0–100) junto a las formas hex / RGB existentes: marcas de agua, plantillas de encabezado / pie de página, bordes de celdas de tabla, entradas de esquema, gráficos, bloques link y svg, annotate_pdf. Cada forma de color de 1.6.0 sigue validándose.
  • 🖨️ PDF/X-4 — pdfx: 'pdfx4' en seis herramientas de generación (generate_basic_pdf, add_table, add_chart, add_barcode, embed_image, add_international_text), perfiles outputIntent CMYK o Gray junto a RGB, print.marks.colourBars, y validate_pdf { standard: 'pdf-x-4' } para verificar el resultado; inspect_pdf informa la afirmación (pdfX, verificar 'pdfx'). Requiere el perfil ICC de la imprenta (clase de dispositivo prtr — ninguno está incluido), necesita embedFonts: true para un archivo conforme (PDFX_NO_FONT_ENTRIES de lo contrario; strict: true se niega), y es exclusivo con pdfA y encrypt. La validación es estructural — no es un preflight certificado; el resultado lo dice por sí mismo en caveats[]. Ver docs/guides/PRINT.md.
  • 🌏 27 escrituras Unicode — add_international_text acepta lo (Lao), nod (Tai Tham), khb (New Tai Lue), tdd (Tai Le) y cjm (Cham); ha, yo, ig, sw son alias de latin (las marcas de tono se adjuntan); los modificadores de tono de piel emoji se renderizan. Tai Tham bajo PDF/A debe usar pdfa2b, no pdfa2u (un glifo carece de una entrada ToUnicode río arriba).
  • 🔁 Reproducible en cada host — cada fecha se escribe en UTC, {date} en un encabezado o pie de página sigue el instante fijado, y el operador puede fijar todo el proceso con PDFNATIVE_MCP_CREATION_DATE o SOURCE_DATE_EPOCH (ver Variables de entorno). No cubierto, por diseño: signingTime, modDate, tokens RFC 3161 y datos de revocación, cifrado, firmas ECDSA. Ver docs/guides/REPRODUCIBLE.md.
  • 🚦 strict escala por código de diagnóstico — PDFA_* → PDF_A_COMPLIANCE_VIOLATION, PDFX_* → PDF_X_COMPLIANCE_VIOLATION (nuevo), cualquier otra cosa → DIAGNOSTIC_ESCALATED (nuevo). Ambos códigos nuevos solo pueden ser devueltos por una llamada que establezca strict: true.
  • 🧩 Un séptimo prompt MCP, typography — y print_ready, reproducible_output, pdfa_valid reescritos para CMYK, PDF/X-4, barras de color y el pin UTC / operador.
  • 🐛 Correcciones — un triple RGB 0–1 (watermark.color, los colores annotate_pdf) ahora renderiza el color que nombra ([1, 0, 0] solía renderizar casi negro); cualquier fallo inesperado de una herramienta que toma entrada PDF se clasifica PDF_PARSE_FAILED en lugar de aparecer sin código.
  • ✅ Cerrado río arriba — un formulario PDF/A construido con embedFonts: true ahora valida bajo veraPDF (la fuente AcroForm está incrustada), y inspect_layout mide un bloque toc exactamente como el build lo dispone.
  • 🧪 Una puerta, CI endurecido, docs verificados — npm run gate es la única definición de verde (el servidor construido se maneja sobre stdio y stdout debe llevar solo marcos JSON-RPC); veraPDF es bloqueante sobre un corpus de conformidad de 41 archivos; una línea base de bytes de 96 muestras protege la salida; npm run verify:docs contiene cada conteo, versión, herramienta, código de error y variable de operador citado en los docs a docs/assets/ecosystem.json y el árbol fuente.
  • 🧾 Catálogo — tools/list crece a ≈ 306 kB (el fragmento de tipografía y los esquemas de color ampliados están en línea en cada herramienta que los lleva; npx tsx scripts/tool-shape.ts --check lo mantiene bajo 320 KiB y las instrucciones bajo 8 KiB); _meta.apiVersion es 1.7.0.
  • ⬆ Actualización del motor — pdfnative v1.8.0. Sin cambio disruptivo en la API de herramientas; los bytes que sí cambian (subconjuntos TrueType incrustados, print.marks, texto conformado con posicionamiento de marcas, fechas UTC) se enumeran bajo Upgrade en release-notes/v1.7.0.md.

Nuevo en v1.6.0:

  • 🧱 Cobertura completa del motor — 13 tipos de bloques — generate_basic_pdf acepta cada DocumentBlock que ofrece pdfnative: los nuevos bloques table, image, link, toc, barcode, svg y formField comparten su cuerpo con las herramientas dedicadas (add_table, embed_image, add_barcode, add_form), de modo que un artefacto independiente y un bloque en línea se validan y renderizan de forma idéntica. Reglas: link acepta solo http: / https: / mailto: (los caracteres de control se rechazan); los bloques image están limitados (12 M de caracteres base64 cada uno, 24 MiB decodificados por llamada; PNG debe ser de 8 bits, no entrelazado, sin alfa ni paleta — se rechaza con un remedio); svg cubre trazados, formas básicas y <text> (sin transform, <g>, degradados ni CSS — se ignoran silenciosamente; nunca se obtiene nada); toc se empareja con outline: 'auto'; formField bajo una declaración PDF/A informa PDFA_UNEMBEDDED_FORM_FONT; barcode no tiene alt (limitación del motor).
  • 📐 Opciones de diseño en las nueve herramientas de documentos — pageSize (A4 por defecto, Letter, Legal, A3, Tabloid), margins (los cuatro, 0–200 pt), headerTemplate / footerTemplate con {page} {pages} {title} {date} (un footerTemplate reemplaza el pie de página predeterminado, por lo que footerText se ignora entonces; {date} era el reloj de pared del día de compilación en 1.6.0 — desde v1.7.0 sigue el instante fijado), compress (flujos FlateDecode — archivo más pequeño, bytes diferentes; XMP permanece simple bajo PDF/A) y debug (rectángulos guía, contenido sin marcar — no para PDF/UA). Ausentes por defecto, por lo que la salida predeterminada permanece byte-idéntica.
  • 🔐 Cifrado en tiempo de compilación — encrypt en siete herramientas de documentos (generate_basic_pdf, add_table, add_form, add_international_text, embed_image, add_barcode, add_chart): Manejador de Seguridad Estándar, AES-128 por defecto / AES-256, conserva el AcroForm (a diferencia de encrypt_pdf, que reconstruye el árbol de páginas). Exclusivo con pdfA (VALIDATION_ERROR), nunca se almacena en caché; no se ofrece en prepare_signature_placeholder (debe permanecer firmable) ni en add_attachment (PDF/A-3).
  • 📏 inspect_layout — la vigésimo octava herramienta: una prueba de paginación de solo lectura sobre los mismos blocks y entradas de diseño, que informa totalPages y la página / x / superior / ancho / alto de cada bloque sin renderizar un PDF. Brecha conocida del motor en 1.6.0 (cerrada en v1.7.0): un bloque toc se medía como 0 pt, por lo que los documentos con un contenido impreso podían paginar una página más tarde de lo previsto.
  • 🔎 inspect_pdf annotations: true — lista cada anotación de página (subtipo, página basada en 0, rect, contenido truncado a 200 caracteres, título, color, quadPoints, URL de enlace) más annotationCount; nuevo check: 'annotations'.
  • 🖼️ Marcas de agua de imagen — watermark.image (JPEG/PNG, opacidad predeterminada 0.10, propio límite de 8 MiB) en generate_basic_pdf y add_table, solo o combinado con text (opacidad predeterminada 0.15); position: 'background' | 'foreground' para ambos. Cualquier opacidad inferior a 1.0 se rechaza bajo pdfa1b.
  • 🧯 PDFNATIVE_MCP_MAX_INFLATE_BYTES — anulación del operador del límite de descompresión de 100 MiB por flujo del motor (entero ≥ 1024; un valor inválido se niega a iniciar). Un flujo adjunto limitado falla extract_attachments includeData: true con PDF_PARSE_FAILED; extract_text degrada a texto de página vacío (el motor traga los fallos de decodificación por página).
  • 📝 Formularios — los bloques add_form y formField ganan listbox y placeholder; fieldType: 'textarea' ahora llega al motor como multilineText (antes se pasaba sin mapear y se renderizaba como un campo de una sola línea — una corrección de errores que cambia los bytes para esa entrada). embed_image gana align y alt.
  • 🔏 Escalera de validación a largo plazo PAdES — sign_pdf gana profile: 'pades' (ETSI EN 319 142-1 baseline, ESS signing-certificate-v2, ETSI.CAdES.detached), timestamp: true (B-T, RFC 3161), RSA-SHA384/512, certChainDerBase64, fieldName / allowMultiple para varias firmas; nuevo add_ltv incrusta un /DSS (B-LT, mode: 'online' a través del proveedor del operador o mode: 'offline' con material DER proporcionado por el llamante); nuevo timestamp_pdf añade un /DocTimeStamp (B-LTA). verify_pdf ltv: true informa perfil, marca de tiempo, estado de revocación y ltvLevel. Ver docs/guides/LTV.md.
  • 🌐 Carta de red — sin solicitudes salientes por defecto. La única salida que el servidor puede realizar alguna vez va a los endpoints RFC 3161 / OCSP / CRL que el operador configuró (PDFNATIVE_MCP_TSA_URL, PDFNATIVE_MCP_REVOCATION, PDFNATIVE_MCP_NETWORK_ALLOWED_HOSTS), detrás de una protección SSRF; los argumentos de las herramientas nunca pueden proporcionar una URL.
  • 🖨️ Producción de impresión — cada herramienta de documentos acepta print (TrimBox / BleedBox / ArtBox / CropBox o la abreviatura bleed, recorte + marcas de registro marks, /UserUnit), metadata (/Author, /Subject, /Keywords, /Trapped) y outputIntent (ICC RGB personalizado para PDF/A); viewerPreferences gana duplex, pickTrayByPDFSize, printPageRange, numCopies. inspect_pdf pages: true informa los recuadros; fusionar / dividir / extraer los conservan. Ver docs/guides/PRINT.md.
  • ✍️ update_metadata — reescribe /Info + XMP de un PDF existente como una actualización incremental (las revisiones anteriores y las firmas se conservan textualmente).
  • 📊 Gráficos v2 — stackedBar / stackedBarH / area / scatter, eje derecho secundario (axis2), axis.scale: 'log', xAxis.type: 'linear' | 'time', dataLabels, labelStride / labelRotation; las etiquetas de categoría superpuestas se adelgazan automáticamente.
  • 📜 PDF/A honesto — embedFonts: true incrusta Noto Sans Latin (la Helvetica base-14 no está incrustada, por lo que una declaración PDF/A sobre texto latino simple es rechazada por veraPDF), strict: true falla en lugar de producir un archivo no conforme, includeDiagnostics: true hace eco de los diagnósticos del motor. Script local de veraPDF (npm run validate:pdfa) sobre un corpus de 26 archivos (24 validados, 3 de ellos canarios negativos; 2 salidas de árbol de páginas omitidas) y un modo VERAPDF_REQUIRED=1 de cierre ante fallos; el flujo de trabajo CI fija el instalador por SHA-256 y permanece no bloqueante en 1.6.0 (bloqueante desde v1.7.0, donde --require-all en la puerta reemplaza VERAPDF_REQUIRED=1). Brechas conocidas del motor en 1.6.0: la salida add_form falla PDF/A-2b incluso con embedFonts (/DR /Helv no incrustado — cerrado en v1.7.0), y una salida prepare_signature_placeholder es conforme solo una vez firmada.
  • 🧰 inspect_pdf — inventario signatures: true, dss / docTimestampCount / trapped (con puerta de presencia), nuevos valores check dss, docTimestamp, trapped; checks lista solo las claves que solicitaste, y signed es estructural (un campo firmado existe — la validez es trabajo de verify_pdf).
  • 🔁 Salida reproducible — creationDate opcional en las nueve herramientas de documentos fija /CreationDate, las fechas XMP y el /ID del tráiler; signingTime en prepare_signature_placeholder (y en sign_pdf, ahora con desplazamientos de zona horaria) fija /Sig /M. Bytes idénticos en la misma zona horaria del host en 1.6.0 (en cada host desde v1.7.0: las fechas se escriben en UTC). Respaldado por el prompt reproducible_output.
  • 🛡️ Límite endurecido — esquemas de entrada estrictos (claves desconocidas o mal escritas → VALIDATION_ERROR en lugar de ignorarse silenciosamente); se toleran prefijos data:…;base64,, se rechazan PEM-donde-DER y cargas doblemente codificadas con el remedio exacto; los errores de índice de página en las herramientas de árbol de páginas son VALIDATION_ERROR con una pista basada en 0; un nombre de herramienta desconocido es un error de protocolo JSON-RPC (-32602, [UNKNOWN_TOOL]).
  • 🔑 Token de portador HTTP — PDFNATIVE_MCP_HTTP_TOKEN opcional protege el endpoint HTTP Streamable (401 + WWW-Authenticate de lo contrario). Sin él, el endpoint de bucle local no tiene autenticación — ver SECURITY.md.
  • 🧾 Catálogo — tools/list es ≈ 245 kB (1.5.0: ≈ 108 kB) porque cada tipo de bloque, opción de diseño y fragmento encrypt ahora se anuncia en línea — sin $ref / $defs por política, por lo que los hosts que reenvían inputSchema a APIs de llamada de funciones nunca encuentran una referencia; las instrucciones del servidor son ≈ 6.7 kB (desde 12.9 kB). La estructura está protegida por scripts/tool-shape.mjs (scripts/tool-shape.ts desde v1.7.0) + tests/catalogue-parity.test.ts, y tests/catalogue-superset.test.ts demuestra que el catálogo en vivo es un superconjunto del publicado en 1.5.0; como máximo dos _meta.examples ejecutables por herramienta, el resto bajo examples/. Cuatro nuevos prompts de recetas: pades_ladder, print_ready, reproducible_output, pdfa_valid.
  • 🐛 Correcciones — los metadatos del firmante (signerName / reason / location / contactInfo) nunca llegaron al diccionario /Sig en pdfnative < 1.7; ahora se hornean en el momento del marcador de posición. verify_pdf ya no informa allValid: false en documentos B-LTA (un /DocTimeStamp se analizaba como una firma CMS).
  • 🔌 MCP 2026-07-28 en el SDK de TypeScript MCP v2 (@modelcontextprotocol/server) con retroceso automático al apretón de manos initialize de la era 2025 — los hosts existentes siguen funcionando sin cambios. Ver cumplimiento del protocolo MCP.
  • ⬆ Actualización del motor — pdfnative v1.7.0 (LTV, producción de impresión, gráficos v2, agilidad de resúmenes, secuencias de emoji con bandera / ZWJ, correcciones UAX #9).

Nuevo en v1.5.0:

  • 📊 Gráficos vectoriales nativos — add_chart renderiza gráficos de barras / barras horizontales / líneas / circulares / donut como operadores de trazado PDF puros (cero rasterización, seguro para PDF/A con texto alternativo automático). generate_basic_pdf también acepta un bloque chart para composición con texto y tablas.
  • 📝 Rellenar y aplanar formularios — read_form_fields lista los campos de un AcroForm existente; fill_form lo rellena y/o aplana mediante una actualización incremental no destructiva (la contraparte de add_form).
  • 🔐 Cifrado de ida y vuelta — encrypt_pdf reasegura con AES-128 / AES-256 (RC4 nunca se emite), decrypt_pdf recupera una copia sin cifrar, una entrada password abre fuentes cifradas en las herramientas de solo lectura, y merge_pdfs / split_pdf / extract_pages ganan password + encrypt.
  • 🔤 Extracción de texto real — extract_text ahora resuelve el CMap /ToUnicode de cada fuente (sin más salida de índice de glifos) y puede devolver runs posicionados.
  • 🔗 Recursos MCP nativos — los PDF generados en sandbox se convierten en recursos pdfnative://output/… (resources/list + resources/read), con un resource_link en resultados de modo archivo para re-referencia entre llamadas.
  • 🏷️ Anotaciones de herramientas — cada herramienta anuncia readOnlyHint / destructiveHint / idempotentHint / openWorldHint.
  • ⬆ Actualización del motor — pdfnative v1.6.0 (descifrar/re-cifrar, extractText, rellenar/aplanar, gráficos; subconjunto de emoji de color 221 → 1167 glifos).

Nuevo en v1.4.0:

  • 🤝 Gobernanza de IA + humano en el circuito — draft_governance_issue permite que un agente redacte un issue de GitHub totalmente conforme localmente (borrador .md + informe de cumplimiento legible por máquina). El agente es un redactor, nunca un remitente autónomo: un humano es la única puerta, y el servidor realiza cero escrituras en GitHub (y, desde v1.6.0, ninguna llamada saliente excepto a los endpoints TSA / OCSP / CRL configurados por el operador). Respaldado por los prompts MCP governance_contract y draft_issue_workflow.
  • ✏️ Anotaciones de marcado — annotate_pdf superpone resaltados, notas adhesivas, subrayados, tachados, ondulados, cuadrados, círculos, líneas y anotaciones de texto libre en un PDF existente mediante actualización incremental. Es una capa de revisión visual, no una redacción — los bytes subyacentes permanecen.
  • 🔢 Etiquetas de página en inspect_pdf — exposición de solo lectura de rangos /PageLabels (romanos, decimales, con prefijo).
  • ∑ Script matemático / científico — add_international_text acepta lang: 'math' (explícito, como emoji) para incrustar la fuente Noto Sans Math bajo demanda.
  • 🧩 Prompts MCP — el servidor ahora anuncia la capacidad prompts con governance_contract y draft_issue_workflow.
  • ⬆ Actualización del motor — pdfnative v1.5.0.

Nuevo en v1.3.0:

  • 🆕 Tres herramientas de árbol de páginas — merge_pdfs, split_pdf, extract_pages (basadas en la API de árbol de páginas de pdfnative v1.4.0; las fuentes cifradas fueron rechazadas hasta que v1.5.0 añadió password).

  • 🔖 Marcadores, etiquetas de página y listas anidadas — generate_basic_pdf gana outline ('auto' o árbol explícito), pageLabels, elementos de list multinivel y viewerPreferences.

  • 📐 Bordes y alineación de celdas de tabla — add_table gana cellBorders, cellVAlign y viewerPreferences; add_international_text gana viewerPreferences.

  • 🔐 Firma de tiempo constante — sign_pdf firma claves RSA y EC-DER a través de un proveedor node:crypto con un respaldo transparente en JS puro (los escalares P-256 sin procesar permanecen en JS puro, y la verificación es en JS puro); las firmas siguen siendo interoperables.

  • ⬆ Actualización del motor — pdfnative v1.4.0.

  • 🆕 Herramienta extract_attachments — lee archivos incrustados de un PDF (completa el ciclo Factur-X / ZUGFeRD) con cargas útiles byte a byte, un filtro filename y una sonda de solo metadatos includeData: false.

  • 💧 Marcas de agua — generate_basic_pdf y add_table aceptan un watermark opcional (texto, opacidad, ángulo, color, posición; image desde v1.6.0) renderizado en cada página.

  • 🌐 Unicode normalize — NFC/NFD/NFKC/NFKD opcionales en generate_basic_pdf y add_international_text.

  • 🪙 Lecturas frugales en tokens — las herramientas de solo lectura (inspect_pdf, verify_pdf, validate_pdf, extract_text, extract_attachments; read_form_fields desde v1.5.0) aceptan entradas opcionales verbosity: 'summary' y fields: […] para respuestas ~90% más pequeñas en resultados grandes, sin pérdida de los campos en los que los agentes se basan. Los valores predeterminados no cambian.

  • 🪙 Sin duplicación de base64 — los PDF generados (modo base64) se devuelven una vez como un bloque de contenido resource incrustado en lugar de copiarse también en structuredContent.

  • 🔧 Corrección de publicación en el registro MCP — mcpName ahora usa el uso de mayúsculas canónico del login de GitHub (io.github.Nizoka/pdfnative-mcp) para que la validación sensible a mayúsculas del registro acepte el paquete npm.

  • ⬆ Dependencia — actualizado a zod 4.

Nuevo en v1.1.0:

  • 🆕 Herramienta validate_pdf — verificación de conformidad estructural PDF/UA (ISO 14289-1) de solo lectura.
  • 🆕 Seis nuevos scripts — telugu, cingalés, tibetano, jemer, birmano, etíope (24 scripts en total).
  • 🆕 Emoji de color COLRv1 — emoji de color nativo con respaldo monocromático.
  • 🆕 Sanitizador de nuevas líneas — \n incrustado en párrafos se divide automáticamente en párrafos separados (PDF/A seguro).
  • 🆕 Normalización NFC automática para add_international_text.
  • 🛠 Actualización del motor — pdfnative v1.3.0: el signo de euro / símbolos CP-1252 ahora se extraen correctamente, y las celdas de tabla envueltas obtienen MCIDs únicos por línea (seguro para PDF/UA).

Nuevo en v1.0.0:

  • 🆕 Tres nuevas herramientas: verify_pdf, add_attachment (Factur-X / ZUGFeRD), extract_text.
  • 🆕 Campos de tabla inteligente: wrap, repeatHeader, zebra, caption, minRowHeight, cellPadding.
  • 🆕 inspect_pdf ahora informa hasSignaturePlaceholder y resumen por adjunto; nuevos valores de check 'placeholder' y 'attachments'.
  • 🆕 Ergonomía de firma: sign_pdf acepta claves ECDSA SEC1 / PKCS#8 DER e inyecta automáticamente un marcador de posición /Sig cuando falta (firma de cualquier PDF en una sola llamada).
  • 🆕 Caché opcional (PDFNATIVE_MCP_CACHE_DIR): clave SHA-256, TTL de 1 h, LRU de 256 MiB.
  • 🆕 _meta.apiVersion y _meta.examples por herramienta para descubrimiento por agentes de IA — ver docs/API_STABILITY.md.
  • 🆕 Guía para agentes de IA: docs/AI_GUIDE.md — árbol de decisión + errores comunes. Ver también el contrato de agente, docs/AGENT_CONTRACT.md (catálogo, árbol de decisión, recetas, tabla de errores); los contribuyentes y agentes de codificación comienzan en AGENTS.md.
  • 🆕 Guía de autoría PDF/A: docs/guides/PDFA.md.
  • 🛠 Renombrado de variable de entorno: PDFNATIVE_MCP_OUTPUT_DIR (antes PDFNATIVE_MPC_OUTPUT_DIR; el nombre antiguo aún funciona con una advertencia de deprecación única).
  • ✅ Ahora incluido: merge_pdfs, split_pdf, extract_pages (v1.3.0), annotate_pdf (v1.4.0), las herramientas add_chart / read_form_fields / fill_form / encrypt_pdf / decrypt_pdf más el ciclo cifrado y los recursos MCP nativos (v1.5.0), y add_ltv / timestamp_pdf / update_metadata más producción de impresión y gráficos v2 (v1.6.0). redact_pdf permanece diferido — pdfnative puede superponer/aplanar pero no eliminar contenido de página, y una "redacción" solo de superposición crearía una falsa seguridad, por lo que intencionalmente no se incluye (seguido como solicitud de eliminación de contenido upstream).

Todas las herramientas admiten dos modos de salida:

  • base64 (predeterminado) — el PDF generado se devuelve una vez como un bloque de contenido resource incrustado (un URI data:application/pdf;base64,…); structuredContent lleva solo { mode, sizeBytes } (más diagnostics[] cuando includeDiagnostics: true, y un summary para add_ltv).
  • file — el PDF se escribe en un directorio sandbox configurado mediante PDFNATIVE_MCP_OUTPUT_DIR. La salida de archivos está deshabilitada a menos que se establezca esta variable; rutas absolutas, traversal de rutas, extensiones no .pdf y bytes NUL son todos rechazados.

Actualización desde v1.1.0: el único cambio de comportamiento es que los bytes en modo base64 ya no se duplican en structuredContent.base64. Léelos del bloque resource incrustado en su lugar:

- 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

Lecturas frugales en tokens (v1.2.0). Las siete herramientas de solo lectura (inspect_pdf, verify_pdf, validate_pdf, extract_text, extract_attachments, read_form_fields, inspect_layout) aceptan dos entradas opcionales:

  • verbosity: 'summary' — devuelve un veredicto compacto solo de escalares (omite los arrays pesados / texto completo). Ej. verify_pdf → { signatureCount, allValid, invalid, summary } (+ ltvLevel con ltv: true); inspect_pdf conserva docTimestampCount / trapped / checksPassed cuando están presentes.
  • fields: ['a', 'b.c'] — proyecta el resultado estructurado a rutas de puntos nombradas; se compone después de verbosity. Las rutas no coincidentes se omiten y se informan en _meta.unmatchedFields (con _meta.availableFields).

La sonda más pequeña "¿Está este PDF firmado y es válido?": { "pdfBase64": "…", "verbosity": "summary", "fields": ["allValid"] }.

¿Por qué pdfnative?

pdfnative-mcp hereda cada garantía del motor subyacente:

  • Cero dependencias de tiempo de ejecución en el motor — JavaScript puro, sin enlaces nativos (este servidor agrega solo el SDK MCP y zod: tres dependencias de tiempo de ejecución en total).
  • Salida conforme a ISO 32000-1 (PDF 1.7).
  • PDF/A-1b/2b/2u/3b, cifrado AES-128/256, AcroForm, firmas digitales.
  • 27 scripts Unicode (34 códigos lang incl. latin, emoji, math y los cuatro alias latin) con reordenamiento BiDi integrado, conformación posicional árabe, conformación OpenType tailandés/devanagari/bengalí/tamil.
  • Intercambio de impresión PDF/X-4, color DeviceCMYK y tipografía fina (control de huérfanas / viudas, justificación, espaciado de puntuación francesa, características OpenType).
  • Compilación ESM con tree-shaking.

🚀 Instalación

# Run directly with npx (recommended for MCP clients)
npx -y pdfnative-mcp

# Or install globally
npm install -g pdfnative-mcp
pdfnative-mcp

Requisitos: Node.js ≥ 22.


⚙️ Configuración

Claude Desktop

Edita ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) o %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

Cualquier cliente compatible con MCP que admita servidores stdio funcionará. Usa el mismo triple command + args + env. Ejemplo para 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 usan la misma forma dentro de sus respectivos archivos de configuración MCP.

🌐 Ecosistema de IA y clientes compatibles

pdfnative-mcp está diseñado para entornos nativos de MCP y funciona con clientes que admiten MCP sobre stdio o Streamable HTTP.

La compatibilidad verificada por la comunidad incluye:

🔌 Cumplimiento del protocolo MCP

Desde v1.6.0 el servidor está construido sobre el SDK TypeScript MCP v2 (@modelcontextprotocol/server) y habla MCP 2026-07-28:

  • Servicio sin estado — server/discover reemplaza el protocolo de enlace de sesión; cada resultado lleva resultType y el sobre _meta serverInfo. Por HTTP, los clientes de 2026-07-28 envían cabeceras Mcp-Method / Mcp-Name con cada POST /mcp.
  • Indicaciones de caché — tools/list y prompts/list son public con un ttlMs de 24 h, server/discover es public durante 1 h, y resources/list / resources/templates/list / resources/read son private con ttlMs: 0 (los PDF generados son datos de usuario por host).
  • Errores de recursos — un URI de recurso desconocido se reporta como JSON-RPC -32602 (Invalid params), como exige la especificación de 2026-07-28.
  • Retrocompatibilidad automática — un cliente que abre con initialize (2025-11-25, 2025-06-18 o 2025-03-26) se atiende mediante la ruta heredada del SDK tanto en stdio como en HTTP. Nada cambia para los hosts existentes.
  • HTTP — GET / DELETE /mcp responden 405 (sin reanudabilidad SSE; el servidor no tiene estado). El enlace de bucle local y la protección Host / Origin no cambian, y el puerto Origin debe ahora coincidir con el puerto del servidor (la comprobación del SDK por sí sola no depende del puerto); PDFNATIVE_MCP_HTTP_TOKEN añade una puerta de token portador opcional (401 + WWW-Authenticate sin ella). Los arrays de lotes JSON-RPC (2025-03-26) se aceptan por HTTP. Las conexiones keep-alive ya no acumulan listeners de socket.
  • stdio — como en todas las versiones del SDK hasta la fecha, una solicitud enviada antes de initialize se descarta sin respuesta y los arrays de lotes JSON-RPC no se aceptan en stdio (sin cambios desde 1.5.0; ningún host importante usa lotes).
  • Errores de protocolo — tools/call con un nombre de herramienta desconocido es un error JSON-RPC (-32602, [UNKNOWN_TOOL] Unknown tool: …) en lugar de un resultado isError, tal como lo clasifica la especificación; isError: true se reserva para fallos de ejecución.
  • Esquemas de salida — cada structuredContent se valida contra el outputSchema de la herramienta (un MUST de 2026-07-28), incluyendo las proyecciones verbosity: 'summary' y fields: las siete herramientas de lectura declaran esquemas proyectables (todas las propiedades opcionales, additionalProperties: false conservado). Los esquemas de entrada no llevan la palabra clave $schema por política (MCP ≥ 2025-11-25 usa por defecto JSON Schema 2020-12; algunos hosts reenvían inputSchema a APIs de llamada a funciones que rechazan palabras clave desconocidas). serverInfo lleva websiteUrl; la plantilla de recurso es pdfnative://output/{+path}.

El payload tools/call (content, structuredContent, isError) es idéntico entre la ruta de 2026-07-28 y la ruta heredada; tests/http-modern.test.ts lo afirma, y tests/schema-conformance.test.ts valida structuredContent con el validador JSON Schema 2020-12 del SDK.

ClienteTransporteProtocolo negociado
Claude Desktop, Cursor, Continue, Zed, Windsurf, Clinestdioinitialize heredado (2025-xx) — sin cambios
ChatGPT y otros hosts Streamable HTTPHTTP POST /mcpHTTP streamable sin estado heredado — sin cambios
Clientes MCP 2026-07-28 (SDK v2 Client, MCP Inspector actual)stdio / HTTPserver/discover, indicaciones de caché, sobre _meta
Ontheiastdioinitialize heredado (verificado por la comunidad, #41)

Variables de entorno

VariablePropósito
PDFNATIVE_MCP_OUTPUT_DIRRuta absoluta al directorio de sandbox. Requerida para habilitar outputMode: 'file'.
PDFNATIVE_MCP_CACHE_DIRRuta absoluta para habilitar la caché de resultados persistente con clave SHA-256 (TTL de 1 h, LRU de 256 MiB; clave con espacio de nombres por API de herramienta + versión del paquete + el instante de creación fijado). Si no se define, la caché está deshabilitada. Nunca almacena en caché encrypt_pdf / decrypt_pdf / sign_pdf / add_ltv / timestamp_pdf / update_metadata ni llamadas de modo de archivo; un acierto lleva _meta.cached: true y devuelve los bytes de la llamada anterior.
PDFNATIVE_MCP_PORTCuando se define a un puerto válido (1–65535), inicia un servidor HTTP en http://127.0.0.1:<port>/mcp en lugar de stdio. Solo enlaza en bucle local y habilita la protección contra rebinding de DNS (Host/Origin extranjeros → 403). Sin autenticación a menos que PDFNATIVE_MCP_HTTP_TOKEN esté definido — otros procesos locales pueden alcanzar el endpoint.
PDFNATIVE_MCP_HTTP_TOKEN(v1.6.0, secreto) Token portador opcional para el transporte HTTP (≥ 16 caracteres, sin espacios en blanco — un valor más débil aborta el arranque). Cuando se define, cada solicitud /mcp debe llevar Authorization: Bearer <token>; de lo contrario 401 + WWW-Authenticate: Bearer realm="pdfnative-mcp" (con error="invalid_token" solo cuando se enviaron credenciales — RFC 6750 §3.1). Comparado en tiempo constante, nunca registrado.
PDFNATIVE_MCP_MAX_INFLATE_BYTES(v1.6.0) Anula el límite de descompresión de 100 MiB por flujo del motor (protección contra zip-bomb): un número entero positivo de bytes ≥ 1024, leído una vez al arrancar — un valor inválido se niega a iniciar. Redúzcalo en un host compartido, auméntelo para archivos confiables de escaneos grandes. Un flujo de adjunto con límite falla extract_attachments includeData: true con PDF_PARSE_FAILED; extract_text degrada a texto de página vacío para un flujo de contenido con límite (comportamiento del motor, sin error expuesto).
PDFNATIVE_MCP_TSA_URL(v1.6.0) URL http(s) absoluta de la autoridad de sellado de tiempo RFC 3161 utilizada por sign_pdf timestamp: true y timestamp_pdf. Sin definir: TSA_NOT_CONFIGURED, no se realiza ninguna solicitud.
PDFNATIVE_MCP_TSA_AUTH(v1.6.0, secreto) Valor de cabecera Authorization opcional enviado a la TSA. Nunca se registra ni se repite.
PDFNATIVE_MCP_REVOCATION(v1.6.0) ocsp, crl o ocsp,crl — habilita la recopilación de revocación en línea para add_ltv mode: 'online'. Sin definir: REVOCATION_NOT_CONFIGURED.
PDFNATIVE_MCP_NETWORK_ALLOWED_HOSTS(v1.6.0) Lista de permitidos separada por comas (host, host:port, *.suffix) para respondedores OCSP / CRL. Obligatoria cuando PDFNATIVE_MCP_REVOCATION está definido — las URL de respondedores provienen de certificados no confiables.
PDFNATIVE_MCP_NETWORK_TIMEOUT_MS(v1.6.0) Tiempo de espera por solicitud para llamadas TSA / OCSP / CRL, 1000–120000 ms (predeterminado 10000).
PDFNATIVE_MCP_CREATION_DATE(v1.7.0) Instante ISO 8601 con zona horaria (p. ej. 2026-01-01T00:00:00Z) que fija el instante de creación de cada documento que el proceso construye: /CreationDate, las fechas XMP, el /ID del trailer y el marcador de posición {date} de cabecera / pie de página. Se lee una vez al arrancar — un valor inválido se niega a iniciar; la fuente del fijado se registra en stderr.
SOURCE_DATE_EPOCH(v1.7.0) La convención de reproducible-builds.org: segundos enteros desde la época Unix, utilizados cuando PDFNATIVE_MCP_CREATION_DATE no está definido. Un valor inválido se niega a iniciar. Muchos entornos de compilación ya lo exportan: desde esta versión fija la fecha de creación de cada documento — desactívelo para el proceso del servidor si no se desea.

Precedencia de fecha de creación (mayor primero): el creationDate por llamada → PDFNATIVE_MCP_CREATION_DATE → SOURCE_DATE_EPOCH → el reloj del sistema. Las fechas siempre se escriben en UTC, por lo que la salida fijada es byte-idéntica en cada host y en cada zona horaria. El fijado no cubre, por diseño: signingTime (sign_pdf, prepare_signature_placeholder), modDate (update_metadata), el segundo /ID regenerado de escritores incrementales (annotate_pdf, fill_form), tokens de sellado de tiempo RFC 3161 y datos de revocación en línea, cifrado (clave de archivo nueva, sales e IVs) y firmas ECDSA (aleatorizadas por diseño). Ver docs/guides/REPRODUCIBLE.md.


🛠 Referencia de herramientas

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"
}

Los 13 tipos de bloque: heading, paragraph, list, table, image, link, toc, barcode, svg, formField, chart, pageBreak, spacer. Un informe compuesto:

{
  "title": "Quarterly report",
  "blocks": [
    { "type": "toc" },
    { "type": "heading", "text": "Sales", "level": 1 },
    { "type": "table", "headers": ["Region", "Revenue"], "rows": [["EMEA", "1.2 M"], ["APAC", "0.9 M"]], "zebra": true },
    { "type": "image", "imageBase64": "<base64 JPEG>", "mimeType": "image/jpeg", "width": 300, "alt": "Revenue chart" },
    { "type": "svg", "data": "M10 10 H 90 V 90 H 10 Z", "viewBox": [0, 0, 100, 100], "fill": "#0a7e8c" },
    { "type": "barcode", "format": "qr", "data": "https://example.com/q1", "align": "center" },
    { "type": "link", "text": "Full dataset", "url": "https://example.com/data" },
    { "type": "formField", "fieldType": "text", "name": "reviewer", "label": "Reviewed by" }
  ],
  "outline": "auto",
  "pageSize": "Letter",
  "headerTemplate": { "right": "{title} — page {page}/{pages}" },
  "embedFonts": true
}

Reglas de bloque: table, barcode, formField y chart toman el mismo cuerpo que add_table / add_barcode / add_form / add_chart; las URL link deben ser http:, https: o mailto:; los bloques image están limitados a 12 M caracteres base64 cada uno y 24 MiB decodificados por llamada (PNG: escala de grises/RGB de 8 bits, no entrelazado, sin alfa, sin paleta — de lo contrario VALIDATION_ERROR con un remedio); svg admite <path>, <rect>, <circle>, <ellipse>, <line>, <polyline>, <polygon>, <text>/<tspan> e ignora silenciosamente transform, <g>, <use>, <image>, gradientes, opacidad y CSS (nunca se obtiene ninguna referencia externa); toc se construye a partir de los bloques de encabezado y se empareja con outline: 'auto'; formField bajo pdfA necesita embedFonts: true para que la fuente de campo también se incruste (PDFA_UNEMBEDDED_FORM_FONT de lo contrario; strict: true entonces falla); barcode no tiene alt. Use inspect_layout con las mismas entradas para previsualizar la paginación antes de renderizar.

Tipografía (v1.7.0). typography es un objeto opcional (12 claves, todas desactivadas por defecto — omitido significa bytes sin cambios) en las nueve herramientas de documentos y en inspect_layout:

{
  "title": "Annual report",
  "embedFonts": true,
  "typography": {
    "splitParagraphs": true, "orphans": 3, "widows": 3,
    "keepHeadingsWithNext": { "minLines": 3 },
    "opticalMargins": true, "kerning": true, "fontFeatures": ["onum", "smcp"],
    "unitBinding": true, "bindShortWords": true, "punctuationSpacing": "fr"
  },
  "blocks": [
    { "type": "heading", "text": "Outlook", "level": 2, "keepWithNext": true },
    { "type": "paragraph", "text": "A long justified paragraph…", "align": "justify" },
    { "type": "paragraph", "text": "Figures by region:", "keepWithNext": true, "splittable": false }
  ]
}
  • Claves: splitParagraphs, orphans / widows (1–10, predeterminado 2, necesitan splitParagraphs), keepHeadingsWithNext (true o { minLines }), unitBinding (true o { units }), bindShortWords (true o { maxLength, words }), punctuationSpacing ('fr', 'fr-CA' o un array de reglas { char, side, space }), opticalMargins, metrics ('approximate' predeterminado / 'exact'), fontFeatures (tnum, pnum, lnum, onum, zero, ordn, sups, subs, smcp, c2sc, case), kerning, hyphenationLanguage.
  • Entradas de bloque: un paragraph toma align (left predeterminado / right / center / justify), keepWithNext y splittable (anula typography.splitParagraphs para ese bloque); un heading toma keepWithNext (anula typography.keepHeadingsWithNext para ese bloque).
  • Límites: kerning, fontFeatures y el espacio sin separación estrecho 'fr' necesitan una fuente incrustada (embedFonts: true; en Helvetica base-14 'fr' degrada a 'fr-CA'); metrics: 'exact' actúa solo en texto base-14; tnum / lnum no cambian nada en el Noto Sans incluido (TYPOGRAPHY_FEATURE_INEFFECTIVE de diagnóstico); no hay diccionario de separación silábica instalado, por lo que hyphenationLanguage no tiene efecto en este servidor — los guiones suaves (U+00AD) en el texto se respetan. Ver docs/guides/TYPOGRAPHY.md y el prompt typography. Colores CMYK (v1.7.0). Cada entrada de color mantiene la forma que siempre ha aceptado (hex en gráficos, plantillas, bloques link y svg; un triplete RGB 0–1 en marcas de agua y annotate_pdf; una cadena libre en entradas de contorno y bordes de celdas de tabla) y añade DeviceCMYK junto a ella: una cadena de operandos 'c m y k' con componentes 0–1 ("1 0.6 0 0.1") y una tupla de porcentaje [c, m, y, k] con componentes 0–100 ([100, 60, 0, 10]). Bajo una declaración PDF/A contra la intención sRGB predeterminada, un color CMYK informa PDFA_DEVICE_CMYK_CONTENT — mantén los colores RGB allí, o proporciona un outputIntent CMYK.

PDF/X-4 (v1.7.0). pdfx: 'pdfx4' en generate_basic_pdf, add_table, add_chart, add_barcode, embed_image y add_international_text escribe un archivo PDF/X-4 (ISO 15930-7): encabezado %PDF-1.6, identificación XMP PDF/X-4, una intención de salida /GTS_PDFX, un TrimBox en cada página y /Trapped.

{
  "title": "Spring catalogue",
  "pdfx": "pdfx4",
  "embedFonts": true,
  "outputIntent": { "iccProfileBase64": "<the printer's CMYK ICC profile, base64>", "outputConditionIdentifier": "FOGRA39" },
  "metadata": { "trapped": "False" },
  "print": { "bleed": 14.17, "marks": { "colourBars": true } },
  "blocks": [{ "type": "paragraph", "text": "Four-colour job exchanged as PDF/X-4." }]
}
  • Requiere outputIntent con el perfil ICC de la condición de impresión (clase de dispositivo prtr, CMYK o Gray — no se incluye ningún perfil de imprenta; pregunta a tu impresor); necesita embedFonts: true para un archivo conforme (PDFX_NO_FONT_ENTRIES en caso contrario; strict: true se niega); es exclusivo con pdfA y encrypt; metadata.trapped debe ser 'True' o 'False'; una página lleva un TrimBox o un ArtBox, no ambos. Las solicitudes incoherentes se rechazan con VALIDATION_ERROR antes de realizar cualquier trabajo. Los enlaces y campos de formulario se informan (PDFX_ANNOTATIONS); con strict: true un diagnóstico PDFX_* hace fallar la llamada con PDF_X_COMPLIANCE_VIOLATION.
  • outputIntent acepta perfiles RGB, CMYK y Gray (≤ 8 MiB, bajo pdfA o pdfx). El perfil debe ser un archivo ICC real (firma acsp, campo de tamaño coherente) — un stub hecho a mano se rechaza.
  • print.marks acepta true o un objeto; marks.colourBars (true o { tints, size }, size 4–72 pt, predeterminado 12) añade los sólidos C M Y K y sus tintas al 50 % en el sangrado. Desactivado por defecto; necesita un sangrado de aproximadamente 5 mm (14.17 pt) y se omite cuando la tira no cabe.
  • Comprueba el resultado con validate_pdf { standard: 'pdf-x-4' } — una verificación estructural, no un preflight certificado. Consulta docs/guides/PRINT.md y el prompt print_ready.

add_barcode

{
  "format": "qr",
  "data": "https://pdfnative.dev",
  "caption": "Scan to learn more",
  "ecLevel": "H",
  "outputMode": "file",
  "outputPath": "tickets/event-42.pdf"
}

Formatos admitidos: qr, code128, ean13, datamatrix, pdf417.

add_international_text

{
  "title": "مرحبا بالعالم",
  "lang": "ar",
  "paragraphs": [
    "هذا اختبار للنص العربي مع تشكيل OpenType ومحارف ثنائية الاتجاه.",
    "Mixed content: العربية + English ✓"
  ]
}

Códigos lang admitidos: los 27 scripts Unicode — ar, he, th, ja, zh, ko, el, hi, bn, ta, ru, ka, hy, tr, pl, vi, te, si, bo, km, my, am, y desde v1.7.0 lo (Lao), nod (Tai Tham), khb (New Tai Lue), tdd (Tai Le), cjm (Cham) — más las caras de utilidad latin, emoji y math. Desde v1.7.0 ha (Hausa), yo (Yoruba), ig (Igbo) y sw (Swahili) son alias de latin: el Noto Sans incluido ancla sus marcas de tono, no se incrusta ninguna fuente adicional. Las fuentes siempre se incrustan (sin entrada embedFonts); fija creationDate para una salida byte-idéntica. La herramienta también acepta typography y pdfx (consulta generate_basic_pdf).

Tai Tham bajo PDF/A: usa pdfA: 'pdfa2b' con lang: 'nod', no pdfa2u — un glifo carece de una entrada ToUnicode en el motor, que veraPDF rechaza bajo PDF/A-2u (rastreado upstream).

Documentos multi-script — pasa un array o una lista separada por comas:

{
  "title": "Mixed Script",
  "lang": ["ar", "emoji"],
  "paragraphs": ["العربية مع رموز 🎉🚀"],
  "pdfA": "pdfa2u"
}

sign_pdf

A partir de v1.0.0, sign_pdf auto-inyecta un marcador de posición /Sig cuando falta — puedes firmar cualquier PDF en una sola llamada:

{
  "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"
}

Para ECDSA P-256: usa algorithm: "ecdsa-sha256" y proporciona ecPrivateKeyDerBase64 (SEC1 o PKCS#8 DER) o ecPrivateScalarHex (64 caracteres hex).

Conversión PEM → DER:

openssl x509 -in cert.pem -outform DER | openssl base64 -A                 # cert
openssl rsa  -in key.pem  -outform DER -traditional | openssl base64 -A    # RSA PKCS#1
openssl pkey -in key.pem  -outform DER | openssl base64 -A                 # ECDSA

Usa prepare_signature_placeholder solo cuando necesites personalizar el marcador de posición (p. ej. placeholderBytes más grande para claves RSA >4096 bits, subFilter: 'ETSI.CAdES.detached', reserveTimestamp: true). De lo contrario, llama a sign_pdf directamente.

Escalera PAdES (v1.6.0). sign_pdf con profile: "pades" produce una firma B-B; añade timestamp: true para B-T (necesita PDFNATIVE_MCP_TSA_URL), luego add_ltv (B-LT) y timestamp_pdf (B-LTA):

// 1. sign_pdf  { ..., "profile": "pades", "timestamp": true, "certChainDerBase64": ["<intermediate DER>"] }
// 2. add_ltv   { "pdfBase64": "<signed>", "mode": "online" }            // or "offline" + certificatesDerBase64 / ocspResponsesDerBase64 / crlsDerBase64
// 3. timestamp_pdf { "pdfBase64": "<ltv>" }                              // re-run before the TSA certificate expires
// 4. verify_pdf { "pdfBase64": "<final>", "ltv": true }                  // -> ltvLevel: "B-LTA"

Los metadatos del firmante (signerName, reason, location, contactInfo) se integran en el marcador de posición; fieldName selecciona uno de varios marcadores de posición sin firmar (PLACEHOLDER_AMBIGUOUS en caso contrario) y allowMultiple: true añade una firma adicional. Consulta docs/guides/LTV.md.


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 },
    { "fieldType": "listbox", "name": "skills", "label": "Skills", "options": ["TypeScript", "PDF", "MCP"] },
    { "fieldType": "textarea", "name": "notes", "label": "Notes", "placeholder": "Anything we should know?" }
  ],
  "outputMode": "base64"
}

Tipos de campo: text, textarea (multilínea, /Ff 4096), checkbox, radio, dropdown, listbox; placeholder muestra texto de sugerencia mientras un campo está vacío. Añade encrypt para producir un formulario protegido por contraseña que conserve su AcroForm. Bajo una declaración PDF/A pasa embedFonts: true para que la fuente del campo también se incruste (desde v1.7.0 dicho formulario valida bajo veraPDF); sin ello, la llamada informa PDFA_UNEMBEDDED_FORM_FONT (strict: true entonces falla).

embed_image

{
  "title": "Product Photo",
  "imageBase64": "<base64-encoded JPEG bytes>",
  "mimeType": "image/jpeg",
  "caption": "Front view of Model X",
  "width": 400,
  "align": "center",
  "alt": "Front view of the Model X chassis",
  "outputMode": "base64"
}

Nota: el decodificador PNG del motor acepta solo imágenes de 8 bits, no entrelazadas, en escala de grises / RGB. Los PNG con canal alfa (tipo de color 4 / 6), paleta (tipo 3), 16 bits y entrelazados se rechazan en el límite con VALIDATION_ERROR y un remedio (aplanar o reexportar) — la misma regla se aplica a bloques image y marcas de agua de imagen. embed_image.imageBase64 mantiene su contrato 1.5.0 sin límite de longitud; el tope de 12 M caracteres se aplica solo a bloques image en línea y marcas de agua de imagen.

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"
}

Pasa los bytes PDF devueltos a sign_pdf para completar el flujo de trabajo de firma.

inspect_pdf

Inspección estructural y de seguridad de solo lectura — útil para verificación downstream, aserciones de CI y agentes de IA que necesitan razonar sobre un PDF antes de actuar sobre él.

{
  "pdfBase64": "<base64 PDF>",
  "pages": true,
  "check": ["pdfa", "signed", "attachments"]
}

Devuelve:

{
  "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[] acepta cualquiera de 'pdfa', 'signed', 'encrypted', 'placeholder', 'attachments', 'dss', 'docTimestamp', 'trapped', 'annotations' (los últimos cuatro desde v1.6.0) y 'pdfx' (desde v1.7.0: el XMP declara PDF/X — la declaración, no su validez; eso es validate_pdf standard: 'pdf-x-4'). pdfX aparece en el resultado solo cuando el XMP declara PDF/X (conservado por verbosity: 'summary'). checksPassed es el AND de todas las comprobaciones solicitadas. signatures: true añade un inventario por campo (subFilter, isDocTimestamp, isPlaceholder, byteRange, vriKey); annotations: true añade annotations[] (cada entrada /Annots: page basado en 0, subtype, rect, y cuando está presente contents truncado a 200 caracteres, title, color, quadPoints, enlace url) más annotationCount; dss, docTimestampCount y trapped aparecen solo cuando están presentes; con pages: true cada entrada perPage también lleva trimBox / bleedBox / artBox / cropBox / userUnit cuando se establecen.

inspect_layout

Simulación de paginación de solo lectura — el mismo blocks que generate_basic_pdf más cada entrada que mueve un bloque (title, footerText, pdfA, normalize, embedFonts, pageSize, margins, headerTemplate, footerTemplate, y desde v1.7.0 typography). No se produce ningún PDF; pasa exactamente lo que darás a generate_basic_pdf y totalPages coincide.

{ "title": "Memo", "blocks": [{ "type": "paragraph", "text": "Short note." }], "pageSize": "Letter", "verbosity": "summary", "fields": ["totalPages"] }

El resultado completo lleva pageWidth, pageHeight, margins, totalPages y pages[].blocks[] (type, page, x, top, width, height en puntos, redondeados a dos decimales). Desde v1.7.0 la simulación y la construcción comparten un planificador de paginación, por lo que cada tipo de bloque — toc incluido — se mide exactamente como se diseña (en 1.6.0 un bloque toc se medía como 0 pt).

validate_pdf

Verificación de conformidad estructural de solo lectura. standard elige el conjunto de reglas: 'pdf-ua-1' (predeterminado — PDF/UA, ISO 14289-1, para un PDF etiquetado) o, desde v1.7.0, 'pdf-x-4' (PDF/X-4, ISO 15930-7). Genera un documento accesible con cualquier herramienta usando pdfA (p. ej. pdfA: 'pdfa2u'), luego valida el resultado:

{ "pdfBase64": "<tagged-pdf-base64>" }

Devuelve:

{
  "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."
}

Verifica el catálogo /MarkInfo /Marked true, /StructTreeRoot (+ /ParentTree), /Metadata (XMP), /Lang y la unicidad de MCID por página. Esta es una puerta rápida de tiempo de desarrollo — no un sustituto de un validador de referencia completo (veraPDF), que además comprueba fuentes, color y renderizado.

PDF/X-4 (v1.7.0). { "pdfBase64": "<pdf>", "standard": "pdf-x-4" } comprueba los requisitos estructurales de ISO 15930-7: encabezado PDF 1.6, sin cifrado, trailer /ID, identificación XMP PDF/X-4, una intención de salida /GTS_PDFX con un perfil ICC prtr incrustado, un TrimBox o ArtBox por página anidado en el BleedBox y MediaBox, cada fuente incrustada, sin anotaciones en el área impresa, sin JavaScript, sin archivo incrustado, color de dispositivo coherente con la intención de salida. El resultado tiene la misma forma más caveats[], que indica lo que un veredicto valid: true no establece: esto no es un preflight certificado, y veraPDF no cubre PDF/X — confirma un trabajo de imprenta con callas pdfToolbox o Acrobat Preflight. La respuesta predeterminada (pdf-ua-1) no cambia y no lleva caveats.

annotate_pdf

Superpone anotaciones de marcado en un PDF existente mediante actualización incremental. Esta es una capa de revisión visual, no una redacción — el contenido subyacente no se toca.

{
  "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" }
  ]
}

Tipos: text, highlight, underline, strikeout, squiggly, square, circle, line, freetext. Los índices de página son basados en 0. color / interiorColor aceptan una cadena hex o de operandos (incluida una cadena de operandos CMYK), un triplete RGB 0–1 ([1, 1, 0]) o, desde v1.7.0, una tupla de porcentaje CMYK ([0, 0, 100, 0]); v1.7.0 también corrige el triplete 0–1, que solía renderizar casi negro. Las fuentes cifradas se rechazan (ENCRYPTED_SOURCE) — ejecuta decrypt_pdf primero (elimina firmas / AcroForm), anota, luego encrypt_pdf de nuevo.

draft_governance_issue

Redacta un issue de GitHub conforme a gobernanza localmente para que un humano lo revise y envíe. El servidor nunca contacta a GitHub (su única salida posible son los endpoints TSA / OCSP / CRL configurados por el operador — consulta Network & egress); devuelve el borrador en Markdown más un informe de cumplimiento legible por máquina.

{
  "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": "add_table with caption + repeatHeader over 2 pages (examples/bordered-table.json, then inspect_pdf)", "result": "Page 2 has no caption row." },
  "expectedBehavior": "The caption repeats with the header on every page.",
  "duplicateSearchPerformed": true
}

Un borrador que propone una dependencia en tiempo de ejecución, omite una reproducción o establece duplicateSearchPerformed: false se rechaza con GOVERNANCE_VIOLATION. Consulta docs/guides/AI_GOVERNANCE.md para el contrato completo de humano en el bucle.

verify_pdf, add_attachment, extract_text

Consulta las secciones dedicadas en docs/AI_GUIDE.md y la referencia en docs/KNOWLEDGE_BASE.md. Los ejemplos listos para ejecutar se encuentran en examples/.


🔐 Modelo de seguridad

pdfnative-mcp se ejecuta dentro del proceso anfitrión y expone un servidor MCP stdio (o un endpoint HTTP solo de bucle local). No realiza ninguna E/S fuera del sandbox configurado.

  • Las escrituras de archivos están controladas por PDFNATIVE_MCP_OUTPUT_DIR. Cuando no está definido, el modo de salida file se rechaza con un SecurityError.
  • La resolución de rutas rechaza rutas absolutas, secuencias de traversal (..), bytes NUL y cualquier extensión distinta de .pdf.
  • El tamaño de salida está limitado a 50 MB por llamada.
  • Las entradas se validan contra esquemas JSON estrictos + comprobaciones de runtime Zod en el límite de cada herramienta: las claves desconocidas o mal escritas (de nivel superior o anidadas) se rechazan con VALIDATION_ERROR, y las cargas útiles base64 / DER se verifican de forma básica (se tolera el prefijo data:, se rechaza la entrada PEM o doblemente codificada con el remedio) antes de que se ejecute cualquier analizador.
  • El transporte HTTP (PDFNATIVE_MCP_PORT) se vincula solo al bucle local; no tiene autenticación a menos que PDFNATIVE_MCP_HTTP_TOKEN esté definido (entonces 401 sin un token portador válido).

Red y salida

El servidor no realiza ninguna llamada de red saliente por defecto. La única salida que puede realizar va a los endpoints RFC 3161 / OCSP / CRL que el operador configuró en el entorno para la validación a largo plazo PAdES (PDFNATIVE_MCP_TSA_URL, PDFNATIVE_MCP_REVOCATION, PDFNATIVE_MCP_NETWORK_ALLOWED_HOSTS), nunca a una URL proporcionada por un argumento de herramienta, nunca a GitHub, nunca para telemetría. Sin esa configuración sign_pdf timestamp: true, timestamp_pdf y add_ltv mode: 'online' fallan rápidamente con TSA_NOT_CONFIGURED / REVOCATION_NOT_CONFIGURED antes de tocar el documento; add_ltv mode: 'offline' incrusta material proporcionado por el llamante con cero acceso a la red.

Las URLs de OCSP / CRL provienen de las extensiones AIA / CRL-distribution-point de certificados no confiables dentro del PDF, por lo que cada búsqueda pasa por una protección SSRF:

  • el host debe coincidir con la lista de permitidos del operador (host, host:port o *.suffix; los comodines simples se rechazan). Las entradas son nombres de host, no URLs: una entrada host:port solo coincide con URLs que llevan un puerto explícito (el analizador de URLs descarta los :80 / :443 predeterminados; lista el host simple para esos); las entradas con comodín no pueden llevar puerto; los nombres de host IDN deben listarse en punycode (xn--…); los literales IPv6 entre corchetes ([2001:db8::1]);
  • solo http: / https:, sin credenciales incrustadas, las redirecciones nunca se siguen;
  • los literales de dirección de bucle local, enlace local, privados, únicos locales, CGNAT, no especificados y multicast (incluidas las grafías decimal / octal / hexadecimal y IPv6 mapeado a IPv4) se rechazan a menos que ese literal esté en la lista de permitidos textualmente. La protección verifica solo literales: un nombre de host listado que resuelve a una dirección interna (DNS rebinding) no se detecta, ya que no hay resolutor sin añadir una dependencia; lista solo hosts que controles;
  • tiempo de espera por solicitud (PDFNATIVE_MCP_NETWORK_TIMEOUT_MS) y límites de respuesta (256 KiB TSA, 1 MiB OCSP, 16 MiB CRL) aplicados mientras se transmite, por lo que una respuesta sobredimensionada se corta en lugar de almacenarse en búfer;
  • las respuestas OCSP y los CRL devueltos por los respondedores se validan mediante análisis antes de que add_ltv los incruste;
  • la URL de TSA es de confianza del operador (solo comprobaciones de esquema y credenciales); el secreto PDFNATIVE_MCP_TSA_AUTH nunca se registra ni se repite en mensajes de error.

Los proveedores se construyen por llamada y se pasan a través de las opciones por llamada de pdfnative: los establecedores de proveedores a nivel de proceso nunca se usan, por lo que las solicitudes concurrentes no comparten nada. Las instrucciones server/discover informan la política de salida actual (solo tipos de endpoint, nunca secretos).

Consulta SECURITY.md para el proceso de divulgación responsable y docs/guides/LTV.md para la configuración del operador.


🧪 Desarrollo local

git clone https://github.com/Nizoka/pdfnative-mcp.git
cd pdfnative-mcp
npm ci                    # .npmrc sets ignore-scripts=true: nothing builds on install
npm run gate:fast         # before every commit: typecheck:all, lint, test, server-json, verify:docs
npm run gate              # the CI profile: build, dist checks, stdio smoke test, tool shape, samples, coverage, docs, corpus, validate:pdfx
npx tsx scripts/gate.ts --publish --require-all   # release branches: everything incl. validate:pdfa (veraPDF); a skipped step fails

npm run gate (scripts/gate.ts) es la única definición de verde: 1631 pruebas, los umbrales de cobertura de vitest.config.ts, y el servidor compilado manejado a través de stdio, donde stdout debe llevar solo tramas JSON-RPC. Imprime una línea por paso y escribe los registros en test-output/.gate/<step>.log; --only <step> ejecuta un paso y --json da salida de máquina (llama al script directamente para pasar banderas: npx tsx scripts/gate.ts --fast). Los pasos individuales son scripts npm ordinarios:

npm run build && npm run test:generate   # drive the built server under TZ=UTC, operator variables scrubbed -> test-output/samples/
npm run verify:samples    # hold the 96 samples to tests/_fixtures/samples.sha256.json (93 by bytes, 3 by a semantic projection)
npm run corpus:pdfa       # write the 41-file conformance corpus (33 claim PDF/A, 6 claim PDF/X-4, 2 page-tree outputs claim nothing)
npm run validate:pdfx     # in-process structural PDF/X-4 check of the corpus; never skips
npm run validate:pdfa     # veraPDF over the PDF/A files (VERAPDF_HOME, JAVACMD); exit 0 ok / 1 conformance / 2 infrastructure
npm run verify:docs       # every count, version, tool, error code, operator variable, link and anchor in the docs vs docs/assets/ecosystem.json and src/
npx tsx scripts/tool-shape.ts --write   # only after a deliberate tools/list schema change (npm run verify:tool-shape checks the fixture)

Un cambio de salida previsto se re-baselinea con npx tsx scripts/verify-samples.ts --update y se declara en la nota de versión, nunca para silenciar una sorpresa. Con veraPDF 1.30.2 el corpus da 27 PASS y 6 fallos esperados (canarios negativos que deben permanecer rechazados). Cada script, sus banderas y sus códigos de salida se enumeran en scripts/README.md.

Prueba de humo del servidor a través de stdio:

node dist/cli.js
# In another terminal, send a JSON-RPC initialize request via stdin (e.g. with mcp-inspector).

Colaboradores: comienza en AGENTS.md (las reglas del repositorio compartidas por cada agente de codificación y colaborador) y consulta docs/guides/LOCAL_TESTING.md para el flujo de trabajo completo de verificación local: la puerta de calidad, ejemplos como pruebas, validación de que los PDF generados son estructuralmente correctos (assertValidPdf, inspect_pdf, validate_pdf, verify_pdf), abrir la salida en un visor, verificación externa de PDF/A con veraPDF y el MCP Inspector.

📣 Proceso de publicación

pdfnative-mcp sigue el mismo formalismo de publicación que pdfnative:

  • Un archivo de nota de versión por etiqueta en release-notes/vX.Y.Z.md
  • CHANGELOG.md refleja cada lista de viñetas de la versión
  • El cuerpo de la versión de GitHub se copia de release-notes/vX.Y.Z.md
  • npx tsx scripts/release-prepare.ts --version X.Y.Z aplica la parte mecánica de un aumento de versión (nunca hace commit, etiqueta ni publica); la versión se mueve en bloque a través de package.json, src/version.ts, server.json y docs/assets/ecosystem.json
  • Una rama de publicación debe pasar npx tsx scripts/gate.ts --publish --require-all: cada paso, incluido veraPDF, sin omisiones
  • La publicación npm se maneja mediante Trusted Publishing de GitHub Actions (OIDC), sin NPM_TOKEN, desde un entorno protegido: el flujo de trabajo verifica que la etiqueta sea igual a la versión del paquete, ejecuta la puerta de publicación y publica con --provenance; un segundo trabajo atestigua el tarball (procedencia de compilación) junto con un SBOM CycloneDX y adjunta ambos a la versión de GitHub
  • Cada trabajo de Linux y Windows comienza con step-security/harden-runner (la acción no admite macOS; el trabajo de macOS es la excepción documentada), hace checkout con persist-credentials: false, instala con npm ci --ignore-scripts y se ejecuta bajo permissions de menor privilegio; las acciones se fijan por SHA de commit; el trabajo de veraPDF es bloqueante
  • Humano en el bucle: los agentes de codificación preparan y verifican; el mantenedor hace push, abre la solicitud de extracción, etiqueta y publica (.github/AGENT_RULES.md)

Consulta release-notes/TEMPLATE.md para la estructura canónica y la lista de verificación de publicación, y CONTRIBUTING.md para el procedimiento de publicación.


📚 Estructura del proyecto

src/
├── cli.ts                      # entrypoint: stdio (default) or Streamable HTTP (PDFNATIVE_MCP_PORT)
├── http.ts                     # Node http <-> Web Request/Response bridge + Host/Origin loopback guard
├── auth.ts                     # opt-in HTTP bearer token (PDFNATIVE_MCP_HTTP_TOKEN)
├── base64.ts                   # base64 / DER boundary decoding with agent-facing diagnostics
├── index.ts                    # public library exports
├── server.ts                   # Server factory, tool registry, cache hints, SERVER_INSTRUCTIONS
├── network.ts                  # operator-configured TSA / OCSP / CRL egress + SSRF guard
├── print.ts                    # print-production schema (boxes, bleed, marks + colour bars, userUnit, RGB / CMYK / Gray outputIntent, metadata, creationDate)
├── pdfx.ts                     # pdfx: 'pdfx4' conformance target + the static conflicts refused before the build
├── color.ts                    # shared colour fragment: CMYK operand string / percent tuple beside each historical form, toEngineColor()
├── typography.ts               # the typography fragment (12 opt-in keys) + TypographyOptions mapper
├── reproducible.ts             # operator-pinned creation instant (PDFNATIVE_MCP_CREATION_DATE, SOURCE_DATE_EPOCH)
├── diagnostics.ts              # engine diagnostics sink (PDFA_* / PDFX_* / TYPOGRAPHY_*), strict escalation by code, includeDiagnostics, embedFonts
├── chart.ts                    # charts v2 schema + ChartBlock mapper
├── blocks.ts                   # the 7 extended document blocks (table, image, link, toc, barcode, svg, formField)
├── layout.ts                   # pageSize / margins / header & footer templates / compress / debug / encrypt / typography (PdfLayoutOptions)
├── table.ts, barcode.ts, form.ts, image.ts   # bodies shared by a dedicated tool and its inline block
├── watermark.ts                # text and/or image watermark + position, PDF/A-1b transparency guard
├── encryption.ts               # password + encrypt schema (Standard Security Handler), decrypt error mapping
├── inflate-cap.ts              # PDFNATIVE_MCP_MAX_INFLATE_BYTES (engine decompression cap) + PDF_PARSE_FAILED mapping
├── 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 (+ print-dialog defaults)
├── pagetree.ts                 # page-tree error mapping (merge/split/extract)
├── crypto-provider.ts          # node:crypto signing provider for DER keys (SHA-256/384/512); verification stays pure JS
├── projection.ts               # verbosity / fields projection for the seven read tools
├── errors.ts                   # ToolError, SecurityError, GovernanceError
└── tools/
    ├── generate-basic-pdf.ts
    ├── inspect-layout.ts
    ├── add-barcode.ts
    ├── sign-pdf.ts
    ├── add-ltv.ts
    ├── timestamp-pdf.ts
    ├── update-metadata.ts
    ├── add-international-text.ts
    ├── add-table.ts
    ├── add-form.ts
    ├── read-form-fields.ts
    ├── fill-form.ts
    ├── add-chart.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
    ├── annotate-pdf.ts
    ├── encrypt-pdf.ts
    ├── decrypt-pdf.ts
    ├── draft-governance-issue.ts
    └── prepare-signature-placeholder.ts
scripts/                        # TypeScript run by tsx — the full table is in scripts/README.md
├── gate.ts                     # THE quality gate (npm run gate / gate:fast; --publish --require-all on release branches)
├── generate-samples.ts         # npm run test:generate — the built server under TZ=UTC -> test-output/samples/
├── verify-samples.ts           # npm run verify:samples — the byte baseline (tests/_fixtures/samples.sha256.json)
├── generate-pdfa-corpus.ts     # npm run corpus:pdfa — the 41-file PDF/A + PDF/X-4 conformance corpus
├── validate-pdfa.ts            # npm run validate:pdfa — veraPDF (PASS/FAIL/XFAIL/XPASS; exit 0 / 1 / 2)
├── validate-pdfx.ts            # npm run validate:pdfx — in-process structural PDF/X-4 check, never skips
├── tool-shape.ts               # structural tools/list fingerprint (--write refreshes tests/_fixtures/tool-shape.json)
├── verify-docs.ts              # npm run verify:docs — the docs held to docs/assets/ecosystem.json and the source tree
├── release-prepare.ts          # mechanical version bump (never commits, tags or publishes)
├── build-claude-rules.ts       # npm run agents:rules — .github/instructions/ -> .claude/rules/
└── verify-issue.mjs            # governance draft checker (npm run verify:issue)
docs/
├── AGENT_CONTRACT.md           # the consumer contract for agents that USE the server (catalogue, decision tree, recipes, error codes)
├── AI_GUIDE.md, KNOWLEDGE_BASE.md, API_STABILITY.md
├── guides/                     # PDFA, PRINT, TYPOGRAPHY, REPRODUCIBLE, CHARTS, FORMS, ENCRYPTION, LTV, AI_GOVERNANCE, LOCAL_TESTING
└── assets/ecosystem.json       # the single source of every count and version quoted in the docs
examples/                       # 43 executable tools/call sequences (npm run examples:check)
AGENTS.md, CLAUDE.md            # repository rules for coding agents; .github/instructions/ holds the per-area rules
.claude/                        # shared agent settings, the fail-closed guard hook, generated rules, the release-audit skill
.github/workflows/              # ci (Node 22 / 24 on Linux, plus Windows and macOS, all running the gate, all required), publish, sample-regression, verapdf (blocking),
                                #   docs, codeql, scorecard, dependency-review, audit
.github/rulesets/               # branch and tag rulesets to import
tests/                          # vitest suites (one per tool / module), _fixtures/ (tool shape, sample baseline, engine-surface matrix,
                                #   the frozen 1.5.0 catalogue), tools/ (repository tooling)

🗺 Hoja de ruta

v1.7.0 está publicado (tipografía fina, colores CMYK, PDF/X-4, 27 escrituras Unicode, salida reproducible en cada host, pdfnative 1.8), sobre v1.6.0 (cobertura completa del motor: 13 tipos de bloques, opciones de diseño, inspect_layout — escalera PAdES LTV, producción de impresión, gráficos v2, update_metadata, MCP 2026-07-28). El plan completo — hitos publicados, trabajo en curso y dirección a largo plazo — está en ROADMAP.md.

Aún diferido:

  • redact_pdf — pdfnative no tiene API de eliminación de contenido; una "redacción" solo de superposición crearía una falsa seguridad.
  • Fuentes personalizadas (un directorio de fuentes del lado del operador) y una anotación link en annotate_pdf — en la hoja de ruta, no en v1.7.0.
  • Verificación ECDSA nativa — pdfnative no exporta ecdsaVerifyHash; verify_pdf mantiene su ruta pura en JS para P-256.
  • Transmisión de páginas HTTP — MCP 2026-07-28 aún no tiene structuredContent parcial, por lo que los resultados grandes siguen siendo de una sola vez.

¿Tienes una idea de funcionalidad? Abre un issue o un PR.


⭐ Marca el proyecto con una estrella

Si pdfnative-mcp te resulta útil, por favor ⭐ este repositorio — y considera también marcar el motor subyacente Nizoka/pdfnative. Las estrellas ayudan a otros a descubrir el proyecto y motivan el desarrollo continuo.


🤝 Contribuciones

Las contribuciones son muy bienvenidas. Por favor, lee CONTRIBUTING.md, revisa los issues abiertos y sigue el código de conducta.


📄 Licencia

MIT © 2026 Nizoka. Material de terceros: THIRD-PARTY-NOTICES.md.

pdfnative-mcp está construido sobre pdfnative y el Model Context Protocol TypeScript SDK.