8.9 KiB
MCP conventions
Start explicitly with pdfcraft-cli mcp [--root DIR] [--compact] [--automation-read-root DIR] [--automation-write-root DIR]. The server uses JSON lines on
stdin/stdout, opens no port, and stops on EOF. --root confines all reads and writes, including
commands invoked through the common tools. Logs stay on stderr.
Supply --root at most once, followed by a nonempty directory path. Missing operands,
repeated roots and another long option in place of the directory are refused before any
script is read or server is started. Prefix a directory name beginning with -- with ./.
Install and register
From the repository root, with Rust 1.95+ and a native linker installed (see development for build and environment notes):
cargo install --path apps/pdfcraft-cli --locked
# POSIX shells, for Cargo's default install location:
export PATH="${CARGO_HOME:-$HOME/.cargo}/bin:$PATH"
pdfcraft-cli --help
pdfcraft-cli mcp --help
The default features include MCP; do not pass --no-default-features for this use.
Cargo installs a binary, not a symlink into target/, so it survives cargo clean.
Re-run the install command after updating the checkout.
By default the executable goes in $CARGO_HOME/bin (~/.cargo/bin when unset), or
%USERPROFILE%\.cargo\bin on Windows. If you set --root, CARGO_INSTALL_ROOT, or
Cargo's install.root, use that installation root's bin directory instead. Add it
to the PATH inherited by the MCP client, not just an interactive shell, and restart
the client after changing its environment.
Register the installed command explicitly (replace /work/project with an existing folder):
claude mcp add pdfcraft -- pdfcraft-cli mcp --root /work/project
Other MCP clients can use this bare-name configuration:
{"mcpServers":{"pdfcraft":{"command":"pdfcraft-cli","args":["mcp","--root","/work/project"]}}}
Linux .deb and .rpm packages already install /usr/bin/pdfcraft-cli; those users
can skip the source install. The downloads list also includes
standalone Linux CLI tarballs and a macOS CLI zip; unpack and copy the executable into
a directory on the client's PATH. Installing does not start or register a server:
MCP remains opt-in and uses stdio only.
If a client reports ENOENT or "Executable not found in $PATH", verify the install with
command -v pdfcraft-cli (PowerShell: Get-Command pdfcraft-cli) in the client's
environment. If that environment cannot be changed, set JSON command to the absolute
installed binary path, not a literal ~ or $HOME expression. Avoid symlinks into
target/release: cleaning the build directory breaks them. A source build alone does
not put the CLI on PATH.
Confining file access
pdfcraft-cli mcp --automation-read-root /work/project --automation-write-root /work/project/out
{"mcpServers": {"pdfcraft": {"command": "pdfcraft-cli",
"args": ["mcp", "--automation-read-root", "/work/project", "--automation-write-root", "/work/project/out"]}}}
The two flags (VectorCraft, PhotoCraft and the other Craft apps take the same ones, so one client config works across the suite) keep the agent to the folders you give it:
- Every file a tool reads must lie inside the read root:
doc_open,page_insert_file,page_replace,doc_combineanddoc_create_multipleinputs,doc_createimages, stamp, picture, attachment, signature and form-image files,doc_watermarkanddoc_backgroundfiles,doc_import_data,form_merge_data, the filesaction_runandocr_recognize_filesprocess, digital ID, graphic and certificate files for signing andsign_trust,text_font_import, and the folderstext_font_filessearches. Withoutdirectories,text_font_filessearches the read root instead of the installed fonts. - Every file a tool writes must lie inside the write root:
doc_save(also saving in place),page_extract,doc_combine,doc_create_multiple,doc_split,doc_reduce,doc_optimize,doc_printto a file, thedoc_export_*tools,image_save,measure_export,comments_summarize, the accessibility report and review record,doc_compare_report,form_merge_data, the output folders ofaction_runandocr_recognize_files,sign_id_createandsign_document. - Read and write access are separate. Give the same folder twice for both. A root left out grants
none of its access: with only
--automation-read-root, nothing can be written to disk, though tools that give their result back instead of a path still do. Without either flag nothing changes. - A path is made absolute against the server's working directory, then its links are followed: a file
(or folder) link that leads out of a root is outside it. For a file that doesn't exist yet, its
nearest existing folder's links are followed and the rest must be plain names, so
..below a folder that doesn't exist is refused, as is a link that leads nowhere (writing through it would create its target). The result is compared with the root by whole folder names:/work2/xis not inside/work. On Windows case is ignored, UNC paths are resolved like drive-letter ones, and device names (CON,NUL,COM1...) are refused. - A refused path is an ordinary tool error the agent can read and correct, for example
automation path rejected: outside the write root /work/project/out: /etc/x.pdforautomation filesystem access is not granted: write authority is absent: /work/project/out/x.pdf. Tools that handle several files report a refused one in its own entry. - The roots must be existing folders; a missing one stops the server with an error. Each flag is
given once (
--flag <dir>or--flag=<dir>). --root DIRstill works and can be combined with these flags. Both then apply: a path must be inside--rootand inside the matching automation root.
pdfcraft-cli mcp always runs the session in its own process (there is no flag to connect it to a
running app), so the roots cover every server it starts. The desktop app is not confined by them.
What the roots do not cover: tools that take no path still read and change the open documents, and
text_extract and the other inspection tools return their content. Keychain and Windows
certificate-store identities for sign_document are not files and are not checked. Sending a job
to a printer is not a file write. Every path a tool reads or writes goes through
Automation::resolve, and a unit test keeps the tool modules from touching the file system
without it.
A path is checked, then opened: another program that swaps a link in between can still win that race, so the roots keep an agent to the files it was given rather than guarding against other software on the computer.
Tools
| Tool | Arguments |
|---|---|
command_list |
doc?, filter?, enabled_only?; returns {commands: [...]} with mapped tool schemas |
command_run |
id, params? (the mapped task tool's arguments, including doc when required) |
command_batch |
steps: [{id, params?}], stop_on_error? (default true) |
doc_inspect |
doc?; one document's info or {documents: [...]} for the session |
render_preview |
doc?, page? (1-based, default 1), max_side? (1–4096, default 1024) |
All existing task tools remain available. Compact mode lists these five tools alongside its
existing core set and tool_search / tool_call. There are no legacy-name aliases. The full
automation guide describes the tools, resources, coordinate
system, root confinement, and errors.
command_run {"id":"file.open","params":{"path":"input.pdf"}}
→ {"doc":1,...}
command_list {"doc":1,"filter":"rotate"}
command_run {"id":"page.rotate","params":{"doc":1,"degrees":90}}
render_preview {"doc":1,"page":1,"max_side":512}
Every tool has a title and the four MCP annotation hints. Unknown top-level argument keys return
JSON-RPC -32602; command parameter keys that do not belong to the mapped task tool are ignored
with warnings. Task failures and escaped tool panics return isError: true, and the server keeps
serving. A failed batch includes per-step errors and is marked isError: true.
Exports complete synchronously. A call's progressToken and cancellation notifications are
harmlessly ignored; the server does not provide background export progress or cancellation.
Resources and protocol versions
pdfcraft://document returns the session's document information (as doc_inspect without
arguments), including an empty documents array before any PDF is opened. pdfcraft://commands
returns the command catalog (as command_list). Existing per-document info, text and page-image
resources and their templates are preserved.
Unknown top-level argument keys are refused with JSON-RPC -32602 (invalid params) instead of
being ignored, so a misspelled argument is reported rather than silently dropped. Clients that
sent extra keys before get an error now.