Skip to main content
Glama
rifqilubis

edit-pdf

by rifqilubis

edit·pdf

Edit PDFs with AI agents. Two ways to drive it:

  1. Built-in agent — chat in the app, bring your own API key.

  2. External agent over MCP — connect Claude Code / Codex / Cursor, and it edits the same live document you have open in the browser.

Edits appear in the open tab within ~1.5 s, from either side. Every change is an undoable batch.


Why this exists

Most "AI PDF" tools paste a new PDF over the old one, so the result is a picture: the text you "replaced" is still there underneath, still copyable, still searchable. That is dangerous for anything you redact.

This project treats editing as a replayable list of operations applied by a real PDF engine, and redaction genuinely deletes glyphs from the content stream.


Related MCP server: fablecut-webmcp

Quick start

bun install
bun dev          # http://localhost:3000

Drop a PDF on the landing page. That creates a session with a short id (/s/abc12345).

To use the built-in agent, open settings in the agent panel and add an API key. It is kept in your browser's localStorage and sent to your local server per request — never written to disk.

Alternatively, set a server-side key:

OPENAI_API_KEY=sk-...  bun dev
# or
ANTHROPIC_API_KEY=sk-ant-...  bun dev

Any OpenAI-compatible endpoint works (OpenRouter, Ollama, LM Studio, vLLM) via the Base URL field.


Connecting an external agent (MCP)

The app exposes an MCP server that talks to the running dev server, so an external agent operates on the session you already have open.

bun run mcp

Register it with your client. For Claude Code:

claude mcp add edit-pdf -- bun run /absolute/path/to/edit-pdf/mcp/server.ts

Or in a JSON config:

{
  "mcpServers": {
    "edit-pdf": {
      "command": "bun",
      "args": ["run", "/absolute/path/to/edit-pdf/mcp/server.ts"],
      "env": { "EDIT_PDF_URL": "http://localhost:3000" }
    }
  }
}

Then, in a conversation with the agent:

List my edit-pdf sessions, open the one called invoice.pdf, and change the total to $0.00.

The agent calls list_sessions → use_session → read_document → find_text → edit_pdf, and the change shows up in your open browser tab.

MCP tools

Tool

Purpose

list_sessions

Sessions currently open in the app

use_session

Pin a default session for the conversation

read_document

Page count, sizes, per-page text preview

find_text

Locate text, returns exact boxes

edit_pdf

Apply operations

render_page

Render a page to PNG to verify visually

undo_last_edit

Revert the last batch

download_pdf

URL to download the current PDF


Coordinate systems (the part that bites)

Two spaces exist, and mixing them is the most common source of misplaced edits:

Space

Origin

Y direction

Used by

PDF user space

bottom-left

up

find_text, page_text, the raw PDF

Edit space

top-left

down

edit_pdf operations, UI drag selection

find_text returns both: x/y (PDF space) and a ready-to-use topLeft box. Pass topLeft straight into edit_pdf and you cannot get it wrong.

Units are PDF points (1/72 inch).


Operations

All coordinates are in edit space (top-left origin, y down).

Op

Notes

add_text

maxWidth auto-shrinks to fit; rotate in degrees

replace_text

Uses find (or an explicit box). Removes the original glyphs, then writes the new text

redact

Removes text in the box from the content stream, then paints over it

highlight

Semi-transparent rectangle

draw_rect / draw_line

Vector primitives

add_image

Base64 PNG/JPEG data URL

watermark

Diagonal text, all pages or a subset

delete_page / insert_blank_page / move_page / rotate_page

Structural

page_numbers

{n} / {total} placeholders

set_meta

Title, author, subject, keywords

Example:

{
  "operations": [
    { "type": "replace_text", "page": 1, "find": "Total: $1,299.00", "text": "Total: $0.00" },
    { "type": "watermark", "text": "DRAFT", "opacity": 0.2 },
    { "type": "page_numbers", "position": "bottom-center" }
  ]
}

HTTP API

Everything the UI does goes through this API, so you can script it directly.

# Upload
curl -X POST localhost:3000/api/sessions -F file=@invoice.pdf
# -> {"session":{"id":"abc12345", ...}}

# Inspect
curl localhost:3000/api/sessions/abc12345/read
curl "localhost:3000/api/sessions/abc12345/find?q=Total"

# Edit
curl -X POST localhost:3000/api/sessions/abc12345/ops \
  -H 'Content-Type: application/json' \
  -d '{"operations":[{"type":"page_numbers"}]}'

# Render a page
curl -o page1.png "localhost:3000/api/sessions/abc12345/page?page=1&scale=2"

# Undo / download
curl -X DELETE localhost:3000/api/sessions/abc12345/ops
curl -o out.pdf "localhost:3000/api/sessions/abc12345?download=1"

Pass baseVersion with an edit to get a 409 if the document changed underneath you, instead of silently clobbering a concurrent edit.


How it works

app/                     Next.js App Router UI + API routes
components/              Workspace, PageViewer, AgentPanel, SettingsDialog
lib/pdf/engine.ts        Op -> pdf-lib mutations (the core)
lib/pdf/content-stream.ts  PDF content-stream tokenizer + text stripping
lib/pdf/redact.ts        Walks page streams and Form XObjects, rewrites them
lib/pdf/render.ts        pdf.js rendering + positioned text extraction
lib/agent/tools.ts       Tool schemas + executeTool (shared by app and MCP)
lib/server/store.ts      Sessions, history, render cache
mcp/server.ts            MCP bridge to the running server

Deterministic edits. A session stores its original upload plus the ordered list of op batches. Undo replays the remaining batches from the original — so undo is exact, not "apply the inverse and hope".

True redaction. content-stream.ts tokenizes the page's content stream, tracks the graphics-state matrix (q/Q/cm) and text matrices (Tm/Td/T*), and drops the Tj/TJ/'/" operators whose origin lands inside the box. Text inside Do-referenced Form XObjects is scrubbed too. The result is re-serialised uncompressed.

One tool surface. The in-app agent and the MCP server both route through executeTool, so they cannot drift apart in capability or behaviour.


Limits

  • Standard fonts only. Non-Latin text (Arabic, CJK, emoji) is coerced to ? because WinAnsi encoding cannot represent it. Embedding a Unicode font is the fix; it is not implemented.

  • RTL scripts are not shaped or reordered.

  • Redaction misses rasterised text. If a page is a scanned image, there are no glyphs to strip — only the visible box is applied, and the tool says so.

  • Covered text in complex layouts. Stripping is by text-run origin. A run that starts outside the box but extends into it is left intact.

  • Sessions are local. Stored in .data/sessions/, no auth. This is a local-first tool; do not expose it to the internet as-is.

  • 40 MB upload cap.


Development

bun dev          # dev server
bun run build    # production build
bun run typecheck
bun run mcp      # MCP server (needs a running dev server)

node is not required — everything runs on Bun.

License

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    F
    maintenance
    Enables AI agents to directly control your real Chrome browser with full context including login sessions, cookies, and open tabs. It provides tools for page scanning, JavaScript execution, CDP control, screenshots, and physical mouse/keyboard input for authentic browser automation.
    20
    245
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables an AI agent to directly drive a live browser video editor session, reading selections and playhead, editing the timeline, and sharing the same undo stack as the human user.
    559 npm
    1
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    Enables AI clients to control the user's already-open, logged-in browser pages in real time over a local WebSocket channel, without Playwright, Puppeteer, Selenium, or any browser driver. Exposes tools for navigation, semantic page snapshots, clicking, form filling, JavaScript evaluation, screenshots, network monitoring, cookie handling, and tab management via the CDP.
    20
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents like Codex and Claude Code to control shared Chrome tabs, offering site-specific tools such as browsing and posting on Reddit. Agents can navigate the visible page and execute actions directly in the same tab.
    MIT