diff --git a/AGENTS.md b/AGENTS.md index a0cf9c4a57..ab9245ddd4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -80,6 +80,13 @@ If you find an asset that breaks these rules, stop and remove it from the reposi - Never copy GPL/AGPL code. Copyleft tools run only as external oracle processes. - Observe Acrobat only with synthetic fixtures. Never capture personal data, account information or recent files. -## 3. Everything else +## 3. Agent control (automation and MCP) + +- **Everything is reachable headlessly.** Every user-facing feature must be callable without the GUI: an engine API, then a tool in `crates/automation` (`printcraft-cli run` and MCP use the same table). A feature is not done until its tool exists and has an end-to-end test (`crates/automation/README.md`, "Adding a tool"). +- **The MCP server is opt-in.** Never make the app, the CLI or an installer start it, register it with an agent, or listen on a port by default. It runs only when a user (or an agent the user configured) launches `printcraft-cli mcp`. Any future network transport must be off by default, bound to loopback and authenticated with a token. +- **Safe by default.** Tools must never discard unsaved work silently, must write atomically, must honour `--root`, and must return errors an agent can act on (say what was wrong and what is allowed). +- **Use it to test.** Agents should drive the engine through `printcraft-cli run --script` or the MCP tools, and look at `page_render` output, to check their own changes. + +## 4. Everything else Follow `CLAUDE.md`. It covers quality gates, layering, commit rules and how to run and look at the app. diff --git a/CLAUDE.md b/CLAUDE.md index 32e7ba5c26..da69205801 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -25,6 +25,7 @@ PrintCraft is a clean-room, open-source, Rust-native PDF application targeting A ## Running and looking at the app - `cargo run -p printcraft -- ` opens the desktop app. +- `cargo run -p printcraft-cli -- run --script steps.json --root DIR` drives the engine headlessly through the automation tools (`printcraft-cli tools` lists them). Use it, together with `page_render`, to check engine changes. `printcraft-cli mcp` is the opt-in MCP server (AGENTS.md §3). - `cargo xtask demo-pdf` builds `dist/demo/printcraft-showcase.pdf` (needs Chrome) for visual checks. - For UI work, **look at the result**: screenshot the window by id (`plan/acrobat/tools/winlist.swift` + `screencapture -l`) until the UI control channel (M3.9) exists, then use `ui.screenshot`. Compare against `plan/acrobat/02-ui-ux.md`. - Parallel agents: use a separate `CARGO_TARGET_DIR` per agent and separate git worktrees. diff --git a/README.md b/README.md index 49b89c753b..916f7eca43 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,7 @@ Press ⌘K to search every tool and command, or browse the **All tool - **Native on macOS, Windows and Linux**, and **in the browser** through WebAssembly, from the same Rust codebase. - **Private by design.** Documents never leave your machine. There's no account, no telemetry and no cloud processing. - **Engine first.** Parsing, rendering and editing live in reusable library crates. The interface is one swappable layer on top. -- **Scriptable.** The `printcraft-cli` tool covers inspecting, rendering, extracting text, editing, combining, extracting pages and splitting. Robustness sweeps run on the same engine as the app. +- **Scriptable.** The `printcraft-cli` tool (see [Built for agents, too](#built-for-agents-too)) covers inspecting, rendering, extracting text, editing, combining, extracting pages and splitting. Robustness sweeps run on the same engine as the app. ```sh printcraft-cli info form.pdf # structure as JSON @@ -204,6 +204,32 @@ printcraft-cli edit in.pdf --rotate 1,2:90 --delete 5 --title "Q3" --out out.pd --- +## Built for agents, too + +Every engine feature is reachable without the GUI, through one table of JSON-Schema-described tools: open, inspect, render pages to PNG, extract and find text, rotate, delete, move and insert pages, set metadata, undo and redo, save, combine, extract and split. Three front doors share it: + +- **`printcraft-cli run`**, for one-off calls and JSON scripts: + + ```sh + printcraft-cli tools # every tool and its JSON Schema + printcraft-cli run text_find doc=1 query=invoice # key=value; values parse as JSON + printcraft-cli run --script steps.json --root ./work # several steps in one session + ``` + +- **An MCP server**, for AI agents such as Claude. **It is opt-in:** PrintCraft never starts it on its own, and it opens no network port. It runs only while an agent launches `printcraft-cli mcp`, talks over stdin/stdout, and stops when the agent disconnects. To enable it, add it to your agent's MCP configuration: + + ```json + { "mcpServers": { "printcraft": { "command": "printcraft-cli", "args": ["mcp", "--root", "/path/to/your/pdfs"] } } } + ``` + + `--root` confines every file the agent can read or write to one directory. Builds that should not include the server at all can use `cargo build -p printcraft-cli --no-default-features`. + +- **The Rust API** (`printcraft_automation::Automation::call`), for embedding. + +Edits stay in memory, undoable, until `doc_save`. Saving to the same file appends an incremental update, so the original bytes are preserved, and the write is atomic. Unsaved changes are never discarded silently. A UI control channel, so agents can drive and screenshot the running app, is next (M3.9). + +--- + ## How it's built PrintCraft is a Cargo workspace of focused crates, layered so the core never depends on the UI: @@ -216,6 +242,7 @@ PrintCraft is a Cargo workspace of focused crates, layered so the core never dep | `printcraft-organize` | Page operations, combine / extract / split, document information | | `printcraft-render` | Rendering, inspection and text extraction with reading order | | `printcraft-engine` | The façade every frontend uses: sessions, edits, undo, saving, the tool catalogue | +| `printcraft-automation` | Agent control: the headless tool table, `printcraft-cli run`, and the opt-in MCP server | | `printcraft-ui-egui` | The desktop and web interface | **Quality gates.** Every change passes the same automated checks: @@ -251,7 +278,8 @@ PrintCraft is young and moving fast. **Available today:** - opening encrypted documents, honouring their permissions, and saving them encrypted; - undo and safe saving; - autosave with crash recovery; -- a single command registry behind menus, shortcuts and the palette. +- a single command registry behind menus, shortcuts and the palette; +- agent control through the CLI and an opt-in MCP server. **On the roadmap:** diff --git a/crates/automation/README.md b/crates/automation/README.md new file mode 100644 index 0000000000..22a445b7c8 --- /dev/null +++ b/crates/automation/README.md @@ -0,0 +1,39 @@ +# printcraft-automation + +Agent control for PrintCraft: a headless tool table over the engine, and an opt-in MCP server. + +- **Layer:** L7 (architecture §13), but headless. It depends on `engine`, `render` and `organize`, never on a UI toolkit. +- **Status:** 20 tools covering inspection, rendering, text, page edits, metadata, undo/redo, save, combine, extract and split. Planned next: the UI control channel (`ui.*`, M3.9), tools for comments, forms and bookmarks as those features land, and MCP resources for page images. + +## API + +```rust +let mut a = Automation::new().with_root("/work")?; // optional: confine all paths +let doc = a.call("doc_open", &json!({ "path": "in.pdf" }))?; // Vec +tools() -> Vec // name, title, description, input_schema, read_only, destructive, command +mcp::McpServer::new(a).serve(stdin, stdout)? // newline-delimited JSON-RPC 2.0 +``` + +`Content` is `Json(Value)` or `Png { data, width, height }`. Errors are `UnknownTool`, `InvalidArgs` (the call didn't match the schema) or `Failed` (a readable message for the agent). + +## Conventions + +- Tool names are `snake_case` (`page_rotate`): MCP clients reject dots. `ToolDef::command` links a tool to the registry id it automates (`page.rotate`), and `command_list` reports the link. +- Pages and positions are **1-based**. Rectangles are PDF points with the origin at the top-left of the displayed page. +- Unknown arguments are rejected, so typos fail loudly. +- Tools that change a document return its summary (`doc`, `pages`, `dirty`, `undo`, `redo`, …). +- `doc_close` refuses to drop unsaved changes unless `discard_changes: true`. `doc_save` writes atomically, incrementally in place, and in full for a new path. +- With a root set, every read and write path must resolve inside it (symlinks and `..` included). + +## MCP server + +**Opt-in only.** Nothing starts it automatically, and it opens no port. It runs while `printcraft-cli mcp [--root DIR]` runs, normally launched by an agent from its MCP configuration. It stops when stdin closes. The CLI's `mcp` Cargo feature (on by default) compiles it out entirely when disabled. + +Implemented: `initialize` (protocol 2025-06-18, 2025-03-26, 2024-11-05), `ping`, `tools/list` (with `readOnlyHint`/`destructiveHint` annotations) and `tools/call` (JSON results also returned as `structuredContent`; images as `image/png`). Tool failures come back as `isError: true` results, so the agent can read and recover from them. + +## Adding a tool + +1. Add the engine capability first, with its tests (the tool is a thin adapter). +2. Add a `ToolDef` in `src/tools.rs`: a precise description, the schema, `ro()`/`destructive()`, and `cmd()` if a registry command exists. +3. Handle it in `Automation::call`, validating pages and positions with the existing helpers. +4. Add an end-to-end test in `tests/automation.rs`. `tool_table_is_well_formed` checks names, schemas and command links.