dither-mcp
by lennrt
README.md

# dither-mcp
**Color with character. Local image dithering for your agent.**
dither-mcp is a local-first [Model Context Protocol](https://modelcontextprotocol.io/) server for image dithering. Your agent can generate random styles, inspect images, compare treatments, and save recipes. The new **MCP Apps studio** adds image controls inside supported MCP hosts. One Go binary provides the same image pipeline through MCP and the CLI.

*Moon Garden original → Atkinson / six-color clay palette / 2× pixels. Local Go programs generated the artwork and dithered treatments.*
[Try random styles](#try-a-random-style) · [Open the studio](#an-image-studio-inside-your-chat) · [Connect your agent](#connect-your-agent) · [Explore the showcase](#explore-the-gallery) · [Watch the demos](#see-it-run) · [Read the specifications](#specified-and-verified)
[Website](http://ka2a.dev/dither-mcp-site/) · [MCP Apps guide](docs/mcp-apps.md) · [Source code](https://github.com/lennrt/dither-mcp) · [Website source](https://github.com/k-a2a/dither-mcp-site)
## What your agent can do
The toolkit provides **18 tools with typed inputs**, **3 resources**, and **3 prompts**. It includes **41 dithering algorithms**, **256 curated palettes**, and custom palettes. It also supports image adjustments, masks, retro effects, comparison sheets, batch renders, motion, and print exports. Tools return structured results. Artifact-producing tools also return local paths and SHA-256 digests. Named favorites return a stable ID and recipe. Image artifacts include dimensions.
| Ask your agent | Workflow |
|---|---|
| “Surprise me with a new dither style.” | `dither_random_style` → `dither_studio` or `dither_render` |
| “Open a studio so I can try a few looks.” | `dither_studio` → **Random style** → Undo or Recent looks → Save favorite or Save image |
| “Find four warm colors for a botanical print.” | Filter `dither_palettes` by name, category, and color count → `dither_render` |
| “Build a palette from this image’s own colors.” | `dither_inspect` → `dither_palette_extract` → `dither_render` |
| “Show me Atkinson, Floyd–Steinberg, and Bayer side by side.” | `dither_catalog` → `dither_compare` |
| “Compare Oat and Ink, Game Boy, and Sepia with the same texture.” | `dither_palettes` → `dither_compare` with `palettes` |
| “Apply the same settings to this folder of images.” | `dither_recipe_load` → `dither_batch` |
| “Dither the subject. Keep the background. Add CRT texture.” | `dither_render` with a geometric or local image mask and effects |
| “Create a short looping animation from this image.” | `dither_animate` → GIF or sprite sheet |
| “Process a short clip with this palette.” | `dither_video`, with local FFmpeg enabled |
| “Keep this as Moonlit paper and use it next time.” | `dither_favorite_save` → `dither_favorites_list` → `dither_favorite_load` |
| “Export these settings as a portable file.” | `dither_recipe_save` |
| “Show me the result. Create separate print plates.” | `dither_preview` → `dither_separate` |
Image processing stays in your configured workspace, with source files and artifacts under your control. Your connected MCP host receives tool results and controls which information reaches its model.
## Try a random style
Ask your agent: **“Open this image in the studio. I want to try random looks.”**
Select **Random style** above the preview, then keep clicking until something catches your eye.
Went past a favorite? Use **Undo** or **Redo**, or select it under **Recent looks**
and choose **Restore look**. The studio keeps 20 recently used looks, including
your starting preview and manual refinements. New experiments keep other recent
looks available.
For example: try three random looks, select Undo to return to the second, then
enter **Moonlit paper** under Favorites and select **Save favorite**. To compare an older look, choose its algorithm,
palette, and seed label in Recent looks and select Restore look.
History lasts for this studio session. Restoring re-previews the complete
settings against the current source and mask. Named favorites survive closing
the studio and are shared by sessions using the same workspace.
For API-driven iteration, call `dither_random_style` with `{}` for a fresh look or
`{"seed":42}` for a repeatable one. It returns `{seed, recipe}` without reading an
image or creating a file. Pass its palette and options to `dither_render` or `dither_studio`. Keep a named look with `dither_favorite_save`.
```sh
./bin/dither-mcp call --root . dither_random_style '{}'
./bin/dither-mcp call --root . dither_random_style '{"seed":42}'
```

*Seeds 42, 314, and 2026, left to right. Same source, 320-pixel width, and 2× pixel grid.*
Reproduce these images and save their recipes with the dependency-free
[example script](examples/random-styles.mjs), using Node.js 20.19 or later:
```sh
make build
node examples/random-styles.mjs docs/assets/source/moon-garden.png output/random-styles 42 314 2026
```
Use a new output directory on another run. The script prints each selected
algorithm, palette, artifact path, and digest. Replay a saved recipe:
```sh
./bin/dither-mcp render --root . \
--input docs/assets/source/moon-garden.png \
--recipe output/random-styles/seed-42.json --output output/replayed-42.png
```
Seeds reproduce selections within the current engine and catalog version.
Save the full recipe to retain its settings for later reuse.
[See the exact API and preview examples](docs/mcp.md#random-style-iteration).
## Keep named favorites across sessions
After a preview succeeds, enter a name under **Favorites** and select **Save
favorite**. Open another studio on the same workspace, choose the saved name,
and select **Load favorite**. **Refresh** discovers favorites added by another
client. Loading previews the saved settings on the current source before
changing your controls. Undo can return to the look you had before loading.
Favorites preserve custom colors, dimensions, crop, masks, and effects. They
live in `.dither/favorites/` under the server's configured workspace, shared by
the MCP API and studio. They contain settings, not images or source paths. Use
the same workspace to find them later; copy the directory to carry the library
to another workspace. **Save recipe** remains available for individual portable
JSON files.
Names are case-sensitive, trimmed, and limited to 80 Unicode characters without
control characters. Existing names cannot be replaced; save a revised look under
a new name. Loading requires a compatible source and, for image-mask recipes,
a current external mask. Geometry is preserved, so incompatible crops or
preview dimensions report an error and retain your edits.
Try the executable example, which saves, discovers, and reloads a favorite
through separate CLI processes, then checks identical preview and PNG bytes:
```sh
make build
node examples/favorites.mjs docs/assets/source/moon-garden.png 'Moonlit paper' output/moonlit-paper.png
./bin/dither-mcp call --root . dither_favorites_list '{}'
```
Use a new name and PNG destination when repeating the example. MCP clients use
`dither_favorite_save` with `{name, recipe}`, `dither_favorites_list` with `{}`,
and `dither_favorite_load` with the returned `{id}`. See the
[named-favorites API example and limits](docs/mcp.md#named-favorites).
## An image studio inside your chat

*Actual studio connected to the local Go server through the official MCP Apps bridge. Bayer 8×8 / six extracted colors / “Warm press” favorite.*
Ask your agent to open `dither_studio` for a local image. In a host that supports [MCP Apps](https://modelcontextprotocol.io/extensions/apps/overview) and local stdio servers, the tool opens an interactive view beside the conversation.
Start with the **Random style** button above the image. Each click generates and previews a new palette, algorithm, and treatment. Click again to explore another look; the newest click wins. Your crop, mask, dimensions, and pixel size stay in place. Use Undo, Redo, or Recent looks to revisit a successful preview. Nothing is saved until you choose **Save favorite**, **Save image**, or **Save recipe**.
To refine a look manually, choose an algorithm and palette, adjust the image, and select **Apply preview**. Zoom in to examine the pixels and inspect the palette swatches. The studio uses all 41 algorithms and 256 palettes, with custom colors and the shared engine options available through the tool.
To build a palette from your image, choose a color count and select **Extract colors**. The studio uses the complete normalized source before crop, tone, and mask settings. It puts the colors into your custom palette. Select **Apply preview** to see them, then save the accepted result.
Previews run in memory and create no file. Omitted dimensions fit within 512 × 512 pixels without enlarging the source. Every preview stays within 1,024 pixels per axis and 2 MiB PNG. Choose **Preview**, **Source**, or **Custom dimensions** for export, then enter a new relative output path. Preview size reproduces the accepted settings. Source size uses the upright image dimensions after cropping. Custom size requires both dimensions. A different resolution can change the dither pattern. The studio preserves all other settings and checks source and mask fingerprints before saving. The filename extension selects the output format.
Select **Save recipe** to keep the accepted treatment in a new relative `.json`
file. It saves the preview's exact colors, dimensions, and advanced settings.
Image export dimensions do not change the recipe. Reuse image-mask recipes with
an external mask file; source and mask paths are not stored.
The Go binary bundles the complete app and official MCP Apps SDK. It keeps the stdio setup and needs no runtime Node.js process, CDN, or external asset service. Hosts without Apps support receive the PNG preview and structured JSON.
The studio is verified in MCP Inspector 2.10.1 and the included embedding host. The host guide records the exact checks and versions. Read the [MCP Apps guide](docs/mcp-apps.md) and [host setup and compatibility](docs/hosts.md).
To embed the studio in your own application, use the supported [AppBridge example](docs/embedding.md). It connects a browser view to the local Go server over stdio. GitHub Pages serves the separate showcase; interactive local processing needs a host and backend.
## Supported files
| Workflow | Input | Output |
|---|---|---|
| Still images | PNG, JPEG, GIF, WebP, BMP, and TIFF | PNG, JPEG, GIF, SVG, PBM, and ASCII |
| Generated animation | One supported still image | Animated GIF or a PNG sprite sheet |
| GIF animation | Animated GIF | Animated GIF or a PNG sprite sheet |
| Video | MP4, MOV, WebM, Matroska, and AVI | MP4 (H.264), WebM (VP9), GIF, or a PNG sprite sheet |
| Print separation | One supported still image | ZIP with PNG ink plates and a JSON manifest |
Still-image tools use the first GIF frame. To process every GIF frame, use `dither_animate` with `effect: "source"`. GIF animation defaults to this mode. It preserves frame composition, disposal, delays, and loop count.
Generated animations start from one still image. Print separation supports at most 16 inks.
Video processing requires **`--allow-video`** and local **FFmpeg** and **ffprobe** executables. It processes short, bounded segments and exports video without audio. Available input decoders and output codecs depend on your FFmpeg build.
Still-image tools apply all eight EXIF orientations before cropping and resizing. They convert supported embedded RGB ICC profiles to sRGB and preserve alpha. Untagged RGB images use sRGB.
Read the [format guide](docs/formats.md) for profile coverage, file behavior, codec requirements, and video limits.
<details>
<summary>See orientation and color normalization</summary>

*Stored pixel samples appear at left. Actual service previews appear at right. [Reproduce this original chart and inspect its results](docs/formats.md#reproduce-the-comparison).*
</details>
## Connect your agent
Build from this source checkout with **Go 1.27**:
```sh
git clone https://github.com/lennrt/dither-mcp.git
cd dither-mcp
go mod download
go build -o bin/dither-mcp ./cmd/dither-mcp
./bin/dither-mcp catalog
```
For an MCP client that uses an `mcpServers` configuration, supply absolute paths:
```json
{
"mcpServers": {
"dither": {
"command": "/absolute/path/to/dither-mcp/bin/dither-mcp",
"args": ["mcp", "--root", "/absolute/path/to/images"]
}
}
}
```
1. Reconnect the client.
2. Ask it to inspect an image.
3. Ask it to open the studio, then try **Random style**.
Paths in tool calls are relative to `--root`. The server exchanges MCP messages on stdin/stdout and sends diagnostics to stderr.
See the [MCP guide](docs/mcp.md) for schemas, examples, resources, prompts, and limits. The [CLI guide](docs/cli.md) documents direct use of the same operations.
## Explore the gallery
The Moon Garden is a waxy-leaf plant in a fluted ceramic pot, with textured stones and a suspended golden sphere. A deterministic Go ray tracer creates its light, shadows, veins, grain, and mineral bands. Each algorithm converts these surfaces into a distinct pixel pattern.

*Left to right, top to bottom: Atkinson · Floyd–Steinberg · Bayer 8×8 · dot screen 8 · blue noise · Riemersma. Same crop, two warm colors, fixed seed.*

*Atkinson / Game Boy · Floyd–Steinberg / custom ember · Bayer 4×4 / CGA.*
### Every palette. A new possibility.
The catalog includes **256 palettes across 16 categories**. Names, descriptions, tags, color counts, and hex values help your agent find a palette. Every palette has a stable ID. Use that ID in renders and recipes.
The categories are:
- Retro, terminal, interface, and print.
- Duotone, botanical, landscape, and ocean.
- Mineral, seasonal, neon, and pastel.
- Food, cosmic, architecture, and neutral.

*The twelve images use the same source and Floyd–Steinberg settings. [View the complete 256-palette atlas](docs/assets/palette-atlas.png). Read the [palette guide](docs/palettes.md) for descriptions, sources, and exact colors.*
<details>
<summary>Explore the complete color atlas</summary>

</details>
```sh
./bin/dither-mcp palettes --category botanical \
--min-colors 4 --max-colors 6 --limit 8
./bin/dither-mcp palettes --query 'oat ink'
./bin/dither-mcp render --root . \
--input docs/assets/source/moon-garden.png \
--output oat-garden.png \
--algorithm floyd-steinberg --palette oat-and-ink \
--width 960 --pixel-scale 2
```
Use a preset or supply **2–256 unique hex colors**. Custom palettes are explicit recipe inputs. Extract a palette from a local image when you want the image’s own colors to guide the result. Extraction scans every visible pixel, so small accents and periodic stripes contribute. A bounded histogram and median cut reduce those colors to your requested count.
```sh
./bin/dither-mcp render --root . \
--input docs/assets/source/mineral-nocturne.png \
--output mineral.png \
--algorithm floyd-steinberg \
--colors '#251d32,#ab4e3b,#e6ad62,#f0e7d3' \
--width 960 --pixel-scale 2
```

*Wave / 24 frames / 12 fps. [Reproduce the artwork and recipes](docs/showcase.md).*
Explore the [dither-mcp website](http://ka2a.dev/dither-mcp-site/) for the MCP Apps studio, a keyboard-accessible comparison slider, actual algorithm previews, a searchable palette explorer, palette studies, and recorded demos. The [website source](https://github.com/k-a2a/dither-mcp-site) lives in a separate repository and supports GitHub Pages.
## Make a reproducible recipe
Start with an inspection and a comparison sheet:
```sh
./bin/dither-mcp inspect --root . \
--input docs/assets/source/moon-garden.png
./bin/dither-mcp compare --root . \
--input docs/assets/source/moon-garden.png \
--output comparison.png \
--algorithms atkinson,floyd-steinberg,bayer-8 \
--palette sepia --width 320
```
To compare colors with the same texture, use a palette shortlist:
```sh
./bin/dither-mcp compare --root . \
--input docs/assets/source/moon-garden.png \
--output palette-studies.png \
--palettes oat-and-ink,gameboy,sepia --algorithm atkinson --width 320
```

*Left to right: Oat and Ink · Game Boy · Sepia. Each cell reports its palette, algorithm, and exact rectangle.*
Compare either palettes or algorithms, with at most 12 candidates. Palette mode
uses shared inline options and excludes a common palette, custom colors, and
saved recipes. Each candidate can then be rendered separately.
Save the settings in a versioned recipe. Use that recipe to render the image:
```sh
./bin/dither-mcp call --root . dither_recipe_save '{
"output": "garden.recipe.json",
"recipe": {
"version": 1,
"palette": "gameboy",
"options": {
"algorithm": "atkinson",
"width": 960,
"pixel_scale": 2,
"seed": 42
}
}
}'
./bin/dither-mcp render --root . \
--input docs/assets/source/moon-garden.png \
--output garden.png --recipe garden.recipe.json
```
Recipes keep image settings portable across source files and destinations. A render result includes the resolved recipe and artifact digest. Explicit seeds make noise, glitch, and animation reproducible. For a saved recipe, apply the complete configuration. For a new treatment, supply inline settings.
A new output path preserves your earlier work. Give each variation a separate name.
## The toolkit
| Area | Included |
|---|---|
| Error diffusion | Floyd–Steinberg, Atkinson, Jarvis–Judice–Ninke, Stucki, Burkes, Sierra variants, Fan variants, Stevenson–Arce, and others |
| Screens and noise | Bayer 2–32, clustered dots, dot screens, line screens, crosshatch, diamonds, spirals, waves, seeded uniform noise, and blue noise |
| Curve traversal | Riemersma with Hilbert traversal |
| Color | Built-in and custom palettes, deterministic palette extraction, sRGB and linear-light RGB matching |
| Input normalization | Automatic EXIF orientation, supported ICC v2/v4 RGB matrix/TRC profiles, and PNG color metadata |
| Image controls | Resize, crop, crisp pixel scale, brightness, contrast, gamma, saturation, threshold, strength, grayscale, invert, and serpentine diffusion |
| Selection | Rectangular, circular, inverted, and local image masks |
| Retro effects | Scanlines, CRT shading, seeded noise, row glitch, and pixel sorting |
| Still exports | PNG, JPEG, GIF, SVG, PBM, and ASCII |
| Print workflows | PNG resolution metadata and spot-color separation plates in a ZIP |
| Motion | Source GIF frames, generated GIF loops, sprite sheets, and bounded local video processing with FFmpeg |
| Agent workflows | Inspect, discover, compare, batch, save, and reload recipes |
| MCP Apps studio | Choose settings, apply an in-memory preview, and export at preview, source, or custom size with source-change checks |
The engine preserves source alpha. Diffusion stops at transparent pixels and mask boundaries. Regions outside the mask retain adjusted source colors. Post-effects can introduce colors beyond the selected palette. See the [feature matrix](docs/feature-matrix.md) for precise coverage and the [architecture](docs/architecture.md) for pipeline order and boundaries.
## See it run
[Charmbracelet VHS](https://github.com/charmbracelet/vhs) recorded these local demos. They use the binary and original artwork from this checkout.

*[Watch the render clip](docs/assets/render.mp4).*

*[Watch the comparison and animation clip](docs/assets/compare.mp4).*

*[Watch the MCP clip](docs/assets/mcp.mp4). The scripted Go client connects to the real stdio server and is ready to replay locally.*

*[Watch the palette discovery clip](docs/assets/palettes.mp4). Search, select, render, and keep the resulting recipe.*
Recreate the source art and gallery with `./scripts/showcase.sh`. Record the terminal clips with `./scripts/showcase-record.sh`. The [showcase guide](docs/showcase.md) lists prerequisites, exact recipes, asset provenance, and recording checks.
## Specified and verified
[OpenSpec](openspec/) records the requirements, scenarios, and implementation changes. [Quint models](spec/) describe input normalization, quantization, artifact publication, and bounded palette discovery. The formal models abstract pixel values, operating-system behavior, and process execution. Their checks complement implementation tests.
```sh
make verify
make spec-install
make spec-check
```
The specification tools are development-only dependencies and require Node 20.19+ and pnpm. See [testing](docs/testing.md) for the checks, their recorded outcomes, and what they establish. [Security](docs/security.md) explains the filesystem boundary, limits, and optional FFmpeg integration.
## License and credits
The project uses the MIT license. See [LICENSE](LICENSE) and [third-party notices](THIRD_PARTY_NOTICES.md). Original showcase artwork and recordings use the same license. The [sources guide](docs/sources.md) lists algorithm references and inspiration. It also records the references and platform credits for classic techniques and retro palettes.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues