Pdfops MCP
Operações rápidas em arquivos PDF
Documentação
pdfops
Ferramentas PDF rápidas para agentes de IA.
Um único binário, 31 ferramentas, JSON na entrada e na saída. Funciona como CLI, servidor MCP e biblioteca 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 que pdfops
- Feito para agentes. Cada comando retorna um documento JSON, erros indicam o que fazer em seguida, e o texto pode ser lido sob um orçamento de caracteres com um ponto de retomada.
- Tudo em um só lugar. Leitura, layout, tabelas, OCR, renderização, cirurgia de páginas, carimbo, redação, anotações, assinaturas, formulários, criptografia, inspeção e criação: 31 ferramentas em um único esquema.
- Nada para configurar. Um único binário sem bibliotecas PDF, sem Python e sem runtime. Apenas OCR precisa de um programa extra, e o pdfops pode baixar os dados de idioma por conta própria.
- Rápido. Arquivos abrem em milissegundos, independentemente do tamanho, e o trabalho em páginas roda em todos os núcleos. Veja os benchmarks.
- Redação confiável. O conteúdo é excluído da página, não coberto, e o resultado é verificado por um segundo interpretador antes de qualquer gravação.
- Seguro em arquivos que você não criou. Cada comando roda sob um limite de memória e um limite de tempo, o servidor MCP executa cada chamada em um processo próprio e pode ser confinado a um diretório, e cada comando é testado contra um corpus de 988 PDFs hostis e malformados no CI.
- Sabe o que um arquivo está fazendo.
scanrelata scripts, ações que disparam sozinhas, programas anexados, nomes disfarçados e texto extraído que não pode ser visto, o vetor de injeção de prompt.sanitizeremove o conteúdo ativo e prova isso escaneando o resultado.
Início rápido
Como servidor MCP, sem nada instalado previamente:
{
"mcpServers": {
"pdfops": { "command": "npx", "args": ["-y", "pdfops-cli", "mcp"] }
}
}
Para Claude Code: claude mcp add pdfops -- npx -y pdfops-cli mcp.
As ferramentas são nomeadas pdf_info, pdf_text, pdf_redact e assim por diante, e aceitam os mesmos argumentos da
CLI. Caminhos relativos são resolvidos em relação ao diretório de trabalho do servidor.
Para manter um agente dentro de uma pasta, adicione --root: todo caminho fora dela é recusado, incluindo caminhos
que chegam dentro de um documento, e caminhos relativos são resolvidos em relação a ela.
{ "command": "npx", "args": ["-y", "pdfops-cli", "mcp", "--root", "/home/me/documents"] }
Instalação
| Método | Comando | Necessita |
|---|---|---|
| npm | npm install -g pdfops-cli ou npx pdfops-cli | Node 18+ |
| Pré-compilado, via cargo | cargo binstall pdfops | cargo-binstall |
| A partir do código-fonte | cargo install pdfops | Rust 1.92+ |
| Docker | docker run --rm -i -v "$PWD:/work" ghcr.io/kayzedd/pdfops mcp --root /work | Docker |
| Manual | baixe um arquivo e coloque pdfops no seu PATH | nada |
O pacote npm é chamado pdfops-cli e instala o comando pdfops. É um pequeno lançador:
na primeira execução, ele baixa o binário para sua plataforma do release do GitHub, verifica-o
contra a soma SHA-256 publicada e o armazena em cache. Os binários pré-compilados cobrem Linux (glibc e musl) e macOS
em x86-64 e ARM64, e Windows em x86-64. A imagem Docker é Alpine com pdfops e tesseract
com inglês; adicione -v para a pasta de trabalho.
OCR adicionalmente precisa do programa tesseract; nada mais precisa. Os dados de idioma são baixados sob
solicitação, sem direitos 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
Ferramentas
| Comando | O que faz | |
|---|---|---|
| Leitura | info | Contagem de páginas, tamanho da página, metadados, criptografia, sumário e resumo de formulários |
text | Texto página por página, com orçamento de caracteres e fallback opcional de OCR | |
search | Encontra texto ou uma regex, retorna páginas e trechos | |
layout | Caixa delimitadora, fonte e tamanho de cada linha ou palavra | |
tables | Tabelas como linhas de células, Markdown ou CSV | |
outline | Marcadores com páginas de destino | |
annotations | Realces, comentários, links e outras marcações, com posições | |
signatures | Assinaturas digitais: quem assinou e se o documento mudou desde então | |
render | Páginas para PNG, para visualizar gráficos, digitalizações e layout | |
images | As imagens desenhadas nas páginas, como arquivos | |
ocr | Texto reconhecido de páginas digitalizadas, opcionalmente gravado em uma cópia pesquisável | |
scan | Scripts, ações automáticas, anexos, conteúdo disfarçado e texto oculto, por gravidade | |
| Construção | create | Um novo PDF a partir de Markdown |
merge | Vários PDFs em um só | |
pages | Manter, reordenar, duplicar ou excluir páginas | |
split | Dividir por contagem de páginas ou por intervalos | |
| Edição | rotate | Girar páginas em múltiplos de 90 graus |
stamp | Marca d'água, cabeçalho, rodapé, números de página, uma imagem como assinatura ou um código QR | |
annotate | Adicionar um realce, sublinhado, tachado, caixa, nota ou link | |
replace | Substituir texto no lugar, na fonte do próprio documento quando possível | |
redact | Remover texto, imagens e desenhos em áreas ou texto correspondente, depois verificar | |
set-meta | Título, autor, assunto, palavras-chave, criador | |
compress | Reduzir: sem perdas por padrão, opcionalmente re-encodificando e reduzindo a escala de imagens | |
| Formulários | forms | Campos com seus tipos, valores e opções |
fill | Preencher campos por nome | |
| Proteção | encrypt | Senhas e permissões AES-256 |
decrypt | Remover proteção por senha | |
sign | Assinar digitalmente com um certificado, mantendo assinaturas anteriores válidas | |
sanitize | Remover scripts, ações arriscadas, anexos, XFA e mídia, depois verificar por escaneamento | |
| Configuração | ocr-langs | Se o tesseract está instalado e quais idiomas são utilizáveis |
ocr-install | Baixar dados de idioma para OCR, opcionalmente instalar tesseract |
Execute pdfops <command> --help para as opções de cada um.
Exemplos
$ 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":[]}
Leitura:
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/
Construção e trabalho em 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/
Edição:
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
Formulários e proteção:
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
Marcação:
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
Convenções
- Saída. Cada comando imprime um documento JSON no stdout. Erros vão para o stderr como
{"error": "..."}com status de saída 1. Adicione--prettypara indentar. - Progresso. Com
--stream,ocr,text --ocr,render,images,split,redactereplaceimprimem uma linha de JSON por página ou arquivo concluído,{"event":"progress","step":"render","done":7,"total":20,"page":12,...}, e o resultado usual como a última linha. As páginas são trabalhadas em paralelo, então os eventos chegam na ordem em que o trabalho termina; cada um nomeia sua página edoneaumenta em um por linha. Via MCP, os mesmos eventos chegam comonotifications/progressquando a chamada carrega um token de progresso. - Gravação. Comandos que gravam aceitam
-o. Pode ser o arquivo de entrada: a saída passa por um arquivo temporário. - Páginas são baseadas em 1 e separadas por vírgula:
3,2-5,7-(até o final),-4(desde o início),5-2(decrescente),last,odd,even,all. - Posições são em pontos com a origem no canto superior esquerdo da página como exibida, y
crescendo para baixo.
layoutas relata,stamp --x/--yeredact --rectas aceitam, e um pixel derender --dpi 72é exatamente um ponto.
Como se compara
| pdfops | PyMuPDF | pypdf | pdfplumber | qpdf | poppler-utils | |
|---|---|---|---|---|---|---|
| Extração de texto | ✓ | ✓ | ✓ | ✓ | – | ✓ |
| Posições e fontes de palavras | ✓ | ✓ | – | ✓ | – | posições |
| Tabelas | ✓ | ✓ | – | ✓ | – | – |
| Renderizar páginas | ✓ | ✓ | – | ✓ | – | ✓ |
| OCR | ✓ | ✓ | – | – | – | – |
| Mesclar, dividir, reordenar, girar | ✓ | ✓ | ✓ | – | ✓ | parcialmente |
| Carimbos de texto, imagem e QR | ✓ | ✓ | por sobreposição | – | por sobreposição | – |
| Redação que remove conteúdo | ✓ | ✓ | – | – | – | – |
| Redação verificada antes de gravar | ✓ | – | – | – | – | – |
| Substituir texto no lugar | ✓ | – | – | – | – | – |
| Preencher formulários | ✓ | ✓ | ✓ | – | – | – |
| Criptografar e descriptografar | ✓ | ✓ | ✓ | – | ✓ | – |
| Adicionar e listar anotações | ✓ | ✓ | ✓ | listar | – | – |
| Assinar digitalmente | ✓ | – | – | – | – | – |
| Verificar assinaturas | ✓ | – | – | – | – | ✓ |
| Inspecionar conteúdo ativo e texto oculto | ✓ | – | – | – | – | – |
| Remover conteúdo ativo | ✓ | ✓ | – | – | – | – |
| Limites de memória e tempo por chamada | ✓ | – | – | – | – | – |
| Criar a partir de Markdown | ✓ | de HTML | – | – | – | – |
| Servidor MCP integrado e esquemas de ferramentas | ✓ | – | – | – | – | – |
| JSON de cada comando | ✓ | biblioteca | biblioteca | biblioteca | parcialmente | – |
| Runtime necessário | nenhum | Python | Python | Python | nenhum | nenhum |
| Licença | MIT | AGPL ou comercial | BSD | MIT | Apache-2.0 | GPL |
PyMuPDF é o mais próximo em escopo e é uma biblioteca excelente; é escrito em C, precisa de Python, e sua licença AGPL importa se você o distribuir. pdfops troca alguma amplitude por um único binário licenciado sob MIT cujas ferramentas um agente pode chamar diretamente.
Benchmarks
Melhor de 3 execuções de processo inteiro, incluindo inicialização, já que é isso que uma chamada de ferramenta custa a um agente.
Documento: o manual do NASM 2.16, 308 páginas e 1,2 MB; images em um livro de 352 páginas e 2,6 MB com
imagens; formulários em um formulário de uma página. Máquina: AMD Ryzen 7 9800X3D de 8 núcleos, Windows 11. Versões:
poppler 25.07, qpdf 12.4, PyMuPDF 1.28, pypdf 6.19, pdfplumber 0.11, pyHanko 0.37 (CLI 0.5),
tesseract 5.5. A entrada mais rápida de cada linha está em negrito; n/a marca uma ferramenta que foi instalada e
não concluiu a tarefa. A entrada do PyMuPDF para sanitize é seu scrub.
| Tarefa | pdfops | ferramenta de linha de comando | PyMuPDF | pypdf | pdfplumber |
|---|---|---|---|---|---|
info | 7 ms | pdfinfo 12 ms | 129 ms | 203 ms | 222 ms |
text, todas as páginas | 53 ms | pdftotext 540 ms | 297 ms | 1273 ms | 11,3 s |
search, todas as páginas | 53 ms | - | 326 ms | - | - |
layout, cada palavra com sua caixa | 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, uma página | 1087 ms | tesseract 759 ms | - | - | - |
images, todas as imagens incorporadas | 230 ms | pdfimages 5621 ms | 1297 ms | 1553 ms | - |
create, 100 seções de Markdown | 12 ms | - | - | - | - |
merge, três cópias | 57 ms | qpdf 204 ms | 401 ms | 2037 ms | - |
pages, manter 10 | 17 ms | qpdf 138 ms | 138 ms | 285 ms | - |
split, um arquivo por página | 156 ms | pdfseparate 46,0 s | 437 ms | 2513 ms | - |
rotate, todas as páginas | 20 ms | qpdf 144 ms | 147 ms | 769 ms | - |
stamp, texto em todas as páginas | 21 ms | - | 279 ms | - | - |
stamp, código QR em todas as páginas | 121 ms | - | - | - | - |
annotations, lista | 18 ms | - | 227 ms | - | 309 ms |
annotate, destacar uma palavra em todas as páginas | 291 ms | - | 700 ms | - | - |
redact, uma palavra em todas as páginas | 564 ms | - | 1926 ms | - | - |
replace, uma palavra em todas as páginas | 575 ms | - | - | - | - |
scan, estrutura e texto oculto | 232 ms | - | - | - | - |
scan, apenas estrutura | 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/d | - | - | - |
forms, listar campos | 7 ms | - | 121 ms | 169 ms | - |
fill, um campo | 9 ms | - | 130 ms | 198 ms | - |
Reproduza com scripts/bench.py em qualquer documento.
Por que é rápido: info, layout, tables, render, images e ocr leem objetos sob demanda, então
abrir um arquivo custa alguns milissegundos, independentemente do tamanho. Extração de texto, layout, tabelas,
renderização, divisão, exportação de imagens e OCR são executados em todos os núcleos. Operações de página copiam apenas os objetos
que as páginas selecionadas alcançam, então o tamanho da saída e o tempo seguem a seleção, não a origem.
Usando sem MCP
pdfops tools imprime [{"name", "description", "inputSchema"}] para cada comando. Passe-os para
qualquer função que chame a API e execute a chamada via CLI ou biblioteca.
let result = pdfops::tools::call(
"pdf_text",
serde_json::json!({"input": "report.pdf", "pages": "1-3"}),
)?;
Pontos de entrada tipados estão em pdfops::ops, por exemplo pdfops::ops::read::text(TextArgs { .. }).
Comportamento que vale a pena conhecer
A versão curta. pdfops <command> --help tem o detalhe para cada comando.
- Qualquer script. Texto que
stamp,fill,replace,createeocr -odesenham é moldado e incorporado como subconjuntos de fonte; onde nenhuma fonte única tem todos os caracteres, várias compartilham o texto. Hebraico e árabe são dispostos da direita para a esquerda e lidos de volta na ordem em que são lidos, então uma palavra é encontrada digitando-a. Alguns agrupamentos indianos e tailandeses que o próprio pdfops escreveu são lidos de volta com seus caracteres reagrupados. - Redação remove, não cobre. Glifos, pixels de imagem em qualquer codificação, desenhos e anotações sob uma área são excluídos, um texto a redigir também é removido de metadados e marcadores, e o resultado é lido de volta por um segundo interpretador antes que qualquer coisa seja escrita.
- Substituir escreve na fonte do próprio documento onde ela tem os glifos e move o resto da
linha adiante; valores de campos de texto e comentários de anotações também são alterados. Uma linha
que cresce além de sua coluna passa suas últimas palavras para a próxima linha, e o parágrafo ganha uma
linha a mais se houver espaço abaixo. Onde o parágrafo não pode ser determinado com certeza, a linha
é deixada como está e
overflow_ptdiz em quanto ela ultrapassa;--dry-runmostra primeiro. - Execução de teste.
redact,replace,annotateestampaceitam--dry-run: todo o trabalho, nada é escrito. - Verificação relata scripts, ações, anexos, conteúdo disfarçado e texto oculto por gravidade. Não é um scanner de vírus e nunca chama um arquivo de seguro. Saneamento remove o conteúdo ativo e verifica o resultado antes de escrevê-lo.
- Tabelas com linhas de grade são lidas célula por célula. Tabelas sem são inferidas a partir do alinhamento
(
detected_by: alignment) e merecem uma olhada. - OCR é do tesseract.
ocr -oescreve o texto reconhecido em uma cópia como uma camada invisível. - Assinaturas.
signanexa, então assinaturas anteriores permanecem válidas. Pode mostrar a assinatura em uma página (--visible) e incorporar a declaração de tempo de uma autoridade de carimbo de tempo (--tsa).signaturesverifica se os bytes não foram alterados e quem assinou; com--trustverifica a cadeia do assinante contra certificados que você nomeia, e com--revocationsuas listas de revogação. Qualquer outra alteração em um PDF assinado invalida suas assinaturas, como deve ser. - Mesclagem e trabalho de página mantêm marcadores e links que levam a páginas na saída, e
renomeiam os campos de formulário de entradas posteriores
doc2.<name>para que nomes iguais não compartilhem um valor. - Arquivos danificados são reparados para leitura e reconstruídos para escrita; o resultado então carrega
repaired_inputs. Um arquivo danificado que também está criptografado não é reconstruído. Arquivos protegidos permanecem protegidos quando editados; apenasdecryptremove a proteção. - Limites. 4 GiB e 300 segundos por comando por padrão:
--max-memory,--timeout, ouPDFOPS_MAX_MEMORYePDFOPS_TIMEOUT; 0 remove um limite. Rasters são limitados a 64 megapixels. - Criar aceita Markdown, e o HTML nele pelo que diz: títulos, ênfase, links, listas, tabelas. Como deve parecer não é lido: não há CSS.
Desenvolvimento
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
Construído sobre lopdf (modelo de objetos), hayro (texto, renderização, posições e decodificação de imagens), rustybuzz (modelagem de texto), subsetter (incorporação de fontes) e pulldown-cmark (Markdown).
Licença
MIT