An MCP server that gives AI assistants full headless access to Ink/Stitch machine-embroidery tooling — no Inkscape installation required.
SVG designs go in; stitch-ready machine files (DST, PES, JEF, EXP, VP3, and ~17 more), PNG previews, validation reports, and digitized lettering come out.
The image above is the bridge's own output: generate_lettering("Hello", "Amitaclo") →
machine-ready DST → render_png_preview.
MCP client (Claude Code / Claude Desktop)
│ stdio (JSON-RPC)
▼
inkstitch-mcp server (FastMCP, this package)
├── native tools ──────────► pystitch + lxml (stats, PNG render, format
│ conversion, param editing, font/format lists)
└── subprocess tools ──────► .venv-inkstitch python
vendor/inkstitch/inkstitch.py --extension=…
(export, import, lettering, validation,
stitch-plan previews, transforms)
| Tool | What it does |
|---|---|
health_check |
Verify toolchain; optional end-to-end smoke export |
list_formats |
Machine formats Ink/Stitch reads (~50) and writes (~22) |
list_fonts |
140 bundled digitized lettering fonts |
list_known_params |
All legal per-element stitch parameters (~136) |
list_elements |
Inventory an SVG: ids, labels, stitch type (fill/stroke/satin), params |
get_element_params / set_element_params |
Read/write inkstitch:* stitch settings on elements |
export_embroidery |
SVG → machine file (format from output extension) + stitch stats |
import_embroidery |
Machine file → editable stitch-plan SVG |
export_zip |
One SVG → many formats in a ZIP, optional panelization |
design_stats |
Stitch/jump/trim/color counts, threads, size (mm) of a machine file |
convert_machine_format |
Machine file → machine file (e.g. DST → PES) |
render_png_preview |
PNG image of the stitches (from machine file or SVG) |
thread_list |
Plain-text thread/color report |
troubleshoot |
Structured validation: errors/warnings with positions & fixes |
density_map |
Stitch-density heat-map layer (thread-break risk) |
preview_stitch_plan |
Render the actual stitch plan into an SVG layer |
cleanup_document |
Remove problem-causing tiny objects (dry-run by default) |
transform_elements |
stroke_to_satin, auto_satin, auto_run, fill_to_stroke, jump_to_stroke, break_apart, outline |
remove_embroidery_settings |
Strip embroidery params/commands from an SVG |
generate_lettering |
Text → embroidered lettering in any bundled font |
Requires Python 3.11 (Ink/Stitch's pinned dependencies do not support newer interpreters) and git. Developed and tested on Windows 11; the code paths are OS-aware but only Windows has been exercised.
# --recursive pulls the Ink/Stitch submodule AND its fonts submodule (140 fonts)
git clone --recursive https://github.com/GreerBK/InkStitchMCP
cd InkStitchMCP
py -3.11 -m venv .venv-inkstitch
# inkex's repo has a test file exceeding Windows path limits; scope the fix to this install:
set GIT_CONFIG_COUNT=1&& set GIT_CONFIG_KEY_0=core.longpaths&& set GIT_CONFIG_VALUE_0=true
.venv-inkstitch\Scripts\python -m pip install -r requirements-inkstitch.txt
.venv-inkstitch\Scripts\python -m pip install -e .If you cloned without --recursive: git submodule update --init --recursive.
Verify everything with the health_check tool (or run the test suites below).
Paths are overridable via INKSTITCH_MCP_ROOT / INKSTITCH_MCP_REPO /
INKSTITCH_MCP_PYTHON / INKSTITCH_MCP_OUTPUT (see src/inkstitch_mcp/config.py),
plus INKSTITCH_MCP_TIMEOUT / INKSTITCH_MCP_SLOW_TIMEOUT (seconds) and
INKSTITCH_MCP_TRACE=1 for stderr tracing of subprocess calls.
Claude Code — either copy .mcp.json.example to .mcp.json (auto-loads for this
project) and fill in your path, or register globally:
claude mcp add inkstitch -- <path-to-InkStitchMCP>\.venv-inkstitch\Scripts\python.exe -m inkstitch_mcpClaude Desktop — add to %APPDATA%\Claude\claude_desktop_config.json:
{
"mcpServers": {
"inkstitch": {
"command": "<path-to-InkStitchMCP>\\.venv-inkstitch\\Scripts\\python.exe",
"args": ["-m", "inkstitch_mcp"]
}
}
}.venv-inkstitch\Scripts\python tests\e2e.py # 34 checks, every tool, real designs
.venv-inkstitch\Scripts\python tests\protocol_smoke.py # real MCP stdio round-tripThese are the traps this bridge encodes; they matter if you modify runner.py:
- Exit codes lie. Ink/Stitch exits 0 for parse errors, "no embroiderable elements", and domain errors — with the explanation on stderr and an empty payload. Success is judged per-tool from payload presence, never from the return code.
- stdout is binary. Embroidery/ZIP payloads come over stdout (the Inkscape
convention); the
outputextension switches stdout toO_BINARYon Windows. Always capture bytes; stderr is UTF-8, flushed once at process exit. stdin=DEVNULLis load-bearing. Under an MCP client, the server's stdin is the live JSON-RPC pipe; letting a child inherit it deadlocks the child interpreter at startup on Windows (diagnosed via py-spy: zero CPU, no Python frames).- Legacy SVGs summon an invisible dialog. Every extension's
load()runs the version migrator with a blocking wx prompt for unversioned files containing Ink/Stitch attributes — including--extension=update_svgitself. The preflight inrunner.pydetects the condition (same XPath aslib/update.py) and migrates via_migrate_helper.py, which calls the migrator directly with the prompt disabled. - Never expose raster preview modes.
png_realistic,png_simple, realistic-DPI stitch plans, and the ZIP--format-png-realistic/simpleflags all shell out to aninkscapebinary. PNG previews here use pystitch's native renderer instead. - The
outputextension's parser is hand-rolled. Options must be single--name=valuetokens,--formatis mandatory, values containing=crash it, and--outputis swallowed (hence stdout capture there,--output=<tmpfile>elsewhere). - Arg spelling is per-extension. Some use hyphens, some underscores, some both
(
batch_letteringmixes them in one parser). Flags are hardcoded per tool from the vendored argparsers — don't "normalize" them. --del_paramsmust always be passed toremove_embroidery_settings: its argparse default is the Python boolTruewithtype=str, which crashes when omitted.batch_letteringdumps the input SVG on validation failure with exit 0 — the ZIP magic (PK) check is what actually detects failure.- Documents parameterized by this bridge are version-stamped.
set_element_paramswrites the currentinkstitch_svg_versionmetadata; without it, the next tool call would run the v0→v4 legacy migration on freshly written modern params and silently change their meaning (e.g. solid strokes forced tozigzag_stitch). - Slow tools run off the event loop. Subprocess-backed tools are registered via
blocking_tool(async +anyio.to_thread); registering them as plain sync tools freezes all MCP traffic for the duration of an export. custom_file_nameis a path fragment inside the zip extension — it is validated against a strict character allowlist to prevent writing outside the target directory.
This bridge code: MIT (see LICENSE). Ink/Stitch is referenced as a git submodule —
not distributed in this repository — and is GPL-3.0-or-later; pystitch and other
dependencies carry their own licenses. If you distribute a bundle that includes the
vendor/inkstitch checkout, GPL terms apply to that component.
