Skip to main content
Glama
inkscape-mcp-server

inkscape-mcp-server

inkscape-mcp-server

CI Python 3.9+ License: MIT Runtime dependencies: none

A standard-library MCP server for editing, analysing and exporting SVG files with Inkscape 1.2+. Every Inkscape-backed operation is checked against its output file rather than its exit code.

Two overlapping shapes are merged, optimised and exported by three tool calls, with every Inkscape result verified.

Quick start

You need Python 3.9 or newer and Inkscape 1.2 or newer.

git clone https://github.com/code-and-crypto/inkscape-mcp-server
cd inkscape-mcp-server
python server.py --check

--check runs a self-test and prints what it found:

self-test: 6/6 passed
  PASS tool schemas resolve: 23 tools, all parameters known
  PASS inkscape png export
  PASS boolean union via object-to-path chain
  PASS python optimiser
  PASS validator
  PASS analyser

Then register it with your MCP client:

{
  "mcpServers": {
    "inkscape-mcp-server": {
      "type": "stdio",
      "command": "python",
      "args": ["/path/to/inkscape-mcp-server/server.py"],
      "env": { "INKSCAPE_BIN": "/path/to/inkscape" }
    }
  }
}

INKSCAPE_BIN is optional. Without it the server checks PATH and the usual install locations, and server_info reports what it found. There is also a pip route:

pip install git+https://github.com/code-and-crypto/inkscape-mcp-server
inkscape-mcp-server --check

Related MCP server: inkscape-mcp

Example

The files below are in examples/, and the output is what the server actually returned. Start with two overlapping shapes and an unused gradient in <defs>:

<svg xmlns="http://www.w3.org/2000/svg" width="240" height="240" viewBox="0 0 240 240">
  <defs>
    <linearGradient id="unused">…</linearGradient>
  </defs>
  <g id="empty"/>
  <circle id="left"  cx="95.00000001" cy="120" r="62" style="fill:#3d5afe"/>
  <rect   id="right" x="105.0000000" y="58" width="124" height="124" rx="14" style="fill:#00bfa5"/>
</svg>

Merge the two shapes:

{ "name": "path_boolean",
  "arguments": { "input_path": "examples/badge.svg",
                 "output_path": "examples/badge_union.svg",
                 "operation": "union",
                 "object_ids": ["left", "right"] } }
{ "success": true, "message": "union applied" }

path-union does nothing to a <rect> or a <circle>, because Inkscape's boolean operations work on paths. The tool inserts object-to-path into the chain first, so the call above merges the shapes instead of silently returning the original file.

Then clean it up:

{ "name": "svg_optimize",
  "arguments": { "input_path": "examples/badge_union.svg",
                 "output_path": "examples/badge_final.svg" } }
{
 "success": true,
 "message": "941 -> 475 bytes (466 saved, 49.5% smaller)",
 "bytes_before": 941, "bytes_after": 475, "percent_saved": 49.52,
 "steps": {
  "unused_defs_removed": 1,
  "unused_def_ids": ["unused"],
  "empty_containers_removed": 2,
  "numbers_rounded_on_attrs": 1
 }
}

The result is one path, with the unused gradient and the empty group gone:

<svg xmlns="http://www.w3.org/2000/svg" width="240" height="240" viewBox="0 0 240 240">
  <path id="left" style="fill:#3d5afe" d="M 95 58 A 62 62 0 0 0 33 120 …"/>
</svg>

The banner at the top of this page was produced and checked with the same tools.

Why output verification matters

Inkscape returns exit code 0 after rejecting an action:

$ inkscape drawing.svg --actions="file-vacuum-defs;export-filename:out.svg;export-do"
InkscapeApplication::parse_actions: could not find action for: file-vacuum-defs
$ echo $?
0

It also returns 0 after writing nothing at all. A wrapper that reads the exit code therefore reports success for work that did not happen.

Every Inkscape call here goes through one function that scans stderr for rejected actions, then checks that the expected output file exists, is not empty, and has a fresh modification time. The last check matters because a leftover file from an earlier run otherwise makes a failed export look successful.

Ten behaviours of Inkscape 1.4.2 were confirmed this way, each with a reproduction, in docs/inkscape-findings.md.

What it can do

157 bundled Inkscape extensions, headlessly

Pattern along Path, Envelope, Perspective, gears, QR, Hershey text

212 preset filters

the Filters menu: bevels, metal, jelly, glass, shadows

8 live path effects

defaults read from Inkscape's lpe-*.cpp

348-action registry with scope

reports which 130 actions are GUI-only instead of failing silently

Rendered PNG returned to the caller

a blank canvas is visible rather than hidden in a payload

SVG optimisation, validation, analysis, animation

in Python, since Inkscape exposes no headless action for these

Offset and inset, fillet, chamfer, powerstroke, taper, radial repeat, tiled clones as real <use> links, mesh gradients, multipage documents, node-level path editing and layer management are all reachable, none of which Inkscape exposes to a headless caller directly.

Tools

Named <group>_<action>, which mirrors the source layout.

Group

Tools

path

path_boolean path_convert path_geometry path_effect

object

object_transform object_style object_arrange object_clone object_filter object_compose object_query

document

document_edit (layers, gradients, pages, nodes, fonts, canvas)

svg

svg_optimize svg_validate svg_analyze svg_animate

export

export_file export_batch export_preview

escape hatches

action_run action_find extension_run

server

server_info

Parameters for each are in docs/tool-reference.md.

Error messages

The caller is usually a model that will act on the message, so a failure names the cause and the call that fixes it:

'p1' is <rect> with no 'd'. Run path_convert kind='object-to-path' on it first.
Inkscape's own inset/outset refuses non-paths too.
'fit-canvas-to-drawing' is in the action registry extracted from the Inkscape source,
but this Inkscape build does not expose it at runtime. Use action_find to search for
a working equivalent.

Tested against

Inkscape 1.4.2 (f4327f4) on Windows 11 and Ubuntu, Python 3.9 through 3.13.

CI runs the self-test on Linux, macOS and Windows, then again with Inkscape installed on Linux and Windows, then builds the wheel and installs it into a clean environment.

Known limitations:

  • macOS has no Inkscape-backed CI job, so only the pure-Python half is verified there.

  • Behaviour is verified against Inkscape 1.4.2. Other versions expose different actions, which is the subject of docs/inkscape-findings.md.

  • Every tool transforms an existing file. There is no way to create a document from scratch yet (#3).

Documentation

Verified Inkscape findings

Ten behaviours, each with a reproduction

Tool reference

All 23 tools and their parameters

Architecture

The layers, and how to replace one

Contributing

Adding tools, conventions, testing

Changelog

What changed in each version

Security

Reporting a vulnerability

Planned work is tracked in Issues.

License

MIT. See LICENSE.

Available Tools

23 tools
action_findA

Search the action registry extracted from the Inkscape source: name, scope (app = headless, win = GUI only), source file, parameter type. 348 actions, of which 130 are window-scoped and can never work headlessly.

Inputs: limit, query, scope.

action_runA

Run any raw Inkscape action chain. Checks scope first and explains precisely why an action failed: window-scoped, a dead registry entry, or a genuine typo.

Inputs: actions, timeout, input_path, output_path, +1 more.

document_editA

Document structure. area: layers | gradient | pages | nodes | fonts | canvas. Layers, gradients (linear/radial/MESH), multi-page, node-level path editing, font enumeration, and canvas geometry (info, fit-to-drawing, resize, set-viewbox, set-units). Inkscape's 24 layer-* actions are all window-scoped, so this is the only headless route.

Inputs: x, y, area, cols, +24 more.

export_batchA

Batch export. operation='layers-to-files' writes every Inkscape layer to its own file; operation='multi-dpi' writes one document at several DPIs. Reports per-item success so one bad layer does not hide the rest.

Inputs: dpi, dpis, format, operation, +2 more.

export_fileA

Export to PNG, SVG, PDF, PS or EPS with full control: dpi, size, margin, area, background, text-to-path, compression. Verifies the output exists and is non-empty, because Inkscape exits 0 on an empty export.

Inputs: id, dpi, area, width, +15 more.

export_previewA

Render to PNG and RETURN THE IMAGE, so the calling model sees its own work. A chain that 'succeeds' and produces a blank canvas is obvious in a picture and invisible in a JSON payload.

Inputs: dpi, input_path, output_path.

extension_runA

Run any of the 157 bundled Inkscape EXTENSIONS headlessly - Pattern along Path, Envelope, Perspective, Jitter Nodes, gears, QR codes, isometric grids, Hershey text. operation: list | describe | run. Calling these as Inkscape actions silently does nothing; this runs the .inx scripts properly.

Inputs: name, params, operation, input_path, +1 more.

object_arrangeC

Align, distribute, remove overlaps, and raise/lower in the stacking order.

Inputs: kind, value, input_path, object_ids, +2 more.

object_cloneB

Tiled clones as real links (like Inkscape's own), and duplicate. Neither exists as an Inkscape action at all.

Inputs: cx, cy, dx, dy, +8 more.

object_composeC

Compose objects: text on path, flow text into a shape, clones, clip and mask (including Power Clip), group/ungroup, trace bitmap, fill-between-paths.

Inputs: kind, value, new_id, target, +6 more.

object_filterC

Inkscape's 212 PRESET FILTERS - the whole Filters menu: bevels, metal, jelly, glass, textures, shadows, glows, distortion. operation: list | menus | apply.

Inputs: name, limit, query, tweaks, +5 more.

object_queryA

Real bounding boxes for every object, via Inkscape's --query-all. Use this to discover the object ids in a document before acting on them.

Inputs: force_full, input_path, object_ids.

object_styleA

Set ANY XML attribute or CSS property on a selection (object-set-attribute / object-set-property). No other Inkscape MCP server exposes this.

Inputs: kind, name, value, input_path, +3 more.

object_transformB

Move, scale, rotate, grow and flip objects. Real geometry, headless.

Inputs: kind, value, input_path, object_ids, +2 more.

path_booleanA

Boolean path ops: union, difference, intersection, exclusion, division, cut. Uses the correct path-* action names, inserts object-to-path automatically (booleans silently no-op on / without it), and chains pairwise for more than two objects (Inkscape only combines two at a time).

Inputs: operation, input_path, object_ids, select_all, +1 more.

path_convertC

Convert paths and shapes: object-to-path, stroke-to-path, text-to-path, combine, break-apart, simplify, flatten, fracture, to-guides, to-marker, to-pattern. Set with kind.

Inputs: kind, value, input_path, object_ids, +2 more.

path_effectB

The 8 live path effects with no other headless route: sketch, rough_hatches, spiro, bspline, dynastroke, lattice, knot, vonkoch. Parameters and defaults taken from Inkscape's lpe-*.cpp. operation: list | apply.

Inputs: effect, new_id, params, object_id, +3 more.

path_geometryA

Computed path geometry: offset/inset (Inkscape's one real headless gap), fillet, chamfer, powerstroke, taper, mirror, radial-repeat. Semantics read from Inkscape's own source so results match the GUI.

Inputs: cx, cy, dx, dy, +11 more.

server_infoA

Server health and self-check. operation: status | version | config | diagnostics | selftest | help. selftest round-trips a real document through both Inkscape and the pure-Python path. Run this FIRST when anything behaves oddly.

Inputs: operation, require_inkscape.

svg_analyzeB

Document analysis. operation: statistics | structure | dimensions | quality | full. The quality score runs 11 weighted checks and explains every deduction with the tool call that fixes it.

Inputs: max_depth, operation, input_path.

svg_animateB

SMIL and CSS animation applied to objects ALREADY IN THE DOCUMENT, by id. operation: list_presets | apply_preset | animate | transform | motion | color | css. 12 presets, 7 easing curves, motion along any path in the document. Inkscape has no animation action of any kind.

Inputs: cx, cy, to, dur, +18 more.

svg_optimizeA

REAL SVG optimisation: prune unreferenced defs, strip inkscape:/sodipodi:/RDF cruft, drop empty groups, round coordinates, remove default style properties, minify. Done in Python because Inkscape's own vacuum-defs is a VERIFIED silent no-op headlessly and document-cleanup is window-scoped. Reports bytes saved.

Inputs: minify, keep_ids, precision, input_path, +6 more.

svg_validateA

SVG quality assurance. operation: validate | viewbox | stroke_fill | size_limits | audit_web | audit_pack (a whole directory). Paint detection reads style= AND presentation attributes AND inherited paint, so Inkscape-authored files are not falsely reported as empty.

Inputs: pattern, operation, input_path, max_dimension, +2 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.4/5.0

    Scored across 23 tools

    Disambiguation4/5

    Most tools are cleanly separated by subsystem (paths, objects, SVG document, export, raw actions), and the prefix helps agents route. A few adjacent tools could be confused—object_clone vs object_compose's clone operation and svg_validate vs svg_analyze's quality checks—but the descriptions draw enough of a boundary.

    Naming Consistency4/5

    The set is uniformly lowercase snake_case with a domain prefix (object_, path_, svg_, export_, action_), which reads predictably. It is not a strict verb_noun pattern throughout—path_geometry, server_info, and path_effect are noun/adjective entries—so it falls just short of perfect consistency.

    Tool Count3/5

    At 23 tools the surface is in the heavy range for an MCP server, and an agent must digest a large catalog before acting. The breadth is defensible because Inkscape is a huge domain and each tool packages many underlying operations, but several aggregator tools could have been split or trimmed.

    Completeness4/5

    Core workflows—querying, editing, styling, path operations, document structure, optimization, export—are well covered, and action_run/extension_run provide escape hatches for the long tail of Inkscape actions. Obvious lifecycle gaps remain (no dedicated document load/save or object creation/deletion tool), but they can often be worked around through raw actions or existing file context.

    Maintenance

    ActivityStale
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers