libresprite-mcp
# libresprite-mcp
An MCP server that drives [LibreSprite](https://libresprite.github.io/) headlessly, so an AI
agent can generate and edit pixel art via tool-calling instead of a human using the GUI.
This exists because every "Aseprite MCP" server on GitHub generates **Lua** and shells out to
real Aseprite -- LibreSprite forked before Aseprite switched to Lua and has its own JavaScript
scripting API instead, so none of those servers work against it. This one is written directly
against LibreSprite's JS API and CLI batch flags.
This is a personal project, scaffolded by Claude Code and implemented by Codex against the
task backlog tracked in the repository's GitHub issues. The repository is public, and the
testing notes are in [docs/testing/README.md](docs/testing/README.md). Local development
notes about LibreSprite's scripting API are kept in `docs/Design-Notes/`.
The current headless API direction is documented in
[docs/testing/Headless-API-Improvement-Plan.md](docs/testing/Headless-API-Improvement-Plan.md).
## Related LibreSprite fork
The MCP server and tooling live in this repository. Development patches for the headless
LibreSprite scripting/API gaps live in the public
[vchopDev/LibreSprite fork](https://github.com/vchopDev/LibreSprite), which is based on
[LibreSprite/LibreSprite](https://github.com/LibreSprite/LibreSprite). The released
LibreSprite builds do not yet include those patches; use the fork when testing layer/frame
creation or frame-tag support, and use a released binary for the currently supported API.
## Requirements
- Python 3.10+
- A [LibreSprite](https://github.com/LibreSprite/LibreSprite) binary on `PATH`, or point
`LIBRESPRITE_BIN` at one. CI downloads the latest release's Linux AppImage; there's no
binary bundled with this repo.
## Usage
```
pip install -e .
LIBRESPRITE_BIN=/path/to/libresprite libresprite-mcp
```
Point an MCP client (Claude Code, Codex, etc.) at the `libresprite-mcp` command over stdio. See
your client's MCP server configuration docs for how to register a local command-based server.
## Project layout
```
src/libresprite_mcp/
client.py Subprocess wrapper around `libresprite -b --script <file>`
seed.py Blank PNG generation (stdlib-only, no image library)
tools.py Sprite operations -- pure functions, one JS script per call
server.py FastMCP wiring: exposes tools.py functions as MCP tools
tests/
test_seed.py Unit tests, no LibreSprite binary needed
integration/ Requires LIBRESPRITE_BIN; skipped otherwise
```
## Architecture in one paragraph
Every LibreSprite invocation is a fresh subprocess -- there's no persistent "active document"
across tool calls, so every tool is path-in/path-out: open a file, mutate it, save it, return
the path. A sprite is a file on disk, not a session handle. LibreSprite has no headless "new
sprite" command (like every `app.command.*` call, `NewFile` is a no-op in batch mode -- see
Known limits), so `create_sprite` writes a minimal blank PNG itself and hands it to
`app.open()`, which does work headlessly.
## Known limits
With an unpatched released LibreSprite binary, the scripting API runs against a `UIContext`
that batch mode never marks as having
an "active document." Anything routed through the command system (`app.command.*`) is
therefore disabled headlessly, confirmed empirically for `NewLayer`, `NewFrame`, and
`CanvasSize`-via-command. This blocks, in this version:
- **Creating new layers or frames on an existing sprite.** Only pre-authored multi-frame/
multi-layer files (built once in the GUI) can be edited per-layer/per-frame; new ones can't
be added from a script.
- **Frame tags** (naming an animation range like "walk" or "idle"): no scripting binding exists
for these at all, independent of the command-system issue above.
- **Palette read/write** is unsafe in the installed Windows 1.1-dev binary: reading
`doc.sprite.palette` causes a native access violation in a fresh headless process. The
upstream `Palette` methods are usable only after `loadPalette()` in that same process, which
cannot safely recover an existing sprite palette across this repo's path-in/path-out calls.
Mutations that go through LibreSprite's `Transaction` API directly instead of the command
system (`sprite.resize()`, the `sprite.width`/`height` setters) are unaffected and confirmed
working headlessly. The corresponding development patches are tracked in the related fork;
they are not part of the released LibreSprite binaries yet.
## Animating with templates
Headless scripts cannot create new frames, but they can edit frames that already exist in a
hand-authored `.ase` template. Create the animation in LibreSprite's GUI with one explicit,
unlinked cel per frame, save the template, then use `set_pixel`/`set_pixels_bulk` with the
desired `frame` index. The per-frame cel images remain independent across save and reopen;
linked or extended cels are not a supported template layout.
## Status
Early scaffold. `create_sprite`, `resize_canvas`, `export_png`, and `get_png_data` are
implemented and covered by integration tests. `get_pixel`/`set_pixel` are stubbed
(`NotImplementedError`) pending a spike into LibreSprite's `pixelColor` packing API -- see the
open issues on this repo for the current task backlog.
## License
This project is released under the [Zero-Clause BSD (0BSD)](LICENSE) license. It permits
use, modification, redistribution, and commercial use without an attribution requirement,
and is provided without warranty.
TDQS
Scored across 7 tools
Most tools target distinct actions: create, resize, export, pixel read/write. The main overlap risk is between export_png and get_png_data, since both produce PNG output, though one writes a file and the other returns base64 data.
All tool names follow a consistent snake_case verb_noun pattern: create_sprite, resize_canvas, export_png, get_png_data, get_pixel, set_pixel, set_pixels_bulk. Verbs are clear and predictable.
Seven tools is well-scoped for a sprite manipulation server. Each tool covers a core operation without excessive overlap or unnecessary additions.
The set covers creation, resizing, export, and pixel access, but lacks fundamental sprite editor operations: loading existing sprites, saving/in-place writes, layer/frame enumeration, and canvas metadata. Agents cannot meaningfully inspect or manipulate a sprite's structure beyond individual pixels.