Skip to main content
Glama
GreerBK

Ink/Stitch MCP Bridge

by GreerBK
README.md
# Ink/Stitch MCP Bridge

An [MCP](https://modelcontextprotocol.io) server that gives AI assistants full headless access to
[Ink/Stitch](https://github.com/inkstitch/inkstitch) 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.

!["Hello" digitized in the Amitaclo satin font by this bridge — 1,882 stitches, 69×28mm](docs/hello_preview.png)

*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)
```

## Tools (21)

| 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 |

## Setup

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.

```bash
# --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.

## Registering with a client

**Claude Code** — either copy `.mcp.json.example` to `.mcp.json` (auto-loads for this
project) and fill in your path, or register globally:

```bash
claude mcp add inkstitch -- <path-to-InkStitchMCP>\.venv-inkstitch\Scripts\python.exe -m inkstitch_mcp
```

**Claude Desktop** — add to `%APPDATA%\Claude\claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "inkstitch": {
      "command": "<path-to-InkStitchMCP>\\.venv-inkstitch\\Scripts\\python.exe",
      "args": ["-m", "inkstitch_mcp"]
    }
  }
}
```

## Tests

```bash
.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-trip
```

## Design notes (hard-won)

These 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 `output` extension switches stdout to `O_BINARY` on Windows. Always
  capture bytes; stderr is UTF-8, flushed once at process exit.
- **`stdin=DEVNULL` is 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_svg` itself. The preflight in
  `runner.py` detects the condition (same XPath as `lib/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/simple` flags all shell out to an
  `inkscape` binary. PNG previews here use pystitch's native renderer instead.
- **The `output` extension's parser is hand-rolled.** Options must be single
  `--name=value` tokens, `--format` is mandatory, values containing `=` crash it, and
  `--output` is swallowed (hence stdout capture there, `--output=<tmpfile>` elsewhere).
- **Arg spelling is per-extension.** Some use hyphens, some underscores, some both
  (`batch_lettering` mixes them in one parser). Flags are hardcoded per tool from the
  vendored argparsers — don't "normalize" them.
- **`--del_params` must always be passed** to `remove_embroidery_settings`: its
  argparse default is the Python bool `True` with `type=str`, which crashes when omitted.
- **`batch_lettering` dumps 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_params`
  writes the current `inkstitch_svg_version` metadata; 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 to `zigzag_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_name` is a path fragment inside the zip extension** — it is
  validated against a strict character allowlist to prevent writing outside the
  target directory.

## License

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.

TDQS

A3.6/5.0

Scored across 21 tools

Disambiguation5/5

Each tool targets a distinct operation: import, list resources, health check, export, transform, validate, generate lettering, etc. No two tools have overlapping purposes; even similar tools like preview_stitch_plan and render_png_preview are clearly differentiated by output (SVG layer vs PNG image).

Naming Consistency5/5

All tool names follow a consistent snake_case verb_noun or verb_noun_noun pattern, with clear action and target. Examples: import_embroidery, list_fonts, set_element_params, etc. No mix of conventions.

Tool Count4/5

With 21 tools, the server covers a broad range of embroidery design workflows. While slightly above the typical 3-15 range, each tool serves a distinct purpose and the count feels justified for the domain.

Completeness5/5

The tool set covers the full lifecycle of embroidery design: importing, inspecting, modifying, generating, validating, previewing, exporting, and housekeeping. Obvious gaps like creating elements from scratch are handled by the SVG-first workflow.

Maintenance

ActivitySlowing
ResponsivenessNo issues