Aseprite MCP Server
<div align="center">
# ๐จ Aseprite MCP Server
**Let AI draw pixel art in Aseprite**
A Model Context Protocol (MCP) server that enables AI to create pixel art in Aseprite through pixel-level drawing primitives, read canvas screenshots, and iterate until satisfied.
[](https://www.python.org/)
[](https://github.com/jlowin/fastmcp)
[](https://aseprite.org/)
[](https://github.com/ZhangDongyang800/Aseprite_MCP/stargazers)
<p align="center">
<a href="README_CN.md">๐จ๐ณ ็ฎไฝไธญๆ</a>
</p>
</div>
<br>
## Demo
All three characters were drawn by an AI through this MCP โ every pixel written by
`apply_operations`. The structural work (adding frames, setting durations, building tags,
exporting the sheet) had to go through `run_lua` when these were made; three of those four now
have ops of their own โ `add_frames`, `set_durations`, `add_tag`.
| Character | โ Down | โ Up | โ Left | โ Right | Sprite Sheet |
|:--|:--:|:--:|:--:|:--:|:--:|
| **Chibi Knight**<br>4 dirs ร 6 frames |  |  |  |  |  |
| **Dark Reaper**<br>4 dirs ร 6 frames |  |  |  |  |  |
| **Slime Devourer**<br>4 dirs ร 5 action frames |  |  |  |  |  |
> The first two are **walk cycles** (the knight steps on anti-phase leg swings; the reaper has no
> legs, so the walk reads through a travelling wave in the robe hem plus alternating bone feet).
> The third is an **action animation**: idle โ crouch โ lunge with open maw โ chomp โ swallow,
> with per-phase frame durations.
> Each character ships a re-runnable parametric generator and a per-frame pixel check under
> `demo/<name>/generator/`.
---
> [!IMPORTANT]
> This project requires a local installation of [Aseprite](https://aseprite.org/) v1.3+. AI performs drawing via the MCP protocol by calling the Aseprite CLI + Lua scripts.
>
> Two execution modes are supported:
> - **CLI mode** (default): Each tool call spawns a headless Aseprite process (`aseprite -b`). No UI, state passed via `.ase` files.
> - **Live mode** (WebSocket): AI operates the running Aseprite instance directly through a WebSocket bridge. UI is visible, state is persistent, and you can watch AI draw in real time. See [Live Mode Setup](#-live-mode-optional-websocket) below.
---
## Table of Contents
- [ Demo](#-demo)
- [Tools](#tools)
- [ How to Use](#-how-to-use)
- [ Live Mode (Optional, WebSocket)](#-live-mode-optional-websocket)
- [ Example Prompts](#-example-prompts)
- [ Contributing](#-contributing)
- [ License](#-license)
---
## Tools
Exactly three MCP tools:
| Tool | Description |
|------|-------------|
| `apply_operations` | Execute a batch of ops inside one transaction โ the only mutation entry point. Pass `session_id` + `ops[]`; `dry_run=true` validates without side effects; `confirmed=true` is required when the batch contains a destructive op (`clear_canvas`, `close_session`). If `session_id` is omitted and the first op is `create_sprite` / `open_sprite`, a session is created automatically. |
| `inspect` | Read-only perception: returns a canvas preview image plus quantitative metrics (palette, color count, bounding box, coverage, semi-transparent and isolated pixels). On animated documents, `frame=N` steps through frames one at a time. Never modifies the document. |
| `run_lua` | Escape hatch: run arbitrary Lua. Requires `unsafe=true` and `confirmed=true`. |
Ops are named operations registered in `src/v2/ops/` (Pydantic parameter models) with their Lua implementations in `scripts/ops_*.lua`. Built-in ops: `create_sprite`, `open_sprite`, `save_sprite`, `close_session`, `draw_pixel`, `draw_rect`, `fill_region`, `clear_canvas`, `undo`, `redo`, plus the structural ones โ `add_frames`, `set_durations`, `add_tag` and `paint_grid`.
`paint_grid` takes a path to a Lua file returning `{palette = {b = "#F0A65A"}, rows = {"..bb..", ...}}`, one character per pixel, `.` for transparent. It keeps whole-sprite art out of the tool call: a 32ร32 four-frame sheet costs a few hundred tokens through `paint_grid` instead of tens of thousands as `draw_pixel` ops, and still runs validated and inside the same rollback as everything else.
```python
apply_operations(ops=[
{"op": "create_sprite", "width": 32, "height": 32},
{"op": "draw_rect", "x": 4, "y": 4, "width": 24, "height": 24, "color": "#E74C3C", "filled": True},
{"op": "draw_pixel", "x": 16, "y": 6, "color": "#FFFFFF"},
])
```
All drawing ops accept `layer` / `frame` (1-based, default 1/1). A batch runs inside one `app.transaction`, so `atomic=true` (the default) rolls the whole batch back if any op fails.
> [!TIP]
> `inspect` is the core of the workflow: after drawing, AI calls it to "see" the canvas, analyze it, and decide whether to fix it, forming a **draw โ inspect โ analyze โ fix** loop.
---
## How to Use
### 1. Prerequisites
| Dependency | Version | Notes |
|------------|---------|-------|
| [uv](https://docs.astral.sh/uv/) | any recent | manages Python and the dependencies for you |
| Aseprite | v1.3+ | note the full path to the **executable**, not the folder holding it |
Install uv with `winget install astral-sh.uv` (Windows), `brew install uv` (macOS), or `curl -LsSf https://astral.sh/uv/install.sh | sh`.
### 2. Clone
```bash
git clone https://github.com/ZhangDongyang800/Aseprite_MCP.git
```
There is no install step: the `uv run` command in step 3 provisions an isolated environment from `pyproject.toml` on first launch, with `fastmcp` / `pillow` / `websockets`.
### 3. Client Configuration
Replace `C:\path\to\Aseprite_MCP` with where you cloned this repo, and the two `env` paths with your own.
**JSON config** (TRAE, Claude Desktop, Cursor, Qoder, โฆ):
```json
{
"mcpServers": {
"aseprite": {
"command": "uv",
"args": ["run", "--directory", "C:\\path\\to\\Aseprite_MCP", "server.py"],
"env": {
"ASEPRITE_PATH": "C:\\Program Files\\Aseprite\\aseprite.exe",
"ASEPRITE_WORK_DIR": "C:\\ase_work"
}
}
}
}
```
If the client cannot find `uv`, give `command` the absolute path to `uv`. Never fall back to a bare `python` โ Windows shadows it with a Store alias โ or to `pip install --user`, because hosts strip `APPDATA` and Python then cannot find the packages. To skip uv entirely, install into a virtualenv and point `command` at that interpreter:
```bash
python -m venv .venv # Windows, if `python` is missing: py -3 -m venv .venv
.venv/Scripts/python.exe -m pip install -e . # Windows
.venv/bin/python -m pip install -e . # macOS / Linux
```
---
## ๐ฅ Live Mode (Optional, WebSocket)
Live mode lets AI operate your **running Aseprite instance** directly โ you can watch every stroke happen in real time on your screen, and the sprite state persists across tool calls (no repeated file open/save overhead).
### How It Works
```
โโโโโโโโโโโ MCP (stdio) โโโโโโโโโโโโโโโโ WebSocket โโโโโโโโโโโโโโโโโโโโ
โ AI/TRAE โ โโโโโโโโโโโโโโโโบ โ Python MCP โ โโโโโโโโโโโโโโบ โ Aseprite Extensionโ
โ โ โโโโโโโโโโโโโโโโ โ Server โ โโโโโโโโโโโโโ โ (WebSocket client) โ
โโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโฌโโโโโโโโโโโโ
โ Lua app.* API
โผ
โโโโโโโโโโโโโโโโ
โ Visible Aseprite โ
โ Sprite + UI โ
โโโโโโโโโโโโโโโโ
```
The Python MCP server starts a WebSocket server on `127.0.0.1:9001`. The Aseprite extension connects to it as a client. Each MCP tool call is forwarded to Aseprite over WebSocket, executed via the existing Lua scripts, and the result is sent back.
### Setup
**1. Install the Aseprite extension**
The extension is in the `extension/` folder of this repo. Install it via:
- Open Aseprite โ `File > Scripts > Open Scripts Folder`
- Copy the entire `extension/` folder contents into the scripts folder (or use `Edit > Preferences > Extensions > Add Extension` and select the `extension/` folder)
**2. Enable WebSocket mode in MCP config**
Add `ASEPRITE_MCP_MODE=ws` to the `env` section of your MCP server config:
```json
{
"mcpServers": {
"aseprite": {
"command": "uv",
"args": ["run", "--directory", "C:\\path\\to\\Aseprite_MCP", "server.py"],
"env": {
"ASEPRITE_PATH": "C:\\Program Files\\Aseprite\\aseprite.exe",
"ASEPRITE_WORK_DIR": "C:\\ase_work",
"ASEPRITE_MCP_MODE": "ws",
"ASEPRITE_WS_HOST": "127.0.0.1",
"ASEPRITE_WS_PORT": "9001"
}
}
}
}
```
**3. Connect Aseprite**
With the MCP server running, open Aseprite and click:
`File > Scripts > MCP Bridge: Toggle Connection`
You should see an alert: "MCP Bridge: Connected to ws://127.0.0.1:9001".
Now AI can operate Aseprite directly โ create a sprite, draw pixels, and you'll see it happen live.
### Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `ASEPRITE_PATH` | auto-detected | Path to the Aseprite executable (overrides auto-detection via `PATH` and common install locations) |
| `ASEPRITE_WORK_DIR` | `<repo>/work` | The server's state directory: sessions live in `<work>/sessions/<uuid>/` and expire after `ASEPRITE_SESSION_TIMEOUT`. Absolute by default, and it must stay ASCII-only โ Aseprite rejects non-ASCII script paths and blames the Lua engine |
| `ASEPRITE_SESSION_TIMEOUT` | `3600` | Seconds an idle session survives before the cleanup thread removes it |
| `ASEPRITE_MCP_MODE` | `cli` | Execution mode: `cli` or `ws` |
| `ASEPRITE_WS_HOST` | `127.0.0.1` | WebSocket server bind address |
| `ASEPRITE_WS_PORT` | `9001` | WebSocket server port |
---
## Example Prompts
The prompt behind each row of the table at the top.
**Chibi Knight ยท 4 directions ร 6 frames ยท 32x32**
> Use Aseprite MCP to generate a pixel art sprite sheet of a brave knight in silver armor holding a long sword, red plume and red cape. Four-direction walk cycle (down, up, left, right), 6 frames per direction, 32x32, flat colors, transparent background, 1px dark outline, light from the top-left.
---
**Dark Reaper ยท 4 directions ร 6 frames ยท 32x32**
> Use Aseprite MCP to generate a pixel art sprite sheet of a dark reaper in a tattered black robe wielding a giant scythe, glowing red eyes under the hood. Four-direction walk cycle (down, up, left, right), 6 frames per direction, 32x32, flat colors, transparent background, 1px dark outline.
---
**Slime Devourer ยท 4 directions ร 5 frames ยท 32x32**
> Use Aseprite MCP to generate a pixel art sprite sheet of a slime monster that devours its prey โ green blob body, huge jaws, fangs. Four-direction devour animation (down, up, left, right), 5 frames per direction: idle โ crouch โ lunge with open maw โ chomp โ swallow. 32x32, flat colors, transparent background, sprite sheet layout.
---
## ๐ค Contributing
Issues and Pull Requests are welcome!
I've tried it, but I can't guarantee it works perfectly. It still needs more optimization.
---
## License
This project is open-sourced under the [MIT License](LICENSE).
Copyright ยฉ 2026 [ZhangDongyang800](https://github.com/ZhangDongyang800)
<div align="center">
<sub>Built with โค๏ธ for pixel art lovers</sub>
</div>
TDQS
Scored across 3 tools
The three tools have clearly distinct purposes: apply_operations is the sole programmatic mutation entry point, run_lua is an escape hatch for arbitrary scripts, and inspect is strictly read-only. No two tools overlap in functionality, making selection unambiguous for an agent.
All names follow a consistent lowercase snake_case pattern starting with a verb: apply_operations, run_lua, inspect. The convention is uniform and predictable, with no mixed styles or ambiguous verbs.
With only 3 tools, the surface is minimal but perfectly aligned with the server's purpose. The heavy lifting is encapsulated inside operation types for apply_operations, so the small count is intentional and well-scoped rather than sparse.
The tool set covers the full lifecycle: mutate (apply_operations), arbitary scripting (run_lua), and read/analyze state (inspect). Supporting operations like create_sprite and open_sprite are exposed as ops within apply_operations, so no critical gap exists for typical Aseprite workflows.