inksmcp
Enables AI assistants to create, edit, and export vector graphics through Inkscape, including measuring elements, arranging layouts, applying path operations, rendering previews, and exporting to formats such as PNG, PDF, SVG, EPS, and EMF.
Click on "Deploy 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., "@inksmcp make an A5 flyer for a book swap and export it as PDF"
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.
inksmcp
An MCP server that lets AI assistants create, edit and export vector graphics with Inkscape.
Describe a poster, a diagram, a chart or a comic page to your assistant, and it builds a real SVG document in Inkscape: measured, laid out and exported to PNG or PDF. It can also open drawings made elsewhere and change them.
|
|
|
|
|
|
Made by Claude through inksmcp during field tests, in 13 to 41 tool calls each. The SVG was never edited by hand. The floor plan and the periodic table used a short script to prepare their data.
Why inksmcp
Asking a model to write SVG by hand works for an icon, but it breaks down on real layouts. The model can't measure text, keeps redoing coordinate arithmetic, and can't see what it drew. inksmcp takes that work off the model:
Measured, not guessed. Bounding boxes come from Inkscape itself, text included, in the document's own units.
Relationships instead of coordinates.
alignandlayoutarrange things.placekeeps a caption below a title's real glyphs.fit_tosizes a box around its content.connectdraws arrows that stay attached when things move.Data-driven.
repeatstamps a template per data row (cards, timelines, legends, table rows, character poses).splitdivides a page into panels or cells. Large data comes from JSON or CSV files, so it doesn't pass through the conversation.Works on existing files. It opens
.svg/.svgzdrawings from other tools.inspect findlocates elements by colour, type, text or symbol. Symbols can be placed from icon libraries.Safe and checkable. Every tool is all-or-nothing. Editing responses warn about overlapping text, and
render_previewshows the agent what it drew.Real output. PNG, PDF (optionally with text as paths), plain SVG, EPS, EMF and more, rendered by Inkscape.
Related MCP server: imagetosvg-mcp
Quick start
Add the server to your MCP client. For Claude Code:
claude mcp add --scope user inkscape -- uvx inksmcpFor Claude Desktop, Cursor and others:
{ "mcpServers": { "inkscape": { "command": "uvx", "args": ["inksmcp"] } } }Ask: "Using the inkscape tools, make an A5 flyer for a book swap on Saturday at 10:00 in the library garden, show me a preview, then export a PDF."
Details for each OS and client, and troubleshooting, are in getting started.
Tools
Area | Tools |
Documents |
|
Elements |
|
Arrangement |
|
Charts |
|
Paths |
|
Output |
|
See the recipes for how they combine, and the tool reference for every parameter.
How it works
The document lives in the server as an lxml tree, which is the source of truth. Edits are applied
there directly. Anything that needs Inkscape (measuring, boolean operations, connectors, rendering,
export) goes to one persistent inkscape --shell process, so a call takes milliseconds instead of
the ~1 s it takes to start Inkscape. The architecture notes have
the details.
Status
inksmcp is beta (0.3). It has been through eleven field tests, in which agents used it for real tasks. The latest one edited files made in other tools. There are 160 tests, and they run against the real Inkscape.
Known limits:
It is developed on Windows with Inkscape 1.4.4. CI also runs on Linux and macOS.
The tool list costs about 9k tokens of context per session.
Gradients and patterns can't be created as style keys yet. They can be applied through raw
stylewhen they already exist in the document.SVG 1.2 flowed text can be moved but not edited.
The roadmap lists what's next.
Contributing
Bug reports, field reports ("I asked for X, here's what the agent struggled with") and pull requests
are welcome. See CONTRIBUTING.md. The project is built experiment-first: every
Inkscape behaviour it relies on is proven by a script in experiments/ and guarded by a test. The
development handbook explains the method.
License
MIT. Inkscape is a separate program under the GPL; inksmcp calls it as an external process.
Available Tools
24 toolsadd_elementsA
Add one or more elements in a single call. Returns the new ids.
Element spec keys — common: type, id, label, layer (name; created if missing), parent (group id), transform, style (css string or dict). Style shorthands: fill, stroke, stroke_width, opacity, fill_opacity, stroke_opacity, stroke_dasharray, stroke_linecap, stroke_linejoin, font_size, font_family, font_weight, font_style, text_anchor. Geometry per type: rect: x, y, width, height, rx, ry, fit_to, fit_padding, fit; circle: cx, cy, r; ellipse: cx, cy, rx, ry; line: x1, y1, x2, y2, marker_start, marker_end; polyline: points, marker_start, marker_end; polygon: points; path: d, marker_start, marker_end; text: x, y, text, line_height, vertical_anchor, width, halo, halo_width; group: -; arrow: x1, y1, x2, y2, shaft_width, head_width, head_length; image: x, y, width, height, href, object_fit, embed; use: x, y, width, height, href. points = [[x,y],...]. text supports '\n' for multiple lines; font_size is in user units. Coordinates are in the parent's system: inside a transformed layer or group (e.g. after layout moved it, or a scaled plan group) they are offset/scaled with it. use: href "symbol_id" places a symbol (or any element) of this document; "library.svg#symbol_id" copies that symbol from a file first (list a library's symbols: document_open it, inspect find {"type": "symbol"}); width/height scale a symbol that has a viewBox. clip: an element id (its current shape) or [x, y, w, h] — the element is cut to it and the clip then moves with the element; null removes it. text halo: '#ffffff' outlines the glyphs behind the fill so text reads over lines (halo_width default 0.3 x font size; 'none' removes). place: {"below": id, "gap": 1.5} (or above / left_of / right_of; "align": start|center|end on the other axis) puts the element beside the MEASURED box of another (real glyph extents, any script) and keeps it there when that element changes; inside repeat, ids are template names; null frees it. Responses warn about overlaps among the touched elements: text on text, text across a shape's edge, text crossed by a line (a text with a halo may cross lines). rect fit_to: [ids] sizes the rect around them after wrapping (fit_padding: n | [v, h] | [t, r, b, l]; fit: both | height | width, e.g. height keeps a card's width); it re-fits when those elements are edited (update_elements {"id": rect} re-fits after moves; fit_to: null frees it).
defaults is merged into every element (only keys valid for its type), e.g. {"font_size": 2.2, "fill": "#2a8a4a", "layer": "Labels"}.
elements_path: a JSON file (a list of specs, or {"elements": [...], "defaults": {...}}) instead of elements — for generated geometry, so it never passes through the conversation.
Inputs: doc_id, preview, defaults, elements, +1 more.
alignA
Align elements to the page, to each other, or inside another element — e.g. centre a label in a box: {"ids": ["label"], "to": "box", "horizontal": "center", "vertical": "middle"}. Operations run in order, each seeing the previous moves. Returns the moves [dx, dy] and new bboxes in user units.
Inputs: operations (required), doc_id, preview.
connectA
Draw arrows/lines between elements that stay attached when things move (align/layout/ update_elements/page_fit). Default: native Inkscape connectors — clipped to the real shape (circles etc.) and still live in the Inkscape GUI. With from_side/to_side/via you control the route (e.g. a loop diagram: {"from": "condenser", "to": "valve", "from_side": "left", "to_side": "top", "routing": "elbow"}); those attach at the middle of the chosen side of the bounding box, as do connectors with start_gap/end_gap. Returns the connector ids, their layers, label ids and warnings.
Inputs: connections (required), doc_id, preview.
delete_elementsA
Delete elements (and their children) by id. Connectors attached to them and their labels
are deleted too; all removed ids are returned. Defs only they used (symbols of an imported library,
gradients, clip paths) go too: defs_removed counts them.
Inputs: ids (required), doc_id.
document_createA
Create a new blank document and make it current. The viewBox matches width/height,
so coordinates are in unit. background (e.g. '#ffffff') adds a full-page rect with id 'background'.
Inputs: width (required), height (required), unit, background.
document_openA
Open an existing SVG (.svg or .svgz) and make it current. Returns its outline, and notes when the
file was normalised for editing without changing how it renders: a page whose viewBox doesn't match its
width/height (or uses %), fill/stroke set on the root (new elements would inherit them), ids given to
elements that had none.
Inputs: path (required).
document_saveA
Save the document as Inkscape SVG (to path, or where it was opened/last saved).
Inputs: path, doc_id.
exportA
Export via Inkscape. Format defaults to the file extension. What is exported:
region [x, y, w, h] (user units, everything visible); or ids — only those objects cropped to
them (only_ids=true, default) or the area around them with everything visible (only_ids=false);
otherwise area 'page' or 'drawing'. For PNG set dpi or width/height (px); background e.g. '#ffffff'
(default transparent).
Inputs: path (required), dpi, ids, area, +8 more.
gridA
Draw a grid / graph paper inside rect [x, y, w, h] — linear or logarithmic per axis — without
computing any line positions. Axis specs:
linear: {"scale": "linear", "major": 10, "medium": 5, "minor": 1, "label_start": 0, "label_step": 1}
(spacings in user units, each a whole multiple of the finest; labels on major lines)
log: {"scale": "log", "cycles": 3, "subdivisions": "standard" | "fine" | "integers", "start": 10}
(decades major, 2..9 medium, subdivisions minor; "start" = value at the origin (default 1);
"labels": "decades" (start, start*10, ...; default when start is given) | "paper" (1..9 per cycle))
"reverse": true flips an axis (default x left→right, y bottom→top); "lines": false keeps the axis
(labels, plot mapping) but draws none of its gridlines, e.g. vertical-only lines for a bar chart.
weights: {"major", "medium", "minor"} stroke widths (defaults 0.45/0.22/0.08 mm); border: stroke width
of the frame (default 0.6 mm, 0 = none). labels: {"sides": ["left", "bottom"], "font_size", "gap",
"color", "font_family", "bold_major", "x_title", "y_title", "title_font_size"} — placed outside the
grid, centred on their lines (measured); major labels are bold unless "bold_major": false; titles go
below / left (rotated) of the tick labels.
Result: one path per weight class in layers '<layer_prefix> minor/medium/major', labels in
'<layer_prefix> labels'. Use plot with grid=<id_prefix> to draw data on it.
Inputs: rect (required), x, y, color, +7 more.
import_fileA
Place a file into the current document. An SVG (e.g. geometry a script generated) becomes one
group scaled to this document's units, its top-left at at (default 0,0); width or height scales it
(both: stretch). Its layers become labelled groups, its defs join ours, clashing ids get "-" in front.
An image (png/jpg/gif/webp/bmp) becomes an image element: natural size at 96 dpi unless width/height
(one keeps the ratio), linked by default (embed: true stores the pixels in the SVG), object_fit
contain|cover|fill for a given box. Relative paths start at the document's folder.
For whole documents use document_open; for data rows see repeat rows_path.
Inputs: path (required), at, id, embed, +7 more.
inkscape_infoA
Inkscape version and location, server version, and open documents.
Inputs: no named parameters.
inspectA
Outline of the document: layers, groups and elements with ids, fill/stroke as rendered (style,
attributes or inherited), text, use targets (href) and real visual bounding boxes [x, y, width, height]
in user units (measured by Inkscape). Layers/groups with more than max_children children are summarised
(counts per type, first/last ids, bbox). To list one of them, pass layer (layer name or group id) and a
larger max_children.
find: a flat list of matching elements instead of the tree, e.g. {"fill": "#99cc32"} (colours compared
in any notation), {"type": "text", "text": "total"}, {"type": "use", "href": "Parking"},
{"type": "symbol"} (symbols in defs, with titles), {"id_prefix": "card-"}.
Inputs: bbox, find, layer, doc_id, +1 more.
layoutB
Arrange items in a row, column or grid with a gap — no coordinate maths needed.
An item is an id or a list of ids that move together, e.g. ["box1", "box1_label"], or
{"ids": [...], "anchor": "fig1-border"} to arrange by that member's box (plot frames line up even when
their tick labels differ in width); the rest moves along.
gap is a number or [horizontal, vertical]. align places items on the cross axis (grid: within cells).
The block stays where the first item is, or its top-left goes to at [x, y], or it is aligned to
to ('page' or an element id) using horizontal/vertical/margin. Connectors follow.
Inputs: items (required), at, to, gap, +8 more.
move_to_layerB
Move elements into a layer (by name; created on top if missing) or into a group/layer by id, at its top or bottom. They keep their relative order and stay visually where they were.
Inputs: ids (required), layer (required), doc_id, preview, +1 more.
page_fitB
Resize the page to fit the drawing (or only ids) plus margin (one number, [vertical, horizontal]
or [top, right, bottom, left], user units). All content moves together so the page keeps its 0,0
top-left; full-page background rects are resized, connectors follow.
Inputs: ids, doc_id, margin, preview.
page_resizeB
Set the page size in user units (e.g. 210 x 297 for A4 in a mm document). anchor='center' keeps the drawing centred on the new page; 'top-left'/'none' leave content where it is. Backgrounds are resized.
Inputs: width (required), height (required), anchor, doc_id, +1 more.
path_operationC
Geometry operations performed by Inkscape on the given ids. For difference, the top-most
(later in document order) object is subtracted from the bottom one. to_path converts shapes
and text into paths. union/intersection/exclusion/difference keep the BOTTOM object's id, style
and layer; combine keeps the TOP object's (E22). result lists the ids that hold the outcome.
Inputs: ids (required), operation (required), doc_id, preview.
plotA
Plot data on a grid made with the grid tool, in DATA values — no coordinate maths.
grid: that grid's id_prefix. Each series: {"points": [[x, y], ...], "line": true, "stroke": "#1f77b4",
"stroke_width", "stroke_dasharray", "marker": "circle"|"square"|"diamond"|"none", "marker_size",
"marker_fill" (default white), "point_labels": ["", "COP 3.2", ...] (one per point, null/"" to skip),
"label_offset": [dx, dy], "label_anchor": "start"|"middle"|"end", "label_halo": "#ffffff"|"none",
"label_font_size", "label_color", "font_family", "id", "layer"}.
Labels: label_offset is from the point to the label's anchor, and the label is vertically CENTRED on
point + dy (dy = 0 → centred on the point). Anchor defaults to start for dx >= 0, else end. Without
label_offset the label goes right of the point, on the side the line is not heading to. Labels carry a
halo stroke (default white) so the line can cross them — set label_halo "none" (or the background
colour) for labels on dark fills; recolouring a label later does not remove its halo (use stroke: "none").
Ids follow the series id: -line, -marker-, -label- (k = 1-based point index).
Returns per series those ids and the points in user units (for annotations); warns about points
outside the grid.
Inputs: series (required), grid, doc_id, preview.
render_previewA
Render to a PNG image you can look at (white background, longest side = max_size px).
Zoom in with region [x, y, w, h] in user units, or with ids (the area around them, everything
still visible). only_ids=true draws just those objects.
Inputs: ids, area, doc_id, region, +2 more.
repeatA
Stamp a block of elements once per data row — timelines, card grids, tables, legends, map
symbols — in one call.
template: element specs as for add_elements, drawn for the FIRST row. "{key}" in any string is
replaced from the row ("{year}"; a value that is exactly "{w}" keeps the row's number), in geometry
and style alike ("fill": "{colour}"); "{n}" is the row number (1-based) and "{i}" the index, so
rows can't use the keys n and i. Ids are local names: "card" becomes card-1, card-2, ... A
"parent" may name another template element (e.g. a group holding a card and its texts); fit_to
and clip may name template elements of the same row.
Each row goes into a group <id_prefix>- moved by n-1 steps: step [dx, dy], or a grid with
columns (step = [column pitch, row pitch]), filled row by row or, with order "column", column
by column. cell ["col", "row"]: each row is placed by its own 1-based column/row values (gaps and
fractions allowed: periodic tables, calendars, timetables, lanes); the template is drawn for cell
(1, 1) and step is the [column, row] pitch. Components (a character, a node, a symbol placed at data positions): a template group
with "transform": "translate({x},{y}) scale({s})" and step [0, 0], parts drawn around a local
origin, pose/shape parts as placeholders ("d": "{arms}").
mirror {"x": 148.5, "rows": "even"|"odd"|"all"} (or "y") mirrors those rows about the axis:
shapes are reflected (pointers flip), texts and groups keep their reading direction and move as
blocks — group a card with its texts so they cross together. Per element "mirror":
"reflect"|"block"|"none" overrides. rows_path: a .json (list of row objects) or .csv file (header
line = keys, numbers parsed) instead of rows, so data never passes through the conversation.
Returns the row groups, ids per template name (runs shortened to "card-1..card-12"), wrapped_lines, fitted.
Inputs: step (required), template (required), cell, rows, +9 more.
run_actionsA
Escape hatch: run raw Inkscape actions (e.g. "object-align:left last", "transform-rotate:30")
after selecting select ids. File, export, window and quit actions are blocked. Extensions
(org.inkscape.*) always act on the whole document, so they can't be combined with select.
Inputs: actions (required), doc_id, select, preview.
splitA
Divide a region into named cells — comic panels, dashboard tiles, poster columns, table grids —
without computing any positions. region: "page", an element id (its measured box) or [x, y, w, h];
margin insets it (one number, [vertical, horizontal] or [top, right, bottom, left]).
rows: one entry per row, either a number of equal columns or a list of column ratios, e.g.
[[2, 1], [1, 1], [1, 2]]; heights: row ratios (default equal); gutter: space between cells
(number or [horizontal, vertical]).
Creates one rect per cell, <id_prefix>-1, -2, ... row by row, in layer — invisible unless style
gives e.g. {"fill": "#fff", "stroke": "#000", "stroke_width": 0.6, "rx": 2}. Use the ids as targets:
clip ("clip": "cell-5"), align/layout ("to": "cell-2"), place, fit, connect. An element region is its
measured box (stroke included).
Tables: split the header strip into columns (e.g. rows [[3, 1, 1]]), then repeat the data rows with
step [0, row pitch] and texts at the returned column edges (numbers: text_anchor end at the right edge).
Returns the cells [x, y, w, h] and their ids per row.
Inputs: rows (required), layer, style, doc_id, +6 more.
update_elementsB
Change existing elements. Each update is {"id": ..., }; only the given keys change. Style shorthands merge into the existing style. Set transform to "" to clear it.
Inputs: updates (required), doc_id, preview.
z_orderA
Change stacking order (what is drawn on top). front/back: top/bottom within the element's own
layer or group. forward/backward: one step past the next object it overlaps (visible change).
above/below: directly above/below target, moving into target's layer/group if needed while keeping
the visual position. Several ids keep their relative order. Returns each id's position.
Inputs: ids (required), operation (required), doc_id, target, +1 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 24 tools
Each tool has a clearly distinct role in the SVG/Inkscape editing workflow. Potential overlaps such as align vs layout, z_order vs move_to_layer, and render_preview vs export are well differentiated by their descriptions and parameters.
All names are consistently snake_case and readable, with common verb_noun or resource_action patterns such as document_create, add_elements, and update_elements. However, single verbs and noun phrases like inspect, repeat, grid, and z_order mean the naming is not a strict uniform verb_noun pattern.
At 24 tools, the surface is on the heavy side but each tool corresponds to a distinct capability area in a full vector-editing server. The count is justified by the domain's breadth, though it sits near the upper end of what is comfortable.
The surface covers document lifecycle, inspection, element creation/update/deletion, data-driven repetition, import, region splitting, connectors, layout, path operations, z-order, layers, alignment, page sizing, grid/plot generation, preview, and export. The run_actions escape hatch fills remaining raw Inkscape gaps, leaving no obvious dead ends.
Maintenance
Related MCP Connectors
Lets AI assistants draw: logos, posters, illustrations and scenes, as editable SVG and PNG.
Generate and vectorize clean, editable SVG graphics from text, images, or both.
Create professional SVG artwork, icons and vector logos from a prompt, or vectorize any image.
Convert PNG, JPG and WebP images, including AI-generated ones, to clean SVG, PDF, EPS or DXF.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to control Inkscape for vector graphics editing via MCP tools.98MIT
- AlicenseAqualityDmaintenanceEnables AI agents to vectorize raster images and import vector files (PDF/AI/EPS), then inspect, edit, render, and optimize SVGs locally.6MIT
- FlicenseNot gradedqualityDmaintenanceEnables to create, compose, and export SVG documents programmatically using the svgwrite library through tool calls. Supports shapes, groups, gradients, and pattern generators.-
- AlicenseAqualityBmaintenanceEnables AI agents to inspect, edit, validate, render, and export SVG/Inkscape documents safely with reversible operations and risk-classed tools.8711MIT





