Aseprite MCP
Provides tools for creating and editing Aseprite sprites, including drawing, animation, layers, frames, tags, palettes, effects, text, tilemaps, and exporting to PNG/GIF/sprite sheets.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Aseprite MCPCreate a 32x32 pixel art of a red apple and export as PNG."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Aseprite MCP
An extensive Model Context Protocol server that lets an AI agent (Claude Code, Claude Desktop, or any MCP client) create and edit Aseprite sprites β draw pixel art, build animations, manage layers/frames/tags/palettes, and export to PNG/GIF/sprite sheets β all headlessly.
π Full tool reference: docs/TOOLS.md β every tool with its parameters.
It works by generating Lua scripts and running them through Aseprite's batch mode
(aseprite -b --script ...), plus the Aseprite CLI for exports. Every operation opens a
real .aseprite file, edits it, and saves β so your files stay fully editable in the
Aseprite GUI.
108 tools β including high-level workflow tools that scaffold and validate whole assets in one call, and a batch op-runner that applies many edits atomically in a single Aseprite process β across sprites, layers, frames, cels, drawing (incl. pixel-perfect & anti-aliased modes), custom brushes & symmetry, palettes (extract/sort/ramps), animation tags, slices/9-patch, effects (gradients/outline/drop-shadow/colour adjustments), text rendering, tilemaps, image stamping, reference/rotoscope layers, transforms, rich export (per-layer/per-tag, sprite sheets, onion-skin), a GUI companion view, and a
health_checkself-test.Sandboxed file access β by default the file capability is scoped to the workspace (relative paths only; absolute/
..paths rejected unless you opt in).No-clobber by default β output-writing tools refuse to overwrite an existing file; pass
overwrite=Trueto replace it intentionally.Structured results β every tool returns JSON describing the updated sprite.
render_previewreturns a PNG so the agent can see its work and self-correct.Deterministic, stateless, robust β each call is an isolated, headless Aseprite run.
Showcase
Everything below was produced entirely through MCP tool calls β no manual pixel-pushing.
π’ Easy β one call scaffolds usable pixel art
π‘ Medium β animation & tag generation
π΄ Hard β the full pipeline: create β animate β validate β export
validate_sprite_for_game_export β passed β
(width, height, color_mode, min_frames, required_tags)
export_game_asset_bundle β hero.png Β· hero.gif Β· hero_sheet.png (+JSON) Β· hero_idle.gif Β· manifest.jsonRelated MCP server: aseprite-mcp
Requirements
Aseprite 1.3+ (the scripting API). The Steam and standalone builds both work.
Python 3.10+
uv (recommended) β or any PEP 517 installer.
Install
git clone https://github.com/MalloyTheDev/aseprite-mcp.git
cd aseprite-mcp
uv syncThat creates a virtual environment and installs the aseprite-mcp package and its
dependencies. Verify it can find Aseprite and run the test suite (tests auto-skip if
Aseprite isn't found):
uv run pytestConfiguration
Everything is configurable via environment variables (all optional):
Variable | Purpose | Default |
| Full path to | Auto-detected (Steam, standalone, PATH). |
| Folder where relative sprite paths are resolved. |
|
| Per-operation timeout in seconds. |
|
| Allow absolute / workspace-escaping paths ( | off (sandboxed) |
On this machine Aseprite was detected at
C:\Program Files (x86)\Steam\steamapps\common\Aseprite\Aseprite.exe, so ASEPRITE_PATH
is not strictly required β but setting it explicitly is the most reliable.
Register with an MCP client
Replace /ABSOLUTE/PATH/TO/aseprite-mcp below with the absolute path to your clone.
Claude Code (CLI)
claude mcp add aseprite -- uv --directory /ABSOLUTE/PATH/TO/aseprite-mcp run aseprite-mcpTo set the Aseprite path explicitly (recommended if auto-detection fails):
claude mcp add aseprite \
--env ASEPRITE_PATH="/path/to/Aseprite.exe" \
-- uv --directory /ABSOLUTE/PATH/TO/aseprite-mcp run aseprite-mcpClaude Desktop / generic MCP client (JSON)
Add this to your client's MCP server config (e.g. claude_desktop_config.json). A
ready-to-copy template lives in mcp-config.example.json
(Windows-style paths shown β adjust for your OS):
{
"mcpServers": {
"aseprite": {
"command": "uv",
"args": ["--directory", "C:\\path\\to\\aseprite-mcp", "run", "aseprite-mcp"],
"env": {
"ASEPRITE_PATH": "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Aseprite\\Aseprite.exe",
"ASEPRITE_MCP_WORKSPACE": "C:\\path\\to\\aseprite-mcp\\workspace"
}
}
}
}Restart the client; the aseprite server and its 108 tools will be available. Ask the
agent to run health_check to confirm Aseprite is wired up correctly.
High-level workflows
Beyond the low-level tools, a few workflow tools scaffold a whole asset in one call and return a manifest (created files, paths, frames, tags, dimensions, and suggested next steps) so an agent can keep going. They compose the low-level tools β deterministic scaffolding, no AI generation.
Tool | Description |
| Transparent canvas + body/details layers + a generated shading ramp + an outlined placeholder. |
| Turn a 1-frame sprite into a 4-frame idle "bob" loop with a tag. |
| Canvas + tilemap layer + a starter tileset (grass/dirt/water/stone, or your own). |
| Grid sheet of icon cells, each a placeholder inside a named slice ( |
| Grid sheet with a named slice per item (sword/shield/potion/β¦). |
| 8-direction walk template β frames + one tag per direction (N/NE/E/β¦). |
| PNG + animated GIF + sprite sheet (+ JSON) + per-tag GIFs + |
| Godot 4 |
| Engine-agnostic |
| Check a sprite is game-ready (dimensions/tile multiple, colour mode, frames, required tags, transparency, palette budget, exports exist) β pass/fail report. |
| Describe an asset once ( |
"Make me an idle-animated hero and a game-ready bundle."
create_character_sprite("hero", 32, 32, base_color="#3878c8")β layers + palette + placeholder.Draw the character on the
body/detailslayers (low-level tools).make_4_frame_idle_animation("hero.aseprite")β 4-frame loop taggedidle.validate_sprite_for_game_export("hero.aseprite", expected_width=32, required_tags=["idle"])β confirm it's game-ready.export_game_asset_bundle("hero.aseprite", scale=8)β PNG/GIF/sheet+JSON/manifest inhero_bundle/.
Workflow manifest contract
Every workflow tool returns a standardized workflow_manifest.v1 object (defined in
core/manifest.py) so the asset layer stays
consistent as it grows. Always present: ok, schema_version, kind, created_files[],
suggested_next_actions[], warnings[]. Included when relevant: sprite{}, exports[],
palette{}, animation{}, tilemap{}. File/export entries are
{role, path, format, metadata_path?}.
{
"ok": true,
"schema_version": "workflow_manifest.v1",
"kind": "character_sprite",
"sprite": { "path": "...", "width": 32, "height": 32, "color_mode": "rgb",
"frames": 1, "layers": ["body", "details"], "tags": [] },
"created_files": [ { "role": "source_sprite", "path": "...", "format": "aseprite" } ],
"palette": { "colors": ["#1b1f2a", "..."], "count": 5 },
"suggested_next_actions": ["Draw the character on the 'body' layer", "..."],
"warnings": []
}Batch operations
apply_operations applies a list of edits to one sprite in a single Aseprite process,
atomically β open once β run every op inside one transaction β save only if all
succeed. This collapses multi-launch agent flows (add layer β draw β add frame β tag)
into one fast, all-or-nothing call. Pass dry_run=true to validate the op list without
launching Aseprite.
apply_operations("hero.aseprite", [
{"op": "add_layer", "args": {"name": "fg"}},
{"op": "fill_layer", "args": {"layer": "fg", "color": "#1d2b53"}},
{"op": "draw_rectangle", "args": {"layer": "fg", "x": 2, "y": 2, "width": 12, "height": 8, "color": "#ff004d"}},
{"op": "add_frame", "args": {"copy_from": 1, "duration_ms": 120}},
{"op": "add_tag", "args": {"name": "idle", "from": 1, "to": 2}}
])If any op fails, the whole batch rolls back and the error names the failing op index. v1 ops: layer add/rename/visible/opacity/remove Β· frame add/duplicate/duration Β· tag add/remove Β· draw set_pixel/line/rectangle/fill_rectangle/ellipse/fill_ellipse/fill_layer/clear_layer Β· slice add/remove Β· replace_color.
Tool catalogue
Relative filenames resolve inside the workspace (absolute paths require
ASEPRITE_MCP_ALLOW_ABSOLUTE=1 β see Security).
Frames and palette-aware operations are 1-based for frames, 0-based for palette
indices. Colours accept #RRGGBB, #RRGGBBAA, r,g,b, r,g,b,a, index:N, or a name
(black, white, red, green, blue, yellow, cyan, magenta, transparent, β¦).
Sprite lifecycle
Tool | Description |
| Create & save a new sprite ( |
| Save a copy under a new path (optionally flattened). |
| Convert between |
| Change canvas size without scaling art ( |
| Crop the canvas to a rectangle. |
| Scale the whole sprite (by factor or to dimensions; |
| Flatten all layers into one. |
| Auto-crop the canvas to non-transparent content. |
| Toggle the opaque Background layer. |
Inspection & preview
Tool | Description |
| Full structured state: size, mode, frames, layer tree, tags, palette. |
| Render a frame to a PNG image you can view (scaled). |
| Read composited pixel colours of a region (β€ 64Γ64 per call). |
| List sprite/image files in the workspace. |
Layers
Tool | Description |
| Add a layer (optional group, opacity, blend mode, visibility). |
| Add a group layer. |
| Delete / rename a layer. |
| Update opacity, blend mode, visibility, editability, name. |
| Reorder a layer (1-based stack index, 1 = bottom). |
| Duplicate / merge a layer down. |
Frames (animation)
Tool | Description |
| Append a frame (empty, or a copy of another). |
| Duplicate / delete a frame. |
| Set per-frame / uniform durations (ms). |
Cels (a layer's image at a frame)
Tool | Description |
| Inspect a cel (exists, position, bounds, opacity). |
| Move / fade a cel. |
| Copy a cel between frames / delete it. |
Animation tags
Tool | Description |
| Tag a frame range with a name, direction, colour. |
| Edit / delete a tag. |
Drawing
Tool | Description |
| Plot individual pixels (per-pixel or shared colour). |
| Line / connected segments; |
| Quadratic BΓ©zier curve. |
| Outline or filled rectangle / ellipse; ellipse has |
| Flood fill (paint bucket) from a point. |
| Fill the whole cel / erase it to transparent. |
Brushes & symmetry
Tool | Description |
| Stamp a custom brush shape (ASCII mask) at many points. |
| Tile an image/sprite across a region (with spacing, opacity, blend). |
| Reflect one half of a layer onto the other (build symmetric art). |
| Plot pixels with horizontal/vertical/4-way mirroring. |
Slices (named regions / 9-patch)
Tool | Description |
| Manage slices with optional 9-patch center, pivot, colour, and data. |
Effects & colour adjustments
Tool | Description |
| Linear/radial gradient, multi-stop, optional Bayer dithering. |
| Two-colour checkerboard pattern. |
| Pixel outline around art (outside/inside, 4/8-connectivity, thickness). |
| Hard drop shadow on a new layer beneath the art. |
| Swap a colour (with per-channel tolerance). |
| Invert RGB (alpha preserved). |
| Colour grading. |
Text
Tool | Description |
| Render text (built-in bitmap font or a TrueType file) as crisp pixels. |
Tilemaps (Aseprite 1.3+)
Tool | Description |
| Create a tilemap layer with a tile size + empty grid. |
| Define/draw the tileset artwork. |
| Place tiles on the grid. |
| Read the grid of tile indices. |
Image stamping
Tool | Description |
| Composite another image/sprite file onto a layer (opacity, blend mode). |
| Composite an inline base64 image onto a layer. |
Palette
Tool | Description |
| Read / replace the whole palette. |
| Edit individual entries / size. |
| Load a palette from |
| Set the transparent index (indexed sprites). |
| Collect the unique colours used in a sprite/image. |
| Sort by hue/luminance/saturation/value (remaps indexed pixels). |
| Build a hue-shifted shading ramp from a base colour. |
Transform & export
Tool | Description |
| Flip (h/v) / rotate (90/180/270) the whole sprite. |
| Export one frame as a flattened PNG (scaled). |
| Export the animation as an animated GIF. |
| Export only a named tag's frames as a GIF. |
| Pack frames into a sheet (+ JSON metadata; layer/tag filters & splits). |
| Export each frame to its own file ( |
| Export one layer / each layer to separate files. |
| Export each animation tag's frames to separate files. |
| Render a frame with neighbouring frames ghosted behind it. |
| Build an editable |
Reference / rotoscope
Tool | Description |
| Add a dimmed, locked layer holding a reference image to trace over. |
| Import a sequence of images as per-frame references (rotoscoping). |
Batch operations
Tool | Description |
| Apply many edits to one sprite atomically in a single process ( |
GUI companion mode
Tool | Description |
| Open a sprite in the live Aseprite GUI window (non-blocking) to watch edits. |
| Report whether the Aseprite GUI can be launched. |
Health & self-test
Tool | Description |
| Self-test: Aseprite found?, version, workspace, tool count, and a real create+export round-trip. |
Live viewing (GUI companion mode)
The server edits files headlessly, but you can watch the work in the real Aseprite
window. Call open_in_editor("sprite.aseprite") and Aseprite opens it in a normal,
non-blocking window. As the agent keeps saving edits with the other tools, Aseprite
detects the on-disk change and offers to reload (or reloads automatically, depending on
your Aseprite preferences) β so edits appear without re-opening.
What this is and isn't: Aseprite's Lua scripting sandbox has no networking or timers,
so the server can't stream a live canvas into the GUI frame-by-frame. The companion view
above (open once β headless edits β Aseprite reloads) is the robust, supported workflow.
The agent's own "eyes" remain render_preview, which returns a PNG it can inspect directly.
Example agent workflow
"Make me a 32Γ32 walking-slime animation."
create_sprite("slime.aseprite", 32, 32, "rgb")fill_layerthe background,add_layer("slime"), draw the body withdraw_ellipse/fill_area/draw_pixels.add_frame(copy_from=1)a couple of times; nudge the body withset_cel_positionto create a bounce.set_all_frame_durations(120)andadd_tag("walk", 1, 3, "pingpong").render_previewto check it, iterate, thenexport_gif("slime.gif", scale=8).
How it works
client (Claude) ββMCPββ> aseprite-mcp (FastMCP, Python)
β builds a Lua body + ARG table
βΌ
luagen.assemble_script ββ> temp .lua
β (prelude: JSON encoder, colour/pixel
β helpers, drawing primitives, info)
βΌ
Aseprite.exe -b --script temp.lua
β prints @@ASEMCP@@<json> (or @@ASEMCP_ERR@@)
βΌ
runner parses the sentinel ββ> dict back to the clientDrawing primitives (line, rectangle, ellipse, flood fill) are implemented deterministically
in Lua and operate on a full-canvas copy of the target cel, so coordinates are always in
sprite space and results are identical across runs. Exports use the Aseprite CLI's native
flags (--sheet, --scale, --data, β¦).
Project layout
src/aseprite_mcp/
app.py FastMCP instance + usage instructions
config.py locate Aseprite, workspace, path resolution
luagen.py Python->Lua serializer + shared Lua PRELUDE + script assembly
runner.py run_lua() / run_cli(), parse sentinel JSON
server.py imports all tool modules, main()
tools/ one module per domain: sprite, inspect, layers, frames, tags,
cels, drawing, brushes, effects, text, tilemap, image, palette,
slices, transform, export, reference (+ common.py)
docs/TOOLS.md full auto-generated tool reference
scripts/ gen_tool_docs.py (regenerates docs/TOOLS.md)
tests/ pytest suite (auto-skips without Aseprite)Security
This server hands an AI agent a file capability, so access is scoped by default:
Workspace-sandboxed paths. Relative filenames resolve under
ASEPRITE_MCP_WORKSPACE(default<repo>/workspace). Absolute paths and paths that escape the workspace via..(or a symlink that points outside it) are rejected unless you setASEPRITE_MCP_ALLOW_ABSOLUTE=1.No-clobber by default. Output-writing tools (
create_sprite,save_sprite_as,export_*,export_game_asset_bundle) refuse to overwrite an existing file; passoverwrite=Trueto replace it on purpose. Multi-file exports validate every target up front, so they fail before writing anything if any target already exists.No shell, no injection. Aseprite is invoked with list-form arguments (never a shell), and every user value is passed into generated Lua through an escaped
ARGtable β user input is never concatenated into Lua source.Bring your own Aseprite. The server only runs the Aseprite binary you point it at.
Run health_check to confirm the configuration (Aseprite path, workspace, sandbox state).
Notes & limitations
Stateless by design. Each tool call is an independent headless Aseprite process, so transient state (the GUI selection, undo history, the "active" sprite) does not persist between calls. Operations that would need a persistent selection instead take explicit coordinates. A future live-GUI mode can layer on top of this without changing the tool API.
Use a
.aseprite/.aseextension to keep layers, frames, and tags editable. Saving to.png/.gifflattens.get_pixelsis capped at 4096 px (e.g. 64Γ64) per call β read in tiles for larger areas.Anti-aliasing (
antialias=True) only applies to RGB sprites; it's ignored on indexed/gray. Tilemaps and reference layers require Aseprite 1.3+.
Troubleshooting
"Could not locate Aseprite." Set
ASEPRITE_PATHto the full executable path.Nothing happens / permission denied on save. Ensure the workspace path is writable. Relative filenames go under
ASEPRITE_MCP_WORKSPACE(default<repo>/workspace).Timeouts on big operations: raise
ASEPRITE_MCP_TIMEOUT(seconds).A tool errors with a Lua message. The message is surfaced verbatim from Aseprite β it usually names the bad argument (e.g. a missing layer/frame).
Tests all skip. That's expected when Aseprite isn't installed/found; set
ASEPRITE_PATHto run them for real.
Development
uv run pytest # integration tests (need Aseprite)
uv run aseprite-mcp # run the server over stdio (manual debugging)
uv run python scripts/gen_tool_docs.py # regenerate docs/TOOLS.mdSee CONTRIBUTING.md for the architecture and how to add a tool, and CHANGELOG.md for release notes.
Contributing
Contributions are welcome! Please read CONTRIBUTING.md, add a test for your change, and regenerate the tool reference before opening a PR.
License
MIT Β© Brendan Malloy.
Disclaimer
This is an independent, unofficial project and is not affiliated with or endorsed by Aseprite or Igara Studio S.A. "Aseprite" is a trademark of its respective owner. You need your own licensed copy of Aseprite for this server to function.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/MalloyTheDev/aseprite-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server