mcp-spritesheet-forge
Spritesheet Forge MCP
A hosted Model Context Protocol (MCP) server for game-dev spritesheet workflows. Connect it to Claude or any MCP-compatible AI client and pack, split, trim, and animate sprites through natural language — no local tools required.
Server endpoint: https://mcp.clawstudiouo.com/mcp
Table of Contents
What This Server Does
Spritesheet Forge exposes 7 image-processing tools over MCP. An AI agent calls them like any other tool — no shell commands, no local dependencies.
Capability | Tools |
GIF → spritesheet grid |
|
GIF → individual frames |
|
Spritesheet → animated GIF/WebP |
|
Multiple PNGs → spritesheet |
|
Spritesheet → split frames + atlas JSON |
|
Frames → animated GIF/WebP |
|
Trim transparent edges from PNGs |
|
Typical agent workflows:
"Convert character.gif into a spritesheet for Unity"
→ gif_to_spritesheet
"Extract every frame from this animation, then trim the transparent borders"
→ gif_to_frames → trim_png (chained — output URL passed directly)
"Turn this spritesheet (4 columns × 3 rows) back into an animated GIF at 120ms per frame"
→ spritesheet_to_animation
"Pack these 12 sprites into a single atlas with a TexturePacker-compatible JSON"
→ png_to_spritesheet (layout=packed, metadata_format=json_hash)Limitations
Input formats: PNG, GIF, WebP
Max file size: 20 MB per file
Output TTL: Files expire 1 hour after creation — do not store output URLs for later
Quota: 100 operations per GitHub account per month (free tier)
Output format: Tools return a download URL; the server does not stream binary data directly
Quick Start
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or the equivalent on your platform:
{
"mcpServers": {
"spritesheet-forge": {
"type": "http",
"url": "https://mcp.clawstudiouo.com/mcp"
}
}
}Restart Claude Desktop. On first use, Claude will open a GitHub login page to authorize access. After approval you can start using the tools immediately.
Claude Code (CLI)
claude mcp add spritesheet-forge --transport http https://mcp.clawstudiouo.com/mcpOther MCP Clients
Any client that supports Streamable HTTP (MCP 2024-11-05 spec) can connect. Use:
MCP endpoint:
https://mcp.clawstudiouo.com/mcpAuth: OAuth 2.1 with PKCE (GitHub as IdP) — see Authentication
Discovery:
GET https://mcp.clawstudiouo.com/.well-known/oauth-authorization-server
Authentication
Spritesheet Forge uses GitHub OAuth 2.1 with PKCE. No API keys to create or rotate — you log in with GitHub and receive a long-lived session token.
How MCP clients handle it (recommended)
MCP clients like Claude Desktop and Claude Code run the OAuth flow automatically — they open a browser window, you approve the GitHub login, and the token is stored for you. No manual steps needed.
Getting a token manually (for benchmark / curl testing)
If you need a Bearer token directly — to run the benchmark script, test with curl, or integrate with a custom client — run this single command (requires Python 3, pre-installed on macOS/Linux):
curl -O https://spritesheet-forge.spritesheet-forge.workers.dev/get-token.py && python3 get-token.pyThis will:
Download the OAuth helper script directly from the server
Register a temporary OAuth client via RFC 7591
Open your browser to the GitHub authorization page
Exchange the code for a Bearer token, print it, and save it to
~/.spritesheet-forge-token
To test against a self-hosted instance:
python3 get-token.py --base-url https://your-worker.workers.devToken lifetime
Session tokens are valid for 30 days. After expiry, re-run the OAuth flow (or let your MCP client handle it automatically on the next connection).
Tools Overview
server_info
Returns this server's runtime configuration — upload endpoint URL, output TTL, file size limits, and encoding rules. Call this first when you need the exact upload URL or are planning a multi-step workflow.
{
"upload_url": "https://mcp.clawstudiouo.com/upload",
"output_ttl_seconds": 3600,
"max_file_bytes": 20971520,
"base64_threshold_bytes": 4194304,
"file_input_rules": { ... }
}gif_to_spritesheet
Converts a GIF animation into a spritesheet PNG. Frames are arranged in a grid; column count is auto-calculated from the frame count if not specified. Optional background removal.
gif_to_frames
Extracts every frame from a GIF and returns them as individual PNGs in a ZIP archive. Useful for editing single frames before reassembling.
spritesheet_to_animation
Slices a spritesheet back into frames and assembles them into an animated GIF or WebP. Supports both grid mode (columns + rows) and cell mode (cell_width + cell_height).
png_to_spritesheet
Merges multiple PNG files into a single spritesheet. Supports grid, horizontal, vertical, and packed (bin-packed) layouts. Can optionally output TexturePacker-compatible atlas JSON alongside the image.
split_spritesheet
The reverse of png_to_spritesheet — slices a spritesheet into individual frame PNGs, generates atlas JSON metadata, or both. Supports grid mode and cell mode.
frames_to_animation
Assembles a sequence of PNG frames into an animated GIF or animated WebP. Accepts frames in any order and sorts them by _N suffix if file_name_order=true.
trim_png
Crops transparent (alpha) edges from one or more PNG files. Single file returns a PNG; multiple files return a ZIP. Useful before packing sprites to remove wasted whitespace.
File Input Guide
All file / files parameters accept three input types:
1. Output URL from a previous tool call (fastest)
If you're chaining tools, just pass the output URL directly:
"file": "https://mcp.clawstudiouo.com/output/output-abc123.png"The server reads from its own storage without making an HTTP request — this is always faster than re-uploading.
2. Base64 data URI (files < ~185 KB)
Encode the raw file bytes and prepend the MIME type:
"file": "data:image/gif;base64,R0lGODlh..."Important: You MUST strip ALL whitespace and newlines from the base64 string before prepending the prefix. Many base64 encoders (e.g.
openssl base64, some shell tools) insert newlines every 76 characters — these will cause anINVALID_BASE64error.
# Correct — strips newlines
base64 -i file.gif | tr -d '\n'
# Python
import base64
base64.b64encode(open("file.gif","rb").read()).decode()AI agent note: In Claude Code / Claude Desktop, shell command output exceeding ~250 KB is written to a temp file that AI tools cannot read back (256 KB tool limit). A ~185 KB file encodes to ~247 KB base64 — just under the limit. For any file larger than ~185 KB, or any file you encoded via a shell command, use the upload endpoint instead.
3. Upload endpoint (files ≥ ~185 KB)
For files ≥ ~185 KB (or any file encoded via shell command), upload first and pass the returned URL:
# 1. Get the upload URL
upload_url=$(curl -s https://mcp.clawstudiouo.com/mcp ... | jq -r '.upload_url')
# Or: call server_info tool to get upload_url
# 2. Upload the file
curl -X POST https://mcp.clawstudiouo.com/upload \
-H "Authorization: Bearer $TOKEN" \
-F "file=@animation.gif;type=image/gif"
# Returns: {"url": "https://mcp.clawstudiouo.com/output/upload-xyz.gif", ...}
# 3. Pass the URL to the toolUpload endpoint details:
URL:
https://mcp.clawstudiouo.com/upload(or callserver_infofor the current value)Method:
POST,multipart/form-dataField name:
fileAuth: Same Bearer token as the MCP connection
Max size: 20 MB
Accepted types:
image/png,image/gif,image/webp
Why not always base64 for large files? A 4.7 MB GIF encodes to a ~6.3 MB JSON-RPC payload, which most MCP clients reject or truncate. The latency is nearly identical either way — for beeg.gif (126 frames), base64 took 12472 ms and upload+URL took 12624 ms. The difference is payload safety, not speed. See Benchmark for full data.
Decision rule
File size | Method |
< ~185 KB | Base64 data URI |
≥ ~185 KB, or encoded via shell command | Upload endpoint → pass URL |
Previous tool output | Pass URL directly |
Output TTL
All output URLs — including upload results and tool outputs — expire 1 hour after creation. Plan multi-step workflows accordingly and do not cache URLs across sessions.
Tool Reference
png_to_spritesheet
Parameter | Type | Default | Description |
|
| required | PNG files |
|
|
|
|
|
| auto | Grid columns |
|
|
|
|
|
| — | Required when |
|
| — | Required when |
|
| — |
|
|
| — |
|
|
|
| Pixel gap between frames |
|
|
|
|
|
|
| Pad output to next power of 2 |
|
|
| Sort by |
|
|
| Auto-trim transparent edges before packing |
|
|
| Extrude outermost pixels by N px per frame |
|
|
|
|
split_spritesheet
Parameter | Type | Default | Description |
|
| required | Spritesheet PNG |
|
| — | Grid columns (grid mode) |
|
| — | Grid rows (grid mode) |
|
| — | Cell width px (cell mode) |
|
| — | Cell height px (cell mode) |
|
|
| Pixel gap between cells |
|
| — | Actual frame count for incomplete last row |
|
| — | e.g. |
|
| — | e.g. |
|
|
| Remove fully transparent frames |
|
|
| Per-edge trim offset |
|
|
| |
|
|
| |
|
|
| |
|
|
|
|
|
| — |
|
trim_png
Parameter | Type | Default | Description |
|
| required | PNG files |
|
|
| Alpha threshold 0–255. Pixels with alpha ≤ threshold are trimmed |
|
|
| Transparent margin to preserve around trimmed content |
gif_to_spritesheet
Parameter | Type | Default | Description |
|
| required | GIF file |
|
| auto | Grid columns |
|
|
| Pixel gap between frames |
|
|
| Remove background from each frame |
|
|
|
|
|
|
| Background removal threshold 0–255 |
gif_to_frames
Parameter | Type | Default | Description |
|
| required | GIF file |
|
|
| Remove background from each frame |
|
|
|
|
|
|
| Background removal threshold 0–255 |
frames_to_animation
Parameter | Type | Default | Description |
|
| required | PNG frames |
|
|
| Frame duration in ms (10–10000) |
|
|
| Loop count. |
|
|
| Sort by |
|
|
| Dimension mismatch: |
|
|
| Fill color when |
|
|
|
|
|
|
| WebP lossy quality 0–100 |
|
|
| WebP lossless mode |
spritesheet_to_animation
Parameter | Type | Default | Description |
|
| required | Spritesheet PNG |
|
| — | Grid columns (grid mode) |
|
| — | Grid rows (grid mode) |
|
| — | Cell width px (cell mode) |
|
| — | Cell height px (cell mode) |
|
| — | Actual frame count for incomplete last row |
|
|
| Pixel gap between cells |
|
| — | e.g. |
|
| — | e.g. |
|
|
| Auto-remove fully transparent frames |
|
|
| Per-edge trim offset |
|
|
| |
|
|
| |
|
|
| |
|
|
| Frame duration in ms |
|
|
| Loop count. |
|
|
|
|
|
|
| WebP quality 0–100 |
|
|
| WebP lossless |
Working with AI Agents
Spritesheet Forge is designed for agentic use. The tools are self-documenting: each file / files parameter description contains the encoding instructions, and the server_info tool returns runtime configuration.
Recommended agent workflow
For best results, give the agent this context upfront:
"I've connected Spritesheet Forge MCP. For any file larger than ~185 KB, call
server_infofirst to get the upload endpoint URL and token instructions, then POST the file there before calling the processing tool."
For tiny files (< ~185 KB) the agent can base64-encode directly. For anything larger, calling server_info first gives the agent the exact upload URL and explains how to obtain a Bearer token.
Chaining tools
Output URLs from one tool can be passed directly as input to the next — no re-encoding needed. This is the most efficient pattern:
gif_to_spritesheet → split_spritesheet → frames_to_animationEach call reuses the previous output URL. The server reads directly from storage on its end, so there is no extra HTTP overhead.
TTL in long workflows
Output URLs expire after 1 hour. If a workflow spans multiple agent turns or takes longer than an hour, re-run the earlier tool to get a fresh URL rather than retrying with a stale one.
What agents see
When a tool call succeeds, the agent receives:
{
"url": "https://mcp.clawstudiouo.com/output/output-abc123.png",
"expires_at": "2026-05-05T12: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 field is what to pass to the next tool or present to the user for download.
Limits & Quotas
Limit | Value |
Max file size | 20 MB |
Base64 practical limit | ~185 KB for AI agents (above this, use upload endpoint) |
Output file TTL | 1 hour |
Upload TTL | 1 hour |
Free quota | 100 operations / GitHub account / month |
Quota reset | 1st of each month |
Session token lifetime | 30 days |
Benchmark
A reproducible benchmark covering all 7 tools and a direct comparison of base64 vs upload-endpoint efficiency is available in benchmark/.
Reference run results (2026-05-05) including actual output files are in benchmark/sample/. The sample/ directory is a fixed snapshot and is not updated on subsequent runs.
Key findings from the reference run:
Method | Fixture | Total |
Direct base64 | smol.gif 221 KB | 2787 ms |
Upload + URL | smol.gif 221 KB | 3519 ms |
Direct base64 | beeg.gif 4.7 MB | 12472 ms |
Upload + URL | beeg.gif 4.7 MB | 12624 ms |
For large files (≥ 4 MB), base64 and upload+URL have nearly identical latency. The reason to use the upload endpoint is payload size: a 4.7 MB GIF encodes to a ~6.3 MB JSON-RPC payload that most MCP clients will reject.
To run the benchmark yourself:
# 1. Get a Bearer token
curl -O https://spritesheet-forge.spritesheet-forge.workers.dev/get-token.py && python3 get-token.py
# 2. Export and run
export SPRITESHEET_TOKEN="your_token"
bash benchmark/run.shFAQ
Q: My base64 upload fails with INVALID_BASE64.
Strip ALL whitespace and newlines from the base64 string before prepending the data URI prefix. Many encoders (e.g. openssl base64) insert newlines every 76 characters. Use | tr -d '\n' in shell or Python's base64.b64encode(...).decode().
Q: I get INVALID_FILE_URL when passing a previous tool's output URL.
The URL has expired (1-hour TTL). Re-run the tool that produced it to get a fresh URL.
Q: How do I chain tool outputs?
Pass the url field from one tool's response directly as the file input of the next tool. No re-encoding or re-uploading needed — the server reads from its own storage.
Q: Which input image formats are supported?
PNG, GIF, and WebP. JPEG is not supported as input (no transparency channel).
Q: What does the output look like? Can I preview it?
The output URL is a direct download link (Content-Disposition: inline for images). You can open it in a browser tab — it will display inline if your browser supports the format — or download it directly with curl. URLs expire after 1 hour.
Q: I hit the quota limit. What now?
Quota resets on the 1st of each month. The current usage is returned in every tool response under the quota field. If you need higher limits, consider self-hosting.
Q: Can I use this without an AI client?
Yes — the MCP endpoint accepts standard JSON-RPC 2.0 over HTTP. You can call it with curl or any HTTP client as long as you include a valid Authorization: Bearer <token> header. See benchmark/run.sh for a working shell example.
Q: The agent keeps trying to encode a large file as base64 instead of using the upload endpoint.
Tell the agent explicitly: "This file is larger than ~185 KB. Call server_info first to get the upload URL and token instructions, then POST the file there before calling the tool." The server_info tool returns the exact upload URL and explains both the threshold and how to obtain a Bearer token.
Self-Hosting
The server runs on Cloudflare Workers backed by KV (sessions/quota) and R2 (output storage). To deploy your own instance:
git clone https://github.com/LAXY9887/Game-Dev.-Spritesheet-Forge.git
cd "Game-Dev.-Spritesheet-Forge"
npm install
# Configure secrets
npx wrangler secret put MCP_KEY # shared key for Cloud Run backend
npx wrangler secret put GITHUB_CLIENT_ID
npx wrangler secret put GITHUB_CLIENT_SECRET
# Set your worker URL in wrangler.toml → [vars] WORKER_BASE_URL
npm run deployYou will also need the two Cloud Run backend services (gif2ss and png2ss) running. See the backend repositories for setup instructions.
This server cannot be installed
Maintenance
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/LAXY9887/Game-Dev.-Spritesheet-Forge'
If you have feedback or need assistance with the MCP directory API, please join our Discord server