mnehmos.aseprite.mcp
by Mnehmos
README.md
# mnehmos.aseprite.mcp
MCP server exposing **headless Aseprite** as five consolidated tools, so AI
agents can create, inspect, edit and export pixel art — sprites, animations,
spritesheets — and convert arbitrary images (e.g. ComfyUI renders) into
indexed pixel art.
Aseprite runs fully batch (`aseprite -b --script <lua>`); no GUI ever opens.
State lives in `.aseprite` project files on disk plus a SQLite audit log —
every tool call is one validated headless run that reports the files actually
written.
## Tools
| Tool | Actions | Purpose |
|---|---|---|
| `aseprite_sprite` | new, list, info, palette, frames, layers | Project lifecycle + inspection (incl. flattened pixel grids, capped at 128x128) |
| `aseprite_draw` | map, pixels, shape, clear | Drawing — `map` is the ASCII pixel-map authoring path |
| `aseprite_export` | png, gif, sheet | Flattened per-frame PNGs, animated GIF, spritesheets (integer upscaling) |
| `aseprite_pixelate` | convert | Image → indexed pixel art (downscale + color quantization) |
| `aseprite_script` | run | Raw Lua escape hatch (full [Aseprite scripting API](https://github.com/aseprite/api)) |
### Safety model
- **Serialized projects**: calls that touch the same `.aseprite` run one at a
time (per-project mutex) — parallel tool calls can't interleave read-modify-
-write cycles and lose pixels.
- **Backups**: every mutating action snapshots the project to
`<file>.aseprite.bak` first; the response reports the `backup` path. One
slot, newest-wins — recovery from a bad draw is always one rename away.
- **Fresh provenance**: exports stat the output directory and only report
files written by that run.
- **`aseprite_script` is the trust boundary**: it runs arbitrary Lua with the
full scripting API and no path sandboxing — that's what makes it the escape
hatch. Run this server only where its Lua privileges are acceptable.
### The ASCII pixel-map (why `map` exists)
Language models draw best when they can *read* the art. A map is a palette
header plus one character per pixel:
```
PAL:
K #1a1c2c
Y #ffcd75
MAP:
..KKKK..
.KYYYYK.
.KYYYYK.
..KKKK..
```
`.` is always transparent; multiple `MAP:` sections become successive
animation frames (draw only what changes — new frames copy the previous
cel). The server validates the map (row widths, declared characters, hex
format) before Aseprite ever runs.
## Layout (canonical, env-overridable)
- Binary: `F:/ComfyUI/tools/aseprite/aseprite.exe` (v1.3.18.3, self-compiled)
- Projects: `F:/ComfyUI/aseprite_output/projects/*.aseprite`
- Exports: `F:/ComfyUI/aseprite_output/` (PNGs here load straight into
ComfyUI `LoadImage` nodes)
- Generated scripts: `F:/ComfyUI/aseprite_output/mcp-scripts/` (kept for
debugging)
- Audit/provenance DB: `data/aseprite-mcp.db`
## Setup
```
npm install
npm run build
npm test # no live Aseprite needed (stubbed runner)
npm run smoke # end-to-end against the real binary
```
Register (ZCode `cli/config.json` → `mcp.servers`):
```json
"aseprite": {
"command": "<node>",
"args": ["F:\\Github\\mnehmos.aseprite.mcp\\dist\\index.js"],
"env": { "ASEPRITE_BIN": "F:\\ComfyUI\\tools\\aseprite\\aseprite.exe" },
"enabled": true
}
```
## Environment
| Var | Default |
|---|---|
| `ASEPRITE_BIN` | `F:/ComfyUI/tools/aseprite/aseprite.exe` |
| `ASEPRITE_PROJECTS_DIR` | `F:/ComfyUI/aseprite_output/projects` |
| `ASEPRITE_EXPORT_DIR` | `F:/ComfyUI/aseprite_output` |
| `ASEPRITE_SCRIPTS_DIR` | `F:/ComfyUI/aseprite_output/mcp-scripts` |
| `ASEPRITE_TIMEOUT_MS` | `60000` |
| `ASEPRITE_MCP_DB` | `<repo>/data/aseprite-mcp.db` |
## Design notes
- **Stateless runs, durable state.** Each call loads the `.aseprite` fresh,
mutates in memory, saves explicitly. Destructive Aseprite commands are safe
on the in-memory copy (e.g. `info` flattens a throwaway copy to read pixels).
- **Report reality.** Export actions stat the files on disk after the run and
return those paths/sizes — Aseprite occasionally renames sequence outputs.
- **API choices validated against the binary**, not guessed:
`Frame.duration` (not `frameDuration`), `Color{index=}`,
`ColorQuantization{maxColors}`, `json.encode` marker payloads, and
flatten-to-new-sprite for deterministic single-frame PNGs (bare
`saveAs('*.png')` writes numbered sequences on multi-frame sprites).
Validation scripts live in `F:/ComfyUI/aseprite_output/scripts/`.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues