aseprite-ai-artist
Drive a live Aseprite window as a pixel-art studio: check the connection, inspect sprites, draw, shade, rig, animate, palette-manage, validate, and export game-ready assets — all in the user's open document with each change undoable.
Preflight & inspect —
preflightconfirms Aseprite is attached;sprite_inforeads dimensions, layers, frames, tags, slices, selection, palette;read_pixelsreturns exact colour grids for computation.See the work —
lookgives an upscaled preview, per-pixel ASCII grid, filmstrip, and frame-to-frame diff;validatelints against palette, strays, outlines, banding, anti-aliasing, timing and export readiness with located findings.Draw —
drawapplies up to 512 batched ops in one undoable action (pixels, lines, polylines, rects, ellipses, fills, replace, dither, gradient, clear, blit) with palette-locking by default.Palette work —
palettereads usage counts, writes/replaces colours, loads presets (PICO-8, Game Boy, CGA, etc.) or files, generates hue-shifted ramps, and analyses duplicates and off-palette usage.Recolour by intent —
recolordoes hue-shifted shading, palette snapping, exact replacement, hue rotation and desaturation over whole regions in one pass.Structure —
sprite_manageopens/creates/saves/closes/resizes sprites;layerbuilds and edits layer rigs (incl. batched, grouped);framemanages and times frames;tagcreates named animation cycles;celmoves, copies and links cels.Transform & select —
transformtranslates, flips, rotates (90°-clean), scales, outlines or crops one cel;selectscopes region-based operations.Reference —
referenceimports an image as a locked, semi-transparent guide layer or samples its dominant palette.Export —
exportwrites PNGs, GIFs, spritesheets with JSON atlases (per-frame/tag), numbered frame sequences, and.asepritecopies.Tilesets —
tilesetlists/creates tilemap layers, stamps tiles, packs hand-painted mockups into deduplicated tilesets, and exports Tiled, Godot 4 or JSON with optional blob47 autotiling.Escape hatch —
run_lua(off by default) for arbitrary scripting.Over MCP this is exposed to Claude Code, omp, Codex, Gemini, Cursor, VS Code and Windsurf, in live-window or headless mode.
Allows AI agents to draw and edit pixel art directly in an open Aseprite document, including creating and modifying sprites, layers, frames, tags, and cels, with tools for pixel-level drawing, selection, recoloring, and validation.
Click on "Deploy 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-ai-artistDraw me a 32x32 knight with a 4-frame idle animation."
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 AI Artist
Your coding agent, painting in the Aseprite window you already have open.
Not a generated PNG. Not a file changed behind your back. The document on your screen, one pixel at a time — and every step is one Ctrl+Z.
☔ 500×400 · 72 frames · 20 layers · one 50-colour palette — drawn by Claude Opus 5.5 through this server. Rain, wind, lightning, a raccoon stealing a cola, and a reader who sips her coffee and turns the page. Every moving part runs on a cycle that divides the loop, so it never seams.
Install · Skills · Models · How it works
✨ What it feels like
You type a sentence:
"Draw me a 32×32 knight in the PICO-8 palette, then give him a 4-frame idle."
…and you watch it happen in Aseprite. The agent picks a palette, blocks in a silhouette, stops to look at what it drew, shades it with hue-shifted ramps, splits the knight onto layers, animates him breathing, tags the cycle — and then tells you honestly what it had to compromise on.
You stay in charge the whole time. Don't like the helmet? Ctrl+Z, or just say so.
It works with Claude Code, omp, Codex CLI, Gemini CLI, Cursor, VS Code and Windsurf — one config line each.
Recommended model: Claude Opus 5.5. Right now it's the best brush we've handed this server — the scene above is its work.
Related MCP server: Aseprite MCP Server
🚀 Get started in three steps
You need Aseprite 1.3+ and Node 22.6+ on macOS, Linux or Windows. Open Aseprite once before you start, so its config folder exists.
1. Install the extension
npx @pebbly/aseprite-ai-artist install-extensionThen quit and reopen Aseprite. It only connects at startup, so an editor that was already running will never find the server.
Updating to a new version? Run the same command again — the extension carries part of every new feature, and an old one answers new requests with a clear "not supported" rather than doing half the job.
2. Connect your agent
/plugin marketplace add with-pebbly/aseprite-ai-artist
/plugin install aseprite@aseprite-ai-artistYou get the server, the /aseprite:* commands, four specialist subagents and the
preview hooks. Don't also add the server by hand — you'd load every tool twice,
on every request.
omp plugin marketplace add with-pebbly/aseprite-ai-artist
omp plugin install aseprite@aseprite-ai-artistSame server, skills, subagents and /aseprite:* commands as Claude Code; the
preview hooks come as an omp extension.
npx @pebbly/aseprite-ai-artist install codex # ~/.codex/config.toml
npx @pebbly/aseprite-ai-artist install gemini # ~/.gemini/settings.json
npx @pebbly/aseprite-ai-artist install cursor # ~/.cursor/mcp.json
npx @pebbly/aseprite-ai-artist install --all # all of the aboveYour existing config is backed up first. --dry-run shows the change without
making it; --project writes into the repo instead of your home directory.
Restart your agent afterwards so it picks up the new server.
3. Check it
npx @pebbly/aseprite-ai-artist doctorTicks all the way down? You're ready. If something's missing, it tells you which half — no guessing. More in docs/INSTALL.md.
💻 macOS, Linux and Windows
It runs the same on all three. CI builds and tests every commit on
macos-latest, ubuntu-latest and windows-latest.
macOS | Linux | Windows | |
MCP server, bridge, installer, | ✅ | ✅ | ✅ |
Aseprite extension | ✅ | ✅ | ✅ |
Where |
|
|
|
Steam, itch.io and self-built Aseprite all use the same config folder as their
platform's standard build. If yours lives somewhere else, point the installer
at it: set ASEPRITE_USER_FOLDER, or pass --dir <path>.
On Windows the bridge starts as a hidden background process, so no console
window pops up while you draw. Both sockets bind 127.0.0.1 only, so Windows
Firewall has nothing to ask about.
🤖 No window? Headless mode
For CI, scripted asset builds or a machine with no display, the server can run Aseprite itself, in batch mode, instead of driving an open window:
npx @pebbly/aseprite-ai-artist install codex --headless # writes ASEPRITE_AI_HEADLESS=1
npx @pebbly/aseprite-ai-artist serve --headless --aseprite /path/to/asepritePlugin installs (Claude Code, omp) turn it on with ASEPRITE_AI_HEADLESS=1 in
the environment the agent starts from. The executable comes from --aseprite,
then ASEPRITE_PATH, then the usual install locations — doctor shows which
one it found. No extension or bridge is needed.
It is the same tools on the same command table: one long-lived aseprite -b
keeps your documents open in memory between calls, so the active layer, frame
and undo history behave exactly as in the editor. Two differences you will
notice:
Nothing is on disk until it is saved.
sprite_managesave/save_asandexportwrite files; everything else stays in memory and is gone when the server stops.preflightsays so, so the agent saves before it finishes.It never touches a file your Aseprite window has open. If the editor is attached and has that file open, headless
open,saveandsave_asrefuse — otherwise your next save there would overwrite the work.
/aseprite:studio picks the mode from the request — headless for a batch of
files or a build step, the window for anything you want to watch — and
/aseprite:studio --headless … (or --live) forces it. Under the hood that is
preflight mode="headless"; each side keeps its own documents when you switch.
It is never a fallback: if your window isn't attached, the agent asks rather
than quietly going headless. Why, in ADR-0008.
🎨 The skills, and how to use them
Skills are the workflows the agent follows — the order an experienced pixel artist would work in, written down. They're served to every client under the same names:
Client | How you call a skill |
Claude Code, omp | slash command: |
Codex, Gemini, Cursor, VS Code, Windsurf | MCP prompt |
Anything else | read |
🎬 Start here: aseprite:studio, the director
If you remember one skill, make it this one. studio is the orchestrator for
everything else. Hand it any request — big or small — and it:
checks Aseprite is attached and reads what's already open;
works out what you actually want and writes down the chain of skills it needs;
asks you once, and only if the request is genuinely open-ended;
for anything new, writes the design down first — scenario, palette, poses — and hands you a ready prompt for an image model. Paste it into ChatGPT, Gemini or Midjourney, send back the concept sheet or storyboard, and the agent redraws it as pixel art frame by frame; or say "continue without" and it draws from the written design alone;
runs each stage, handing parts to the specialists where your client has them;
looks at the result after every stage that changed pixels — side by side with the reference when there is one;
finishes with a review, fixes what it finds, and reports what exists now.
/aseprite:studio an animated knight for my Godot game, 32×32, idle and walkBehind the scenes that becomes brief → concept → new → palette → draw → shade → rig → animate → review → export — and you didn't have to know any of those names.
Why the image model? The model drawing pixels is at its worst when it must invent the character, the pose, the camera and the palette while placing every pixel. With a concept sheet the job becomes reproduce this design at 32×32 in these six colours — and the reference is a guide for shapes and poses, never pixels that get downscaled onto the canvas.
The rest of the toolbox
Reach for these directly when you know exactly which step you want.
Skill | Use it when… | Try |
📝 | the idea is still vague. Settles size, palette, view, light and outline in one message before a pixel is drawn. |
|
🖼️ | anything new. Writes the art spec, gives you a prompt for a concept sheet or storyboard, then imports what you send back — one storyboard panel per frame. Also the way in when you already have reference art. |
|
📄 | you're starting fresh. Sets up canvas, colour mode, palette and layers so nothing fights you later. |
|
🎨 | colour is the question — a retro look, hue-shifted ramps, cleaning up ninety near-identical browns, or building a tight palette out of the art itself. |
|
✏️ | it's time to make the thing. Silhouette first, then materials, shading, outline, verify. Labels and title cards too, in a crisp pixel font — measured first, so they land centred. |
|
🌗 | the art looks flat. Adds light and shadow one step at a time, with hue shifting. |
|
🦴 | a character is about to move. Splits it onto head, torso, arm and leg layers — |
|
🏃 | something needs to move. Key poses first, timing that breathes, tagged cycles. Checks each in-between over onion-skin ghosts; a cape's drift or a lantern's sway can be generated instead of hand-placed. |
|
🧱 | you need terrain or level art — seamless tiles, autotiles, Tiled/Godot export. |
|
🔍 | you want the truth. Mechanical checks plus eyes-on checks, reported with evidence — including rules you set, like "the sword never covers the face". |
|
🩹 | something exists and needs changing without wrecking what's already right. |
|
📦 | it's done and has to leave Aseprite — spritesheets with JSON atlases, GIFs, scaled PNGs, nine-slice panels and pivot points for your engine. |
|
🗂️ | you want your sprite in the public gallery or the benchmark. Exports the files, writes an honest |
|
🧑🎨 The specialists
In Claude Code and omp, studio can hand a stage to one of four subagents.
Each reads the same rulebook, so the result is the same whether it or the main
agent does the work — they just keep the main conversation lighter.
Agent | What it owns |
palette-smith | proposes a palette and explains why it fits |
rig-builder | plans and builds the layer rig |
animation-director | key poses, timing and tags before a frame is drawn |
pixel-critic | a scored, located critique — read-only, never touches your sprite |
🏆 Which model should hold the brush?
Not a benchmark — an honest log of what drew the art on this page. Models we haven't tried are marked untested rather than guessed at. For repeatable, scored runs instead of a log entry, see the gallery and benchmark.
Model | What it drew | How it went |
Claude Opus 5.5 | the rainy bookshop up top | Best so far. 500×400, 72 frames, 20 layers in one session — plus a few rounds of user notes (café table, hoodie, an arm rig redone with fixed-length IK, lightning, raccoon). |
Claude Fable 5.1 | the robot at the easel, below | Very strong. One session, no review passes needed. |
Codex CLI | the harbour below, and the mascot | Strong, but it took five rounds of critique. |
Claude Opus 5 | the server, the rulebook, every review pass | The planner and the critic. Its own drawing attempt got scrapped. |
Gemini 3 Pro, Sonnet 5, Cursor, others | — | Untested. Run one and send us the sprite! |
Method mattered more than the model. Every good result came the same way:
Generate, don't hand-place. Write a small program that emits every frame, then push it. Placing pixels one call at a time by eye is where weak attempts died.
Look at frames full-size, one at a time. A filmstrip is a trap — at that size you see what you already expect to be there.
Turn reasoning up before you blame the model. A 32×32 grid is a spatial problem.
💡 Why this one
🌍 It works everywhere, not just in Claude Code. Most Aseprite MCP projects keep their craft knowledge inside a Claude Code plugin, so Codex and Cursor get raw tools and none of the discipline. Here the rules and skills are served over MCP, so every client reads the same source of truth.
🪶 Eighteen tools, not ninety. Every tool schema sits in the model's context
on every turn, drawing or not. Grouping by noun with an op enum covers the same
ground at a sixth of the cost — and makes batching the default, so one draw
call is one undo step for you.
👀 It has to look at its own work. look gives the agent an upscaled
preview, a one-glyph-per-pixel text grid, a filmstrip, an onion skin, a
frame-to-frame diff and a side-by-side against the reference it is drawing
from. validate then checks the sprite mechanically before
anything is called done.
🛡️ It can't quietly wreck your file. With Aseprite detached, every tool
refuses immediately instead of timing out — because an agent that "recovers" by
editing the .aseprite on disk makes changes you never see, and your next save
overwrites them.
There's a whole page on the other projects in this space, and where they're still better.
🔧 Under the hood
Eighteen tools, grouped by noun — preflight · sprite_info ·
sprite_manage · look · read_pixels · draw · select · transform ·
recolor · layer · frame · tag · cel · palette · validate ·
reference · export · tileset, plus run_lua as an escape hatch, off by
default. Full reference: docs/TOOLS.md.
A rulebook in rules/ — palette discipline, hue-shifted shading,
silhouette, outlines, animation timing, layer rigging, the review checklist.
Skills point at rules instead of restating them, so each rule has exactly one
place to be wrong.
your agent ──stdio/MCP──▶ server ──ws:9932──▶ bridge ──ws:9931──▶ AsepriteAseprite's Lua WebSocket can only be a client, so a small bridge holds the listening socket. It runs as its own process: restarting the MCP server — which agent hosts do freely — doesn't drop your Aseprite connection, and a second agent window can attach without stealing the first one's replies. Details in docs/ARCHITECTURE.md.
Both ports bind 127.0.0.1 only. run_lua is arbitrary code execution inside
the app holding your unsaved work, so it stays off unless you turn it on — full
threat model in SECURITY.md.
🖼️ More, drawn the same way
🤖 192×96, 54 frames, one palette. The paint appears under the brush, every frame. Then the robot wipes the canvas clean and starts again — which is why the loop has no seam.
⚓ 256×144, 28 frames, ten layers. Only six of them move — beam, windows, water, smoke, boat, stars — each on its own cycle length, which keeps an ambient loop from feeling mechanical.
Who drew what
Art | Model |
☔ Rainy bookshop (hero, | Claude Opus 5.5 |
🤖 Robot at the easel ( | Claude Fable 5.1 |
⚓ Night harbour ( | Codex CLI, |
🐾 Mascot ( | Codex CLI, |
🗂️ The gallery and the benchmark
Every sprite people make with the plugin can go into gallery/:
the .aseprite source, a cover, the animation, and exactly how it was made —
the prompts in order, the model behind each step, the harness and the plugin
version. The benchmark is built from the same store: a fixed prompt like
the knight gets one block, with every
model × plugin version scored against written criteria.
Both are published as a site built from apps/web. To add your
own run, finish the sprite and ask your agent for /aseprite:submit — it
exports the files, writes generation.yaml, checks it and opens the pull
request. The rules are in gallery/README.md; the design is
ADR-0006.
🛠️ Development
The repository is a pnpm workspace run by turborepo: the plugin is the root
package, gallery/ holds the generations, and apps/web
is the site.
pnpm install && pnpm run build
pnpm test # TypeScript
pnpm run test:pure # Lua that needs no editor — what CI runs
pnpm run test:extension # the real handlers, headless, against a real sprite
pnpm gallery:check # every generation and benchmark prompt, as CI checks them
pnpm web:dev # the gallery site on localhosttest:extension needs Aseprite installed, so CI can't run it. Before changing
anything, read AGENTS.md — it lists the rules that aren't
negotiable and the Lua gotchas that have already cost someone a day.
📜 Licence
MIT — see LICENSE. Aseprite is a trademark of Igara Studio S.A.; this project isn't affiliated with them.
Available Tools
18 toolscelCelsADestructive
Manage cels — one layer's image on one frame. Ops: 'list', 'create', 'clear', 'delete', 'move', 'copy', 'link', 'unlink', 'set' (position/opacity), 'tween', 'oscillate'. 'move' is how you shift a whole limb between frames without redrawing it; 'link' shares one image across frames so a static part of a cycle stays in sync. 'tween' interpolates position or opacity between two frames with an easing curve; 'oscillate' adds a sinusoidal position offset over a frame range — both fill in any missing in-between cels from the start cel, in one transaction.
| Name | Required | Description | Default |
|---|---|---|---|
| x | No | Cel origin, for 'set' and 'move'. | |
| y | No | ||
| dx | No | Relative move. | |
| dy | No | ||
| op | Yes | ||
| to | No | For 'tween': end value — {x,y} for property 'position', 0-255 for 'opacity'. | |
| frame | No | 1-based frame number. Omit to use the active frame. | |
| layer | No | Layer name. Omit to use the active layer. | |
| phase | No | For 'oscillate': 0-1 fraction of one period. | |
| easing | No | For 'tween'. | linear |
| frames | No | For 'link' across a range. | |
| period | No | For 'oscillate', in frames. Default: the whole fromFrame..toFrame span. | |
| sprite | No | Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused. | |
| opacity | No | ||
| toFrame | No | ||
| toLayer | No | ||
| property | No | For 'tween'. | |
| fromFrame | No | For 'tween'/'oscillate': the start frame. | |
| amplitudeX | No | For 'oscillate'. | |
| amplitudeY | No | For 'oscillate'. |
Output Schema
| Name | Required | Description |
|---|---|---|
| cels | No | |
| frames | No | Only present for 'tween' and 'oscillate': one entry per frame touched, ascending. |
| sprite | Yes | |
| applied | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and readOnlyHint=false, so the safety profile is covered. The description adds genuinely useful behavior beyond that: tween/oscillate operate 'in one transaction' and 'fill in any missing in-between cels from the start cel,' disclosing atomicity and side effects. It stops short of stating reversibility or permission needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose followed by the op list, then targeted explanations of the less obvious operations. Dense but nearly every sentence carries information an agent needs to disambiguate ops. Slightly long, though not padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 20-parameter, 11-operation tool with an output schema present, return values need not be explained. The description covers the op semantics and transaction behavior well, but leaves a few parameter interactions (sprite selection, toLayer/toFrame pairing) to the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 70%, so the schema does much of the work, but the description adds real semantic value by mapping operations to intent (move vs link vs tween vs oscillate) and clarifying the opacity/position distinction for tween. It does not document every parameter (e.g. sprite, toLayer, frames) but complements the schema well.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource and defines the concept precisely: 'Manage cels — one layer's image on one frame.' It then enumerates all 11 supported operations, so an agent immediately knows the tool's exact scope. It is clearly distinguishable from siblings like layer or frame.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explains what several operations mean and implicitly when to prefer them: 'move' for shifting a limb without redrawing, 'link' for sharing an image across frames, 'tween'/'oscillate' for procedural in-betweens. It gives clear operational context but never explicitly routes the agent away from alternative sibling tools (layer, frame).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
drawDrawA
Apply a batch of drawing operations to one cel, as a single undoable action. Ops: pixels, line, polyline, rect, ellipse, fill, replace, dither, gradient, clear, blit, text. Batch aggressively — a whole sprite in one call is normal and correct, and it means the user can undo your work with one Ctrl+Z. Set paletteLock (default true) to snap every colour to the sprite's palette by perceptual distance before anything is written, so you cannot silently widen a curated palette. Ops run in array order, so paint fills before outlines and outlines before highlights. 'text' is laid out here from a bitmap font and expanded to plain pixels before it reaches Aseprite — pass measureOnly: true with only 'text' ops to get each one's ink bounds without touching the sprite, e.g. to centre a label first.
| Name | Required | Description | Default |
|---|---|---|---|
| ops | Yes | Applied in order, in one transaction. | |
| frame | No | 1-based frame number. Omit to use the active frame. | |
| label | No | Name shown in Aseprite's undo history. Describe the intent, e.g. 'shade helmet'. | |
| layer | No | Layer name. Omit to use the active layer. | |
| sprite | No | Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused. | |
| createCel | No | Create the cel if the target layer/frame has none. | |
| measureOnly | No | Every op must be 'text'. Returns each one's ink bounds without calling Aseprite at all — use to size a panel or centre a label before actually drawing it. | |
| paletteLock | No | Snap every colour to the nearest palette entry (CIELAB ΔE). Set false only when the user asked to introduce new colours. | |
| selectionOnly | No | Clip every op to the current selection. |
Output Schema
| Name | Required | Description |
|---|---|---|
| frame | No | |
| layer | No | |
| bounds | No | Bounding box actually touched. |
| sprite | No | |
| opsApplied | No | |
| textBounds | No | Only present when `measureOnly` was set. |
| measureOnly | No | |
| colorsSnapped | No | Colours palette-lock moved, and how far. A large ΔE means the palette lacks that colour. |
| pixelsChanged | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say it is a non-readonly, non-destructive, non-idempotent write. The description adds genuinely useful behavior beyond that: ops execute in array order, palette colours are snapped by CIELAB ΔE before writes, text is expanded to pixels locally, and measureOnly returns ink bounds without calling Aseprite.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Long but dense and front-loaded: the core action leads, then the op list, then ordering, palette lock, and the text/measureOnly caveat. Nearly every sentence carries actionable detail, though it runs slightly long for a definition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Output schema exists so return shape is covered, and the description fills the remaining gaps for a mutation tool: ordering semantics, palette safety, cel creation defaults, and the measure-only escape hatch. An agent has what it needs to call this correctly on the first try.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the prose adds meaning the schema does not: array-order execution, the intent behind paletteLock's default and when to flip it, and the label parameter's purpose as an undo-history name. It does not document every op field, but it enriches the highest-risk ones.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Apply a batch of drawing operations to one cel') plus the transactional scope ('single undoable action'). An agent can distinguish it from transform, recolor, and read_pixels without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear operational guidance — batch aggressively, paint fills before outlines, use measureOnly for centering a label — and states the condition for disabling paletteLock. It does not explicitly route the agent between this tool and siblings like transform or recolor, so it stops short of full when/when-not coverage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
exportExportADestructive
Write game-ready files. Ops: 'png' (single frame), 'gif' (animation), 'spritesheet' (packed sheet plus a JSON atlas with per-frame and per-tag data), 'frames' (numbered PNGs), 'aseprite' (save a copy of the source). Spritesheet is what an engine actually consumes: pass sheetType and byTag to control layout. Exporting does not save the working document — use sprite_manage op 'save' for that.
| Name | Required | Description | Default |
|---|---|---|---|
| op | Yes | ||
| path | Yes | Output file path. For 'frames', a pattern containing {frame}, e.g. 'walk_{frame}.png' — Aseprite expands it into one file per frame, numbered from 0. | |
| tags | No | Export only these tags. | |
| trim | No | Trim transparent margins; the atlas keeps the original offsets. | |
| byTag | No | Split the sheet by animation tag. | |
| frame | No | 1-based frame number. Omit to use the active frame. | |
| scale | No | Integer upscale, nearest-neighbour. | |
| layers | No | Export only these layers. | |
| sprite | No | Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused. | |
| padding | No | Pixels between frames; prevents bleed at non-integer zoom. | |
| sheetType | No | Spritesheet layout. | horizontal |
| includeJson | No | Write a sibling JSON atlas for 'spritesheet'. |
Output Schema
| Name | Required | Description |
|---|---|---|
| op | Yes | |
| atlas | No | |
| files | Yes | |
| frame | No | Which frame a single-frame export wrote. Defaults to the active frame, which is not always the one you drew on. |
| width | No | |
| height | No | |
| sprite | Yes | |
| frameCount | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, and the description adds meaningful context by clarifying that export does not persist the working document, which is a key behavioral distinction from sprite_manage. It also explains the output format (JSON atlas for spritesheet) without contradicting the annotations. It does not mention overwrite behavior or side effects on existing files, but the annotation already covers destructiveness, so the added context is sufficient.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is four sentences with zero wasted words. The main verb is front-loaded, ops are listed compactly, the critical spritesheet guidance is placed early, and the save-disambiguation is at the end. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (12 parameters, 5 ops, multiple formats), the description covers the essential context: what each op does, the spritesheet consumption hint, and the crucial distinction from sprite_manage. The schema covers parameter details, and an output schema exists (not shown), so the description does not need to explain return values. It is complete for an agent to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 92% (most parameters have descriptions), so the baseline is 3. The description adds value beyond the schema by explaining the ops and how parameters like `sheetType` and `byTag` control spritesheet layout, and by clarifying the `path` pattern for 'frames'. This helps an agent understand the semantics without opening the schema, slightly exceeding the baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource ('Write game-ready files') and then enumerates each operation with a short, distinct explanation (png, gif, spritesheet, frames, aseprite). It explicitly differentiates the spritesheet op as the engine-consumable format and names the sibling tool (sprite_manage op 'save') for a different purpose, making its scope unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides explicit when-not-to-use guidance by stating 'Exporting does not save the working document — use sprite_manage op 'save' for that.' It also gives usage direction for the spritesheet op ('Spritesheet is what an engine actually consumes: pass `sheetType` and `byTag` to control layout'), helping an agent choose the right op and parameters.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
frameFramesADestructive
Manage animation frames. Ops: 'list', 'add', 'duplicate', 'delete', 'set_duration', 'activate', 'reorder'. Durations are milliseconds per frame and carry most of the life in a cycle — hold a contact pose longer than a pass pose. Use count to add several at once and durations to set a whole cycle's timing in one call.
| Name | Required | Description | Default |
|---|---|---|---|
| op | Yes | ||
| count | No | ||
| frame | No | 1-based frame number. Omit to use the active frame. | |
| sprite | No | Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused. | |
| toIndex | No | For 'reorder'. | |
| linkCels | No | For 'duplicate': share the cel image instead of copying it, so edits apply to both. | |
| durations | No | Per-frame durations from frame 1, for 'set_duration'. | |
| afterFrame | No | Insert position. Default: at the end. | |
| durationMs | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| frames | No | |
| sprite | Yes | |
| frameCount | Yes | |
| activeFrame | No | |
| totalDurationMs | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and readOnlyHint=false, so the mutating nature is known. The description adds domain context about durations carrying the life of a cycle, which is helpful but does not disclose operational details like irreversibility of delete or that operations affect the active sprite. Since annotations cover the core safety profile, the description's modest additional disclosure merits a 3.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is tightly written, with the ops list front-loaded and each sentence earning its place. The usage guidance for durations and batch operations is concise and directly actionable, with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (9 parameters, 7 ops), an output schema exists to define returns, and annotations cover safety. The description covers the operation scope and provides key usage tips. It does not explain every edge case (e.g., behavior when frame is omitted, or activate semantics), but the schema already documents frame omission, and the ops list is self-explanatory. This is adequate for an experienced user.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 67%, with descriptions for frame, sprite, toIndex, linkCels, durations, and afterFrame. The description adds value beyond the schema by explaining the semantics of durations (milliseconds per frame) and the purpose of count and durations in batch operations. This compensates for the undocumented parameters (op, count, durationMs) with practical usage context.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Manage animation frames' and enumerates seven operations (list, add, duplicate, delete, set_duration, activate, reorder), making the resource and verb specific. It does not explicitly distinguish this tool from siblings like layer or tag, but the ops list leaves little ambiguity about scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides practical guidance on when to use certain parameters ('Use count to add several at once and durations to set a whole cycle's timing in one call') and explains the role of durations in animation cycles. It lacks explicit alternative routing to sibling tools, but the context is clear and not misleading.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
layerLayersADestructive
Manage layers. Ops: 'list', 'create', 'rename', 'delete', 'reorder', 'set' (visibility/opacity/blend/lock), 'group', 'ungroup', 'merge', 'duplicate', 'activate'. Pass batch to run several in one undoable action — building a rig in one call is the normal use. 'duplicate' takes an optional toSprite to copy the layer's cels into another OPEN sprite instead of this one, by frame index; frames past the target's frame count are dropped and reported. A character that will be animated wants its parts on separate layers (head, torso, arm-far, arm-near, leg-far, leg-near) before any frames exist; splitting baked pixels apart later is far more work.
| Name | Required | Description | Default |
|---|---|---|---|
| op | No | Single operation. Use `batch` instead for several. | |
| name | No | ||
| batch | No | Operations applied in order, in one undoable action. Same shape as the single-op arguments. | |
| index | No | Target stack position for 'reorder'; 0 is bottom. | |
| names | No | For 'merge' and bulk 'group'. | |
| parent | No | Group layer to nest under. | |
| sprite | No | Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused. | |
| newName | No | ||
| opacity | No | ||
| visible | No | ||
| editable | No | ||
| toSprite | No | For 'duplicate': copy the layer into another OPEN sprite instead of this one. | |
| blendMode | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| layers | No | |
| sprite | Yes | |
| applied | No | |
| duplicated | No | Present when a 'duplicate' op in this call used `toSprite`. |
| activeLayer | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and readOnlyHint=false, so the safety profile is covered. The description adds real behavioral detail beyond that: batch is a single undoable action, and duplicate-to-another-sprite drops and reports frames past the target's frame count.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the op list, then batch semantics, then the duplicate edge case, then a workflow tip. Efficient overall, though the final rigging-advice sentence is more ambient guidance than operational instruction.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Output schema exists so return values need no explanation, and annotations carry the destructive flag. However, with 13 parameters at 54% coverage, the description never says which parameters apply to which op (e.g. name vs newName, what 'set' can change), leaving ambiguity for a multi-mode tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 54%, so the schema documents some parameters while leaving others bare (name, newName, opacity, visible, editable have no descriptions). The description clarifies batch grouping, toSprite target semantics, and that 'set' covers visibility/opacity/blend/lock, but does not map the remaining params to their ops.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States the resource ('Manage layers') and enumerates all eleven operations, so an agent can see exactly what surface it covers. It clearly reads as the layer-level counterpart to siblings like cel and frame, though it never names a sibling to differentiate itself explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete context: pass `batch` to run several ops in one undoable action and 'building a rig in one call is the normal use', plus the constraint that `duplicate`'s toSprite must be another OPEN sprite. No explicit when-not or named alternative tools, but the conditions that select the main modes are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
lookLook at the spriteARead-only
See what is actually on the canvas. Ops: • 'preview' — a nearest-neighbour upscaled PNG of the active frame (~1024px long edge). Use for overall read: silhouette, colour, whether it looks like the thing. • 'ascii' — an exact text grid, one glyph per pixel with a colour legend and coordinate rulers. Use to verify precise pixel positions and values, to count cells, or on any client without vision. Capped at 64×64; pass a region to crop. • 'filmstrip' — every frame composited into one image. The only reliable way to review an animation, since a vision model reads just the first frame of a GIF. • 'onion' — the target frame at full opacity over ghosted neighbouring frames, oldest-first. Use to check in-betweens and spacing while animating, without stepping through frames one at a time. • 'diff' — a pixel-level text diff between two frames: '.' unchanged, '-' erased, glyph = the new colour. Use it to confirm exactly what an edit touched. • 'compare' — the reference layer (left, full opacity) beside the art without any reference layer (right), same frame, same scale. Use it when drawing from a concept or storyboard: name the few largest mismatches and fix only those. Draw, then look, then fix. Do not report a sprite finished without looking at it.
| Name | Required | Description | Default |
|---|---|---|---|
| op | No | preview | |
| frame | No | 1-based frame number. Omit to use the active frame. | |
| layer | No | Read a single layer instead of the composited image. | |
| scale | No | Integer upscale for image ops, 1-128 (clamped so the output stays under ~2048px). Omit it — the automatic choice targets a ~1024px long edge, which is what a vision model can actually read. | |
| region | No | Crop. Required for 'ascii' on sprites larger than 64×64. | |
| sprite | No | Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused. | |
| toFrame | No | Diff: the later frame. | |
| fromFrame | No | Diff: the earlier frame. | |
| reference | No | Compare: the reference layer. Default 'reference'. | |
| framesAfter | No | Onion: ghost frames after the target. | |
| framesBefore | No | Onion: ghost frames before the target. | |
| ghostOpacity | No | Onion: opacity of the ghosted (non-target) frames. |
Output Schema
| Name | Required | Description |
|---|---|---|
| op | Yes | |
| rows | No | |
| text | No | The grid, for 'ascii' and 'diff'. |
| scale | No | |
| width | Yes | |
| frames | No | Filmstrip: frame count. |
| height | Yes | |
| legend | No | glyph → #rrggbb. |
| sprite | Yes | |
| columns | No | |
| framesUsed | No | Onion: 1-based frame numbers composited. |
| totalPixels | No | |
| changedBounds | No | Diff: tight bounding box of every changed pixel. Null when nothing changed. |
| changedPixels | No | |
| percentChanged | No | Diff: changed / total * 100. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint/openWorldHint; the description adds real behavioral context beyond them: the 64×64 ascii cap and region-crop requirement, the automatic ~1024px scaling target and clamping behavior, and the non-obvious caveat that a vision model reads only the first frame of a GIF (hence 'filmstrip'). These are exactly the operational facts an agent cannot derive from annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Long but well-structured: one summary sentence followed by a bullet per op, each front-loading the op name before its use case. The closing workflow line earns its place. Slightly verbose for an 11-word tool name, but nothing is padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 12-parameter, multi-mode, nested-schema tool with an output schema present, the description covers all six modes, their constraints, and the intended workflow. Return-value format is handled by the output schema, so no gap remains.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 92%, so the baseline is 3. The description still adds per-op meaning for the 'op' enum and explains non-obvious constraints (region required for large sprites, scale defaults chosen for vision-model legibility, layer/frame selection semantics), earning above baseline without duplicating the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a concrete verb+resource ('See what is actually on the canvas') and then defines six distinct modes of inspection, each with its own purpose. An agent can tell immediately that this is a read/inspection tool and what each mode yields, distinct from a drawing or transform tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Each op carries an explicit 'Use for...' statement (silhouette read, precise pixel verification, animation review, spacing checks, edit confirmation, reference comparison), plus workflow advice ('Draw, then look, then fix'). No sibling is named, however — the pixel-level 'ascii' op overlaps conceptually with the read_pixels sibling and the description never routes between them.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
palettePaletteA
Read and shape the sprite's palette. Ops: • 'get' — current palette with per-colour usage counts. • 'set' — write specific indices, or replace the palette wholesale. • 'preset' — load a bundled palette (pico8, gameboy, gameboy-pocket, cga, 1bit, grayscale-8). • 'load' — read a .gpl/.hex/.pal/.png palette file from disk. • 'ramp' — generate a hue-shifted ramp from a base colour and append it. Shadows rotate toward blue, highlights toward orange; a ramp that only changes brightness is the clearest tell of machine-made pixel art. • 'analyze' — report ramp structure, contrast, near-duplicate entries and colours used in the art that are not in the palette. • 'extract' — replace the palette with one quantized from the art itself (RGB sprites only). Use to derive a curated palette from a reference image imported at full colour. Decide the palette before drawing. Retro-fitting one onto finished art means repainting.
| Name | Required | Description | Default |
|---|---|---|---|
| op | Yes | ||
| base | No | Base colour for op 'ramp'. | |
| path | No | Palette file for op 'load'. | |
| steps | No | Ramp length. | |
| colors | No | For op 'set': the colours to write. | |
| preset | No | Preset key for op 'preset'. | |
| spread | No | How far the ramp reaches into shadow and light. | |
| sprite | No | Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused. | |
| replace | No | For op 'set'/'preset': replace the whole palette instead of merging. | |
| remapArt | No | When replacing a palette, repaint existing pixels to the nearest new colour instead of leaving them off-palette. | |
| maxColors | No | For op 'extract': target palette size. | |
| startIndex | No | For op 'set': where `colors` starts. Ignored when `replace` is true. |
Output Schema
| Name | Required | Description |
|---|---|---|
| op | Yes | |
| path | No | Palette file read by op 'load'. |
| ramp | No | |
| size | No | |
| usage | No | |
| colors | No | |
| sprite | No | |
| analysis | No | |
| availablePresets | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false, openWorldHint=false, idempotentHint=false. The description adds real behavioral context beyond this: it implicitly separates read ops (get, analyze) from mutating ops (set, preset, load, ramp, extract), warns that 'extract' is RGB-sprite-only, and explains remap/replace effects. It does not cover permission needs or whether writes are reversible, so not a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded one-line summary followed by a scannable bullet list keyed by op name. Most sentences earn their place, though the 'ramp' entry runs longer than needed with aesthetic rationale attached.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return values need not be explained. For a 12-param, seven-op tool the description covers every op's purpose, the key edge cases (RGB-only extract, replace vs merge), and the ordering caution for palette work — nothing an agent needs to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is high (92%), so the baseline is 3, but the description adds meaning the schema lacks: it names the bundled preset keys (pico8, gameboy, cga, 1bit, grayscale-8), explains the hue-shift direction of 'ramp', and clarifies that 'extract' quantizes from the art. It leaves some params (base, spread, startIndex) to the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('Read and shape the sprite's palette') and then enumerates all seven ops with precise semantics, from 'get' (usage counts) to 'extract' (quantize from art). An agent can distinguish this palette-management tool from siblings like recolor, draw, or look without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear contextual guidance ('Use to derive a curated palette from a reference image imported at full colour', 'Decide the palette before drawing'), including the workflow consequence of retrofitting a palette. It stops short of naming sibling tools as explicit alternatives or stating when-not-to-use, so it lands at clear-context rather than full routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
preflightPreflightARead-only
Check that Aseprite is connected and report what this session can do. Call this FIRST in any pixel-art task and stop if ready is false — every editing tool writes into the user's open Aseprite window ('live') or into a batch Aseprite this server owns ('headless'), and there is no useful fallback when that Aseprite is not there. Pass mode to choose which one the session works with from now on — only because the user asked (e.g. --headless) or the request plainly needs no window (files for CI, a batch of assets). Never switch to get around a live session that is not ready: ask the user instead. Switching loses nothing; each side keeps its documents.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | Switch the session to this Aseprite before checking. Omit to keep the current one. |
Output Schema
| Name | Required | Description |
|---|---|---|
| mode | Yes | 'live' = the user's open window. 'headless' = a batch Aseprite with no window; nothing is on disk until you save or export. |
| ready | Yes | True only when Aseprite is attached and accepting commands. |
| features | Yes | Optional capabilities this extension build supports. |
| switched | Yes | True when this call changed the session's mode. |
| directive | Yes | What to do next, in one sentence. |
| activeSprite | No | |
| asepriteVersion | No | |
| bridgeConnected | Yes | |
| pluginConnected | Yes | |
| extensionVersion | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description is genuinely rich about behavior — it explains that every editing tool writes into the live window or into a server-owned headless Aseprite, that there is no fallback, and that switching loses nothing because each side keeps its documents. However, it also states that passing mode changes what the session works with 'from now on', i.e. a persistent state mutation, which conflicts with the readOnlyHint=true annotation. The disclosure itself is strong, but it directly contradicts the structured safety hint, so this is flagged.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the action and the 'call FIRST' imperative, then the failure condition, then the mode guidance. It is longer than average but every sentence carries decision-relevant information; nothing is padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists (ready/capabilities), so return values need not be described. Combined with the safety, prerequisite, and mode-selection context the description supplies, an agent has everything needed to call this correctly first in a workflow.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the enum is documented, so the baseline is 3. The description adds meaning beyond the schema by explaining the consequence of the parameter ('which one the session works with from now on'), the conditions that justify switching, and the risk of switching for the wrong reason.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Check that Aseprite is connected and report what this session can do') and immediately positions itself as the mandatory entry point ('Call this FIRST in any pixel-art task'). An agent can distinguish it from every sibling editing tool without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use ('FIRST in any pixel-art task'), when-to-stop ('stop if ready is false'), when to pass mode ('only because the user asked... or the request plainly needs no window'), and an explicit anti-pattern ('Never switch to get around a live session that is not ready: ask the user instead'). This is about as complete as usage guidance gets.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_pixelsRead pixelsARead-only
Read a rectangular region as structured data: a list of distinct colours plus a row-major index grid. Use this when you need to compute over pixels (sample a palette from art, find a silhouette edge, copy a region) rather than just look at them. For eyeballing, 'look' is cheaper.
| Name | Required | Description | Default |
|---|---|---|---|
| frame | No | 1-based frame number. Omit to use the active frame. | |
| layer | No | Layer name. Omit to use the active layer. | |
| region | No | Omit to read the whole canvas. | |
| sprite | No | Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused. | |
| composite | No | Read the flattened image. False reads only the target layer's cel. |
Output Schema
| Name | Required | Description |
|---|---|---|
| x | Yes | |
| y | Yes | |
| grid | Yes | Row-major indices into `colors`. |
| width | Yes | |
| colors | Yes | Distinct colours; index 0 is fully transparent. |
| height | Yes | |
| sprite | Yes | |
| uniqueColors | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the read-only behavior is known. The description adds value by describing the output structure ('a list of distinct colours plus a row-major index grid'), which goes beyond the annotation. It doesn't contradict annotations and provides useful context about the tool's non-mutating nature and return format.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise: two sentences that front-load the core function, then provide usage context and an alternative. Every word earns its place, with no redundancy or fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has an output schema (as indicated by context signals), the description does not need to detail return values. It covers purpose, usage, and differentiation from a key sibling. With read-only annotations and a fully documented parameter schema, the description is complete for an agent to select and invoke correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has 100% coverage with descriptions for all 5 parameters, including nested 'region' details. The tool description does not add any additional parameter-specific meaning beyond what the schema already provides. Per calibration, with high schema coverage, baseline is 3, and no extra param info is given, so this is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: 'Read a rectangular region as structured data: a list of distinct colours plus a row-major index grid.' It uses a specific verb (read) and resource (rectangular region), and explicitly differentiates from the sibling 'look' tool by contrasting computation vs. eyeballing. This allows an agent to distinguish it immediately.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit usage guidance: 'Use this when you need to compute over pixels (sample a palette from art, find a silhouette edge, copy a region) rather than just look at them. For eyeballing, 'look' is cheaper.' This clearly states when to use this tool and when to use an alternative, leaving no ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
recolorRecolorA
Change colours in a region by intent, staying palette-legal. Ops: • 'shade' — darken or lighten with proper hue shifting (shadows cool, highlights warm). Use this instead of picking a darker hex by hand. • 'snap' — pull off-palette pixels onto the nearest palette colour by perceptual (CIELAB) distance. • 'replace' — swap one exact colour for another. • 'hue_shift' — rotate hue, e.g. to make a colour variant of a finished sprite. • 'desaturate' — drop toward grey, useful for a value check. Operates on a region's distinct colours in one pass, so a 64×64 recolour is one undo step, not four thousand.
| Name | Required | Description | Default |
|---|---|---|---|
| op | Yes | ||
| to | No | For 'replace'. | |
| from | No | For 'replace'. | |
| frame | No | 1-based frame number. Omit to use the active frame. | |
| layer | No | Layer name. Omit to use the active layer. | |
| amount | No | For 'shade': negative darkens, positive lightens. One ramp step is roughly 0.15. | |
| region | No | Omit to affect the whole cel. | |
| sprite | No | Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused. | |
| degrees | No | For 'hue_shift'. | |
| strength | No | For 'desaturate'. | |
| selectionOnly | No | ||
| clampToPalette | No | Snap the result back onto the palette. Turn off only when growing the palette deliberately. |
Output Schema
| Name | Required | Description |
|---|---|---|
| op | Yes | |
| sprite | Yes | |
| mapping | Yes | Exactly which colour became which, and how many pixels each move touched. |
| pixelsChanged | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes beyond annotations by explaining behavioral details: operations run in one pass so a 64×64 recolour is one undo step, and it emphasizes palette legality and the clampToPalette option. It also clarifies that shade does proper hue shifting (shadows cool, highlights warm). Annotations indicate it's not read-only, which aligns with the mutation described. No contradictions, and the description adds valuable context about how the tool behaves.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is structured with a clear opening statement followed by a bulleted list of ops, making it scannable. While it is fairly long, each sentence adds information about operations or parameters. The front-loaded purpose and op list help an agent quickly grasp the tool's scope. Some redundancy exists (e.g., palette-legal mentioned twice), but overall it's efficient.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the complexity (12 parameters, nested region object), the description is remarkably complete: it explains each op's use case, relevant parameters, and notes on palette behavior and undo granularity. The output schema is present, so return values are covered elsewhere. It could mention edge cases like invalid colors or when clampToPalette might fail, but for an agent deciding to call the tool, it's sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is high (83%), but the description adds crucial meaning: it explains the amount parameter with 'negative darkens, positive lightens. One ramp step is roughly 0.15,' defines from/to for replace, and clarifies clampToPalette's role in palette growth. These enrich the raw schema and help an agent select correct parameter values without needing extra inference.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: 'Change colours in a region by intent, staying palette-legal.' It then enumerates specific operations (shade, snap, replace, hue_shift, desaturate) with examples, making it obvious what the tool does and how it differs from manual color editing. It's specific and distinct from sibling tools like draw or transform.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit guidance for each operation, e.g., 'Use this instead of picking a darker hex by hand' for shade, and 'useful for a value check' for desaturate. However, it does not explicitly mention alternative tools or when NOT to use recolor in favor of another sibling, so it's not fully exhaustive but gives solid contextual usage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
referenceReference imageA
Bring an external image into the sprite as a reference layer, so you can trace proportions or sample colours from it. Ops: 'import' (place an image on a locked, semi-transparent layer above the art), 'sample_palette' (read its dominant colours without importing), 'list', 'remove'. A concept sheet or storyboard arrives as one image: region crops one panel of it, and grid cuts it into panels and puts panel i on frame i of a single reference layer, so each animation frame is drawn over its own storyboard panel. Importing at a different size uses nearest-neighbour; a reference scaled down to 32×32 is a guide for proportion and pose, never a finished sprite.
| Name | Required | Description | Default |
|---|---|---|---|
| x | No | ||
| y | No | ||
| op | Yes | ||
| fit | No | How to size the image (or each panel) against the canvas. | contain |
| grid | No | Import a storyboard: split the (cropped) source into equal panels and place panel i on frame `frame`+i-1 of one reference layer. The sprite must already have enough frames. | |
| name | No | Layer name. Default: 'reference'. | |
| path | No | Image file to read. | |
| frame | No | Import: the frame the image — or the first `grid` panel — lands on. Default: the active frame, or frame 1 with `grid`. | |
| colors | No | For 'sample_palette'. | |
| region | No | Use only this rectangle of the SOURCE image, in its own pixels — one panel of a concept sheet. Applies to 'import' and 'sample_palette'. | |
| sprite | No | Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused. | |
| opacity | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| op | Yes | |
| layer | No | |
| width | No | |
| frames | No | Import: the frames that received a reference cel. |
| height | No | |
| panels | No | Import: how many panels were placed. |
| sprite | Yes | |
| palette | No | Dominant colours with their share of the image. |
| references | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false and openWorldHint=false; the description adds real behavior: import lands on a locked, semi-transparent layer above the art; resizing uses nearest-neighbour; a 32×32 reference is a proportion guide, never a finished sprite; grid requires the sprite to already have enough frames. Missing specifics like whether 'remove' is reversible, but coverage is strong.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose, then the op list, then the storyboard/grid mechanism, then the scaling caveat. Dense and mostly waste-free, though the panel-index explanation is slightly elaborate for the space it consumes.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need no explanation, and the description covers the four ops, region, and grid for a 12-param nested tool. Gaps remain around 'list'/'remove' semantics and the 'fit'/'opacity' parameters, but nothing critical to correct invocation is absent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 67% schema coverage and 12 params, the description meaningfully supplements the schema: 'region' crops one panel of the SOURCE image, 'grid' places panel i on frame i of one reference layer, and import's scaling behavior is explained. It does not unpack the 'fit' enum values (contain/cover/stretch) or 'colors'/'opacity' effects, which remains a gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States the specific resource (external image as reference layer) and enumerates the four ops ('import', 'sample_palette', 'list', 'remove') with a one-clause gloss of each. An agent can distinguish this from the sibling 'layer', 'palette', and 'look' tools without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear when-to-use context per op: 'sample_palette' reads colours 'without importing', 'region' handles a single panel of a concept sheet, 'grid' handles a storyboard. It never names an alternative tool (e.g. why not use 'palette' to sample colours), so it stops short of the 5 bar.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
selectSelectionAIdempotent
Read or change the active selection. Ops: 'get', 'none', 'all', 'rect', 'ellipse', 'color' (select every pixel matching a colour), 'invert', 'grow', 'shrink'. A selection scopes draw, transform and recolor, which is usually cheaper and safer than masking by hand.
| Name | Required | Description | Default |
|---|---|---|---|
| op | Yes | ||
| mode | No | How this selection combines with the existing one. | replace |
| rect | No | ||
| color | No | For op 'color'. | |
| frame | No | 1-based frame number. Omit to use the active frame. | |
| layer | No | Layer name. Omit to use the active layer. | |
| amount | No | Pixels, for grow/shrink. | |
| sprite | No | Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused. | |
| tolerance | No | ||
| contiguous | No | For op 'color'. |
Output Schema
| Name | Required | Description |
|---|---|---|
| empty | Yes | |
| bounds | No | |
| sprite | Yes | |
| pixelCount | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and idempotentHint=true. The description adds that the tool reads or changes the selection and that it affects downstream operations, which is useful behavioral context beyond the annotations. No contradiction found.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the main action and a concise op list. The note about scoping is valuable and adds no fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 10 parameters, a nested object, and an output schema, the description covers the tool's purpose and effect on other operations. It does not explain return values (output schema exists) or detailed param behaviors (schema covers most). Adequate for agent use.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema provides descriptions for several parameters (mode, color, frame, layer, amount, contiguous, sprite). The description itself only clarifies the 'color' op. With 70% schema coverage, the description adds marginal value over the schema, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states the verb 'read or change' and the resource 'active selection', then enumerates the supported operations. It clearly distinguishes this tool from drawing/transform/recolor operations by explaining that selection scopes them, which separates it from sibling tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains that a selection scopes draw, transform and recolor, and notes it is cheaper/safer than manual masking. This gives clear context for when to use selection, though it does not explicitly name alternatives or list exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sprite_infoSprite infoARead-only
Full structured state of a sprite: dimensions, colour mode, palette, every layer (with opacity, blend mode, visibility, group nesting), every frame with its duration, animation tags, slices and the current selection. Read this before editing — guessing at layer names or frame counts is the most common way an agent corrupts someone's file.
| Name | Required | Description | Default |
|---|---|---|---|
| sprite | No | Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused. | |
| includeSlices | No | ||
| includePalette | No | Include the full palette as hex. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Stable id for this open document. Pass it back as '#<id>'. |
| name | Yes | |
| tags | Yes | |
| width | Yes | |
| frames | Yes | |
| height | Yes | |
| layers | Yes | |
| slices | No | |
| palette | No | |
| filename | No | |
| colorMode | Yes | |
| selection | No | |
| frameCount | Yes | |
| activeFrame | No | |
| activeLayer | No | |
| transparentIndex | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the safety profile (readOnly, not openWorld). The description adds real behavioral value: it is a comprehensive introspection tool, and it names the corruption risk of guessing at layer names/frame counts — an agent-relevant failure mode not encoded anywhere in structured data.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the full return contents, followed by the usage directive. Every clause earns its place; no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Output schema exists, so return values needn't be described in prose. Description enumerates the report's parts, gives usage context, and the schema handles parameter detail. Complete for a read-only introspection tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 67%. The description's 'current selection' line complements schema. The sprite param already has a rich schema description (id/filename/display name, omit for focused). includePalette is self-documenting, but includeSlices lacks a schema description — the description doesn't clarify it either.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (full structured state of a sprite) and enumerates the exact contents: dimensions, colour mode, palette, layers, frames, tags, slices, selection. Distinguishes itself from siblings like layer, frame, palette which each cover subsets.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clear directive: 'Read this before editing — guessing at layer names or frame counts is the most common way an agent corrupts someone's file.' Strong when-to-use guidance tied to a concrete failure mode. No explicit when-not-to-use or named alternatives beyond the general edit workflow.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sprite_manageManage spritesADestructive
Open, create, focus, resize, save and close sprites in the running Aseprite session. Ops: 'list' (open documents), 'new', 'open', 'activate', 'save', 'save_as', 'close', 'resize_canvas', 'set_properties', 'slice_create', 'slice_update', 'slice_delete'. Canvas resize keeps existing pixels — pass an anchor to say where they land. Slices name a rectangular region of the canvas for an engine to read back — a 9-patch panel's center, or a hotspot's pivot.
| Name | Required | Description | Default |
|---|---|---|---|
| op | Yes | ||
| name | No | Slice name. Required for 'slice_create'; identifies it for update/delete. | |
| path | No | File path for 'open' and 'save_as'. | |
| color | No | For slice ops: slice colour in the timeline, #rrggbb. | |
| force | No | Allow 'close' to discard unsaved changes. Ask the user before setting this. | |
| pivot | No | For slice ops. | |
| width | No | ||
| anchor | No | Where existing pixels sit after 'resize_canvas'. | center |
| bounds | No | Slice rectangle. Required for 'slice_create'. | |
| center | No | For slice ops: nine-slice centre region, relative to `bounds`. | |
| height | No | ||
| sprite | No | Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused. | |
| colorMode | No | For 'new'. Indexed keeps a sprite honest about its palette. | |
| pixelAspect | No | e.g. '1:1' or '1:2'. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | Stable id of the affected document; pass it back as '#<id>'. |
| op | Yes | |
| name | No | Slice name, for 'slice_delete'. |
| path | No | |
| slice | No | The affected slice, for 'slice_create'/'slice_update'. |
| width | No | |
| height | No | |
| sprite | No | |
| sprites | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, idempotentHint=false and readOnlyHint=false, so the safety profile is carried by structured data. The description adds useful operational context beyond that ('Canvas resize keeps existing pixels', what slices are for), but it never says which ops are the destructive ones (close/save), nor that ops act on a session-bound focused sprite. Solid addition, not rich.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three dense sentences, front-loaded with the verb/resource scope before the op list and the two clarifying notes. Every sentence contributes (op inventory, resize behaviour, slice semantics); no filler, though the op list is long by necessity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 14-parameter, 12-op multiplexed tool with nested objects and an output schema, the description gives an adequate map: full op inventory plus targeted clarifications for the two least obvious families (resize anchor, slices). It stops short of per-op parameter pairing, but the schema's per-field op hints largely cover that.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is a high 79%, so the baseline is 3, and the description genuinely adds meaning beyond it: the anchor parameter's effect on where existing pixels land, and the purpose of slice fields (nine-slice 'center', hotspot 'pivot'). It clarifies semantics of ops-scoped parameters rather than just restating names.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb list and resource ('Open, create, focus, resize, save and close sprites in the running Aseprite session') and enumerates all 12 supported ops, so the agent knows the exact capability surface. It does not, however, name or differentiate itself from close siblings like sprite_info, transform, or look, leaving that routing to inference.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied through the op enumeration and one conditional note ('Omit to use the sprite Aseprite has focused'). There is no explicit when-to-use-this-vs-alternative guidance, e.g. why resize_canvas here rather than transform, or when to prefer sprite_info over 'list'. A capable agent can infer intent from op names, but nothing routes it deliberately.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tagAnimation tagsADestructive
Manage animation tags — the named frame ranges a game engine imports as 'idle', 'walk', 'attack'. Ops: 'list', 'create', 'update', 'delete'. Tag every cycle before export; an untagged spritesheet is a pile of frames the engine cannot address.
| Name | Required | Description | Default |
|---|---|---|---|
| op | Yes | ||
| to | No | Last frame, 1-based, inclusive. | |
| from | No | First frame, 1-based, inclusive. | |
| name | No | ||
| color | No | Tag colour in the timeline, #rrggbb. | |
| sprite | No | Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused. | |
| newName | No | ||
| repeats | No | Loop count; 0 means forever. | |
| direction | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| tags | Yes | |
| sprite | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already indicate destructiveHint=true and readOnlyHint=false, so the agent is aware this can modify state. The description adds that untagged spritesheets are unusable by the engine, but it does not disclose behavioral details such as immediate persistence or side effects of update/delete. This is adequate but not rich.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact, front-loaded with the core concept, and every sentence earns its place. The first sentence defines the resource and operations, and the second gives a practical workflow warning without padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers what tags are and why they matter, and the schema provides parameter details. However, it does not clarify which parameters are required for each operation (e.g., create vs. delete vs. update), which is a notable gap for correct invocation. Output schema exists, so return-value documentation is not the missing piece.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already documents several parameters (from, to, color, sprite, repeats), while 'name' and 'newName' benefit only from the description's 'named frame ranges' context. With 56% schema coverage, the description adds some conceptual meaning but does not fully compensate for the undocumented parameters or op-specific requirements.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool manages animation tags (named frame ranges) and enumerates the supported operations: list, create, update, delete. It uses concrete examples like 'idle', 'walk', 'attack' to make the resource unambiguous. It does not explicitly differentiate from sibling tools, but the resource and examples make the purpose clear.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives a clear workflow cue: 'Tag every cycle before export', and explains why tagging matters. It does not explicitly state when not to use this tool or point to alternatives among the siblings, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tilesetTilesetsA
Work with Aseprite tilemap layers. Ops:
• 'list' — tilesets in the sprite.
• 'create_layer' — a new empty tilemap layer on a given grid.
• 'get' — tile count and, with a path, the tiles as one packed PNG.
• 'stamp' — place tiles by grid coordinate. x/y are grid cells, not pixels.
• 'pack' — turn a hand-painted mockup layer into a tileset plus a tilemap that reconstructs it exactly. Deduplicates identical cells; tolerance also merges near-identical ones. The canvas must be a whole number of tiles, and the source layer is hidden rather than deleted so you can compare.
• 'export' — writes the packed PNG plus the file the engine reads: 'tiled' (.tsj tileset and a .tmj map that uses it), 'godot' (Godot 4 .tres TileSet), or 'json' (tile grid plus the tilemap layout).
Painting a level by hand and packing it produces better tilesets than authoring tiles in isolation, because you see the whole picture while drawing. layout: 'blob47' adds a Tiled wangset for autotiling, and assumes the tileset is authored in canonical blob47 order after the empty tile — it refuses rather than writing a wangset that would autotile wrongly. Requires an extension build advertising the 'tileset' feature — check preflight first.
| Name | Required | Description | Default |
|---|---|---|---|
| op | Yes | ||
| name | No | ||
| path | No | Output path for 'export' and 'get'. | |
| frame | No | 1-based frame number. Omit to use the active frame. | |
| layer | No | Layer name. Omit to use the active layer. | |
| tiles | No | For 'stamp'. x/y are tile-grid cells, not pixels. | |
| format | No | tiled | |
| layout | No | Autotile layout for 'export'. | grid |
| sprite | No | Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused. | |
| tileWidth | No | ||
| tolerance | No | For 'pack': maximum per-channel difference at which two cells count as the same tile. 0 means exact, which is also much faster — anything above 0 compares every cell against every tile found so far. | |
| tileHeight | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| op | Yes | |
| rows | No | |
| files | No | |
| layer | No | |
| format | No | |
| layout | No | |
| sprite | Yes | |
| columns | No | |
| skipped | No | For 'stamp': placements outside the grid or naming a tile that does not exist. |
| tilesets | No | |
| cellCount | No | For 'pack': grid cells examined. |
| tileCount | No | |
| tileWidth | No | |
| tileHeight | No | |
| reusedExact | No | For 'pack': cells that matched an existing tile exactly. |
| reusedFuzzy | No | For 'pack': cells merged by `tolerance`. A high number here with a low tolerance means the mockup has near-duplicate tiles worth cleaning up. |
| sourceLayer | No | For 'pack': the mockup layer, now hidden. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only set readOnlyHint=false and openWorldHint=false, leaving the description to carry behavioral detail. It discloses non-obvious behaviors: 'stamp' uses grid cells not pixels, 'pack' deduplicates and hides (not deletes) the source layer, 'blob47' refuses when ordering is wrong, and 'tolerance' has performance implications. This goes well beyond the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but appropriately so for a multi-operation tool. It front-loads the purpose, then lists ops in a scannable bullet format, and adds a rationale paragraph at the end. Every sentence serves a purpose, with no filler. It could be slightly more compact, but the structure aids readability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 12 parameters and six operations, the description covers most critical details: output formats, grid semantics, preconditions, and error behavior. An output schema exists (per context), which likely covers return structures. Minor gaps include exact return values for 'list' and 'create_layer', but these are not critical for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 58%, so the description must compensate for undocumented parameters. It explains 'tolerance', 'layout', 'sprite', and 'tiles' in detail, and clarifies grid-vs-pixel semantics. While 'name', 'tileWidth', and 'tileHeight' lack description-level context, they are straightforward from the schema defaults and bounds. The description meaningfully enhances understanding of the complex parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description begins with 'Work with Aseprite tilemap layers' and then enumerates six distinct operations (list, create_layer, get, stamp, pack, export). It clearly states what the tool does and each op has a specific verb and resource. It distinguishes itself from sibling tools like 'layer' and 'export' by focusing on tileset-specific functionality, so an agent can immediately understand its scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear guidance on when to use the 'pack' operation ('Painting a level by hand and packing it produces better tilesets than authoring tiles in isolation') and warns about prerequisites ('Requires an extension build advertising the 'tileset' feature — check preflight first'). It does not explicitly contrast with sibling tools like 'layer' or 'export', but the internal op routing is clear and context is sufficient.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
transformTransformA
Move, flip, rotate or scale pixels on ONE cel — the target layer's image on the target frame. Ops: 'translate', 'flip', 'rotate', 'scale', 'outline', 'crop_to_content'. To transform part of a cel, crop the region out with draw op 'blit' first; there is no selection-scoped transform. Rotation is only clean at 90° multiples — arbitrary angles destroy pixel art, so anything else needs allowLossy. Scaling is nearest-neighbour and integer-only for the same reason.
| Name | Required | Description | Default |
|---|---|---|---|
| dx | No | ||
| dy | No | ||
| op | Yes | ||
| axis | No | For 'flip'. | |
| side | No | For 'outline'. 'outside' paints transparent pixels touching opaque ones, growing the cel by one pixel each way. 'inside' recolours opaque pixels that touch transparency instead, same size in and out. | outside |
| angle | No | Degrees, clockwise. For 'rotate'. | |
| color | No | Outline colour, for 'outline'. | |
| frame | No | 1-based frame number. Omit to use the active frame. | |
| layer | No | Layer name. Omit to use the active layer. | |
| factor | No | For 'scale'. | |
| sprite | No | Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused. | |
| diagonals | No | For 'outline'. Treat diagonal neighbours as touching too (8-neighbour instead of 4). | |
| thickness | No | ||
| allowLossy | No | Permit a non-90° rotation or non-integer scale. Ask the user first. |
Output Schema
| Name | Required | Description |
|---|---|---|
| op | Yes | |
| bounds | No | |
| sprite | Yes | |
| pixelsChanged | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the mutation/read profile, and the description adds meaningful behavioral context on top: rotation is clean only at 90° multiples, arbitrary angles 'destroy pixel art' and require `allowLossy`, and scaling is nearest-neighbour and integer-only. This is genuinely useful operational disclosure beyond the annotation trio. It does not describe output or failure modes, keeping it from a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with the verb/resource/scope, then the op list, then the two constraints. Every clause carries information (no selection transform, lossy gating, integer scaling) with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 14-parameter mutation tool with an output schema present, the description covers scope, ops, lossy caveats and the partial-transform workaround adequately. Minor gaps remain, e.g. how translation handles out-of-bounds pixels (clip vs wrap) and the default outline color, but nothing blocking correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 71%, so the schema documents most parameters (axis, side, angle, color, factor, sprite, diagonals, allowLossy). The description still adds value by defining op-level semantics and the integer/90° constraints that explain why `factor` is integer-only and why `allowLossy` exists, which the schema's max/min bounds alone do not convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb set and resource ('Move, flip, rotate or scale pixels on ONE cel'), scopes it precisely to 'the target layer's image on the target frame', and enumerates the six ops. It also distinguishes itself from siblings by naming `draw` and ruling out any selection-scoped transform, so an agent can tell it apart from `select`/`draw` without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a concrete when-not case and a workaround: partial transforms must go through `draw` op 'blit' because 'there is no selection-scoped transform'. It also gates lossy behavior behind `allowLossy` with an explicit 'Ask the user first'. No explicit statement of the ordinary when-to-use happy path, but the boundaries are clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
validateValidateARead-only
Lint a sprite against pixel-art rules and report concrete, located problems. Checks: off-palette colours, orphan/stray pixels, broken or doubled outlines, banding, unintentional anti-aliasing on a hard-edged sprite, odd-pixel asymmetry, empty layers, untagged frames, inconsistent frame timing, and cross-frame volume drift in an animation. Run this before telling the user a sprite is finished. It answers 'is this actually done' with evidence rather than optimism.
| Name | Required | Description | Default |
|---|---|---|---|
| checks | No | Omit to run everything. | |
| expect | No | The animation contract you already know. Runs independently of `checks`, whenever given: flags art outside a layer's expected frame ranges (or a missing cel inside one), and any frame where two named layers' opaque pixels overlap. | |
| sprite | No | Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused. | |
| strict | No | Treat style warnings as failures. Use when the user asked for a specific discipline. |
Output Schema
| Name | Required | Description |
|---|---|---|
| score | Yes | |
| passed | Yes | |
| sprite | Yes | |
| summary | Yes | |
| findings | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, so safety is covered; the description adds real value on top by enumerating the inspected fault classes (off-palette, strays, outlines, banding, anti-aliasing, asymmetry, empty layers, untagged frames, timing, volume drift) and promising located evidence. It stops short of saying whether findings block anything or how they are structured.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with verb + resource, then the check catalogue, then a one-line usage rule and rationale. The long middle sentence is dense but every clause maps to a distinct, verifiable check rather than padding, so it reads as substantive rather than verbose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be described, and parameters are fully covered by the schema. The description is complete enough for a read-only linter: it states what is examined, when to run it, and what kind of result to expect. Only the relationship to the `preflight` sibling is left unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, and `expect`/`strict`/`sprite` are fully documented in the schema, which sets a baseline of 3. The description earns an extra point by glossing the otherwise opaque `checks` enum values — it explains that 'palette' means off-palette colours, 'strays' means orphan pixels, and so on — adding semantics the bare enum strings do not carry.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb + resource ('Lint a sprite'), followed by an explicit catalogue of what is checked, so an agent knows exactly what class of operation this is. It falls short of a 5 only because it never distinguishes itself from the sibling `preflight`, which reads as an overlapping pre-export verification tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear timing guidance — 'Run this before telling the user a sprite is finished' — and frames the tool's question as 'is this actually done'. It does not, however, name an alternative or state when this should be skipped in favour of `preflight` or `sprite_info`, so no exclusions are offered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
11 tool updates
v0.4.0- Changed
cel10 fields changed- added
Input schema / properties / amplitudeXAdded value: +{ + "default": 0, + "description": "For 'oscillate'.", + "type": "integer" +} - added
Input schema / properties / amplitudeYAdded value: +{ + "default": 0, + "description": "For 'oscillate'.", + "type": "integer" +} - added
Input schema / properties / easingAdded value: +{ + "default": "linear", + "description": "For 'tween'.", + "enum": [ + "linear", + "ease_in", + "ease_out", + "ease_in_out", + "smoothstep" + ], + "type": "string" +} - added
Input schema / properties / fromFrameAdded value: +{ + "description": "For 'tween'/'oscillate': the start frame.", + "exclusiveMinimum": 0, + "type": "integer" +} - changed
Input schema / properties / op / enumPrevious value: -[ - "list", - "create", - "clear", - "delete", - "move", - "copy", - "link", - "unlink", - "set" -]New value: +[ + "list", + "create", + "clear", + "delete", + "move", + "copy", + "link", + "unlink", + "set", + "tween", + "oscillate" +] - added
Input schema / properties / periodAdded value: +{ + "description": "For 'oscillate', in frames. Default: the whole fromFrame..toFrame span.", + "minimum": 2, + "type": "integer" +} - added
Input schema / properties / phaseAdded value: +{ + "default": 0, + "description": "For 'oscillate': 0-1 fraction of one period.", + "maximum": 1, + "minimum": 0, + "type": "number" +} - added
Input schema / properties / propertyAdded value: +{ + "description": "For 'tween'.", + "enum": [ + "position", + "opacity" + ], + "type": "string" +} - added
Input schema / properties / toAdded value: +{ + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": [ + "x", + "y" + ], + "type": "object" + }, + { + "maximum": 255, + "minimum": 0, + "type": "integer" + } + ], + "description": "For 'tween': end value — {x,y} for property 'position', 0-255 for 'opacity'." +} - added
Output schema / properties / framesAdded value: +{ + "description": "Only present for 'tween' and 'oscillate': one entry per frame touched, ascending.", + "items": { + "additionalProperties": false, + "properties": { + "frame": { + "type": "integer" + }, + "opacity": { + "type": "integer" + }, + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": [ + "frame" + ], + "type": "object" + }, + "type": "array" +}
- Changed
draw5 fields changed- added
Input schema / properties / measureOnlyAdded value: +{ + "default": false, + "description": "Every op must be 'text'. Returns each one's ink bounds without calling Aseprite at all — use to size a panel or centre a label before actually drawing it.", + "type": "boolean" +} - changed
Input schema / properties / ops / items / anyOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "color": { - "description": "Colour as #rrggbb or #rrggbbaa.", - "pattern": "^#?([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$", - "type": "string" - }, - "kind": { - "const": "pixels", - "type": "string" - }, - "points": { - "items": { - "additionalProperties": false, - "properties": { - "color": { - "$ref": "#/properties/ops/items/anyOf/0/properties/color", - "description": "Per-pixel colour. Falls back to the op's `color`." - }, - "x": { - "type": "integer" - }, - "y": { - "type": "integer" - } - }, - "required": [ - "x", - "y" - ], - "type": "object" - }, - "maxItems": 20000, - "minItems": 1, - "type": "array" - } - }, - "required": [ - "kind", - "points" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "color": { - "$ref": "#/properties/ops/items/anyOf/0/properties/color" - }, - "from": { - "additionalProperties": false, - "properties": { - "x": { - "type": "integer" - }, - "y": { - "type": "integer" - } - }, - "required": [ - "x", - "y" - ], - "type": "object" - }, - "kind": { - "const": "line", - "type": "string" - }, - "thickness": { - "default": 1, - "exclusiveMinimum": 0, - "maximum": 64, - "type": "integer" - }, - "to": { - "additionalProperties": false, - "properties": { - "x": { - "type": "integer" - }, - "y": { - "type": "integer" - } - }, - "required": [ - "x", - "y" - ], - "type": "object" - } - }, - "required": [ - "kind", - "color", - "from", - "to" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "closed": { - "default": false, - "type": "boolean" - }, - "color": { - "$ref": "#/properties/ops/items/anyOf/0/properties/color" - }, - "fill": { - "$ref": "#/properties/ops/items/anyOf/0/properties/color", - "description": "Fill colour; only meaningful when closed." - }, - "kind": { - "const": "polyline", - "type": "string" - }, - "points": { - "items": { - "additionalProperties": false, - "properties": { - "x": { - "type": "integer" - }, - "y": { - "type": "integer" - } - }, - "required": [ - "x", - "y" - ], - "type": "object" - }, - "minItems": 2, - "type": "array" - } - }, - "required": [ - "kind", - "color", - "points" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "color": { - "description": "Outline colour.", - "pattern": "^#?([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$", - "type": "string" - }, - "fill": { - "$ref": "#/properties/ops/items/anyOf/0/properties/color", - "description": "Omit for an outline only." - }, - "kind": { - "const": "rect", - "type": "string" - }, - "rect": { - "additionalProperties": false, - "properties": { - "height": { - "exclusiveMinimum": 0, - "type": "integer" - }, - "width": { - "exclusiveMinimum": 0, - "type": "integer" - }, - "x": { - "type": "integer" - }, - "y": { - "type": "integer" - } - }, - "required": [ - "x", - "y", - "width", - "height" - ], - "type": "object" - } - }, - "required": [ - "kind", - "color", - "rect" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "color": { - "$ref": "#/properties/ops/items/anyOf/0/properties/color" - }, - "fill": { - "$ref": "#/properties/ops/items/anyOf/0/properties/color", - "description": "Colour as #rrggbb or #rrggbbaa." - }, - "kind": { - "const": "ellipse", - "type": "string" - }, - "rect": { - "additionalProperties": false, - "description": "Bounding box of the ellipse.", - "properties": { - "height": { - "$ref": "#/properties/ops/items/anyOf/3/properties/rect/properties/height" - }, - "width": { - "$ref": "#/properties/ops/items/anyOf/3/properties/rect/properties/width" - }, - "x": { - "$ref": "#/properties/ops/items/anyOf/3/properties/rect/properties/x" - }, - "y": { - "$ref": "#/properties/ops/items/anyOf/3/properties/rect/properties/y" - } - }, - "required": [ - "x", - "y", - "width", - "height" - ], - "type": "object" - } - }, - "required": [ - "kind", - "color", - "rect" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "at": { - "additionalProperties": false, - "properties": { - "x": { - "type": "integer" - }, - "y": { - "type": "integer" - } - }, - "required": [ - "x", - "y" - ], - "type": "object" - }, - "color": { - "$ref": "#/properties/ops/items/anyOf/0/properties/color" - }, - "contiguous": { - "default": true, - "type": "boolean" - }, - "kind": { - "const": "fill", - "type": "string" - }, - "tolerance": { - "default": 0, - "maximum": 255, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "kind", - "color", - "at" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "from": { - "$ref": "#/properties/ops/items/anyOf/0/properties/color" - }, - "kind": { - "const": "replace", - "type": "string" - }, - "region": { - "$ref": "#/properties/ops/items/anyOf/3/properties/rect" - }, - "to": { - "$ref": "#/properties/ops/items/anyOf/0/properties/color" - } - }, - "required": [ - "kind", - "from", - "to" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "colorA": { - "$ref": "#/properties/ops/items/anyOf/0/properties/color" - }, - "colorB": { - "$ref": "#/properties/ops/items/anyOf/0/properties/color" - }, - "kind": { - "const": "dither", - "type": "string" - }, - "pattern": { - "default": "bayer4", - "enum": [ - "checker", - "bayer2", - "bayer4", - "bayer8", - "noise" - ], - "type": "string" - }, - "ratio": { - "default": 0.5, - "description": "0 = all colorA, 1 = all colorB.", - "maximum": 1, - "minimum": 0, - "type": "number" - }, - "rect": { - "$ref": "#/properties/ops/items/anyOf/3/properties/rect" - } - }, - "required": [ - "kind", - "rect", - "colorA", - "colorB" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "direction": { - "default": "vertical", - "enum": [ - "vertical", - "horizontal", - "diagonal", - "radial" - ], - "type": "string" - }, - "dither": { - "default": false, - "description": "Dither the band boundaries.", - "type": "boolean" - }, - "from": { - "$ref": "#/properties/ops/items/anyOf/0/properties/color" - }, - "kind": { - "const": "gradient", - "type": "string" - }, - "rect": { - "$ref": "#/properties/ops/items/anyOf/3/properties/rect" - }, - "steps": { - "default": 4, - "description": "Banded, not smooth — a smooth gradient is not pixel art.", - "maximum": 64, - "minimum": 2, - "type": "integer" - }, - "to": { - "$ref": "#/properties/ops/items/anyOf/0/properties/color" - } - }, - "required": [ - "kind", - "rect", - "from", - "to" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "kind": { - "const": "clear", - "type": "string" - }, - "region": { - "$ref": "#/properties/ops/items/anyOf/3/properties/rect", - "description": "Omit to clear the whole cel." - } - }, - "required": [ - "kind" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "flipHorizontal": { - "default": false, - "type": "boolean" - }, - "flipVertical": { - "default": false, - "type": "boolean" - }, - "from": { - "$ref": "#/properties/ops/items/anyOf/3/properties/rect" - }, - "fromFrame": { - "exclusiveMinimum": 0, - "type": "integer" - }, - "fromLayer": { - "type": "string" - }, - "kind": { - "const": "blit", - "type": "string" - }, - "skipTransparent": { - "default": true, - "type": "boolean" - }, - "to": { - "additionalProperties": false, - "properties": { - "x": { - "type": "integer" - }, - "y": { - "type": "integer" - } - }, - "required": [ - "x", - "y" - ], - "type": "object" - } - }, - "required": [ - "kind", - "from", - "to" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "color": { + "description": "Colour as #rrggbb or #rrggbbaa.", + "pattern": "^#?([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$", + "type": "string" + }, + "kind": { + "const": "pixels", + "type": "string" + }, + "points": { + "items": { + "additionalProperties": false, + "properties": { + "color": { + "$ref": "#/properties/ops/items/anyOf/0/properties/color", + "description": "Per-pixel colour. Falls back to the op's `color`." + }, + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": [ + "x", + "y" + ], + "type": "object" + }, + "maxItems": 20000, + "minItems": 1, + "type": "array" + } + }, + "required": [ + "kind", + "points" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "color": { + "$ref": "#/properties/ops/items/anyOf/0/properties/color" + }, + "from": { + "additionalProperties": false, + "properties": { + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": [ + "x", + "y" + ], + "type": "object" + }, + "kind": { + "const": "line", + "type": "string" + }, + "thickness": { + "default": 1, + "exclusiveMinimum": 0, + "maximum": 64, + "type": "integer" + }, + "to": { + "additionalProperties": false, + "properties": { + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": [ + "x", + "y" + ], + "type": "object" + } + }, + "required": [ + "kind", + "color", + "from", + "to" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "closed": { + "default": false, + "type": "boolean" + }, + "color": { + "$ref": "#/properties/ops/items/anyOf/0/properties/color" + }, + "fill": { + "$ref": "#/properties/ops/items/anyOf/0/properties/color", + "description": "Fill colour; only meaningful when closed." + }, + "kind": { + "const": "polyline", + "type": "string" + }, + "points": { + "items": { + "additionalProperties": false, + "properties": { + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": [ + "x", + "y" + ], + "type": "object" + }, + "minItems": 2, + "type": "array" + } + }, + "required": [ + "kind", + "color", + "points" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "color": { + "description": "Outline colour.", + "pattern": "^#?([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$", + "type": "string" + }, + "fill": { + "$ref": "#/properties/ops/items/anyOf/0/properties/color", + "description": "Omit for an outline only." + }, + "kind": { + "const": "rect", + "type": "string" + }, + "rect": { + "additionalProperties": false, + "properties": { + "height": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "width": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": [ + "x", + "y", + "width", + "height" + ], + "type": "object" + } + }, + "required": [ + "kind", + "color", + "rect" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "color": { + "$ref": "#/properties/ops/items/anyOf/0/properties/color" + }, + "fill": { + "$ref": "#/properties/ops/items/anyOf/0/properties/color", + "description": "Colour as #rrggbb or #rrggbbaa." + }, + "kind": { + "const": "ellipse", + "type": "string" + }, + "rect": { + "additionalProperties": false, + "description": "Bounding box of the ellipse.", + "properties": { + "height": { + "$ref": "#/properties/ops/items/anyOf/3/properties/rect/properties/height" + }, + "width": { + "$ref": "#/properties/ops/items/anyOf/3/properties/rect/properties/width" + }, + "x": { + "$ref": "#/properties/ops/items/anyOf/3/properties/rect/properties/x" + }, + "y": { + "$ref": "#/properties/ops/items/anyOf/3/properties/rect/properties/y" + } + }, + "required": [ + "x", + "y", + "width", + "height" + ], + "type": "object" + } + }, + "required": [ + "kind", + "color", + "rect" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "at": { + "additionalProperties": false, + "properties": { + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": [ + "x", + "y" + ], + "type": "object" + }, + "color": { + "$ref": "#/properties/ops/items/anyOf/0/properties/color" + }, + "contiguous": { + "default": true, + "type": "boolean" + }, + "kind": { + "const": "fill", + "type": "string" + }, + "tolerance": { + "default": 0, + "maximum": 255, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "kind", + "color", + "at" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "from": { + "$ref": "#/properties/ops/items/anyOf/0/properties/color" + }, + "kind": { + "const": "replace", + "type": "string" + }, + "region": { + "$ref": "#/properties/ops/items/anyOf/3/properties/rect" + }, + "to": { + "$ref": "#/properties/ops/items/anyOf/0/properties/color" + } + }, + "required": [ + "kind", + "from", + "to" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "colorA": { + "$ref": "#/properties/ops/items/anyOf/0/properties/color" + }, + "colorB": { + "$ref": "#/properties/ops/items/anyOf/0/properties/color" + }, + "kind": { + "const": "dither", + "type": "string" + }, + "pattern": { + "default": "bayer4", + "enum": [ + "checker", + "bayer2", + "bayer4", + "bayer8", + "noise" + ], + "type": "string" + }, + "ratio": { + "default": 0.5, + "description": "0 = all colorA, 1 = all colorB.", + "maximum": 1, + "minimum": 0, + "type": "number" + }, + "rect": { + "$ref": "#/properties/ops/items/anyOf/3/properties/rect" + } + }, + "required": [ + "kind", + "rect", + "colorA", + "colorB" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "direction": { + "default": "vertical", + "enum": [ + "vertical", + "horizontal", + "diagonal", + "radial" + ], + "type": "string" + }, + "dither": { + "default": false, + "description": "Dither the band boundaries.", + "type": "boolean" + }, + "from": { + "$ref": "#/properties/ops/items/anyOf/0/properties/color" + }, + "kind": { + "const": "gradient", + "type": "string" + }, + "rect": { + "$ref": "#/properties/ops/items/anyOf/3/properties/rect" + }, + "steps": { + "default": 4, + "description": "Banded, not smooth — a smooth gradient is not pixel art.", + "maximum": 64, + "minimum": 2, + "type": "integer" + }, + "to": { + "$ref": "#/properties/ops/items/anyOf/0/properties/color" + } + }, + "required": [ + "kind", + "rect", + "from", + "to" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "kind": { + "const": "clear", + "type": "string" + }, + "region": { + "$ref": "#/properties/ops/items/anyOf/3/properties/rect", + "description": "Omit to clear the whole cel." + } + }, + "required": [ + "kind" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "flipHorizontal": { + "default": false, + "type": "boolean" + }, + "flipVertical": { + "default": false, + "type": "boolean" + }, + "from": { + "$ref": "#/properties/ops/items/anyOf/3/properties/rect" + }, + "fromFrame": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "fromLayer": { + "type": "string" + }, + "kind": { + "const": "blit", + "type": "string" + }, + "skipTransparent": { + "default": true, + "type": "boolean" + }, + "to": { + "additionalProperties": false, + "properties": { + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": [ + "x", + "y" + ], + "type": "object" + } + }, + "required": [ + "kind", + "from", + "to" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "anchor": { + "default": "top_left", + "description": "Resolved against the glyph ink box, not the full advance box — outline and shadow never shift it.", + "enum": [ + "top_left", + "top", + "top_right", + "left", + "center", + "right", + "bottom_left", + "bottom", + "bottom_right", + "baseline_left", + "baseline", + "baseline_right" + ], + "type": "string" + }, + "bold": { + "default": 0, + "description": "Grid-cell passes grown rightward; 0 is regular weight.", + "maximum": 3, + "minimum": 0, + "type": "integer" + }, + "color": { + "$ref": "#/properties/ops/items/anyOf/0/properties/color" + }, + "font": { + "default": "pixel5x7", + "description": "Name in knowledge/fonts, without the .json extension.", + "type": "string" + }, + "kind": { + "const": "text", + "type": "string" + }, + "letterSpacing": { + "default": 1, + "type": "integer" + }, + "lineSpacing": { + "default": 1, + "type": "integer" + }, + "outlineColor": { + "$ref": "#/properties/ops/items/anyOf/0/properties/color", + "description": "Colour as #rrggbb or #rrggbbaa." + }, + "outlineDiagonals": { + "default": true, + "description": "8-neighbour outline instead of 4.", + "type": "boolean" + }, + "scale": { + "default": 1, + "maximum": 8, + "minimum": 1, + "type": "integer" + }, + "shadowColor": { + "$ref": "#/properties/ops/items/anyOf/0/properties/color", + "description": "Colour as #rrggbb or #rrggbbaa." + }, + "shadowOffset": { + "additionalProperties": false, + "default": { + "x": 1, + "y": 1 + }, + "properties": { + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": [ + "x", + "y" + ], + "type": "object" + }, + "text": { + "description": "Multiline via \\n.", + "minLength": 1, + "type": "string" + }, + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": [ + "kind", + "text", + "x", + "y", + "color" + ], + "type": "object" + } +] - added
Output schema / properties / measureOnlyAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / textBoundsAdded value: +{ + "description": "Only present when `measureOnly` was set.", + "items": { + "additionalProperties": false, + "properties": { + "bounds": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "height": { + "type": "integer" + }, + "width": { + "type": "integer" + }, + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": [ + "x", + "y", + "width", + "height" + ], + "type": "object" + }, + { + "type": "null" + } + ], + "description": "Ink bounds of the laid-out text; null for text with no ink (e.g. all spaces)." + }, + "index": { + "description": "Position of this op in the `ops` array.", + "type": "integer" + } + }, + "required": [ + "index" + ], + "type": "object" + }, + "type": "array" +} - removed
Output schema / requiredRemoved value: -[ - "sprite", - "layer", - "frame", - "opsApplied", - "pixelsChanged", - "colorsSnapped" -]
- Changed
layer3 fields changed- added
Input schema / properties / batch / items / properties / toSpriteAdded value: +{ + "description": "For 'duplicate': copy the layer into another OPEN sprite instead of this one.", + "type": "string" +} - added
Input schema / properties / toSpriteAdded value: +{ + "description": "For 'duplicate': copy the layer into another OPEN sprite instead of this one.", + "type": "string" +} - added
Output schema / properties / duplicatedAdded value: +{ + "description": "Present when a 'duplicate' op in this call used `toSprite`.", + "items": { + "additionalProperties": false, + "properties": { + "framesDropped": { + "description": "Cels whose frame index exceeded the target's frame count.", + "type": "integer" + }, + "name": { + "description": "Name it got in the target sprite; suffixed on a name clash.", + "type": "string" + }, + "sourceLayer": { + "type": "string" + }, + "toSprite": { + "description": "Target as '#<id>' — stable even when several documents share a name.", + "type": "string" + } + }, + "required": [ + "sourceLayer", + "name", + "toSprite", + "framesDropped" + ], + "type": "object" + }, + "type": "array" +}
- Changed
look9 fields changed- added
Input schema / properties / framesAfterAdded value: +{ + "default": 1, + "description": "Onion: ghost frames after the target.", + "maximum": 8, + "minimum": 0, + "type": "integer" +} - added
Input schema / properties / framesBeforeAdded value: +{ + "default": 1, + "description": "Onion: ghost frames before the target.", + "maximum": 8, + "minimum": 0, + "type": "integer" +} - added
Input schema / properties / ghostOpacityAdded value: +{ + "default": 90, + "description": "Onion: opacity of the ghosted (non-target) frames.", + "maximum": 255, + "minimum": 0, + "type": "integer" +} - changed
Input schema / properties / op / enumPrevious value: -[ - "preview", - "ascii", - "filmstrip", - "diff" -]New value: +[ + "preview", + "ascii", + "filmstrip", + "diff", + "onion", + "compare" +] - added
Input schema / properties / referenceAdded value: +{ + "description": "Compare: the reference layer. Default 'reference'.", + "type": "string" +} - added
Output schema / properties / changedBoundsAdded value: +{ + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "height": { + "type": "integer" + }, + "width": { + "type": "integer" + }, + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": [ + "x", + "y", + "width", + "height" + ], + "type": "object" + }, + { + "type": "null" + } + ], + "description": "Diff: tight bounding box of every changed pixel. Null when nothing changed." +} - added
Output schema / properties / frames / descriptionAdded value: +"Filmstrip: frame count." - added
Output schema / properties / framesUsedAdded value: +{ + "description": "Onion: 1-based frame numbers composited.", + "items": { + "type": "integer" + }, + "type": "array" +} - added
Output schema / properties / percentChangedAdded value: +{ + "description": "Diff: changed / total * 100.", + "type": "number" +}
- Changed
palette2 fields changed- added
Input schema / properties / maxColorsAdded value: +{ + "default": 16, + "description": "For op 'extract': target palette size.", + "maximum": 256, + "minimum": 2, + "type": "integer" +} - changed
Input schema / properties / op / enumPrevious value: -[ - "get", - "set", - "preset", - "load", - "ramp", - "analyze" -]New value: +[ + "get", + "set", + "preset", + "load", + "ramp", + "analyze", + "extract" +]
- Changed
preflight5 fields changed- added
Input schema / additionalPropertiesAdded value: +false - added
Input schema / properties / modeAdded value: +{ + "description": "Switch the session to this Aseprite before checking. Omit to keep the current one.", + "enum": [ + "live", + "headless" + ], + "type": "string" +} - added
Output schema / properties / modeAdded value: +{ + "description": "'live' = the user's open window. 'headless' = a batch Aseprite with no window; nothing is on disk until you save or export.", + "enum": [ + "live", + "headless" + ], + "type": "string" +} - added
Output schema / properties / switchedAdded value: +{ + "description": "True when this call changed the session's mode.", + "type": "boolean" +} - changed
Output schema / requiredPrevious value: -[ - "ready", - "bridgeConnected", - "pluginConnected", - "features", - "directive" -]New value: +[ + "ready", + "switched", + "mode", + "bridgeConnected", + "pluginConnected", + "features", + "directive" +]
- Changed
reference6 fields changed- changed
Input schema / properties / fit / descriptionPrevious value: -"How to size the image against the canvas."New value: +"How to size the image (or each panel) against the canvas." - added
Input schema / properties / frameAdded value: +{ + "description": "Import: the frame the image — or the first `grid` panel — lands on. Default: the active frame, or frame 1 with `grid`.", + "exclusiveMinimum": 0, + "type": "integer" +} - added
Input schema / properties / gridAdded value: +{ + "additionalProperties": false, + "description": "Import a storyboard: split the (cropped) source into equal panels and place panel i on frame `frame`+i-1 of one reference layer. The sprite must already have enough frames.", + "properties": { + "columns": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "count": { + "description": "Panels actually used, read row-major. Default: columns × rows.", + "exclusiveMinimum": 0, + "type": "integer" + }, + "gap": { + "default": 0, + "description": "Source pixels between panels.", + "minimum": 0, + "type": "integer" + }, + "rows": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "columns", + "rows" + ], + "type": "object" +} - added
Input schema / properties / regionAdded value: +{ + "additionalProperties": false, + "description": "Use only this rectangle of the SOURCE image, in its own pixels — one panel of a concept sheet. Applies to 'import' and 'sample_palette'.", + "properties": { + "height": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "width": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "x": { + "minimum": 0, + "type": "integer" + }, + "y": { + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "x", + "y", + "width", + "height" + ], + "type": "object" +} - added
Output schema / properties / framesAdded value: +{ + "description": "Import: the frames that received a reference cel.", + "items": { + "type": "integer" + }, + "type": "array" +} - added
Output schema / properties / panelsAdded value: +{ + "description": "Import: how many panels were placed.", + "type": "integer" +}
- Changed
sprite_info5 fields changed- changed
Output schema / properties / slices / items / properties / bounds / additionalPropertiesPrevious value: -{ - "type": "number" -}New value: +false - added
Output schema / properties / slices / items / properties / bounds / propertiesAdded value: +{ + "height": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "width": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } +} - added
Output schema / properties / slices / items / properties / bounds / requiredAdded value: +[ + "x", + "y", + "width", + "height" +] - added
Output schema / properties / slices / items / properties / centerAdded value: +{ + "$ref": "#/properties/slices/items/properties/bounds", + "description": "Nine-slice centre region, relative to the slice. Only when set." +} - added
Output schema / properties / slices / items / properties / pivotAdded value: +{ + "additionalProperties": false, + "properties": { + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": [ + "x", + "y" + ], + "type": "object" +}
- Changed
sprite_manage8 fields changed- added
Input schema / properties / boundsAdded value: +{ + "additionalProperties": false, + "description": "Slice rectangle. Required for 'slice_create'.", + "properties": { + "height": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "width": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": [ + "x", + "y", + "width", + "height" + ], + "type": "object" +} - added
Input schema / properties / centerAdded value: +{ + "$ref": "#/properties/bounds", + "description": "For slice ops: nine-slice centre region, relative to `bounds`." +} - added
Input schema / properties / colorAdded value: +{ + "description": "For slice ops: slice colour in the timeline, #rrggbb.", + "type": "string" +} - added
Input schema / properties / nameAdded value: +{ + "description": "Slice name. Required for 'slice_create'; identifies it for update/delete.", + "type": "string" +} - changed
Input schema / properties / op / enumPrevious value: -[ - "list", - "new", - "open", - "activate", - "save", - "save_as", - "close", - "resize_canvas", - "set_properties" -]New value: +[ + "list", + "new", + "open", + "activate", + "save", + "save_as", + "close", + "resize_canvas", + "set_properties", + "slice_create", + "slice_update", + "slice_delete" +] - added
Input schema / properties / pivotAdded value: +{ + "additionalProperties": false, + "description": "For slice ops.", + "properties": { + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": [ + "x", + "y" + ], + "type": "object" +} - added
Output schema / properties / nameAdded value: +{ + "description": "Slice name, for 'slice_delete'.", + "type": "string" +} - added
Output schema / properties / sliceAdded value: +{ + "additionalProperties": false, + "description": "The affected slice, for 'slice_create'/'slice_update'.", + "properties": { + "bounds": { + "additionalProperties": false, + "properties": { + "height": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "width": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": [ + "x", + "y", + "width", + "height" + ], + "type": "object" + }, + "center": { + "$ref": "#/properties/slice/properties/bounds", + "description": "Nine-slice centre region, relative to the slice. Only when set." + }, + "name": { + "type": "string" + }, + "pivot": { + "additionalProperties": false, + "properties": { + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": [ + "x", + "y" + ], + "type": "object" + } + }, + "required": [ + "name", + "bounds" + ], + "type": "object" +}
- Changed
transform2 fields changed- added
Input schema / properties / diagonalsAdded value: +{ + "default": false, + "description": "For 'outline'. Treat diagonal neighbours as touching too (8-neighbour instead of 4).", + "type": "boolean" +} - added
Input schema / properties / sideAdded value: +{ + "default": "outside", + "description": "For 'outline'. 'outside' paints transparent pixels touching opaque ones, growing the cel by one pixel each way. 'inside' recolours opaque pixels that touch transparency instead, same size in and out.", + "enum": [ + "outside", + "inside" + ], + "type": "string" +}
- Changed
validate1 field changed- added
Input schema / properties / expectAdded value: +{ + "additionalProperties": false, + "description": "The animation contract you already know. Runs independently of `checks`, whenever given: flags art outside a layer's expected frame ranges (or a missing cel inside one), and any frame where two named layers' opaque pixels overlap.", + "properties": { + "layerFrames": { + "additionalProperties": { + "items": { + "items": [ + { + "exclusiveMinimum": 0, + "type": "integer" + }, + { + "exclusiveMinimum": 0, + "type": "integer" + } + ], + "maxItems": 2, + "minItems": 2, + "type": "array" + }, + "type": "array" + }, + "description": "Per layer, the [from,to] frame ranges (1-based, inclusive) it should hold ink in.", + "type": "object" + }, + "mustNotOverlap": { + "description": "Layer name pairs whose opaque pixels must never intersect on the same frame.", + "items": { + "items": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "maxItems": 2, + "minItems": 2, + "type": "array" + }, + "type": "array" + } + }, + "type": "object" +}
17 tool updates
v0.1.6- Changed
cel1 field changed- changed
Input schema / properties / sprite / descriptionPrevious value: -"Sprite filename or id. Omit to use the sprite Aseprite has focused."New value: +"Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused."
- Changed
draw1 field changed- changed
Input schema / properties / sprite / descriptionPrevious value: -"Sprite filename or id. Omit to use the sprite Aseprite has focused."New value: +"Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused."
- Changed
export2 fields changed- changed
Input schema / properties / path / descriptionPrevious value: -"Output file path. For 'frames', a pattern like 'walk_{frame}.png'."New value: +"Output file path. For 'frames', a pattern containing {frame}, e.g. 'walk_{frame}.png' — Aseprite expands it into one file per frame, numbered from 0." - changed
Input schema / properties / sprite / descriptionPrevious value: -"Sprite filename or id. Omit to use the sprite Aseprite has focused."New value: +"Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused."
- Changed
frame1 field changed- changed
Input schema / properties / sprite / descriptionPrevious value: -"Sprite filename or id. Omit to use the sprite Aseprite has focused."New value: +"Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused."
- Changed
layer10 fields changed- changed
Input schema / properties / batch / descriptionPrevious value: -"Array of operation objects, each shaped like the single-op arguments. Applied in order."New value: +"Operations applied in order, in one undoable action. Same shape as the single-op arguments." - changed
Input schema / properties / batch / items / additionalPropertiesPrevious value: -{}New value: +false - added
Input schema / properties / batch / items / propertiesAdded value: +{ + "blendMode": { + "$ref": "#/properties/blendMode" + }, + "editable": { + "type": "boolean" + }, + "index": { + "description": "Target stack position; 0 is bottom.", + "type": "integer" + }, + "name": { + "type": "string" + }, + "names": { + "description": "For 'merge' and bulk 'group'.", + "items": { + "type": "string" + }, + "type": "array" + }, + "newName": { + "type": "string" + }, + "op": { + "$ref": "#/properties/op" + }, + "opacity": { + "maximum": 255, + "minimum": 0, + "type": "integer" + }, + "parent": { + "description": "Group layer to nest under.", + "type": "string" + }, + "visible": { + "type": "boolean" + } +} - added
Input schema / properties / batch / items / requiredAdded value: +[ + "op" +] - added
Input schema / properties / batch / maxItemsAdded value: +128 - changed
Input schema / properties / sprite / descriptionPrevious value: -"Sprite filename or id. Omit to use the sprite Aseprite has focused."New value: +"Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused." - added
Output schema / properties / layers / items / properties / celsAdded value: +{ + "description": "1-based frames that have a cel on this layer.", + "items": { + "type": "integer" + }, + "type": "array" +} - added
Output schema / properties / layers / items / properties / editableAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / layers / items / properties / isTilemapAdded value: +{ + "type": "boolean" +} - changed
Output schema / properties / layers / items / requiredPrevious value: -[ - "name", - "index", - "visible", - "opacity", - "blendMode", - "isGroup" -]New value: +[ + "name", + "index", + "visible", + "editable", + "opacity", + "blendMode", + "isGroup", + "isTilemap", + "cels" +]
- Changed
look3 fields changed- changed
Input schema / properties / scale / descriptionPrevious value: -"Integer upscale for image ops. Omitted means auto."New value: +"Integer upscale for image ops, 1-128 (clamped so the output stays under ~2048px). Omit it — the automatic choice targets a ~1024px long edge, which is what a vision model can actually read." - changed
Input schema / properties / scale / maximumPrevious value: -16New value: +128 - changed
Input schema / properties / sprite / descriptionPrevious value: -"Sprite filename or id. Omit to use the sprite Aseprite has focused."New value: +"Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused."
- Changed
palette2 fields changed- changed
Input schema / properties / sprite / descriptionPrevious value: -"Sprite filename or id. Omit to use the sprite Aseprite has focused."New value: +"Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused." - added
Output schema / properties / pathAdded value: +{ + "description": "Palette file read by op 'load'.", + "type": "string" +}
- Changed
read_pixels1 field changed- changed
Input schema / properties / sprite / descriptionPrevious value: -"Sprite filename or id. Omit to use the sprite Aseprite has focused."New value: +"Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused."
- Changed
recolor1 field changed- changed
Input schema / properties / sprite / descriptionPrevious value: -"Sprite filename or id. Omit to use the sprite Aseprite has focused."New value: +"Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused."
- Changed
reference1 field changed- changed
Input schema / properties / sprite / descriptionPrevious value: -"Sprite filename or id. Omit to use the sprite Aseprite has focused."New value: +"Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused."
- Changed
select1 field changed- changed
Input schema / properties / sprite / descriptionPrevious value: -"Sprite filename or id. Omit to use the sprite Aseprite has focused."New value: +"Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused."
- Changed
sprite_info5 fields changed- changed
Input schema / properties / sprite / descriptionPrevious value: -"Sprite filename or id. Omit to use the sprite Aseprite has focused."New value: +"Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused." - added
Output schema / properties / idAdded value: +{ + "description": "Stable id for this open document. Pass it back as '#<id>'.", + "type": "integer" +} - removed
Output schema / properties / tags / items / properties / repeatRemoved value: -{ - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ] -} - added
Output schema / properties / tags / items / properties / repeatsAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Loop count; 0 means forever." +} - changed
Output schema / requiredPrevious value: -[ - "name", - "width", - "height", - "colorMode", - "frameCount", - "layers", - "frames", - "tags" -]New value: +[ + "id", + "name", + "width", + "height", + "colorMode", + "frameCount", + "layers", + "frames", + "tags" +]
- Changed
sprite_manage7 fields changed- changed
Input schema / properties / sprite / descriptionPrevious value: -"Sprite filename or id. Omit to use the sprite Aseprite has focused."New value: +"Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused." - added
Output schema / properties / idAdded value: +{ + "description": "Stable id of the affected document; pass it back as '#<id>'.", + "type": "integer" +} - added
Output schema / properties / sprites / items / properties / colorModeAdded value: +{ + "type": "string" +} - added
Output schema / properties / sprites / items / properties / framesAdded value: +{ + "type": "integer" +} - added
Output schema / properties / sprites / items / properties / idAdded value: +{ + "type": "integer" +} - added
Output schema / properties / sprites / items / properties / layersAdded value: +{ + "type": "integer" +} - changed
Output schema / properties / sprites / items / requiredPrevious value: -[ - "name", - "width", - "height", - "active", - "modified" -]New value: +[ + "id", + "name", + "width", + "height", + "colorMode", + "frames", + "layers", + "active", + "modified" +]
- Changed
tag3 fields changed- removed
Input schema / properties / repeatRemoved value: -{ - "description": "0 means loop forever.", - "minimum": 0, - "type": "integer" -} - added
Input schema / properties / repeatsAdded value: +{ + "description": "Loop count; 0 means forever.", + "minimum": 0, + "type": "integer" +} - changed
Input schema / properties / sprite / descriptionPrevious value: -"Sprite filename or id. Omit to use the sprite Aseprite has focused."New value: +"Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused."
- Changed
tileset14 fields changed- changed
Input schema / properties / sprite / descriptionPrevious value: -"Sprite filename or id. Omit to use the sprite Aseprite has focused."New value: +"Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused." - changed
Input schema / properties / tolerance / descriptionPrevious value: -"For 'pack': treat near-identical tiles as one."New value: +"For 'pack': maximum per-channel difference at which two cells count as the same tile. 0 means exact, which is also much faster — anything above 0 compares every cell against every tile found so far." - changed
Input schema / properties / tolerance / maximumPrevious value: -64New value: +255 - added
Output schema / properties / cellCountAdded value: +{ + "description": "For 'pack': grid cells examined.", + "type": "integer" +} - added
Output schema / properties / columnsAdded value: +{ + "type": "integer" +} - added
Output schema / properties / formatAdded value: +{ + "type": "string" +} - added
Output schema / properties / layoutAdded value: +{ + "type": "string" +} - added
Output schema / properties / reusedExactAdded value: +{ + "description": "For 'pack': cells that matched an existing tile exactly.", + "type": "integer" +} - added
Output schema / properties / reusedFuzzyAdded value: +{ + "description": "For 'pack': cells merged by `tolerance`. A high number here with a low tolerance means the mockup has near-duplicate tiles worth cleaning up.", + "type": "integer" +} - added
Output schema / properties / rowsAdded value: +{ + "type": "integer" +} - added
Output schema / properties / skippedAdded value: +{ + "description": "For 'stamp': placements outside the grid or naming a tile that does not exist.", + "type": "integer" +} - added
Output schema / properties / sourceLayerAdded value: +{ + "description": "For 'pack': the mockup layer, now hidden.", + "type": "string" +} - added
Output schema / properties / tileHeightAdded value: +{ + "type": "integer" +} - added
Output schema / properties / tileWidthAdded value: +{ + "type": "integer" +}
- Changed
transform4 fields changed- removed
Input schema / properties / scopeRemoved value: -{ - "default": "selection", - "description": "What the transform applies to. 'selection' falls back to the cel when nothing is selected.", - "enum": [ - "selection", - "cel", - "layer", - "sprite" - ], - "type": "string" -} - changed
Input schema / properties / sprite / descriptionPrevious value: -"Sprite filename or id. Omit to use the sprite Aseprite has focused."New value: +"Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused." - removed
Output schema / properties / scopeRemoved value: -{ - "type": "string" -} - changed
Output schema / requiredPrevious value: -[ - "sprite", - "op", - "scope", - "pixelsChanged" -]New value: +[ + "sprite", + "op", + "pixelsChanged" +]
- Changed
validate1 field changed- changed
Input schema / properties / sprite / descriptionPrevious value: -"Sprite filename or id. Omit to use the sprite Aseprite has focused."New value: +"Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused."
18 tool updates
v0.1.0- First observed
cel - First observed
draw - First observed
export - First observed
frame - First observed
layer - First observed
look - First observed
palette - First observed
preflight - First observed
read_pixels - First observed
recolor - First observed
reference - First observed
select - First observed
sprite_info - First observed
sprite_manage - First observed
tag - First observed
tileset - First observed
transform - First observed
validate
TDQS
Scored across 18 tools
Each tool targets a distinct resource or action within the Aseprite workflow—preflight, sprite_info, sprite_manage, look, draw, layer, cel, palette, etc.—with clear boundaries. Minor overlap exists between draw's 'replace' op and recolor's 'replace' op, but the descriptions clarify recolor's intent-based and region-scoped nature. Overall, an agent can reliably select the right tool for each task.
All names are lowercase and use snake_case for multi-word identifiers (sprite_info, read_pixels), which is internally consistent. However, the set mixes nouns (layer, cel, palette) and verbs (draw, validate, recolor) rather than adhering to a single verb_noun pattern, making the naming predictable in style but not in grammatical role.
18 tools is slightly above the ideal 3-15 range, but the domain is rich (animation, tilesets, palettes, layers, cels, export, validation) and each tool covers a distinct, well-scoped area. The count feels appropriate for a full-featured pixel-art editor, not bloated.
The surface covers the full lifecycle: connectivity (preflight), inspection (sprite_info, look, read_pixels), creation/editing (draw, transform, recolor, layer, cel, frame, palette, tileset), selection, reference, validation, tagging, and export. No obvious dead ends or missing operations for a pixel-art workflow; even niche tasks like tile packing and storyboard import are included.
Maintenance
Related MCP Connectors
Generate pixel art sprites, animations, 8-direction rotations and palettes for games.
AI game assets for agents: consistent sprites, 2D animations, tiles, maps, music and engine exports.
Agent-Native design tool - create and edit visual designs with agent assistance
Create AI animations and export transparent sprite sheets, alpha video, frames, and game assets.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to visually interact with LibreSprite for real-time pixel art creation and automated drawing with self-healing capabilities.MIT
- AlicenseAqualityBmaintenanceEnables AI to create pixel art in Aseprite through pixel-level drawing primitives, read canvas screenshots, and iterate until satisfied.312MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to create, inspect, edit, and export pixel art, sprites, animations, and spritesheets using headless Aseprite, and to convert arbitrary images into indexed pixel art.MIT
- AlicenseBqualityCmaintenanceEnables AI assistants to create and edit pixel art and animated sprites in Aseprite through 104 tools covering drawing, layers, animation, palettes, tilemaps, exports, visual analysis, and raw Lua scripting.116MIT