{
  "markdown": "# quillpdf-mcp\n\nPDF operations for MCP. An MCP server and CLI for local PDF manipulation:\n\n- Merge, split, organize, delete pages\n- Rotate, crop\n- Watermark, Bates numbering, page numbers\n- Flatten forms, images → PDF\n- Metadata cleaning, page count\n- AES-256 protect / unlock\n\nBuilt on [pdf-lib](https://github.com/Hopding/pdf-lib). MIT licensed. stdio transport. No network calls — files never leave your machine.\n\n## Install\n\n```bash\nnpx -p quillpdf-mcp quillpdf --help   # CLI\nnpx quillpdf-mcp --help               # MCP server (your MCP client launches it over stdio)\n```\n\nOr globally:\n\n```bash\nnpm install -g quillpdf-mcp\nquillpdf --help\n```\n\nRequires Node.js 18+.\n\n## Scope\n\n`quillpdf-mcp` implements structural PDF transforms and AES-256 protect/unlock. It does not perform OCR, redaction, or compression — those are available in the browser version ([quillpdf.com](https://quillpdf.com)), where processing also stays client-side. They'll ship here only when they can run fully locally.\n\n## Use it from an AI assistant (MCP)\n\nThe MCP server speaks stdio and exposes one tool per operation.\n\n### Claude Desktop\n\nAdd to `claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"quillpdf\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"quillpdf-mcp\"]\n    }\n  }\n}\n```\n\n### Claude Code\n\n```bash\nclaude mcp add quillpdf -- npx -y quillpdf-mcp\n```\n\nThen ask your assistant things like *\"merge invoice-1.pdf and invoice-2.pdf into combined.pdf\"* or *\"Bates-number discovery.pdf with prefix ABC starting at 1.\"* The assistant supplies file paths on your machine; the server reads and writes them locally.\n\nA ready-to-copy config lives in [`examples/mcp-config.json`](examples/mcp-config.json).\n\n### Tools\n\n| Tool | What it does |\n|------|--------------|\n| `pdf_merge` | Merge 2+ PDFs into one, in order |\n| `pdf_split` | Split by page ranges (one file per group) **or** extract specific pages into one file |\n| `pdf_rotate` | Rotate pages by a multiple of 90° (all pages or a chosen subset) |\n| `pdf_watermark` | Stamp text on every page (size / opacity / color / position) |\n| `pdf_bates` | Sequential Bates numbering (prefix, start, zero-pad, corner) |\n| `pdf_clean_metadata` | Strip info-dict fields + dates, and delete the XMP metadata stream |\n| `pdf_page_count` | Count pages |\n| `pdf_organize` | Keep pages in the order you list — reorder, delete and duplicate in one step |\n| `pdf_delete_pages` | Remove listed pages, keeping the rest in order |\n| `pdf_page_numbers` | Stamp readable page numbers (`3`, `3 of 12`, `Page 3 of 12`), optional unnumbered cover |\n| `pdf_crop` | Trim page margins by setting the CropBox — **hides, does not remove** (not redaction) |\n| `pdf_flatten` | Paint filled form values onto the page so they can't be edited or read back as form data |\n| `pdf_from_images` | Build a PDF from PNG/JPEG images, one per page |\n| `pdf_protect` | Encrypt with **AES-256** so the document needs a password to open |\n| `pdf_unlock` | Remove the password from a document you have the password for |\n\n## Use it from the command line\n\n```bash\n# Merge (order is preserved)\nquillpdf merge a.pdf b.pdf c.pdf -o combined.pdf\n\n# Split into one file per range group → out-1.pdf (pages 1-3), out-2.pdf (page 5)\nquillpdf split in.pdf --ranges \"1-3,5\" -o out.pdf\n\n# Extract specific pages into a single file (reordering/duplicates allowed)\nquillpdf split in.pdf --pages \"5,1,1\" -o extract.pdf\n\n# Rotate every page 90°, or just some pages\nquillpdf rotate in.pdf --angle 90 -o rotated.pdf\nquillpdf rotate in.pdf --angle 180 --pages \"2,4\" -o rotated.pdf\n\n# Watermark\nquillpdf watermark in.pdf --text \"DRAFT\" --opacity 0.15 --position center -o wm.pdf\n\n# Bates numbering → PLT-000001, PLT-000002, …\nquillpdf bates in.pdf --prefix \"PLT-\" --start 1 --pad 6 -o numbered.pdf\n\n# Strip document metadata\nquillpdf clean-metadata in.pdf -o clean.pdf\n\n# Page count (prints a number)\nquillpdf page-count in.pdf\n```\n\nEvery command has `--help`.\n\n**Existing files are never silently replaced.** If the `-o` path already\nexists, the command stops with an error instead of clobbering it — add\n`--force` (or `force: true` on the MCP tools) to replace it deliberately.\n\n## Privacy & security notes\n\n- **Local only.** All PDF processing happens in-process via [`pdf-lib`](https://github.com/Hopding/pdf-lib). The server communicates over stdio and never opens a network connection. (The MCP SDK bundles optional HTTP/SSE transports in `node_modules`, but this server only ever uses stdio, so none of that code runs.)\n- **Password-protected PDFs are refused, not silently unlocked.** Every operation except `unlock` stops with a clear message rather than stripping the password for you. `unlock` decrypts *only* with a password you supply — it does not recover, guess, or brute-force one.\n- **Encryption engine.** `protect` and `unlock` run [QPDF](https://github.com/qpdf/qpdf) (Apache-2.0) compiled to WebAssembly, in-process and offline like everything else. `pdf-lib` cannot encrypt at all, and the community forks that add it are unmaintained — encryption is the one operation where a subtle bug means a document you believe is locked isn't, so this uses the reference implementation instead.\n- **AES-256 only.** `protect` emits PDF 2.0 encryption revision 6. The 40-bit and 128-bit modes are deliberately not exposed: QPDF classes both as weak crypto, and shipping a \"protected\" file that isn't meaningfully protected is worse than shipping nothing.\n- **Permission flags are a request, not a guarantee.** `--no-print` / `--no-copy` / `--no-modify` set bits that a well-behaved reader honours; they are not enforced by the encryption. The password is the part that actually protects the file. (Extraction *for accessibility* stays permitted even with `--no-copy`, so screen readers keep working.)\n- **Cropping is not redaction.** `crop` sets the CropBox, so trimmed content is hidden but still present in the file and fully recoverable. Do not use it to remove sensitive material.\n- **`clean-metadata` scope.** It clears the title, author, subject, keywords, and creator fields plus the CreationDate/ModDate in the document information dictionary, **and** deletes the document-level XMP metadata stream — the copy Acrobat, Word, InDesign, and LibreOffice write, which is often the authoritative one. Note that `pdf-lib` rewrites the `/Producer` field with its own signature on save, so that one field is *replaced* rather than emptied. It does not scrub page-level annotations, embedded file attachments, or the text/image content of pages.\n\n## Not here yet (roadmap)\n\nThese need a Node image backend or an OCR engine and are planned for a later release — they are **not** in this package today:\n\n- `compress` (image re-encode) — needs an image codec such as `sharp`\n- `pdf-to-image` (render pages to PNG) — needs a rasterizer (`pdfjs-dist` + canvas)\n- `redact` (true rasterize-on-redact removal) — needs the same rasterizer\n- `ocr` (searchable-text layer) — needs `tesseract.js`\n\nEach would roughly quadruple this package's dependency surface, which is the whole reason\nthey're held back: the pitch here is a small, local, no-network tool. They'll ship only when\nthey can run **fully locally** without giving that up.\n\n## Philosophy\n\nKeep the server small, local, and easy to audit. Most \"PDF API\" tools answer document work with \"upload it to us\" — for legal discovery, medical records, or financials, the upload is the problem. This runs where you are; the bytes stay on your disk.\n\n## Development\n\n```bash\nnpm install\nnpm run build     # tsc → dist/\nnpm test          # builds, then runs the unit suite (node:test)\n```\n\nThe engine (`src/core.ts`) is pure: bytes in, bytes out, no I/O. The CLI (`src/cli.ts`) and MCP server (`src/server.ts`) are thin front-ends over it.\n\n## License\n\nMIT © [Purple Directive](https://purpledirective.com). See [LICENSE](LICENSE).\n\nLearn more at [quillpdf.com](https://quillpdf.com).\n",
  "bytes": 7915,
  "sha": "0152e52f3c41a5459c4b9d8e540afe478cdcff13d3c63631cdaca7f3e910adbb",
  "repo_slug": "purpledirective/quillpdf-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_purpledirective_quillpdf_mcp_153a18ba/readme"
}