Pdfops MCP
Operaciones rápidas en archivos PDF
Documentación
pdfops
Herramientas PDF rápidas para agentes de IA.
Un solo binario, 31 herramientas, JSON de entrada y salida. Funciona como CLI, servidor MCP y biblioteca de Rust.
$ pdfops tables invoice.pdf --format markdown # tables as tables, not as a blob of text
$ pdfops redact contract.pdf --text "Jan Kowalski" -o safe.pdf # removed from the file, then verified
$ pdfops text scan.pdf --ocr --ocr-lang pol+eng # OCR only where there is no text layer
$ pdfops stamp offer.pdf --image signature.png --x 380 --y 690 -o signed.pdf
Por qué pdfops
- Diseñado para agentes. Cada comando devuelve un documento JSON, los errores indican qué hacer a continuación y el texto se puede leer bajo un presupuesto de caracteres con un punto de reanudación.
- Todo en un solo lugar. Lectura, diseño, tablas, OCR, renderizado, cirugía de páginas, sellado, redacción, anotaciones, firmas, formularios, cifrado, inspección y creación: 31 herramientas detrás de un solo esquema.
- Sin configuración. Un único binario sin bibliotecas PDF, sin Python y sin tiempo de ejecución. Solo el OCR necesita un programa adicional, y pdfops puede obtener los datos de idioma por sí mismo.
- Rápido. Los archivos se abren en milisegundos sin importar su tamaño, y el trabajo de páginas se ejecuta en todos los núcleos. Consulta los puntos de referencia.
- Redacción confiable. El contenido se elimina de la página, no se cubre, y el resultado se verifica con un segundo intérprete antes de escribir cualquier cosa.
- Seguro en archivos que no escribiste. Cada comando se ejecuta bajo un límite de memoria y un límite de tiempo, el servidor MCP ejecuta cada llamada en un proceso propio y se puede confinar a un directorio, y cada comando se prueba contra un corpus de 988 PDF hostiles y malformados en CI.
- Sabe qué está haciendo un archivo.
scaninforma scripts, acciones que se activan solas, programas adjuntos, nombres disfrazados y texto que se extrae pero no se puede ver, el portador de la inyección de prompts.sanitizeelimina el contenido activo y lo demuestra escaneando el resultado.
Inicio rápido
Como servidor MCP, sin nada instalado previamente:
{
"mcpServers": {
"pdfops": { "command": "npx", "args": ["-y", "pdfops-cli", "mcp"] }
}
}
Para Claude Code: claude mcp add pdfops -- npx -y pdfops-cli mcp.
Las herramientas se llaman pdf_info, pdf_text, pdf_redact y así sucesivamente, y aceptan los mismos argumentos que la
CLI. Las rutas relativas se resuelven contra el directorio de trabajo del servidor.
Para mantener a un agente dentro de una carpeta, agrega --root: se rechaza toda ruta fuera de ella, incluidas las rutas
que llegan dentro de un documento, y las rutas relativas se resuelven contra ella.
{ "command": "npx", "args": ["-y", "pdfops-cli", "mcp", "--root", "/home/me/documents"] }
Instalación
| Método | Comando | Requiere |
|---|---|---|
| npm | npm install -g pdfops-cli o npx pdfops-cli | Node 18+ |
| Precompilado, vía cargo | cargo binstall pdfops | cargo-binstall |
| Desde el código fuente | cargo install pdfops | Rust 1.92+ |
| Docker | docker run --rm -i -v "$PWD:/work" ghcr.io/kayzedd/pdfops mcp --root /work | Docker |
| Manual | descarga un archivo y pon pdfops en tu PATH | nada |
El paquete npm se llama pdfops-cli e instala el comando pdfops. Es un pequeño lanzador:
en la primera ejecución descarga el binario para tu plataforma desde la versión de GitHub, lo verifica contra
la suma SHA-256 publicada y lo almacena en caché. Los binarios precompilados cubren Linux (glibc y musl) y macOS
en x86-64 y ARM64, y Windows en x86-64. La imagen de Docker es Alpine con pdfops y tesseract
con inglés; agrega -v para la carpeta en la que trabajar.
El OCR además necesita el programa tesseract; nada más lo necesita. Los datos de idioma se descargan
a petición, sin derechos de administrador:
pdfops ocr-langs # is tesseract there, which languages can be used
pdfops ocr-install --lang pol+eng # download language data into the user's data directory
pdfops ocr-install --engine # install tesseract itself where that needs no password
Herramientas
| Comando | Qué hace | |
|---|---|---|
| Leer | info | Número de páginas, tamaño de página, metadatos, cifrado, esquema y resumen de formularios |
text | Texto página por página, con un presupuesto de caracteres y respaldo OCR opcional | |
search | Busca texto o una expresión regular, devuelve páginas y fragmentos | |
layout | Cuadro delimitador, fuente y tamaño de cada línea o palabra | |
tables | Tablas como filas de celdas, Markdown o CSV | |
outline | Marcadores con páginas de destino | |
annotations | Resaltados, comentarios, enlaces y otro marcado, con posiciones | |
signatures | Firmas digitales: quién firmó y si el documento cambió desde entonces | |
render | Páginas a PNG, para ver gráficos, escaneos y diseño | |
images | Las imágenes dibujadas en las páginas, como archivos | |
ocr | Texto reconocido de páginas escaneadas, opcionalmente escrito en una copia buscable | |
scan | Scripts, acciones automáticas, adjuntos, contenido disfrazado y texto oculto, por gravedad | |
| Construir | create | Un nuevo PDF desde Markdown |
merge | Varios PDF en uno | |
pages | Conservar, reordenar, duplicar o eliminar páginas | |
split | Dividir por número de páginas o por rangos | |
| Editar | rotate | Rotar páginas en múltiplos de 90 grados |
stamp | Marca de agua, encabezado, pie de página, números de página, una imagen como una firma o un código QR | |
annotate | Agregar un resaltado, subrayado, tachado, cuadro, nota o enlace | |
replace | Reemplazar texto en su lugar, en la fuente propia del documento cuando sea posible | |
redact | Eliminar texto, imágenes y dibujos en áreas o texto coincidente, luego verificar | |
set-meta | Título, autor, asunto, palabras clave, creador | |
compress | Reducir: sin pérdida por defecto, opcionalmente recodificando y reduciendo la escala de las imágenes | |
| Formularios | forms | Campos con sus tipos, valores y opciones |
fill | Rellenar campos por nombre | |
| Proteger | encrypt | Contraseñas y permisos AES-256 |
decrypt | Eliminar la protección con contraseña | |
sign | Firmar digitalmente con un certificado, manteniendo válidas las firmas anteriores | |
sanitize | Eliminar scripts, acciones riesgosas, adjuntos, XFA y medios, luego verificar escaneando | |
| Configuración | ocr-langs | Si tesseract está instalado y qué idiomas son utilizables |
ocr-install | Descargar datos de idioma OCR, opcionalmente instalar tesseract |
Ejecuta pdfops <command> --help para las opciones de cada uno.
Ejemplos
$ pdfops info manual.pdf
{"encrypted":false,"file":"manual.pdf","form_fields":0,
"metadata":{"created":"2026-06-30T09:07:46+00:00","producer":"GPL Ghostscript 10.07.1"},
"outline_entries":645,"page_size_pt":{"height":792.0,"width":595.0},"pages":357,"pdf_version":"1.3",
"size_bytes":1386723,"uniform_page_size":true}
$ pdfops search manual.pdf "calling convention" --max-results 1 --context 40
{"file":"manual.pdf","matches":[{"match":"Calling Convention","page":12,
"snippet":"... 10.5.1 The Pascal Calling Convention ..."}],"query":"calling convention",
"total_matches":17,"unreadable_pages":[]}
Lectura:
pdfops text manual.pdf --pages 12- --max-chars 4000 # resume_at_page says where to continue
pdfops text scan.pdf --ocr --ocr-lang pol+eng # OCR only the pages that have no text
pdfops tables report.pdf --pages 4 --format markdown
pdfops layout report.pdf --pages 4 --level words # bbox, font and size per word
pdfops render report.pdf --pages 1-3 --dpi 150 -o out/ # look at charts and layout
pdfops --stream ocr scan.pdf --lang pol # a line per page as it finishes
pdfops ocr scan.pdf --lang pol -o searchable.pdf # the same file, with text to search
pdfops images report.pdf -o images/
Construcción y trabajo de páginas:
pdfops create notes.md -o notes.pdf
pdfops merge a.pdf b.pdf -o merged.pdf
pdfops pages in.pdf --keep "3,1,5-" -o out.pdf
pdfops split in.pdf --every 10 -o parts/
Edición:
pdfops stamp in.pdf --text "Poufne · {page}/{pages}" --position footer -o out.pdf
pdfops stamp in.pdf --image signature.png --x 380 --y 690 --width 140 --pages last -o out.pdf
pdfops stamp in.pdf --qr "https://example.com/doc/42" --anchor bottom-right -o out.pdf
pdfops redact in.pdf --text "Jan Kowalski" --text "\d{11}" --regex -o redacted.pdf
pdfops redact in.pdf --rect "2:100,200,300,220" -o redacted.pdf
pdfops replace in.pdf --find "2025" --with "2026" -o out.pdf
pdfops replace in.pdf --find "2025" --with "2026" --dry-run -o out.pdf # the plan, nothing written
pdfops compress in.pdf --max-image-edge 1600 --image-quality 70 -o small.pdf
Formularios y protección:
pdfops forms form.pdf
pdfops fill form.pdf --set name="Ada Lovelace" --set agree=true -o filled.pdf
pdfops encrypt in.pdf --owner-password secret --deny-copy -o locked.pdf
pdfops sign in.pdf --p12 identity.p12 --p12-password secret --reason "Approved" -o signed.pdf
pdfops sign in.pdf --cert me.crt --key me.key --visible "1:360,700,560,760" -o signed.pdf
pdfops signatures signed.pdf # valid, unchanged, who and when
pdfops signatures signed.pdf --trust company-root.pem # and whether the signer is one of yours
pdfops sign in.pdf --p12 identity.p12 --tsa http://timestamp.digicert.com -o signed.pdf
pdfops scan inbox/offer.pdf # what is in it, before reading it
pdfops sanitize inbox/offer.pdf -o offer-clean.pdf # scripts, actions, attachments removed
Marcado:
pdfops annotate in.pdf --text "liability" --comment "check with legal" -o marked.pdf
pdfops annotate in.pdf --kind link --rect "1:72,50,300,70" --url https://example.com -o out.pdf
pdfops annotations marked.pdf
Convenciones
- Salida. Cada comando imprime un documento JSON en stdout. Los errores van a stderr como
{"error": "..."}con estado de salida 1. Agrega--prettypara sangrar. - Progreso. Con
--stream,ocr,text --ocr,render,images,split,redactyreplacese imprime una línea de JSON por página o archivo terminado,{"event":"progress","step":"render","done":7,"total":20,"page":12,...}, y el resultado habitual como la última línea. Las páginas se procesan en paralelo, por lo que los eventos llegan en el orden en que el trabajo termina; cada uno nombra su página ydoneaumenta en uno por línea. A través de MCP, los mismos eventos llegan comonotifications/progresscuando la llamada lleva un token de progreso. - Escritura. Los comandos que escriben aceptan
-o. Puede ser el archivo de entrada: la salida pasa por un archivo temporal. - Páginas están basadas en 1 y separadas por comas:
3,2-5,7-(hasta el final),-4(desde el inicio),5-2(descendente),last,odd,even,all. - Posiciones están en puntos con el origen en la esquina superior izquierda de la página tal como se muestra, y
creciendo hacia abajo.
layoutlas informa,stamp --x/--yyredact --rectlas aceptan, y un píxel derender --dpi 72es exactamente un punto.
Cómo se compara
| pdfops | PyMuPDF | pypdf | pdfplumber | qpdf | poppler-utils | |
|---|---|---|---|---|---|---|
| Extracción de texto | ✓ | ✓ | ✓ | ✓ | – | ✓ |
| Posiciones de palabras y fuentes | ✓ | ✓ | – | ✓ | – | posiciones |
| Tablas | ✓ | ✓ | – | ✓ | – | – |
| Renderizar páginas | ✓ | ✓ | – | ✓ | – | ✓ |
| OCR | ✓ | ✓ | – | – | – | – |
| Fusionar, dividir, reordenar, rotar | ✓ | ✓ | ✓ | – | ✓ | parcialmente |
| Sellos de texto, imagen y QR | ✓ | ✓ | por superposición | – | por superposición | – |
| Redacción que elimina contenido | ✓ | ✓ | – | – | – | – |
| Redacción verificada antes de escribir | ✓ | – | – | – | – | – |
| Reemplazar texto en su lugar | ✓ | – | – | – | – | – |
| Rellenar formularios | ✓ | ✓ | ✓ | – | – | – |
| Cifrar y descifrar | ✓ | ✓ | ✓ | – | ✓ | – |
| Agregar y listar anotaciones | ✓ | ✓ | ✓ | listar | – | – |
| Firmar digitalmente | ✓ | – | – | – | – | – |
| Verificar firmas | ✓ | – | – | – | – | ✓ |
| Inspeccionar contenido activo y texto oculto | ✓ | – | – | – | – | – |
| Eliminar contenido activo | ✓ | ✓ | – | – | – | – |
| Límites de memoria y tiempo por llamada | ✓ | – | – | – | – | – |
| Crear desde Markdown | ✓ | desde HTML | – | – | – | – |
| Servidor MCP integrado y esquemas de herramientas | ✓ | – | – | – | – | – |
| JSON desde cada comando | ✓ | biblioteca | biblioteca | biblioteca | parcialmente | – |
| Tiempo de ejecución necesario | ninguno | Python | Python | Python | ninguno | ninguno |
| Licencia | MIT | AGPL o comercial | BSD | MIT | Apache-2.0 | GPL |
PyMuPDF es el más cercano en alcance y es una biblioteca excelente; está escrito en C, necesita Python, y su licencia AGPL importa si lo distribuyes. pdfops intercambia algo de amplitud por un único binario con licencia MIT cuyas herramientas un agente puede llamar directamente.
Puntos de referencia
Mejor de 3 ejecuciones de proceso completo, incluido el inicio, ya que eso es lo que cuesta una llamada de herramienta a un agente.
Documento: el manual de NASM 2.16, 308 páginas y 1.2 MB; images en un libro de 352 páginas y 2.6 MB con
imágenes; formularios en un formulario de una página. Máquina: AMD Ryzen 7 9800X3D de 8 núcleos, Windows 11. Versiones:
poppler 25.07, qpdf 12.4, PyMuPDF 1.28, pypdf 6.19, pdfplumber 0.11, pyHanko 0.37 (CLI 0.5),
tesseract 5.5. La entrada más rápida de cada fila está en negrita; n/a marca una herramienta que estaba instalada y
no completó la tarea. La entrada de PyMuPDF para sanitize es su scrub.
| Tarea | pdfops | herramienta de línea de comandos | PyMuPDF | pypdf | pdfplumber |
|---|---|---|---|---|---|
info | 7 ms | pdfinfo 12 ms | 129 ms | 203 ms | 222 ms |
text, todas las páginas | 53 ms | pdftotext 540 ms | 297 ms | 1273 ms | 11.3 s |
search, todas las páginas | 53 ms | - | 326 ms | - | - |
layout, cada palabra con su cuadro | 194 ms | - | 346 ms | - | 10.9 s |
tables, 50 páginas | 20 ms | - | 1558 ms | - | 1629 ms |
outline | 15 ms | - | 128 ms | 221 ms | - |
render, 20 páginas a 150 dpi | 41 ms | pdftoppm 2677 ms | 593 ms | - | - |
ocr, una página | 1087 ms | tesseract 759 ms | - | - | - |
images, todas las imágenes incrustadas | 230 ms | pdfimages 5621 ms | 1297 ms | 1553 ms | - |
create, 100 secciones de Markdown | 12 ms | - | - | - | - |
merge, tres copias | 57 ms | qpdf 204 ms | 401 ms | 2037 ms | - |
pages, conservar 10 | 17 ms | qpdf 138 ms | 138 ms | 285 ms | - |
split, un archivo por página | 156 ms | pdfseparate 46.0 s | 437 ms | 2513 ms | - |
rotate, todas las páginas | 20 ms | qpdf 144 ms | 147 ms | 769 ms | - |
stamp, texto en cada página | 21 ms | - | 279 ms | - | - |
stamp, código QR en cada página | 121 ms | - | - | - | - |
annotations, lista | 18 ms | - | 227 ms | - | 309 ms |
annotate, resaltar una palabra en cada página | 291 ms | - | 700 ms | - | - |
redact, una palabra en cada página | 564 ms | - | 1926 ms | - | - |
replace, una palabra en cada página | 575 ms | - | - | - | - |
scan, estructura y texto oculto | 232 ms | - | - | - | - |
scan, solo estructura | 16 ms | - | - | - | - |
sanitize | 32 ms | - | 2072 ms | - | - |
set-meta | 19 ms | - | 140 ms | 771 ms | - |
compress | 31 ms | qpdf 179 ms | 378 ms | - | - |
encrypt, AES-256 | 25 ms | qpdf 174 ms | 150 ms | 825 ms | - |
decrypt | 38 ms | qpdf 170 ms | 158 ms | 875 ms | - |
sign, RSA-2048 | 23 ms | pyhanko 615 ms | - | - | - |
signatures, verificar | 16 ms | pdfsig n/a | - | - | - |
forms, listar campos | 7 ms | - | 121 ms | 169 ms | - |
fill, un campo | 9 ms | - | 130 ms | 198 ms | - |
Reproducir con scripts/bench.py en cualquier documento.
Por qué es rápido: info, layout, tables, render, images y ocr leen objetos bajo demanda, por lo que
abrir un archivo cuesta unos pocos milisegundos sea cual sea su tamaño. La extracción de texto, el diseño, las tablas,
el renderizado, la división, la exportación de imágenes y el OCR se ejecutan en todos los núcleos. Las operaciones de página copian solo los objetos
a los que llegan las páginas seleccionadas, por lo que el tamaño de salida y el tiempo siguen a la selección, no al origen.
Uso sin MCP
pdfops tools imprime [{"name", "description", "inputSchema"}] para cada comando. Pásalos a
cualquier función que llame a la API y luego ejecuta la llamada a través de la CLI o la biblioteca.
let result = pdfops::tools::call(
"pdf_text",
serde_json::json!({"input": "report.pdf", "pages": "1-3"}),
)?;
Los puntos de entrada tipados viven en pdfops::ops, por ejemplo pdfops::ops::read::text(TextArgs { .. }).
Comportamiento que vale la pena conocer
La versión corta. pdfops <command> --help tiene el detalle de cada comando.
- Cualquier script. El texto que
stamp,fill,replace,createyocr -odibujan se moldea y se incrusta como subconjuntos de fuentes; donde ninguna fuente tiene todos los caracteres, varias comparten el texto. El hebreo y el árabe se componen de derecha a izquierda y se leen de vuelta en el orden en que se leen, por lo que una palabra se encuentra escribiéndola. Algunos grupos índicos y tailandeses que el propio pdfops escribió se leen de vuelta con sus caracteres reagrupados. - La redacción elimina, no cubre. Los glifos, los píxeles de imagen en cualquier codificación, los dibujos y las anotaciones bajo un área se eliminan, un texto a redactar también se elimina de los metadatos y marcadores, y el resultado es leído de vuelta por un segundo intérprete antes de escribir nada.
- Reemplazar escribe en la fuente propia del documento donde tiene los glifos y mueve el resto de
la línea; los valores de los campos de texto y los comentarios de las anotaciones también se cambian. Una línea
que crece más allá de su columna pasa sus últimas palabras a la siguiente línea, y el párrafo recibe una
línea más si hay espacio debajo. Donde el párrafo no se puede determinar con certeza, la línea se deja como está y
overflow_ptdice en cuánto se excede;--dry-runlo muestra primero. - Ejecución de prueba.
redact,replace,annotateystampaceptan--dry-run: todo el trabajo, nada escrito. - Escaneo informa scripts, acciones, adjuntos, contenido disfrazado y texto oculto por severidad. No es un escáner de virus y nunca llama seguro a un archivo. Sanear elimina el contenido activo y escanea el resultado antes de escribirlo.
- Tablas con líneas de regla se leen celda por celda. Las tablas sin ellas se infieren por la alineación
(
detected_by: alignment) y merecen una revisión. - OCR es el de tesseract.
ocr -oescribe el texto reconocido en una copia como una capa invisible. - Firmas.
signañade, por lo que las firmas anteriores siguen siendo válidas. Puede mostrar la firma en una página (--visible) e incrustar la declaración de tiempo de una autoridad de sellado de tiempo (--tsa).signaturescomprueba que los bytes no han cambiado y quién firmó; con--trustcomprueba la cadena del firmante contra certificados que tú nombras, y con--revocationsus listas de revocación. Cualquier otro cambio en un PDF firmado invalida sus firmas, como debe ser. - Fusión y trabajo de páginas conservan marcadores y enlaces que llevan a páginas en la salida, y
renombran los campos de formulario de entradas posteriores
doc2.<name>para que nombres iguales no compartan un valor. - Archivos dañados se reparan para lectura y se reconstruyen para escritura; el resultado lleva entonces
repaired_inputs. Un archivo dañado que también está cifrado no se reconstruye. Los archivos protegidos siguen protegidos al editarse; solodecryptelimina la protección. - Límites. 4 GiB y 300 segundos por comando por defecto:
--max-memory,--timeout, oPDFOPS_MAX_MEMORYyPDFOPS_TIMEOUT; 0 levanta un límite. Los rásteres están limitados a 64 megapíxeles. - Crear acepta Markdown, y el HTML en él por lo que dice: encabezados, énfasis, enlaces, listas, tablas. Cómo debería verse no se lee: no hay CSS.
Desarrollo
cargo test # under a second; fixtures are generated in memory
cargo test -- --ignored # OCR test, needs tesseract with a language pack
cargo clippy --all-targets -- -D warnings
cargo fmt --check
scripts/bench.py --help # regenerate the benchmark table
Construido sobre lopdf (modelo de objetos), hayro (texto, renderizado, posiciones y decodificación de imágenes), rustybuzz (moldeado de texto), subsetter (incrustación de fuentes) y pulldown-cmark (Markdown).
Licencia
MIT