Skip to main content
Glama
ZhangDongyang800

Aseprite MCP Server

README.md
<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.

[![Python](https://img.shields.io/badge/Python-3.10%2B-3776AB?style=flat-square&logo=python&logoColor=white)](https://www.python.org/)
[![FastMCP](https://img.shields.io/badge/FastMCP-4.x-FF6B35?style=flat-square)](https://github.com/jlowin/fastmcp)
[![Aseprite](https://img.shields.io/badge/Aseprite-v1.3%2B-7D9F37?style=flat-square)](https://aseprite.org/)
[![Stars](https://img.shields.io/github/stars/ZhangDongyang800/Aseprite_MCP?style=flat-square&logo=github&color=yellow)](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 | ![](demo/Knightling/knight_walk_down.gif) | ![](demo/Knightling/knight_walk_up.gif) | ![](demo/Knightling/knight_walk_left.gif) | ![](demo/Knightling/knight_walk_right.gif) | ![](demo/Knightling/chibi_knight_spritesheet.png) |
| **Dark Reaper**<br>4 dirs ร— 6 frames | ![](demo/dark_reaper/dark_reaper_walk_down.gif) | ![](demo/dark_reaper/dark_reaper_walk_up.gif) | ![](demo/dark_reaper/dark_reaper_walk_left.gif) | ![](demo/dark_reaper/dark_reaper_walk_right.gif) | ![](demo/dark_reaper/dark_reaper_spritesheet.png) |
| **Slime Devourer**<br>4 dirs ร— 5 action frames | ![](demo/slime_devourer/slime_devourer_devour_down.gif) | ![](demo/slime_devourer/slime_devourer_devour_up.gif) | ![](demo/slime_devourer/slime_devourer_devour_left.gif) | ![](demo/slime_devourer/slime_devourer_devour_right.gif) | ![](demo/slime_devourer/slime_devourer_spritesheet.png) |

> 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

A4.3/5.0

Scored across 3 tools

Disambiguation5/5

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.

Naming Consistency5/5

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.

Tool Count5/5

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.

Completeness5/5

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.

Maintenance

ActivityActive
ResponsivenessNo issues