sprite-canon
sprite-canon
一个 MCP 服务器,让 AI 生成的游戏精灵看起来像同一个游戏。
![]()
一个角色,三套服装——蓝色和红色行是 sprite_repaint 调用,而不是重新生成。相同的着色顺序,相同的轮廓,每次结果一致。
AI 生成器擅长制作漂亮的精灵,却不擅长让它与上一个匹配。要求同一个角色两次,调色板就会漂移,服装会变异,新帽子会漂浮在头顶上方 3 像素处——每个资源单独看都很好,但组装起来游戏看起来就不对劲。反复“直到匹配”的重新生成不会收敛;它烧钱,而且你无法对结果进行差异比较。
sprite-canon 采取了相反的方法,该方法源自一个真实游戏项目,该项目生成了约 4,000 帧,并艰难地吸取了所有教训:
你的一致性规则变成数据——一个
sprite-canon.json(“canon”)保存调色板、命名颜色区域(皮肤、服装、轮廓等)、相对比例和检查阈值。与你的资源一起提交。验证是数字化的,而非视觉化的。 你无法用肉眼检查 96 种服装变体 × 8 个方向 × 4 帧。
sprite_verify返回针对实际发布缺陷的硬性通过/失败数字:偏离调色板的像素、帧间抖动的配饰、从背面亮而从正面暗的区域、触及脸部的重绘。修复是确定性的像素操作,而非重新生成。 将区域重绘到新的颜色渐变上会保留着色和轮廓,绝不触及受保护区域,并且每次产生相同输出。服装变体是一次工具调用,而不是提示词彩票。
安装
Claude Desktop — 一个文件,无需配置
从最新版本下载
sprite-canon.mcpb。在 Claude Desktop 中,打开 设置 → 扩展(Windows 上为 ☰ 菜单 → 文件 → 设置)。
将
.mcpb文件拖入扩展页面,查看并点击安装。
(如果您的操作系统已注册 .mcpb 关联,双击文件也可以——拖放始终有效。替代方法:扩展 → 高级设置 → 安装扩展 → 选择文件。)
这就是全部安装过程:该捆绑包自带依赖项,Claude Desktop 提供 Node 运行时。需要 Claude Desktop 应用——Claude Code 请参见下文。
Claude Code / 其他 MCP 客户端
git clone https://github.com/useka12-eng/sprite-canon
cd sprite-canon && npm install然后在项目的 .mcp.json(或任何 MCP 客户端配置)中注册:
{
"mcpServers": {
"sprite-canon": {
"command": "node",
"args": ["/path/to/sprite-canon/src/mcp/server.mjs"]
}
}
}需要 Node 18+。无原生依赖——PNG/GIF 编解码器是自包含的。
自行构建捆绑包
npx @anthropic-ai/mcpb pack . dist/sprite-canon.mcpbRelated MCP server: Spiritmarch Aseprite MCP
工具
工具 | 功能 |
| 创建 canon;从示例图像中学习调色板(使用次数 ≥ N 的颜色——较稀有的通常是抗锯齿噪声) |
| 通过采样几个像素、列出颜色或 HSL 规则来定义区域。记录区域的亮度范围。将面部/轮廓标记为 |
| 显示解析后的 canon + 对照它统计文件(未匹配的像素 = 区域定义中的缺口) |
| 按频率和亮度列出实际使用的颜色——canon 决策的原始材料 |
| 每帧解剖(边界框、帽/头宽度、腰部行、每个区域的第一行)+ 跨帧抖动 |
| 数字检查: |
| 确定性地将区域重新着色到暗→亮渐变上;受保护区域不可触碰 |
| 缩放后的联系表以图像形式内联返回——在表上判断一致性,而不是在游戏中 |
| 无损 GIF 操作:跨所有颜色表进行调色板替换(零生成损失)、重新定时 |
输入可以是 PNG、动画 GIF 或 PNG 精灵表(cellW/cellH)。
工作流程
canon_init → learn the palette from your existing good assets
canon_learn → sample skin / outfit / outline once; mark face + outline protected
sprite_measure → read the numbers before placing anything ("where do the eyes start?")
sprite_repaint → make variants deterministically (outfits, teams, seasons)
sprite_verify → prove it: face untouched, nothing left over, no jitter, on palette
sprite_sheet → look at the result as a sheet, zoomed, before it enters the game此工具编码的教训
这些不是假设——每一个都首先作为真实缺陷出现:
测量,不要假设比例。 将帽檐放置在“头部高度的 52%”恰好落在眼睛上:在 20 像素高的头上,眼睛距顶部 7–9 像素,因此每个固定比例都会碰到它们。
sprite_measure报告每帧面部实际开始的位置。使用固定亮度范围进行重绘。 按图像归一化会将相同的源颜色映射到不同的输出,具体取决于区域可见多少——我们的帽子从背面亮,从正面暗。canon 记录每个区域的范围一次;重绘始终使用它。
结构性保护区域。 “在面部周围小心”在规模上会失败。
protected: true意味着重绘不能触及它,验证证明它没有触及。修补 GIF 调色板,不要重新编码。 索引 GIF 的颜色存在于其颜色表中——全局和每帧局部(仅修补全局表是经典的半修复)。替换表条目会完美同步地重新装扮每一帧,零损失。
区域定义有缺口;统计它们。 重绘后残留的 12 个旧颜色像素对眼睛不可见,但对
leftover显而易见。当它触发时,canon_info的统计显示你的区域未覆盖哪些颜色。
比例表
sprite_verify 的 scale 检查读取 canon.scale.heights——相对于参考资源(等于 1 的条目)的相对大小。目前没有工具写入此部分;手动添加到 sprite-canon.json:
"scale": { "heights": { "hero": 1, "house": 3.4, "chicken": 0.45 } }然后使用 scaleNames 将文件基名映射到这些键进行验证。这会在玩家发现之前一周捕捉到经典的“房子比英雄小”问题。
实用说明
始终传递
canonPath(或 canon 所在目录之上的文件)。stdio MCP 服务器的工作目录属于客户端,而不是你的项目,因此工具拒绝从 cwd 猜测。编解码器限制:PNG 必须是 8 位、非隔行、RGB/RGBA/调色板(常见的像素艺术情况;16 位或隔行文件会被拒绝并显示明确错误)。GIF 编码器在每文件最多 255 种不透明颜色时精确——超出后,最近调色板吸附。
sprite_sheet内联返回图像,最大约 800 KB;更大的表仅返回文件路径。精灵表逐格往返:空单元格保持为空,不会压缩。
这不是什么
不是生成器。将其与任何制作艺术品的工具配对(PixelLab、Aseprite、Gemini、手绘像素);sprite-canon 是保持结果连贯的层。
不是图集打包器/碰撞工具——sprite-tools 很好地覆盖了这一点。
不是魔法:你每个项目花约 10 分钟教它你的 canon。正是这种投入使每个后续检查和修复都值得信赖。
开发
npm test # unit + end-to-end MCP tests (22)测试套件包括针对 v0.1 中对抗性多代理审查发现的每个错误的回归测试——表单元格压缩、GIF 处置语义、假成功响应、静默零检查通过。如果其中一个失败,说明曾经存在的错误又回来了。
MIT
Available Tools
9 toolscanon_infoA
Show the resolved canon (palette size, regions with their rules and protection flags, scale table, thresholds) and optionally census a file against it — how many pixels each region matches, and how many match nothing (unmatched pixels mean your region definitions have gaps). Pass canonPath or censusFile — without either there is nowhere to search from (the server's working directory is the client's, not your project's).
| Name | Required | Description | Default |
|---|---|---|---|
| cellH | No | spritesheet cell height (PNG sheets only) | |
| cellW | No | spritesheet cell width (PNG sheets only) | |
| canonPath | No | path to sprite-canon.json (default: search upward from the target file) | |
| censusFile | No | count region membership of this file's first frame (also used as the search origin for the canon) | |
| listColors | No | include the full palette hex list |
canon_initA
Create sprite-canon.json for a project: the single source of truth for palette, regions, scale and check thresholds. Learns the palette from sample images (colours used at least minCount times; rarer colours are usually anti-aliasing noise). Run once per project, then commit the file.
| Name | Required | Description | Default |
|---|---|---|---|
| dir | Yes | project directory to create sprite-canon.json in | |
| name | No | project name | |
| cellH | No | spritesheet cell height (PNG sheets only) | |
| cellW | No | spritesheet cell width (PNG sheets only) | |
| minCount | No | min occurrences for a colour to enter the palette (default 4) | |
| sampleFiles | No | PNG/GIF files or directories to learn the palette from | |
| updatePalette | No | relearn just the palette of an EXISTING canon from sampleFiles (regions/scale/checks untouched) |
canon_learnA
Define or update a named region in the canon (e.g. skin, hair, outfit, outline). Give a few [x,y] points on sample images; the sampled colours (optionally expanded by absorb) become the region. The region's luminance range is recorded — repaint uses that FIXED range so the same source colour always maps to the same output colour in every frame and direction. Mark regions like face/eyes/outline as protected: repaint will never touch them and verify will fail if anything else does. Instead of points you may pass explicit colors, or an HSL rule (hue/sat/light ranges) for anti-aliased art — an HSL-only region additionally needs lumRange if you ever want to repaint it. Updates MERGE additively into an existing region; pass replace=true to redefine it from scratch (the only way to remove a mis-sampled colour).
| Name | Required | Description | Default |
|---|---|---|---|
| hue | No | HSL rule: hue range in degrees | |
| sat | No | HSL rule: saturation range 0..1 | |
| cellH | No | spritesheet cell height (PNG sheets only) | |
| cellW | No | spritesheet cell width (PNG sheets only) | |
| light | No | HSL rule: lightness range 0..1 | |
| absorb | No | also absorb colours within this per-channel distance of sampled ones (default 0) | |
| colors | No | explicit hex colours instead of samples | |
| region | Yes | region name, e.g. 'skin' / 'outline' / 'cloak' | |
| replace | No | redefine the region from scratch instead of merging (default false) | |
| samples | No | ||
| lumRange | No | fixed luminance range (0-255) for repainting — required for HSL-only regions, auto-computed for colour lists | |
| canonPath | No | path to sprite-canon.json (default: search upward from the target file) | |
| protected | No | mark as protected (never repainted, verified untouched) |
colors_inspectA
List the colours actually used in files, sorted by frequency, with luminance — the raw material for canon_init/canon_learn decisions. Use it to spot near-duplicate colours, anti-aliasing noise, and which hexes belong to which visual part before defining regions.
| Name | Required | Description | Default |
|---|---|---|---|
| top | No | how many colours to list (default 40) | |
| cellH | No | spritesheet cell height (PNG sheets only) | |
| cellW | No | spritesheet cell width (PNG sheets only) | |
| files | Yes |
gif_patchA
Operations on animated GIFs. 'palette' swaps colours in every colour table (global AND per-frame local — patching only the global table is a classic half-fix) without touching frame data: zero generation loss, every frame changes in perfect sync — the correct way to re-dress an indexed sprite. The response reports how many table entries each source colour actually matched; 0 means that hex isn't in the file (use colors_inspect to find the exact hexes). 'retime' re-times all frames to msPerFrame. GIF delays are quantised to 10ms steps with a 20ms minimum; pixels survive a decode/re-encode that is exact up to 255 opaque colours (beyond that, nearest-palette snapping). Real bug this fixes: an animation authored at 1.8s being cut off by game code that frees the sprite after 0.62s — retime the GIF instead of dropping frames.
| Name | Required | Description | Default |
|---|---|---|---|
| file | Yes | ||
| action | Yes | ||
| outFile | Yes | ||
| colorMap | No | for 'palette': { fromHex: toHex, ... } | |
| msPerFrame | No | for 'retime': delay per frame in ms (quantised to 10ms steps, min 20ms) |
sprite_measureA
Measure a sprite's anatomy per frame and its cross-frame jitter: bounding box, top row, widest row, cap (head) width, waist (neck) row, and — with canon regions — the first row of each region (e.g. where the face starts). Use before placing anything relative to a sprite: measured rows beat guessed proportions. Real bug this catches: a hat brim placed at '52% of head height' landed exactly on the eyes, because eye rows vary per frame — measure, don't assume.
| Name | Required | Description | Default |
|---|---|---|---|
| file | Yes | PNG / GIF / spritesheet | |
| cellH | No | spritesheet cell height (PNG sheets only) | |
| cellW | No | spritesheet cell width (PNG sheets only) | |
| canonPath | No | path to sprite-canon.json (default: search upward from the target file) |
sprite_repaintA
Deterministically recolour a canon region onto a colour ramp (dark→light hex list) — the alternative to regenerating with AI and losing consistency. Shading survives (luminance maps onto the ramp using the region's FIXED recorded range), silhouettes never change, protected regions are never touched, and the same input always gives the same output. Use maskFromFile when repainting an already-recoloured variant: regions are matched on the original (identical pixel layout), paint is applied to the variant. Writes to outFile (never overwrites the input unless outFile equals it).
| Name | Required | Description | Default |
|---|---|---|---|
| file | Yes | ||
| ramp | Yes | hex colours dark→light, e.g. ['4a3418','7a5628','a8813c','cfae6b'] | |
| rows | No | restrict painting to this row range [from,to] | |
| cellH | No | spritesheet cell height (PNG sheets only) | |
| cellW | No | spritesheet cell width (PNG sheets only) | |
| region | Yes | canon region to repaint | |
| outFile | Yes | ||
| canonPath | No | path to sprite-canon.json (default: search upward from the target file) | |
| maskFromFile | No | match the region on this file instead (same pixel layout) |
sprite_sheetA
Compose a zoomed contact sheet (PNG on a checkerboard) from sprites — one row per file, one column per frame — and return it as an image so you can LOOK at what you just made. Judge consistency on sheets, not in-game: every visual defect we ever caught was caught on a sheet. Use crop to zoom into the area under suspicion (e.g. just the head).
| Name | Required | Description | Default |
|---|---|---|---|
| crop | No | crop every cell to this rect before zooming | |
| zoom | No | pixel zoom (default 4) | |
| cellH | No | spritesheet cell height (PNG sheets only) | |
| cellW | No | spritesheet cell width (PNG sheets only) | |
| files | Yes | files or directories; each file becomes a row | |
| outFile | Yes | where to write the sheet PNG | |
| maxFrames | No | max frames per row (default 8) | |
| returnImage | No | also return the sheet inline as an image (default true; sheets over ~800KB are returned as path only) |
sprite_verifyA
Run numeric consistency checks and get hard pass/fail numbers — the replacement for eyeballing hundreds of frames. Checks: 'palette' (every pixel on the canon palette), 'jitter' (landmarks stay put across frames), 'spread' (a region's mean luminance agrees across a file group — catches 'bright from behind, dark from the front'), 'protected' (vs baseFile: face/outline pixels untouched), 'leftover' (vs baseFile: no source-region pixel survived inside a repainted area), 'scale' (relative sizes match the canon table). Files may be directories (all PNG/GIF inside).
| Name | Required | Description | Default |
|---|---|---|---|
| cellH | No | spritesheet cell height (PNG sheets only) | |
| cellW | No | spritesheet cell width (PNG sheets only) | |
| files | Yes | files or directories to verify | |
| checks | No | which checks to run (default: palette + jitter) | |
| region | No | region name for 'spread'/'leftover' | |
| baseFiles | No | originals for 'protected'/'leftover'. Paired to files by identical filename (case-insensitive) when names line up, otherwise by list position (equal lengths required). Each result echoes which base it was compared against. | |
| canonPath | No | path to sprite-canon.json (default: search upward from the target file) | |
| scaleNames | No | for 'scale': map of filename -> name in canon.scale.heights |
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
9 tool updates
v0.1.0- First observed
canon_info - First observed
canon_init - First observed
canon_learn - First observed
colors_inspect - First observed
gif_patch - First observed
sprite_measure - First observed
sprite_repaint - First observed
sprite_sheet - First observed
sprite_verify
TDQS
Scored across 9 tools
Each tool has a clearly different job: inspecting colors, defining canon, measuring sprites, verifying, repainting, compositing sheets, and patching GIFs. The only mild overlap is between sprite_repaint and gif_patch palette swapping, but their contexts (canon region remapping vs. indexed GIF color tables) are distinct enough to avoid real confusion.
Names follow a clear snake_case pattern of a domain prefix (colors, canon, sprite, gif) plus an action or noun. Minor grammatical inconsistency exists (colors_inspect, canon_info, sprite_sheet are noun-heavy while canon_learn and sprite_repaint are verb-focused), but the overall convention is predictable and readable.
Nine tools is a well-scoped set for a sprite canon workflow: each tool covers a distinct step from inspection and canon creation to measurement, verification, repainting, contact sheets, and GIF operations. Nothing feels redundant or missing at the count level.
The suite covers the full workflow: inspect, initialize canon, define regions, measure, verify, repaint, and generate contact sheets. Minor gaps exist, such as no explicit tool for deleting or renaming a region, and global canon settings like scale/thresholds can only be managed through init or by editing the file directly.
Maintenance
Related MCP Connectors
Turn images into pixel art, extract and remap palettes, and upscale sprites without blur.
Generate game sprite sheets and animations from an image, and clean up sprite backgrounds.
Game-dev sprite tools: PNG/GIF to spritesheet, split, trim, animate. OAuth-authenticated MCP server.
Generate pixel art sprites, animations, 8-direction rotations and palettes for games.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables AI coding agents to create, draw, and refine Aseprite documents through deterministic canvas, layer, frame, tag, and palette tools, with pixel analyzers, style validation, and preview/diff visual feedback. It also supports spritesheet and animated GIF export, producing multi-layer game-ready assets.1MIT
- AlicenseAqualityCmaintenanceEnables CLI-first Aseprite automation with staging workspaces, supporting sprite inspection, patching, export, preview, and atomic file commits via file_commit.6MIT
- AlicenseAqualityBmaintenanceEnables agents to author, validate, inspect, patch, and render editable JSON pixel-art and animation recipes through local tools, producing PNG contact sheets, APNGs, sprite atlases, and related export bundles.281,388 npm47MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to create layered pixel-art sprites and animations, inspect pixels, and export images, spritesheets, GIFs, APNGs, and TrueType pixel fonts. Documents and edits stay local, accessible over stdio MCP or callable directly from a shell.26MIT