---
name: spriteoven
description: Generate, organize and export pixel-art game assets with Spriteoven — characters, item sets, FX, decor, UI kits and biome tilesets — through the Spriteoven MCP server. Use whenever the user asks for game sprites, pixel art, a tileset, an item set, an icon pack, UI frames, or wants to export existing assets to Godot or Unity.
---

# Spriteoven

Spriteoven is a pixel-art asset **library and forge**. The MCP server lets you work in the
user's own account: read what they already have, generate new pieces, and export them in
engine format.

**Generating spends the user's credits.** Read the consent rule below before anything else.

---

## 🔴 The consent rule — read this first

Every `generate_*` tool works in **two steps**, and you must not skip the first one.

1. **Call it WITHOUT `confirm_token`.** Nothing is spent. You get the exact price, the
   balance before and after, and a `confirm_token`.
2. **Show that price to the user and ask.** Wait for an actual answer.
3. **Call it again with the SAME parameters plus `confirm_token`.** Now it generates.

The quote is signed over the parameters and lasts **300 seconds**. If you change a single
parameter, the token stops working — that is deliberate, so a quote can never be used to buy
something more expensive than what the user approved.

**Never batch approvals.** "Generate 5 effects" is five prices. Quote them, add them up, show
one total, and ask once — but confirm each one only after the user says yes.

If the user has already said "go ahead, don't ask me again for this batch", you may confirm
without asking again *within that batch*. A new request is a new approval.

---

## The two things to do before you generate anything

**1. Look at the balance.** `get_credits`.

> ⚠️ The free tier counts **CALLS, not credits**. One `high` generation burns the same
> free generation as a `low` one — it is not prorated. `free_gens_remaining` and
> `credits_balance` are two different budgets and both are in the response.

**2. Look at what they already have.** `list_assets` (optionally with `family`, `tag`, or
`query`). Re-generating something the user already owns costs them money for nothing. This is
the single most valuable thing you can do here, and it is free.

`list_assets` hides variants by default (a character can have dozens of child views); pass
`include_variants: true` only when you actually need them. It returns real `total` and
`has_more` — if `has_more` is `true`, there is more, and if it is `null` the server could not
count, which is not the same as "no more".

---

## The families

| family | generate by MCP? | what it is |
|---|---|---|
| `items` | ✅ `generate_item_set` | sets of items / equipment pieces |
| `fx` | ✅ `generate_fx` | effects and spells, animated |
| `deco` | ✅ `generate_deco` | scenery props, static or animated |
| `uikit` | ✅ `generate_uikit` | buttons, windows, bars, 9-slice frames |
| `personajes` | ❌ | character sprites |
| `mapas` | ❌ | biome tilesets |

**Characters and maps cannot be generated through MCP**, and the reason is not that nobody got
around to it: those two pipelines charge per generation inside the worker and do not publish a
price function, so there is no exact price to show the user before spending. Since the whole
consent flow rests on the price being exact, they are not served here. Tell the user to make
those in the web app (Sprite Lab) — they can still `list_assets`, `get_asset` and read them
here afterwards.

---

## Style: what to pick, and for which size

Call `list_styles` — it returns each family's real catalogue with a one-line `promise` for each
rung. There are **three separate style axes**, and mixing them up is the most common mistake:

- **The art ladder** (`personajes`, `items`, `deco`, `uikit`): `hd` · `pixel-modern` ·
  `pixel-classic` · `8-bit` · `pixel-chunky` · `cel-shaded`. Default is `pixel-modern`.
- **FX** uses the same ladder plus `hyperreal`.
- **Maps** uses a different axis entirely — the biome **surface**. Do not ask a biome for a
  character style.

**Which rung for which pixel size.** `list_styles` marks this with `recommended_px` when the
product declares it. What it says today:

- **32 px and 48 px → `8-bit` or `pixel-chunky`.** These are the *structural* rungs: fat
  blocks, short palette. At small sizes they are the ones that stay readable.
- **Larger canvases → `pixel-modern`, `pixel-classic`, `cel-shaded` or `hd`.** These are
  *palette* rungs: fine pixels and lots of colour, which need room to read.
- `hyperreal` (fx only) is a *photo* rung — no outline, no pixels. It is a deliberate break
  from the rest of a pixel set; do not reach for it unless the user asks for that look.

Rule of thumb: **if the sprite is 48 px or smaller, go structural.** A `pixel-modern` icon at
32 px turns to mush.

When the user names a style in plain words, match it to a `style_id` from `list_styles` rather
than inventing one — the forge rejects ids it does not know.

---

## Generation is asynchronous

A confirmed `generate_*` returns a **`job_id`**, not an asset. That is a queue ticket.

Poll `get_job(job_id)` every ~5 seconds until `done` is `true` or `failed` is `true`. When it
is done it hands you `asset_ids`.

**If a job fails, do not silently retry.** Every attempt costs the user. Tell them what failed
and ask.

Typical wall-clock: tens of seconds to a few minutes, depending on family and quality.

---

## Exporting

`export_asset(asset_id, engine, size|scale)` returns a **signed URL to a .zip**, never bytes.
The URL expires — hand it to the user promptly.

Get the valid `engine` and size values from `get_asset(asset_id)` → `formats`. Do not guess
them: they differ per family, and `formats.reason` explains the gap — either a missing export
ladder, or a missing **engine axis** on a family that does have a ladder (an item set re-cuts by
`item_size` and a gear piece by a probed `size`; neither has an engine to choose). A `null` in
`formats` always arrives with its reason.

> ⚠️ **Export works on what was generated recently.** The export package is rebuilt from the
> generation job, and job results are pruned by retention. For older library assets you will
> get `EXPORT_PACKAGE_EXPIRED` — that is expected, not a bug. Their raw master PNG is still
> available through `get_asset` (`master.url`), which is often all the user needs.

Spriteoven is the **source**. If the user also has a pixel-editor MCP connected (Aseprite,
Godot), the natural division of labour is: generate and version here, refine and sync there.

---

## A good session, end to end

> *"Make me a medieval item set in 8-bit and give it to me for Godot."*

1. `get_credits` — check there is budget.
2. `list_assets({ family: "items" })` — maybe they already have one.
3. `list_styles({ family: "items" })` — confirm `8-bit` is a real rung.
4. `generate_item_set({ description: "medieval items", count: 6, style: "8-bit" })` — **no
   token**. You get a price.
5. **Show the price. Ask.**
6. Same call **plus `confirm_token`**. You get a `job_id`.
7. `get_job` every ~5 s until done.
8. `get_asset` on the result to read `formats`, then `export_asset` with the engine it lists.
9. Give the user the download URL and say when it expires.

---

## Errors worth recognising

Errors come back as `[CODE] message`. The code tells you what to do:

| code | what it means | what to do |
|---|---|---|
| `MCP_SCOPE_MISSING` | the token cannot spend | ask the user for a token with the `generate` scope |
| `MCP_TOKEN_REVOKED` / `MCP_TOKEN_EXPIRED` | the connection is dead | ask them to create a new token; retrying will not help |
| `QUOTE_EXPIRED` | the 300 s quote lapsed | quote again (call without `confirm_token`) |
| `QUOTE_PARAMS_CHANGED` | parameters moved after quoting | quote again — never try to force it |
| `INSUFFICIENT_CREDITS` | not enough balance | tell them the shortfall; the message has the numbers |
| `UNKNOWN_FAMILY` | bad family name | the message lists the valid ones |
| `ASSET_NOT_FOUND` | not in their library | do not retry; `list_assets` to find the real id |
| `EXPORT_PACKAGE_EXPIRED` | job result pruned | offer `get_asset` → `master.url` instead |

---

## Things not to do

- **Do not spend without showing the price.** Nothing on the server can force you to ask a
  human — the whole design assumes you will.
- **Do not re-generate what `list_assets` already shows.** Looking is free; generating is not.
- **Do not retry a failed generation on your own.**
- **Do not inline images.** Every image is a URL. Pass the URL along; do not fetch it into
  context.
- **Do not invent style ids, engines, sizes or UI component names.** Read them from
  `list_styles` / `get_asset`, and read the component names from the `component` enum that
  `generate_uikit` publishes in its own schema. Its three old examples (`marco`, `boton`,
  `ventana`) were in Spanish and the endpoint rejected all three — **after** quoting a price.
  Fixed 2026-09-10 (PULIDO-1a §5): the vocabulary is now in the schema, so a client that
  enforces schemas catches a wrong name before the call goes out.
