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: inkscape-mcp
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/jjjsood/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 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) 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 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) 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 (LRU). Floored. |
|
| Total byte budget for the live render cache (LRU eviction). Floored. |
|
| Frame-coalescing latency budget: a repeated identical-key render within this window returns the just-cached frame instead of re-rendering. |
|
| Age retention for loop/live render frames, pruned by the explicit retention sweep (boot + |
|
| Total byte budget for loop/live render frames (newest kept), pruned by the explicit sweep. |
|
| Master gate for live mode. 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. 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. Discovery is read-only and unaffected. |
|
| Advanced-mode gate for the |
|
| Engine transport for render/export/path/boolean/action-chain (ADR-007). |
|
| Max concurrent warm shell workers when |
|
| Seconds before an idle warm shell worker is reaped. Floored at 1. |
|
| Tool-disclosure profile. |
|
| Tool-DESCRIPTION mode, 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). The four raster tools (render_preview / capture_frame / export_document /
export_object) also return the PNG inline as an MCP image block when it is under the inline
byte threshold (≈5 MiB; tune via max_output_bytes, opt out with inline=False) — view that
image; do NOT Read the returned path. Because the path is root-relative to the server's
workspace (a client Read resolves it against its own CWD and fails), the inline text payload omits
the path entirely (it carries a note); the resolvable path stays in structured_content for
programmatic use. 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. 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. 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[]
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. 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: per-doc |
Render & export — low
Every render/export result carries a stale: bool staleness signal: 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).
Render/export results also self-certify content truth, 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, returned inline as an MCP image block by default (view it; do NOT |
|
| 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. 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: 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: 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 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: remove objects by id from the DOM → |
Typed batch edit — medium (max over members), reversible
Tool | Signature | Description |
|
| Batch: apply an ordered list (≤ 64) of TYPED edits — a discriminated union over the existing DOM ops ( |
|
| Selector → op: 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 (read / write / 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: 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). 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. 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 write engine, no raw-Action/code),
routes it through run_live_mutation (HIGH + approval_token + Live Operation Record — the SAME
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: 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 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 |
|
| low when perceive-only / high when it acts | Run ONE perceive→decide→act→observe loop iteration. 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: 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. 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 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 # read-only quality report (wraps validate + inspect + 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 (element-creation
# + defs/gradients + grouping engines), optimize.py (web-optimize: strip editor
# cruft + drop dead structure + reduce coord precision; reversible), paths.py
# (HIGH-risk path geometry via the Inkscape engine — arg-list Actions →
# safe-parse → DOM-replace)
actions/ # 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 run_raw_action escape hatch)
render/ # Inkscape CLI render/export engine (cli.py) + export profiles (profiles.py) +
# bounded typed batch export (batch.py: item cap + byte budget + dry-run) +
# in-process content-truth verifier (verify.py: PDF is_vector/fonts_outlined,
# raster opaque_px/all_blank — no pdffonts/Pillow subprocess)
engine/ # 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/ # live read + live write + 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 semantics
# + bounded view validators), records.py (Live Operation Records + approval gate),
# scene.py (LiveScene), diff.py (focused visual diff), loop.py (perceive→decide→act→observe orchestrator — composes the above, zero new authority),
# cache.py (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 (export/recolor orientation prompts), authoring.py
# (compose_artwork / restyle_artwork — 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, export,
# profiles, export_batch, optimize, snapshots, style,
# text_object, create (element-creation + defs/gradients + grouping),
# transform, paths (path geometry), actions (discovery +
# Action chains + 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: 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/ # 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: 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 — 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
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
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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