inkscape-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@inkscape-mcpOpen my logo.svg and change the fill to blue."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
inkscape-mcp
A Model Context Protocol (MCP) server that makes Inkscape / SVG documents agent-ready — inspect, edit safely, validate, render, and export vector graphics from any MCP client.
inkscape-mcp exposes a small, strongly-typed tool surface over your SVG documents. An LLM agent
can open a drawing, read its structure, recolour and re-letter objects, transform geometry, render
previews, and export production assets — all headless-first and reversible by construction.
Every mutating operation runs on a working copy, takes a snapshot, and records an Operation Record,
so nothing the agent does touches your originals or can't be undone.
Table of contents
Related MCP server: Excalidraw MCP Server
Why
LLM agents are good at reasoning about what should change in a drawing ("make the logo blue, bump the heading to 24 px, export a 512 px icon") but bad at safely poking at raw XML or driving a GUI. A naive "run this Inkscape command" tool is dangerous: it can overwrite originals, shell out unsafely, or silently corrupt a file with no way back.
inkscape-mcp solves that with a bounded, typed API: each capability is its own small tool
with explicit parameters and a declared risk class. Simple structural edits go through a direct
lxml DOM layer; rendering, export, and complex geometry go through the Inkscape CLI. Originals
are never mutated, every change is snapshot-backed and reversible, and subprocess calls use
argument lists — never shell strings.
Highlights
88 typed tools across read, validate, render, export, optimize, safe-edit, element-creation, defs/grouping, path-geometry, snapshot, save, and live groups.
Headless-first. No GUI required; the Inkscape binary is used only for render / export / geometry, and the server probes the runtime instead of assuming a version.
Reversible by construction. Every mutating op = pre-mutation snapshot + before/after preview
Operation Record.
restore_snapshotrolls back.
Originals are sacred. Documents open into tracked working copies. Nothing writes over the source file; saving goes to a new path, and overwrites are gated behind explicit approval.
Risk-classed. Each tool declares
low/medium/high/restricted; the policy layer enforces it (high-risk needs a per-operation approval token; restricted never ships in the MVP).Sandboxed. Workspace-root jail, path normalization + symlink guard, input/output/export size limits, per-process timeouts, safe XML parsing (no entity expansion), no network, no arbitrary extension execution.
MCP resources expose document structure (summary / tree / layers / objects / styles / fonts / assets) and the runtime capability matrix as addressable URIs.
How it works
MCP client (Claude, etc.)
│ STDIO / JSON-RPC
▼
FastMCP app ──► typed @mcp.tool functions (risk-classed, validated args)
│
├─ direct-DOM engine (lxml) → structure read + safe edits
├─ Inkscape CLI adapter (arg-list) → render / export / geometry
├─ snapshot engine → pre-mutation copy + reversible restore
├─ Operation Records → audit trail of every mutation
└─ workspace sandbox → path/size/timeout/XML safetyopen_documentcopies your SVG into a tracked workspace document and hands back an opaquedoc_id. The original file is never opened for writing again.Read tools / resources inspect that working copy — tree, layers, styles, fonts, assets.
Edit tools mutate the working copy through the pipeline: take a snapshot → apply the change → render a before/after preview → write an Operation Record. All medium-risk and reversible.
Render / export tools shell out to the Inkscape CLI (argument lists only) and drop artifacts into the workspace artifacts / exports directories as workspace-relative paths.
save_document_aswrites the working copy to a new file (validated before and after). Overwriting an existing file is a separate, approval-gated high-risk path.
Requirements
Python ≥ 3.12 and
uv.Runtime deps (installed by
uv sync): FastMCP 3.x, lxml, and Pillow (the focused live before/after visual diff,live_diff_view, uses Pillow to pixel-diff frames and draw the annotation overlay).Inkscape on
PATHfor the render / export / geometry tools (developed and tested against Inkscape 1.4.x). Read / edit / validate tools work without it.Probe your install with
inkscape --version/inkscape --action-list, or run thediagnose_runtimetool.
At least one workspace root configured (see Configuration) — the sandbox refuses to touch anything outside it.
Install & quickstart
The package exposes one console script, inkscape-mcp → inkscape_mcp.server:main, which starts the
FastMCP app over STDIO. Install it via uvx (one-shot), pipx (persistent), or from source.
Not on PyPI yet — install from source / git (same package, same script). Bare-name
uvx inkscape-mcp/pipx install inkscape-mcpwill work once published.
# uvx — run without installing (from a local checkout; the repo root holds pyproject.toml):
uvx --from /abs/path/to/inkscape-mcp inkscape-mcp
# uvx — straight from git:
uvx --from "git+https://github.com/johnnyjagatpal/inkscape-mcp.git" inkscape-mcp
# pipx — persistent install of the console script:
pipx install /abs/path/to/inkscape-mcp
# from source (development / dogfooding):
cd inkscape-mcp # the repo root
uv sync # install runtime + dev dependencies
uv run pytest # run the test suite
uv run inkscape-mcp # start the STDIO MCP serverThe launched server waits on stdin for MCP JSON-RPC (an MCP host drives it). Confirm it boots with
inkscape-mcp </dev/null or uv run python -c "from inkscape_mcp.server import main". Full install
matrix (incl. the claude mcp add form): docs/install/install.md.
Quality gates:
uv run ruff check --fix .
uv run ruff format .
uv run mypy srcTests that need a real Inkscape binary are marked
@pytest.mark.inkscape. They auto-skip when noinkscapeis onPATH(centralpytest_collection_modifyitemshook intests/conftest.py), so the suite is green on a host without Inkscape; they run normally when the binary is present. Force-skip explicitly withuv run pytest -m "not inkscape".
CI. .github/workflows/ci.yml (E7-02) runs ruff + ruff-format
mypy + pytest on Linux/macOS/Windows (headless + the cross-platform live-transport suite), the full suite incl. real-Inkscape tests on Linux, and a packaged
pipx-install STDIO boot smoke on all three OSes, plus a full-surface MCP smoke (ci_surface_smoke.py, E9-03) that asserts the registered primitive counts (99 tools / 7 prompts / 16 resources) and reads every resource over an in-memory client. CI helper scripts live inscripts/(ci_diagnostics.py,ci_boot_smoke.py,ci_surface_smoke.py).
Evals. evals/ holds a deterministic, CI-runnable tool-usability harness (E15-06) that
makes "agent-friendly" measurable without a live LLM: tool_selection_scenarios.json is a labelled
set of natural-language asks (mirroring the intent catalog below), and run_eval.py drives the
server's own discovery layer (how_do_i / intents.py) to report
per-group + overall tool-selection accuracy and out-of-scope flagging (uv run python evals/run_eval.py, --json for the report dict). tests/test_eval_harness.py gates it against
regressions; the scenario schema is runner-agnostic. An OPTIONAL, report-only live-agent runner
(run_live_eval.py, E15-06a) reuses the SAME dataset + scorer to capture real tool-call traces +
turn count via a pluggable AgentDriver (deterministic ReplayDriver by default; the real MCP+LLM
path is off unless --driver real / INKSCAPE_MCP_EVAL_DRIVER=real) and never gates CI.
Connecting an MCP client
The server speaks MCP over STDIO. Point any MCP-capable client at the inkscape-mcp console
script. Ready-to-copy host configs live in examples/
(claude_desktop_config.json,
mcp.json); full per-host instructions (Claude Desktop, Claude Code +
claude mcp add, generic STDIO) are in
docs/install/host-configs.md. Example client config:
{
"mcpServers": {
"inkscape": {
"command": "uvx",
"args": ["--from", "/absolute/path/to/inkscape-mcp", "inkscape-mcp"],
"env": {
"INKSCAPE_MCP_WORKSPACE_ROOTS": "/absolute/path/to/your/svgs"
}
}
}
}(With a pipx install, use "command": "inkscape-mcp", "args": [].) To dogfood straight from a
source checkout, point a host at uv run --directory /abs/path/to/inkscape-mcp inkscape-mcp once
uv sync has run. Map any platform/feature gap with the
compatibility matrix and
troubleshooting docs.
Configuration
All configuration is environment-driven (no config file needed). The sandbox is the only required
setting — without a workspace root the server has nothing it is allowed to touch. The same table (and
the claude mcp add / generic-host forms) is in
docs/install/host-configs.md.
Env var | Default | Purpose |
| (none) | Required. OS-path-separated list of directories the server may read/write. Everything else is rejected. |
|
| Max size of an input SVG. |
|
| Max raster dimension for render/export. |
|
| Max size of a produced artifact. |
|
| Per-Inkscape-process timeout (seconds). |
|
| Max concurrent Inkscape subprocesses. |
|
| Snapshots retained per document. |
|
| Snapshot age retention. |
|
| Hard cap on snapshots per document. |
|
| Hard cap on snapshot bytes. |
|
| Artifact age retention. |
|
| Total artifact byte budget. |
|
| Per-document artifact byte budget. |
|
| Max frames in the per-session live render cache (E8-06; LRU). Floored. |
|
| Total byte budget for the live render cache (E8-06; LRU eviction). Floored. |
|
| Frame-coalescing latency budget (E8-06): a repeated identical-key render within this window returns the just-cached frame instead of re-rendering. |
|
| Age retention for loop/live render frames (E8-06), pruned by the explicit retention sweep (boot + |
|
| Total byte budget for loop/live render frames (E8-06; newest kept), pruned by the explicit sweep. |
|
| Master gate for live mode (E3). On by default (operator-chosen); set a falsy value ( |
| (none) | Optional explicit path to the live helper's rendezvous file (otherwise discovered under the Inkscape user data dir / temp dir). |
| (built-in defaults) | OS-path-separated list of Inkscape Action ids added to the built-in allowlist (E6-02). Server-side, never client-supplied; cannot remove a default or open arbitrary passthrough. Each Action must also exist in the version-keyed capability map to run. |
| (empty) | OS-path-separated list of Inkscape extension ids added to the (empty) execution allowlist (E6-02). Discovery is read-only and unaffected. |
|
| Advanced-mode gate for the |
|
| Engine transport for render/export/path/boolean/action-chain (E12 / ADR-007). |
|
| Max concurrent warm shell workers when |
|
| Seconds before an idle warm shell worker is reaped. Floored at 1. |
|
| Tool-disclosure profile (E18-03). |
|
| Tool-DESCRIPTION mode (E20-03), orthogonal to |
Tool reference
89 tools. Risk classes: low (read / render / export / quality / Action discovery) · medium
(write-new / element-creation / defs-grouping / style / text / transform / web-optimize / typed batch; reversible)
· high (overwrite / delete / path geometry / Action chains / raw Action; approval-gated) ·
restricted (live helper install).
Mutating tools return an EditResult carrying the operation_id, snapshot id, and a before/after
preview.
Conventions (param + path naming). Single-object tools take object_id; multi-object tools take
object_ids. A caller-chosen write target is dest_path (a file) or out_dir + name_prefix (a
directory); a relative dest_path/out_dir anchors to the workspace root, never the process
CWD, and is sandbox + symlink checked (path rejected: outside workspace otherwise). Every
artifact-producing tool returns a workspace_relative_path (root-relative, opens directly with no
find/stat) alongside the managed artifact_path; no absolute host path ever appears in a result
(sec.12). changed on a mutating result is decided in ONE place — the edit pipeline canonical-serializes
the document before and after the mutation. A real change reports changed: true with a linked snapshot +
Operation Record; a genuine no-op (e.g. replace_color matching nothing, normalize_viewbox on a
valid viewBox, set_fill to the colour already present, a second fit_to_content) reports
changed: false, empty operation_id/snapshot_id, and writes no snapshot and no Operation Record —
nothing happened, so nothing clutters the snapshot list or the audit trail.
MCP ToolAnnotations (E17-01). Every tool also carries machine-readable MCP annotations —
readOnlyHint, destructiveHint, idempotentHint, openWorldHint, and a human title — derived
from ONE central map (src/inkscape_mcp/tool_annotations.py) keyed off the tool's existing risk
class, applied as a post-registration pass at boot. readOnlyHint follows the risk class directly
(low ⇒ read-only); destructive (overwrite/delete/outline), idempotent (pure re-set), and open-world
(host probes + live_*) sets are explicit in that one module. A client reads read-vs-write,
destructiveness, and idempotency without parsing docstring prose; titles are static labels only (no
host path, sec.12). Adding a tool with a Risk class: docstring line auto-annotates it.
Tags + progressive disclosure (E17-02). Every tool also carries exactly one domain tag —
create / edit / transform / paths / export / live / actions / system / quality —
and one risk tag (low / medium / high / restricted), stamped from ONE central map
(src/inkscape_mcp/tool_tags.py) by the same boot pass. The two EXISTING operator flags then drive
tag-based exclusion so a default client sees a smaller core surface and opts into the advanced /
live groups (FastMCP disable(tags=…) visibility transforms — they only NARROW tools/list, never
widen it):
Flag | Default | Off → hides |
|
| every |
|
| the ADR-003 hatch group: |
So the default surface (live on, advanced off) exposes the core 86 tools; turn advanced mode on
to add the paths/actions geometry + Action surface (full 98), or turn live off to drop the live
group (66 with both off). The self-describing list_capabilities.tool_count / tools[] (E16-01)
report the active post-filter surface, since they read the same mcp.list_tools() the transforms
filter. The generated llms.txt manifest still documents the FULL catalog (generated with both flags
forced on).
Minimal core profile (E18-03). For a still-smaller default model-context footprint, the opt-in
INKSCAPE_MCP_TOOL_PROFILE env (full default · core) narrows tools/list further to a curated
essential authoring set — the document / find / create / style / transform / export /
snapshots modules (open/inspect/find/create-*/style/transform/export/snapshot). Spike finding: the
default 85-tool surface is ~76k tokens of tools/list every turn; the 40-tool core set is ~31k —
a ~60% per-turn saving. The profile only NARROWS within the flag-allowed surface (it disables the
non-core tools by name; it can never expose a tool the live/advanced flags hide — sec.12 / ADR-003),
reuses the same disable(...) machinery, and is idempotent + re-evaluatable. A stray value floors to
full. tool_count / tools[] report the active surface; llms.txt still documents the FULL
catalog (generated with profile full). Everything outside core stays reachable by selecting full.
System & diagnostics — low
Tool | Signature | Description |
|
| Probe the local Inkscape + Python runtime fresh and return the capability matrix (version, actions, export formats, DBus/live, inkex, fonts) — plus the curated |
|
| Return the cached capability matrix (probed once, then reused). Includes an additive |
|
| Map a natural-language goal to the concrete tool name(s) that achieve it (best match first: |
|
| Read-only on-disk size + sha256 of one sandboxed artifact → |
|
| Set variant of |
Document — low (create is medium)
Tool | Signature | Description |
|
| Open an SVG into a tracked workspace working copy; returns an opaque |
|
| Create a blank, tracked working-copy document from scratch — no source file required. |
|
| Refresh a working copy from its source under the same |
|
| Aggregate inspection: tree, layers, styles, fonts, external assets, and an addressable |
|
| Read-only filter over a document's addressable objects (AND semantics) → |
Compose / adopt SVG — high (approval-gated), reversible
Tool | Signature | Description |
|
| Replace the whole working copy with an agent-composed SVG string (root must be |
|
| Insert an agent-composed SVG fragment (one element subtree) under |
|
| Lay out N different assets in a |
|
| Place an existing document OR one named object INTO another document at |
Validation — low
Tool | Signature | Description |
|
| Validate a loaded document; returns structured, machine-readable findings. Includes a per-text-element glyph-coverage check ( |
|
| Machine-readable quality report: wraps the |
|
| Quality-report a set in one read-only call (E16-05): per-doc |
Render & export — low
Every render/export result carries a stale: bool staleness signal (E14-06): False on a freshly
produced artifact (it reflects the current working copy); reserved to flag a previously-returned
artifact that the working copy has since outgrown (full mtime tracking is a follow-up — see E14-06a).
Render/export results also self-certify content truth (E16-07), computed in-process at produce time
(no pdffonts/pdfimages/mutool shell-out, no Pillow subprocess): a raster (PNG) result carries
opaque_px (drawn non-transparent pixel count) + all_blank so "the render actually drew something"
is checkable from the result; a PDF result carries is_vector (no embedded raster image) +
fonts_outlined (no embedded font — text outlined to paths), true vector when both hold. Each field is
additive and None for outputs it does not apply to (or when verification was skipped).
Tool | Signature | Description |
|
| Render a PNG preview of the whole document into the artifacts dir. Successive calls at the same width never clobber (unique frame per call; optional |
|
| Export the whole document to PNG / PDF / SVG in the exports dir (or a sandbox-checked |
|
| Export a single object (by id) clipped to its bounding box; reports the actual clipped raster size + the same content-truth fields (E16-07). The id is charset-validated and never passed raw to Inkscape. |
|
| Capture the next numbered PNG screenshot in a per-run frame series ( |
|
| List the frames of a |
Export profiles & batch — low
Tool | Signature | Description |
|
| Web asset set: one PNG raster plus one plain SVG. Pass |
|
| Multi-size square PNG icon set from the source document. Over-cap and ≤0 sizes give distinct error messages. |
|
| Print-oriented vector PDF of the whole document; applies and reports print-specific export settings (so output differs from a plain PDF and is auditable). |
|
| Run a typed list of export specs ( |
|
| Batch-export a set in one call (E16-05): runs |
Optimize — medium, reversible
Tool | Signature | Description |
|
| Web-optimize the working copy: strip editor metadata / namespaced attrs / comments, drop unreferenced |
|
| Web-optimize a set in one call (E16-05): runs |
Snapshots — low
Tool | Signature | Description |
|
| Snapshot the current working copy and index it. |
|
| List a document's snapshots in order, with metadata. |
|
| Revert the working copy to a chosen snapshot; returns |
|
| Apply the retention policy (keep-N / keep-days + hard caps), deleting superseded snapshots and orphaned Operation Records, and (E8-06) the document root's loop/live render frames by age + byte budget (never a frame referenced by a Live Operation Record). Never touches the working copy or original. Explicit maintenance sweep — also runs once at boot; never triggered implicitly by a mutating tool. |
Style edits — medium, reversible
Tool | Signature | Description |
|
| Set fill colour (and optional fill opacity). Colour is validated; CSS-injection punctuation rejected. |
|
| Set stroke colour, width, and/or opacity. |
|
| Set element-level opacity ( |
|
| Replace one colour with another across the document (or within |
|
| Apply many |
Text & object edits — medium, reversible
Tool | Signature | Description |
|
| Replace the text content of a |
|
| Set font-family / font-size / font-weight (at least one required) on text objects. Returns |
|
| Duplicate an object/group in place, inserting the clone right after the original. |
|
| Replicate an object into an N×M grid (clone |
|
| Change an object's |
|
| High risk, reversible (E16-08): remove objects by id from the DOM → |
Typed batch edit — medium (max over members), reversible
Tool | Signature | Description |
|
| Batch (E19-01): apply an ordered list (≤ 64) of TYPED edits — a discriminated union over the existing DOM ops ( |
|
| Selector → op (E20-02): declarative bulk edit without a code hatch — resolves a target SET via the EXISTING |
Element creation — medium, reversible
Direct-DOM (ADR-005) shape primitives — one small typed tool per shape (no catch-all
add_element(tag, attrs) per ADR-002/003). Each inserts into an optional parent_id (must exist)
or the document default parent (first inkscape:groupmode="layer", else the root), and returns a
CreateResult (the EditResult extended with object_id + an analytic bbox; bbox is None for
path/text whose geometry is not analytically cheap). Every value is strictly validated (finite
numbers, charset-safe ids, control-char-scrubbed text, command/charset-allowlisted path d).
Tool | Signature | Description |
|
| Insert a |
|
| Insert a |
|
| Insert an |
|
| Insert a |
|
| Insert a closed |
|
| Insert an open |
|
| Insert a |
|
| Insert a |
Defs, gradients & grouping — medium, reversible
Gradient defs land in the document <defs> (auto-created as the first child if absent); the returned
id is usable as a url(#id) paint. Grouping/structure tools reorganize existing objects.
Tool | Signature | Description |
|
| Add a |
|
| Add a |
|
| Insert an empty |
|
| Wrap existing objects (≥ 1, must exist) in a NEW |
|
| Move an object under a new parent (rejects a descendant/self parent; coordinate space may shift). |
|
| Insert a |
Transforms — medium, reversible
Tool | Signature | Description |
|
| Translate by |
|
| Scale by |
|
| Rotate by |
|
| Set canvas |
|
| Normalize or repair the root |
|
| Set the root |
Path geometry — high (approval-gated), dry-run by default, reversible
Destructive path operations that run through the Inkscape engine (select-by-id;<action>
arg-lists, never a shell string), not direct DOM (ADR-005). Every tool is HIGH risk: a real change
requires a non-empty approval_token; dry_run (typed param, default True) validates the
targets and reports which object ids + which Inkscape Action would run, writing nothing. Each
applied op is snapshotted + recorded + before/after-previewed (reversible). Object ids are
validated (argv-safe charset + must exist) before reaching the engine.
Tool | Signature | Description |
|
| Simplify path(s) ( |
|
| Union ≥2 paths into one ( |
|
| Subtract the upper path(s) from the lowest ( |
|
| Combine ≥2 paths into one multi-subpath path ( |
|
| Break a compound path into its subpaths ( |
|
| Outline each stroke into a filled path ( |
|
| Remove redundant/degenerate path data ( |
Actions & extensions — discovery low; chain execution high (approval-gated)
A controlled way to use Inkscape Actions without an open-string passthrough (ADR-003). Discovery
is probe-driven (reuses inkscape --action-list); an execution surface is built from a typed,
ordered chain of ActionSteps — never a raw string. Every step is validated against the
server-side allowlist (INKSCAPE_MCP_ACTION_ALLOWLIST, env-additive onto built-in defaults —
never client-supplied) and a versioned Action capability map (persisted at
<root>/.inkscape-mcp/action-maps/<version>.json, keyed by detected Inkscape version) so an Action
absent on the host is refused cleanly. Chain execution runs through the Inkscape engine (arg-lists,
shell=False) over the working copy and is snapshotted + recorded + before/after-previewed. The
single-Action raw escape hatch (run_raw_action, ADR-003) reuses the same gates behind an
opt-in, OFF-by-default advanced-mode switch (INKSCAPE_MCP_RAW_ACTION_ENABLED).
Tool | Signature | Description |
|
| Discover the host's actual Action surface + the allowlisted/available subsets; persists the version-keyed capability map. |
|
| List the server-side allowlisted extension set + probe notes (diagnostic; nothing executes; empty by default). |
|
| Dry-run: validate a typed |
|
| High risk: run a validated chain over the working copy via the mutating pipeline (snapshot + Operation Record + before/after preview, reversible). Requires a non-empty |
|
| High risk, advanced mode (OFF by default). The ADR-003 escape hatch: run ONE allowlisted Action (typed |
Save — medium / high (approval-gated)
Tool | Signature | Description |
|
| Save the working copy to a new file (validated before & after). New file = medium risk. Overwriting an existing file requires |
Live mode (E3 read / E4 write / E8 view loop) — on by default (operator-chosen)
Control of a running Inkscape, cross-platform via a transport abstraction (extension-socket
bridge on any OS; DBus org.gtk.Actions an optional Linux fast-path). Gated by
INKSCAPE_MCP_LIVE_ENABLED (default on; set falsy to opt out); absent/unsupported transports are
reported cleanly, never as errors. No-freeze (E3-07): the socket bridge is a modal inkex
effect extension (freezes the GUI for the whole session); the Linux DBus path runs in Inkscape's own
main loop and does not freeze the GUI — live_connect(prefer="no_freeze") selects it on Linux for
viewport, style/transform writes, and a structured export-to-file read (live SVG/PNG/active-doc).
Selection-id reads stay on the (modal) socket path; Windows/macOS live stays modal (best-effort).
The command schema is a fixed enum (wire protocol v5) — no arbitrary code or raw Action
passthrough (ADR-003). E4 adds semantic write: the three mutating tools are HIGH risk and require
an explicit approval_token, each producing a Live Operation Record with before/after canvas
renders; live never mutates unapproved. E8 adds the view loop: view-only viewport/region tools and
structured perception — live_get_scene pairs each rendered frame with a machine-readable
LiveScene (active-doc ref, selection ids + bboxes, viewport, canvas size, visible-object summary
reusing the headless ObjectInfo shape) so the agent reasons over structure, not pixels (ADR-006) —
plus change detection: live_wait_for_change polls a CHEAP server-hashed state token (revision +
selection + viewport — never the full doc or a PNG) on a bounded, cancelable wait so the loop renders
ONLY on change (including the user's own GUI edits), never busy-rendering. And a focused visual
diff: live_diff_view reuses a mutation's captured before/after frames, pixel-diffs them to a
changed-region bbox, and emits ONE annotated overlay (changed bbox + selection outline) linked back to
the Live Operation Record — a targeted diff, not two raw whole-window screenshots.
View/perception/change/diff tools are LOW risk — no document mutation, no Operation Record, no approval.
Finally, the loop orchestrator live_session_step frames ONE perceive→decide→act→observe
iteration: it captures the LiveScene + frame (perceive), the agent picks ONE typed semantic act from
a FIXED set (apply/insert_svg/set_text — each 1:1 with an E4 write engine, no raw-Action/code),
routes it through run_live_mutation (HIGH + approval_token + Live Operation Record — the SAME E4
write path, zero new authority per ADR-006), then captures an after-scene + a focused live_diff_view
(observe). With no act it is perceive-only (no record). It is bounded + cancelable by construction —
a single step is one iteration; the agent drives the loop by re-calling it (there is no server-side
autonomous runner). The live_canvas_assist Prompt is the §4.1 entry point that orients the agent
on this loop.
Tool | Signature | Risk | Description |
|
| low | Report every live transport probed on this host (not assumed by OS), the best read-capable one, and whether the helper is installed. |
|
| medium | Connect over the best-ranked transport; records the chosen transport + active document. |
|
| low | Current session state: enabled, connected, active transport, available transports. Never raises. |
|
| low | Tear down the live session (the X1 disable switch). Idempotent. |
|
| restricted | Install the shipped extension-socket helper into the Inkscape user extensions dir. Gated by the master switch. |
|
| restricted | AUTO-ARM the socket helper (E16-10f): install it (idempotent) then launch a headful Inkscape with the helper effect auto-invoked via |
|
| low | Identity of the document open in the live instance. |
|
| low | Current selection as object ids. |
|
| low | Per-object detail for the selection (reuses the headless |
|
| low | Rasterize the live canvas to a PNG under the live artifacts dir. Optional region/bbox (all four together) + |
|
| low | Control the live canvas viewport: |
|
| low | Capture one live frame: the rendered PNG plus a structured |
|
| low | Block until the live state changes or the bounded timeout elapses. Polls a CHEAP server-hashed state token (revision + selection + viewport — never the full doc or a PNG; |
|
| medium | Save the live document as a new tracked workspace document (atomic write, never overwrites; Operation Record + snapshot). |
|
| high | Apply a validated style and/or simple transform to the live selection (reuses E2 semantics). Approval-gated; Live Operation Record + before/after render. |
|
| high | Insert a safe-parsed SVG fragment into the running document. Approval-gated; recorded + rendered. |
|
| high | Replace the selected text object's content (length/control-char guarded). Approval-gated; recorded + rendered. |
|
| low | Export just the current live selection to a PNG under the live artifacts dir (read-only feedback, no record). |
|
| low | Produce a FOCUSED, annotated before/after visual diff of a live op — not two raw window screenshots. REUSES the op's E4-02 |
|
| low when perceive-only / high when it acts | Run ONE perceive→decide→act→observe loop iteration (E8-05). PERCEIVE = |
The helper extension can also be installed without the server via the dynamic installers in
scripts/: install-live-helper.sh (Linux / macOS / Windows under Git Bash or WSL)
and install-live-helper.ps1 (native Windows PowerShell). Both resolve the Inkscape user
extensions dir at runtime (inkscape --user-data-directory, with INKSCAPE_PROFILE_DIR and an
OS-aware fallback) and do not require the master gate.
Resource reference
Read-only MCP resources addressable by URI. Document resources are templated on doc_id.
URI | Description |
| Cached runtime capability matrix, including the authoritative MCP tool surface ( |
| Curated goal→tool intent map ( |
| Index of open documents and their concrete per-doc resource URIs (discoverable via |
| Index of registered MCP prompts (name + one-line purpose + arguments, from the live |
| Top-level document summary. |
| Element tree. |
| Layer list. |
| Object inventory. |
| Style usage. |
| Fonts referenced. |
| External assets / references. |
| Live-session state (enabled / connected / transport). Clean when no session. |
| Current live selection (object ids). Empty when no session. |
| Current live frame's structured metadata: the |
| Latest live change state: the current cheap state token + classified deltas ( |
| Recent Live Operation Records (E4): what each mutation changed, approval, before/after renders. Paths are workspace-relative or opaque ( |
Prompt reference
MCP Prompts orient the agent on how to use the tool surface safely; they grant no capability of their own (architecture §4.1).
Prompt | Args | Description |
|
| Entry point for the live-view co-pilot loop (E8-05). Orients the agent to drive a running Inkscape toward |
|
| Orients the agent on producing web-ready assets (optionally optimize + quality-check first, then |
|
| Orients the agent on producing a multi-size square PNG icon set via |
|
| Orients the agent on producing a print-ready vector PDF via |
|
| Orients the agent on recoloring to a brand/theme palette via |
|
| On-ramps the E14 generative loop toward |
|
| On-ramps the OBJECT-TARGETED restyle loop toward |
Try asking your agent to…
Natural-language asks and the tool(s) each exercises. This catalog is aligned with the same
curated goal→tool map that powers the how_do_i tool and the intents section of
list_capabilities (src/inkscape_mcp/intents.py) — so the doc, the
discovery tool, and the runtime matrix never diverge. Don't know which tool fits? Just ask
how_do_i("…") with the goal in words and it returns the same mapping. For the full create→render→
export workflow, snapshots/reversibility, and the risk/approval model see the
agent-usage guide.
Generate / draw
"Draw a rectangle / circle / line on a new canvas." →
create_document,create_rect,create_circle,create_line"Add a text label." →
create_text"Add a gradient fill (linear or radial)." →
add_linear_gradient,add_radial_gradient,set_fill"Group these objects together." →
group_objects,create_group
Edit
"Make this shape blue / change its fill." →
set_fill"Change the stroke / outline and opacity." →
set_stroke,set_opacity"Swap one colour for another across the whole document." →
replace_color,apply_palette"Move / scale / rotate an object." →
move_object,scale_object,rotate_object"Delete these objects by id." (HIGH-risk, approval-gated, reversible) →
delete_object"Resize the canvas or fit it to the content." →
resize_canvas,fit_to_content,normalize_viewbox"Simplify / clean up these paths." (HIGH-risk, dry-run first) →
simplify_path,cleanup_paths"Union / subtract these shapes." (HIGH-risk) →
boolean_union,boolean_difference,combine_paths
Inspect
"Open this SVG and tell me what's in it." →
open_document,inspect_document"Find the red shapes / all text / objects by id." →
find_objects"Is this document valid? Give me a quality report." →
validate_document,quality_report"Audit a whole icon system for consistency (viewBox/stroke/id naming)." →
quality_report_set"What's the byte size / sha256 of this file (or this set)?" →
stat_artifact,stat_artifacts"What can this server do?" →
list_capabilities,how_do_i
Export
"Export a 512 px PNG (or a preview)." →
export_document,render_preview"Export just this one object." →
export_object"Export a whole icon set / many sizes at once." →
create_icon_set,export_batch"Lay out a 12-icon system as a contact/spec sheet in one call." →
compose_grid"Export / optimize a whole set of documents at once." →
export_set,optimize_set"Make the SVG smaller for the web." →
svg_web_optimize"Export web-ready / print-ready assets." →
export_web_profile,export_print_profile"Save it to a new file." →
save_document_as
Live
"Snapshot / undo / restore the document state." →
create_snapshot,list_snapshots,restore_snapshot"Connect to my running Inkscape and work on the open canvas." →
live_connect,live_get_scene,live_apply_to_selection
Safety model
The server is built to be safe to hand to an autonomous agent:
Workspace sandbox. Every path is normalized and resolved; symlinks are guarded; access outside the configured workspace root(s) is rejected before any I/O.
Originals untouched. Documents are opened as working copies. No tool overwrites the source;
save_document_aswrites elsewhere, and overwriting an existing file needs explicit approval.Reversibility. Mutating tools snapshot first and emit an Operation Record;
restore_snapshotrolls the working copy back to any indexed snapshot. An explicit retention sweep (boot-time +prune_snapshots) bounds snapshot growth without ever pruning the baseline or the live head.Risk policy.
low/mediumare permitted;highrequires a per-operationapproval_token(minted out of band, bound to one operation — never an ambient flag a model can set);restricted(code / network / fs-escape) never ships in the MVP.Subprocess hygiene. The Inkscape CLI is always invoked with argument lists, never shell strings. Object ids and formats are charset-validated before reaching the binary.
Resource limits. Input/output/export size caps, per-process timeouts, and a concurrency cap.
Safe XML. Parsing disables entity expansion and external-entity resolution (no XXE / billion laughs). No network access. No arbitrary extension execution.
Stable errors. Tools raise
ToolErrors with host-path-free public messages; full detail goes to stderr logs only (stdout is the MCP channel).
Project layout
src/inkscape_mcp/
server.py # FastMCP app + STDIO entry point (register_tools wires every module)
config.py # process Settings + operator-tunable limits (env-driven)
registry.py # doc_id <-> path registry; opens working copies, never mutates originals
operations.py # Operation Record model + persistence (ADR-004)
snapshots.py # snapshot engine + reversible restore
retention.py # snapshot + live-frame retention/cleanup (keep-N/keep-days/hard caps + live-frame
# age/byte caps; explicit boot sweep + prune tool — never implicit)
validate.py # read-only validation engine
quality.py # E5-05 read-only quality report (wraps validate + inspect + E5-04 optimizer counts)
logging_setup.py # stderr-only structured logging (stdout reserved for MCP STDIO)
document/ # direct-DOM inspection engine (summary/tree/layers/styles/fonts/assets)
edit/ # safe-edit engines: dom.py (lxml primitives + shared SAFE_ID_RE), pipeline.py
# (snapshot + before/after preview + Operation Record wrapper; risk-classed),
# style.py, text_object.py, transform.py, create.py (E14-01/E14-04 element-creation
# + defs/gradients + grouping engines), optimize.py (E5-04 web-optimize: strip editor
# cruft + drop dead structure + reduce coord precision; reversible), paths.py
# (E6 HIGH-risk path geometry via the Inkscape engine — arg-list Actions →
# safe-parse → DOM-replace)
actions/ # E6-02 controlled Action surface: capability_map.py (version-keyed Action map +
# discovery, persisted under action-maps/), chains.py (typed ActionStep chains →
# allowlist+map validation → arg-list --actions argv → engine; no raw passthrough;
# reused by the E6-03 run_raw_action escape hatch)
render/ # Inkscape CLI render/export engine (cli.py) + export profiles (profiles.py) +
# E5-06 bounded typed batch export (batch.py: item cap + byte budget + dry-run) +
# E16-07 in-process content-truth verifier (verify.py: PDF is_vector/fonts_outlined,
# raster opaque_px/all_blank — no pdffonts/Pillow subprocess)
engine/ # E12 / ADR-007 opt-in warm `inkscape --shell` engine: process.py (one supervised
# worker — read-until-prompt framing, per-command timeout+kill, crash/idle reap),
# manager.py (per-working-copy worker pool, serialized, LRU, freshness reopen),
# ops.py (shell export/action composition). Gated by INKSCAPE_MCP_ENGINE_MODE=shell
# with an automatic per-call CLI fallback; render/paths/chains route through it.
live/ # E3 live read + E4 live write + E8 live view: transport ABC + capability-aware
# backend selection, extension-socket + DBus backends, protocol.py wire schema
# (v3: read + semantic write + view-only commands, region/scale render),
# session manager, render/sync, edit.py (write + view engine reusing E2 semantics
# + bounded view validators), records.py (Live Operation Records + approval gate),
# scene.py (E8-02 LiveScene), diff.py (E8-04 focused visual diff), loop.py (E8-05
# perceive→decide→act→observe orchestrator — composes the above, zero new authority),
# cache.py (E8-06 bounded LRU render cache keyed (doc_revision, viewport, scale) +
# coalescing budget; freshness via the revision key), helper_extension/ (runs inside
# Inkscape). Gate on by default.
prompts/ # MCP Prompts (architecture §4.1): live.py (live_canvas_assist — live-view loop entry
# point), library.py (E5-07 export/recolor orientation prompts), authoring.py
# (E15-04 compose_artwork / restyle_artwork — E14 generative on-ramp prompts)
resources/ # MCP resource templates (runtime caps, document/{id}/..., live/{session,selection,operations})
runtime/ # Inkscape capability probe
tools/ # typed @mcp.tool modules: system, document, validate, quality (E5-05), export,
# profiles, export_batch (E5-06), optimize (E5-04), snapshots, style,
# text_object, create (E14-01/E14-04 element-creation + defs/gradients + grouping),
# transform, paths (E6 path geometry), actions (E6-02 discovery +
# Action chains + E6-03 run_raw_action), save, live (read + write)
workspace/ # sandbox/path safety, limits, risk policy, safe XML parse, subprocess wrapper
scripts/ # install-live-helper.sh (Linux/macOS/Git-Bash) + .ps1 (Windows): copy the live
# helper extension into Inkscape's user extensions dir, resolved dynamically;
# gen_llms_txt.py (E15-01): regenerate llms.txt / llms-full.txt from the live registry
llms.txt # GENERATED LLM index (one line + risk class per tool); do not hand-edit
llms-full.txt # GENERATED full manifest (full descriptions + key params + prompts + resources)
evals/ # E15-06 deterministic tool-usability harness: tool_selection_scenarios.json +
# run_eval.py (drives how_do_i/intents; reports tool-selection accuracy). ruff-checked.
# run_live_eval.py (E15-06a): OPTIONAL report-only live-agent runner (same dataset/scorer).
examples/ # ready-to-copy MCP host configs (claude_desktop_config.json, mcp.json)
tests/ # pytest; Inkscape-dependent tests marked @pytest.mark.inkscapeInstall / config / compatibility / troubleshooting docs: docs/install/.
Driving the server from an agent (create→render→export loop, snapshots, risk/approval gate, tool
selection): docs/agent-usage-guide.md.
Development
Stack: Python ≥ 3.12 ·
uv· FastMCP 3.x ·lxml· Pillow (live visual diff) · Inkscape CLI (per-call, plus an opt-in warminkscape --shellengine — E12/ADR-007) · STDIO transport.Conventions: small typed tools (no portmanteau /
run_action(string)), risk-classed, mutating ops emit Operation Records and never overwrite originals, subprocess via arg-lists. SeeCONTRIBUTING.mdfor the full contributor workflow + conventions.Lint / format:
ruff(selectsE,F,I,B,UP,S,RUF;S603/S607are intentionally ignored because the Inkscape CLI adapter needs subprocess — safety enforced by arg-lists + review).Types:
mypy --strictoversrc.Tests:
pytest; mark Inkscape-binary tests with@pytest.mark.inkscape.
uv run pytest # full suite
uv run pytest -m "not inkscape" # skip tests needing the Inkscape binary
uv run ruff check --fix . && uv run ruff format .
uv run mypy srcLicense
MIT © Johannes Sood
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/jjjsood/inkscape-mcp-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server