A hosted MCP server for game-dev spritesheet workflows. Connect it to Claude or any MCP-compatible AI client to pack, split, trim, and animate sprites through natural language โ no local tools required.
Connect Spritesheet Forge to your AI client in under 2 minutes.
Add to claude_desktop_config.json (find it via Settings โ Developer):
{
"mcpServers": {
"spritesheet-forge": {
"type": "http",
"url": "https://mcp.clawstudiouo.com/mcp"
}
}
}
claude mcp add spritesheet-forge --transport http https://mcp.clawstudiouo.com/mcp
Uses GitHub OAuth 2.1 with PKCE. MCP clients run the flow automatically. To get a Bearer token manually (for curl or benchmark scripts):
curl -O https://spritesheet-forge.spritesheet-forge.workers.dev/get-token.py && python3 get-token.py
Opens a GitHub login page, then prints and saves the token to ~/.spritesheet-forge-token. Requires Python 3 (pre-installed on macOS/Linux).
7 processing tools + 1 config tool. All inputs accept base64 data URIs, https:// URLs, or output URLs from previous tool calls.
Extracts all frames from an animated GIF and packs them into a spritesheet grid. Optionally removes the background from each frame.
| Parameter | Type | Default | Description |
|---|---|---|---|
file | string | required | GIF file input |
columns | integer | auto | Grid columns |
padding | integer | 0 | Pixel gap between frames |
remove_bg | boolean | false | Remove background from each frame |
bg_color | string | "auto" | "auto" or "#RRGGBB" |
tolerance | integer | 30 | Background removal threshold 0โ255 |
Reassembles a spritesheet back into an animated GIF or WebP. Supports grid mode (columns ร rows) or cell mode (fixed pixel dimensions). You can select frame ranges, trim offsets, and skip empty frames.
| Parameter | Type | Default | Description |
|---|---|---|---|
file | string | required | Spritesheet PNG |
columns | integer | โ | Grid columns (grid mode) |
rows | integer | โ | Grid rows (grid mode) |
cell_width | integer | โ | Cell width px (cell mode) |
cell_height | integer | โ | Cell height px (cell mode) |
frame_count | integer | โ | Actual frames (for incomplete last row) |
duration | integer | 100 | Frame duration in ms |
loop | integer | 0 | Loop count (0 = infinite) |
output_format | string | "gif" | "gif" | "webp" |
skip_empty | boolean | true | Remove fully transparent frames |
Packs multiple PNG files into a single spritesheet. Supports grid, horizontal, vertical, and bin-packed layouts. Optionally generates a JSON / CSS atlas for use in game engines.
| Parameter | Type | Default | Description |
|---|---|---|---|
files | string[] | required | PNG files |
layout | string | "grid" | "grid" | "horizontal" | "vertical" | "packed" |
columns | integer | auto | Grid columns |
padding | integer | 0 | Pixel gap between frames |
metadata_format | string | "none" | "none" | "json_array" | "json_hash" | "css" |
power_of_2 | boolean | false | Pad output to next power of 2 |
trim_input | boolean | false | Auto-trim transparent edges before packing |
Extracts each frame from an animated GIF as an individual PNG file, delivered as a ZIP archive. Useful when you need separate frame assets for a game engine.
| Parameter | Type | Default | Description |
|---|---|---|---|
file | string | required | GIF file input |
remove_bg | boolean | false | Remove background |
bg_color | string | "auto" | "auto" or "#RRGGBB" |
tolerance | integer | 30 | Background removal threshold 0โ255 |
Splits a spritesheet PNG into individual frame PNGs (ZIP) and/or a JSON/CSS atlas describing each frame's coordinates. Supports grid mode and cell mode.
| Parameter | Type | Default | Description |
|---|---|---|---|
file | string | required | Spritesheet PNG |
columns | integer | โ | Grid columns |
rows | integer | โ | Grid rows |
cell_width | integer | โ | Cell width px (cell mode) |
cell_height | integer | โ | Cell height px (cell mode) |
output | string | "frames" | "frames" | "metadata" | "both" |
metadata_format | string | โ | "json_array" | "json_hash" | "css" |
skip_empty | boolean | true | Remove fully transparent frames |
Composes multiple PNG frames into an animated GIF or WebP. Frames are composited onto a shared canvas โ mismatched sizes can be filled, padded with transparency, or rejected.
| Parameter | Type | Default | Description |
|---|---|---|---|
files | string[] | required | PNG frames |
duration | integer | 100 | Frame duration ms |
loop | integer | 0 | Loop count (0 = infinite) |
output_format | string | "gif" | "gif" | "webp" |
resize | string | "transparent" | "error" | "fill" | "transparent" |
file_name_order | boolean | false | Sort by _N filename suffix |
Crops transparent (alpha) edges from one or more PNG files. Single file returns a PNG; multiple files return a ZIP. Useful for tightening sprite bounds before packing.
| Parameter | Type | Default | Description |
|---|---|---|---|
files | string[] | required | PNG files (single โ PNG, multiple โ ZIP) |
threshold | integer | 0 | Alpha threshold 0โ255 |
padding | integer | 0 | Transparent margin to preserve |
Returns the upload endpoint URL, output TTL, file size limits, and base64 encoding rules. No parameters. Call this before working with large files (โฅ 4 MB) or chained workflows.
All file / files parameters accept three input types. Choose based on file size.
Base64-encode and prepend a data URI:
data:image/gif;base64,R0lGODl...
Strip ALL whitespace and newlines from the base64 string. Encoders like openssl base64 insert newlines every 76 chars โ these cause INVALID_BASE64.
# Shell (no newlines)
base64 -i file.gif | tr -d '\n'
# Python (no newlines by default)
base64.b64encode(data).decode()
Upload first, then use the returned URL:
curl -X POST https://mcp.clawstudiouo.com/upload \
-H "Authorization: Bearer <token>" \
-F "file=@animation.gif"
# โ { "url": "https://..." }
Why ~185 KB and not 4 MB? In Claude Code / Claude Desktop, shell output > ~250 KB is written to a temp file that AI tools cannot read back (256 KB limit). A 185 KB file encodes to ~247 KB base64 โ just under the limit. When in doubt, use upload.
Pass the url from one tool directly as file input to the next โ no re-encoding needed. The server reads chained URLs directly from its own storage with no HTTP overhead.
// Example chain: GIF โ spritesheet โ back to animation
const step1 = await gif_to_spritesheet({ file: gifDataUri, columns: 3 });
const step2 = await spritesheet_to_animation({ file: step1.url, columns: 3, rows: 2 });
// step2.url is the final animated GIF
Every tool returns a JSON object:
{
"url": "https://mcp.clawstudiouo.com/output/output-abc123.png",
"expires_at": "2026-05-05T13:00:00.000Z",
"content_type": "image/png",
"size_bytes": 516432,
"quota": { "used": 4, "limit": 100, "reset_at": "2026-06-01T00:00:00.000Z" }
}
The url is also a direct browser-viewable download link.
Tips for getting the best results when using Spritesheet Forge through Claude or another AI client.
I've connected Spritesheet Forge MCP. For files โฅ 4 MB, call server_info
first to get the upload URL, then POST the file there before calling the
processing tool. Output URLs expire in 1 hour.
initialize, the server returns an instructions field pointing to the public documentation page.server_info.MCP clients (Claude Desktop, Claude Code) store their token in an encrypted internal store โ there is no config file or keychain entry you can read. Run the one-line helper instead (Python 3, no extra dependencies):
curl -O https://spritesheet-forge.spritesheet-forge.workers.dev/get-token.py && python3 get-token.py
~/.spritesheet-forge-token.| Error | Cause | Fix |
|---|---|---|
INVALID_BASE64 | Whitespace or newlines in base64 string | Strip with tr -d '\n' or Python's b64encode().decode() |
INVALID_FILE_URL | Output URL expired (past 1-hour TTL) | Re-run the originating tool |
INVALID_CONTENT_TYPE | Non-PNG/GIF/WebP input | Convert to a supported format first |
FILE_TOO_LARGE | File exceeds 20 MB | Compress or split before uploading |
quota_exceeded | 100 ops/month used | Wait for monthly reset (check quota.reset_at) |
| Upload 401 | Missing or expired Bearer token | Run get-token.py or use public URL |
Free tier limits. All limits are per GitHub account.
| Limit | Value |
|---|---|
| Max file size | 20 MB |
| Recommended base64 limit | 4 MB (use upload endpoint above this) |
| Output / upload file TTL | 1 hour |
| Free quota | 100 operations / GitHub account / month |
| Quota reset | 1st of each month |
| Session token lifetime | 30 days |
| Supported input formats | PNG, GIF, WebP |
End-to-end latency measured from a local machine over the public endpoint (2026-05-05). All 11 tests passing.