# aseprite-mcp: driving Aseprite through 104 named MCP tools

> aseprite-mcp is a Python MCP server that hands an AI assistant 104 named tools for drawing, animating and exporting pixel art through the Aseprite API. It is built for people who already own Aseprite and want a model to produce a real sprite file, not a description of one.

**diivi/aseprite-mcp** — MCP server for interacting with the Aseprite API

- Repository: https://github.com/diivi/aseprite-mcp
- Stars: 803 · Forks: 83
- Language: Python
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/diivi-aseprite-mcp

## Aseprite MCP exposes 104 named tools instead of one generic script call

aseprite-mcp is a Python MCP server whose job is to give an AI assistant direct command over the Aseprite API. The README counts 104 tools spread across 17 categories, and the tables underneath name each one: create_canvas, draw_pixels, draw_line, draw_polygon, apply_gradient_rect, draw_text, export_spritesheet, render_onion_skin. The intended user is not someone who wants advice on pixel art. It is an engineer or technical artist who already owns Aseprite and wants a language model to produce an actual sprite file, an actual animation and an actual export. Worked examples live under examples/swordsman, where a model produced a 32x32 still and a 4 frame slash sequence, then exported the sequence as a sprite sheet. The meaningful part is not the tool count. It is that each operation is a named entry point an assistant can select on its own, rather than a single function that takes a script string.

## Text is rasterised on the server because Aseprite's Lua API cannot draw glyphs

Text is the one place where this server does not simply forward to Aseprite. The Lua API inside Aseprite has no text drawing, so glyphs are rasterised on the server and composited into the sprite afterwards. Two font backends are supported. Bitmap sprite-sheet fonts are the better fit for pixel art because the glyphs are already pixels and the size setting scales them by whole numbers. TrueType .ttf and .otf files work too, rendered without antialiasing unless you ask for it. Fonts are discovered in ~/.aseprite-mcp/fonts, where a bitmap font is a directory holding a font.json file plus its sheet PNGs, and a TrueType font is simply the font file. Installed system fonts are listed as well, and the font argument also accepts a direct path. Layout work should start with a measurement call, since it returns the same metrics the drawing call will use:
```
measure_text(text="MODERATOR", font="minecraft", size=2, bold=1)
  -> width=115 height=15 advance_width=117 above_baseline=14 below_baseline=1 left_bearing=0
```
Once a label is measured, the drawing call takes anchors, faux bold, outline, drop shadow and letter spacing. Anchor values cover the usual grid: topleft, top, topright, left, center, right, bottomleft, bottom, bottomright, plus baselineleft, baseline and baselineright when several labels must sit on one shared baseline. The shadow and outline are drawn as a stamp around the glyphs, and the documentation is explicit that they grow the stamp without moving the glyphs themselves. That matters for layouts built from pixel budgets, because a label that fits its measured width can still overflow once an outline is added. The text category is small, three tools in total: list_text_fonts to see what is available, measure_text for the metrics, and draw_text to place the result. A panel can therefore be sized and centred in one pass instead of being drawn twice to find out how wide it ended up.

## Every drawing call names a layer and frame, and the _at variants create the cel on demand

Addressing is explicit. The drawing tools come in two flavours: a plain variant and an _at variant that targets a specific layer and frame, creating the cel on demand if it does not exist yet. Coordinates are sprite-global, so the same numbers mean the same pixel regardless of which layer is active. On top of that sits a small set of canvas tools for setting state, among them set_frame, set_frame_duration, set_layer, add_layer, add_group and add_frame. The layers category handles deletion, renaming, duplication, reordering, blend modes, merging and flattening, seven tools in all. The animation category is the largest block at 24 tools, covering frames, cels, tags, tweening with easing and propagation. Durations are set per frame in milliseconds, and export happens per layer or per tag. The trade-off in this design is verbosity: a model must keep track of layer and frame identity on every call rather than relying on a single ambient cursor.

## Getting it running: Python 3.13, uv, and a three package dependency list

The packaging metadata is small and readable. The project requires Python 3.13 or newer and depends on httpx, the mcp CLI extra and pillow, with pytest kept in a separate dev group:
```toml
[project]
name = "aseprite-mcp"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
    "httpx>=0.28.1",
    "mcp[cli]>=1.6.0",
    "pillow>=10.0.0",
]
```
Two details are worth knowing before you plan an upgrade. The description field still reads "Add your description here", so the package metadata has never been filled in properly. And requirements.txt lists a wider set than the pyproject dependencies: typing_extensions, python-dotenv, pytest, black and flake8 alongside pillow, with no version ceiling on most of them. The two lists do not agree, so the two installation paths may not produce identical environments. The top level also holds .mcp.json and sample.env, the conventional pair for pointing an MCP client at a server and for supplying its environment, but the repository does not show their contents, so you will need to supply your own values for the Aseprite API connection. The README does not document a step by step installation for a specific client either.

## The Docker path uses a Homebrew base image and prepares a Steam install of Aseprite

The container route is built on a Homebrew base image rather than a slim Python image, and it installs the toolchain with brew:
```dockerfile
FROM ghcr.io/homebrew/brew:latest
RUN brew install python@3.13 uv
ENV PATH="/home/linuxbrew/.linuxbrew/bin:$PATH"
WORKDIR /app
```
The image also installs steamcmd and the minimum Ubuntu libraries, then exposes the Aseprite acquisition through environment variables rather than baking any secret into the layer. The app id points at the Steam release of Aseprite, and the install directory is set alongside it:
```dockerfile
ENV STEAM_APPID=431730 \
	STEAM_INSTALL_DIR=/opt/steamapps
```
Dependency installation happens during the build, and the container starts through a wrapper script that can install Aseprite via Steam at runtime if that is requested:
```dockerfile
RUN uv sync
ENTRYPOINT ["/bin/bash", "/app/scripts/docker-entrypoint.sh"]
CMD []
```
The compose file defines a service named aseprite-mcp that builds from the local Dockerfile, mounts the working tree at /app and restarts unless stopped. The port publishing line is present but commented out, so nothing is exposed to the host unless you enable it, and the environment file entry is commented out as well. A second service, aseprite-mcp-dev, overrides the entrypoint to keep a container alive for interactive work and sits behind a dev profile so it does not start by default:
```yaml
services:
  aseprite-mcp:
    build:
      context: .
      dockerfile: Dockerfile
    image: aseprite-mcp:latest
    container_name: aseprite-mcp-server
    restart: unless-stopped
    volumes:
      - .:/app
```

## Two routes to the same sprite: curated tools or the raw Lua escape hatch

The scripting category holds exactly one tool, described as a raw Lua escape hatch for anything the other tools do not cover. That gives you a real choice of approaches. The curated route offers named operations with documented parameters, a tool table an assistant can read, and errors that come back as structured tool failures. The Lua route hands arbitrary code to Aseprite and works for any operation the catalogue has not modelled, but the assistant has to already know Aseprite's Lua API, and any mistake surfaces as a Lua error rather than a typed result. The third option, driving Aseprite directly through its own scripting, removes the model from the loop and gives up everything this project adds: no server-side text rasterisation, no preview server, no frame diffing and no quality checks.

## The assistant can only check its own work through previews, diffs and a quality pass

Verification is a separate concern and it is where the cost of this approach shows up. The analysis category contains three tools, onion-skin renders, frame diffing and colour statistics, and the quality category adds four tools that validate and sanitise animation consistency. The preview category adds a local HTTP preview server and a workflow guide. The example workflow in the README draws, then inspects its own output with scaled export_frame previews and render_onion_skin, and only then exports. Every one of those checks is a round trip, so a model that verifies its work before shipping pays for that in latency and tokens rather than in accuracy by default. The catalogue also carries an internal inconsistency worth raising with the maintainers: the headline says 17 categories while the table lists 18 rows, and the Canvas row claims 6 tools while naming 7.

## MIT licensed, no tagged releases, and a last push on 2026-07-29

The project code is MIT licensed, which is a permissive choice with no copyleft obligations for a tool that talks to a commercial editor. The repository is not archived. It also has no GitHub releases at all, so there is no changelog to read before an upgrade and no version to pin to from a release page. The only version marker is the 0.1.0 field in pyproject.toml, and the dependency ranges carry no upper bounds, so a fresh install can pull newer httpx, mcp and pillow releases than the ones the author tested against. A uv.lock file is committed, which gives you a reproducible path if you use uv. The README does not document a rollback procedure for the server, nor how to migrate an existing .mcp.json client configuration across a tool rename.

## Conclusion

Adopt aseprite-mcp if you own Aseprite, work from a Python 3.13 environment, and want a model to drive pixel art through named tools instead of a shell script. Skip it if your pipeline only needs batch conversion, because the surface is built for interactive drawing and animation rather than bulk asset conversion. Before committing, run measure_text and draw_text against your own font files, confirm that the Aseprite API reachable from your client matches the tools the tables describe, and note that the Canvas row lists seven tools under a heading that says six. The last push was on 2026-07-29 and the repository has no GitHub releases, so pin the 0.1.0 version recorded in pyproject.toml.

## FAQ

### What is aseprite-mcp and what does it actually do?

aseprite-mcp is a Python MCP server for the Aseprite API. It exposes 104 tools across 17 categories covering canvas setup, drawing, text, layers, effects, animation, palettes, exports and analysis, so an AI assistant can operate the editor through named calls instead of a script string.

### How do I install aseprite-mcp?

The project requires Python 3.13 or newer and depends on httpx, mcp[cli] and pillow. The Dockerfile installs python@3.13 and uv through Homebrew and then runs uv sync, and the repository also ships a docker-compose.yml defining an aseprite-mcp service that mounts the working tree at /app.

### Can aseprite-mcp draw text in a sprite?

Yes, though not through Aseprite itself. Aseprite's Lua API has no text drawing, so glyphs are rasterised on the server and composited into the sprite. Bitmap sprite-sheet fonts found in ~/.aseprite-mcp/fonts and TrueType .ttf or .otf files are both supported, and measure_text returns the same metrics that draw_text will use.

### Is aseprite-mcp maintained, and are there releases to install?

The repository is not archived, but the last push was on 2026-07-29 and it has no GitHub releases. The only version marker is 0.1.0 in pyproject.toml, and the README does not document a rollback procedure or an upgrade path for an existing client configuration.

## Sources

- [diivi/aseprite-mcp on GitHub](https://github.com/diivi/aseprite-mcp)
- [Issues](https://github.com/diivi/aseprite-mcp/issues)
- [License: MIT](https://github.com/diivi/aseprite-mcp/blob/main/LICENSE)
- [README](https://github.com/diivi/aseprite-mcp/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/diivi-aseprite-mcp
