gimp-mcp
README.md
<p align="center">
<img src="assets/crest.png" alt="gimp-mcp crest" width="300">
<br>
<sub><i>this crest was designed entirely by an agent driving gimp-mcp — shapes + arc-text + transparency, no human in the pixels</i></sub>
</p>
```
_
__ _(_)_ __ ___ _ __ _ __ ___ ___ _ __
/ _` | | '_ ` _ \| '_ \ _____| '_ ` _ \ / __| '_ \
| (_| | | | | | | | |_) |_____| | | | | | (__| |_) |
\__, |_|_| |_| |_| .__/ |_| |_| |_|\___| .__/
|___/ |_| |_|
```
# gimp-mcp
[](https://github.com/constant-itis/gimp-mcp/actions/workflows/ci.yml)
Drive **GIMP 2.10** from an AI agent. An [MCP](https://modelcontextprotocol.io)
server bridges tool calls to GIMP's built-in **Script-Fu** server, exposing the
*entire* GIMP procedure database (every filter, layer op, transform, exporter) —
plus a **vision feedback loop** so the model can actually *see* what it edits.
```
Agent ──MCP/stdio──▶ server.py ──TCP :10008──▶ GIMP Script-Fu server ──▶ GIMP PDB
```
Works with any MCP client. Examples below use [Claude Code](https://docs.anthropic.com/en/docs/claude-code).
## What makes it work: the model sees its own edits
The single most useful tool is `look` — it renders the current image and returns it
**inline** to the model as an image, so there's no blind editing. The loop is:
> **edit → `look` → judge → adjust**
Backed by `inspect` (region luminance/contrast + a text-placement hint — quantitative
eyes for "is this area dark enough for light text?") and `describe` (exact size / mode /
layers / dpi), the agent works the way a person does: make a change, look at the result,
correct. Everything else is in service of that loop.
`look` and `render_preview` take a `bg` argument — `auto` (default) shows a transparency
**checkerboard** when the image has alpha, so transparent/cut-out art is actually visible
instead of vanishing onto white. Use `bg=black`/`bg=white` to preview art as it'll sit on
a dark or light background (shirt, sticker, page), or `bg=none` to keep the alpha.
## Built for agents driving it (frontier *and* local models)
The tool surface is shaped around how an LLM actually works this, not how a human clicks:
- **See transparency, not white** — the vision loop composites alpha onto a checkerboard
by default, so the model never edits blind on cut-out art.
- **No coordinate math** — `place` / `add_text(anchor=…)` position by gravity
(`center`, `top-center`, `bottom-left`, …). One call instead of a
measure → compute → set-offset round-trip that small models routinely get wrong.
- **Native transparency ops** — `color_to_alpha` (soft-key a background out) and
`trim_to_content` (crop to the alpha bounding box) make print/sticker workflows one call.
- **Recoverable errors** — failures are translated into actionable hints ("font missing —
call `list_fonts`", "stale image id — call `list_images`", "server down — run
`start-gimp-server.sh`"). This matters most for local models (Hermes, Qwen, etc.) that
otherwise stall on GIMP's opaque `"returned no return values"`.
- **Fewer round-trips** — `gimp_batch` runs many statements in one call with per-step
error capture; wrappers return the ids/bboxes you'd otherwise have to re-query.
## Not just editing — drawing from scratch
<p align="center">
<img src="assets/showcase.png" alt="a landscape drawn from scratch by an agent" width="620">
<br>
<sub><i>no source image — every shape here (sunburst, sun, layered mountains, stars) was
drawn by an agent from the <code>generate</code> pack primitives, checking each move with <code>look</code></i></sub>
</p>
The `generate` pack (`draw_ellipse`/`draw_polygon`/`draw_star`/`draw_line`/`sunburst` +
procedural `render_plasma`/`render_noise`) turns the tool from an *editor* into a *canvas*:
an agent composes original vector/procedural art and iterates against the vision loop.
## Beyond one image — automation, vectors, motion
GIMP's real power isn't editing a single file; it's scale and range. Three packs open that up:
- **`batch`** — process whole *folders*: `batch_resize`, `batch_convert`, `batch_watermark`,
`batch_recipe` (apply any saved recipe to every image), `contact_sheet`. The
production-pipeline unlock: "watermark these 200 photos," "vintage the whole shoot."
- **`paths`** — `draw_curve`: real smooth bezier curves through anchor points (waves,
ribbons, organic outlines) — proper vector illustration, not just rectangles and ellipses.
- **`animate`** — `frames_to_gif` (layers → animated GIF), `gif_from_folder` (a frame
sequence → GIF), `spin_gif` (rotate a layer into a looping spinner). Motion graphics.
## Why a Script-Fu bridge (not a GIMP plugin)
On modern Linux, GIMP 2.10's **Python-Fu is effectively gone** — `gimp-python` was
dropped because it depended on Python 2 (EOL), so a Python plugin won't install.
But **Script-Fu (Scheme) is always built in**, and GIMP ships a Script-Fu *server*
that listens on TCP and runs any PDB command. This project bridges to that. **Nothing
is installed into GIMP itself** — it just talks to a socket.
## Requirements
- **GIMP 2.10** (2.10.30+ verified) on the `PATH` as `gimp`
- **Python 3.10+**
- **[`fastmcp`](https://github.com/jlowin/fastmcp)** — the only third-party dep (`pip install -r requirements.txt`)
- Linux/macOS (Windows should work via the Script-Fu server but is untested)
## Install
```bash
git clone https://github.com/constant-itis/gimp-mcp.git
cd gimp-mcp
pip install -r requirements.txt
```
Register the server with your MCP client. For Claude Code:
```bash
claude mcp add gimp -s user -- python3 "$(pwd)/server.py"
```
(For other clients, point them at `python3 /abs/path/to/server.py`, stdio transport.)
**Go lean if you want.** The tool surface is modular (core + opt-in packs). Load only
what you need with `GIMP_MCP_PACKS` — e.g. `GIMP_MCP_PACKS=core,text,select` gives ~24
tools instead of 72, which context-limited / local models appreciate. See [PACKS.md](PACKS.md).
## Quickstart
**Headless (fast, no window):**
```bash
./start-gimp-server.sh # GIMP on 127.0.0.1:10008, idempotent
```
Then ask your agent — the `gimp` tools load automatically:
> "Load ~/pic.jpg, scale to 800px wide, bump the contrast, `look` at it, export a PNG."
The model composes the Scheme and checks its own work with `look`. No GIMP scripting
needed on your end.
## Watch it work — the designer workflow
The common case isn't "generate an image from scratch." It's: **you already have GIMP
open with your artwork** and you say *"hey, do X to this."* Because GIMP is
single-instance, you point the server at the window you already have open:
```bash
# 1. open GIMP with your image (normally)
# 2. attach the server to that same window:
./start-gimp-server.sh --gui # (or in GIMP: Filters ▸ Script-Fu ▸ Start Server)
# 3. tell your agent: "work on the image I have open — do X"
```
The agent finds your open image (`list_images` / `suggest`), `show`s it, and edits it
**live in your window** — every tool flushes the display so you watch it happen. Ask to
"see it" any time and it opens/refreshes the view.
**House rules — [AGENTS.md](AGENTS.md).** A short convention set for *any* LLM driving
the tool: attach to the file you already have open, compose abilities (don't railroad),
**show a preview + offer options at each stage**, and **snapshot (`checkpoint`) before
any destructive/automated step** — GIMP 2.10 has no API undo, so the snapshot *is* the
undo. `suggest` gives the agent a context-aware menu of next moves to offer you.
## Recipes & journaling — the power-user layer
Techniques are **saved, parameterized, and reused**, not rebuilt each time:
- **`apply_recipe(name, image_id, params)`** runs a named, tunable pipeline on any image
— e.g. `distressed-text` (grit dial), `vintage`, `sticker-outline`. Bundled recipes are
just editable JSON in `recipes/`; your own live in `~/.config/gimp-mcp/recipes/`
(`$GIMP_MCP_RECIPES`). `list_recipes` / `show_recipe` to browse.
- **`journal`** is a macro-recorder: `journal start` → do edits → `journal show` /
`journal script` (a standalone replay `.py`) / `save_recipe(from_journal=True)` to turn
what you just did into a reusable recipe. Pure queries and preview scratch are filtered
out, so the log reads like the recipe you'd hand-write.
Recipes are **abilities, not baked-in behavior** — data you can read, edit, share, and
extend; nothing forces a workflow.
## Tools (16 core + 73 in packs = 89)
Modular: the **core** (~16, always on) reaches the whole PDB and drives the vision loop;
the rest live in opt-in **packs** ([PACKS.md](PACKS.md)). Listing below is the full set.
**The core three** make the whole PDB reachable — the rest are conveniences:
- `gimp_eval(scheme)` — raw escape hatch; runs any Scheme/PDB expression
- `pdb_query(keyword)` — search the PDB for procedure names
- `pdb_help(procedure)` — a procedure's blurb + typed argument list
**Vision & watch:** `look` (inline render — the feedback loop, transparency-aware `bg`),
`render_preview`, `describe` (metadata), `inspect` (region luminance/contrast + placement
hint), `show` (open it in the GIMP window to watch live), `suggest` (context-aware menu of
next moves)
**Recipes & journal:** `apply_recipe`, `list_recipes`, `show_recipe`, `save_recipe`,
`delete_recipe`, `journal` (record → replay script / new recipe)
**Editing:** layers (`new_layer`, `add_layer_from_file`, `set_layer`, `list_layers`,
`merge_visible`, `delete_layer`), text (`add_text`, `list_fonts`, `outline_text`,
`text_with_shadow`, `arc_text` — text on a circular arc, for seals/badges/mission
patches), transforms (`crop`, `autocrop`, `rotate`, `flip`, `resize_canvas`,
`scale_image`, `scale_to_fit`), color/tone (`brightness_contrast`, `hue_saturation`,
`desaturate`, `invert`, `auto_levels`, `curves_adjust`), filters (`gaussian_blur`,
`sharpen`, `pixelize`, `drop_shadow`, `vignette`, `oilify`, `emboss`, `lens_flare`,
`motion_blur`), selections (`select`, `select_by_color` — magic-wand/keying,
`feather_selection`, `grow_shrink_selection`), fills/shapes (`fill`, `draw_rect`,
`gradient_fill`, `add_border`, `overlay_blend`), placement & transparency
(`place` — anchor a layer by gravity, `color_to_alpha` — soft-key a bg to transparent,
`trim_to_content` — crop to the alpha bounds)
**Draw from scratch (`generate`):** `draw_ellipse`, `draw_polygon`, `draw_star`,
`draw_line`, `sunburst`, `render_plasma`, `render_noise` — vector + procedural primitives
an agent composes into original art, checking each move with `look`.
**Session & safety:** `load_image`, `list_images`, `new_image`, `export_image`,
`save_xcf`, `export_layers`, `close_image`, `checkpoint`/`restore_checkpoint`
(immutable snapshots — GIMP 2.10 has no undo over the PDB), `gimp_batch`
(multi-statement run with per-step error capture), `gimp_status`, `gimp_docs`
## Knowledge base (`knowledge/`)
An **AI-native, self-regenerating** GIMP reference — generated *from the installed GIMP
by introspection*, so it never drifts from what you can actually call:
- `pdb_full.json` — all ~1264 procedures with typed args (machine ground truth)
- `pdb_index.md` — categorized one-line index of everything
- `cookbook/00..11` — dense per-domain guides with working Scheme (read `00-overview` first)
- `recipes.md` — "I want to do X" → which tool / cookbook
- search it in-session with the `gimp_docs` tool; rebuild after a GIMP upgrade with `./build-knowledge.sh`
See `knowledge/README.md` for the full layout and regeneration story.
## Files
| file | role |
|------|------|
| `server.py` | entry point — loads the core + enabled packs, serves over stdio |
| `_core.py` | the lean substrate: eval/introspection, vision loop, IO, safety, shared infra |
| `packs/` | opt-in tool bundles (layers, text, fx, recipes, watch, …) — see PACKS.md |
| `recipes/` | bundled recipe pipelines (editable JSON) |
| `AGENTS.md` · `PACKS.md` | house rules for driving it · the pack system |
| `gimp_bridge.py` | zero-dep socket client for the Script-Fu wire protocol |
| `start-gimp-server.sh` | launches GIMP (headless, or `--gui` to watch) with the Script-Fu server |
| `build_pdb_dump.py` | introspects the live PDB → `knowledge/pdb_full.json` |
| `build_index.py` | builds `knowledge/pdb_index.md` + per-domain `_slices/` |
| `build-knowledge.sh` | one-shot regenerate of the machine-readable knowledge |
| `knowledge/` | the AI-native GIMP reference (see `knowledge/README.md`) |
## Teaching a smaller model (`teach/`)
The Opus→Fable move: a strong model manufactures a **verified** corpus of worked
examples that teaches a small / local model (Hermes, a Qwen, a fine-tune) to drive
gimp-mcp. [`teach/factory.py`](teach/) executes JSON task specs against a live GIMP,
records the exact tool-call trace, renders + checks each, and emits `demos.jsonl`
(fine-tune ready), `fewshot.md` (prompt-ready examples), and a `contact-sheet.png`. The
bundled curriculum (36 verified design tasks) was authored by a seed pass + subagents;
the factory drops anything that doesn't actually run. See [teach/README.md](teach/README.md).
## Wire protocol (confirmed on GIMP 2.10.30)
- Request: `'G'` + uint16_be(len) + scheme
- Response: `'G'` + err_byte(0 ok / 1 err) + uint16_be(len) + body
- `plug-in-script-fu-server` args: `run-mode, ip(STRING), port(INT), logfile(STRING)`
### Gotchas worth knowing
- **Don't launch GIMP with `-f`/`--no-fonts`** — `gimp-text-fontname` then silently
returns `-1` and renders nothing. `start-gimp-server.sh` omits it deliberately.
- `script-fu-*` procedures take **no** run-mode arg; `plug-in-*` procedures **do**
(`RUN-NONINTERACTIVE`).
- The Script-Fu interpreter is one long-lived process, so context (foreground color,
brush, etc.) and `define`s persist across calls — use `gimp-context-push/pop`.
- `gimp-drawable-histogram` returns mean/std on a **0..255** scale on 2.10 (white =
255.0), not 0..1.
## Troubleshooting
| Symptom | Cause & fix |
|---|---|
| **"cannot reach GIMP Script-Fu server"** / connection refused | The server isn't up. Run `./start-gimp-server.sh` (headless) or `--gui` (watchable). Check `gimp` is on your `PATH` and `gimp_status` reports OK. |
| **Text renders nothing / blank** | GIMP was launched with `-f`/`--no-fonts`, which makes `gimp-text-fontname` silently no-op. Use `start-gimp-server.sh` (it omits `-f`); don't add it. Also check the font name with `list_fonts`. |
| **New tools don't show up in your client** | The stdio MCP server loads tools at session start. **Restart the MCP session** (e.g. a new Claude Code session) after pulling changes or editing packs. |
| **Only *some* tools appear** | `GIMP_MCP_PACKS` is limiting them. Unset it (or set `all`) for the full 89; see [PACKS.md](PACKS.md). |
| **`"returned no return values"` error** | Usually a missing font (see above) or bad args. The tool's error text now appends a hint — follow it (`list_fonts`, `pdb_help`, etc.). |
| **Transparent art looks black or white** | For *viewing*: use `look(bg="checker")` (auto already does this when there's alpha). For *exporting*: `export_image` preserves alpha by default (PNG/WebP); pass `flatten=True` only if you want it composited. To *make* a transparent canvas use `new_image(transparent=True)` — `fill_white=False` alone leaves an opaque layer. |
| **Can't watch it work in a window** | Start with `./start-gimp-server.sh --gui`. GIMP is single-instance, so it attaches to a GIMP you already have open. Then `show <image_id>` pops it into the window; every tool flushes the display. |
| **Port already in use / multiple GIMPs** | Override with `GIMP_HOST` / `GIMP_PORT` env vars (both the launcher and the server read them). |
| **Edits go to the wrong image/layer** | State is by integer id. Re-check with `list_images` / `list_layers` / `describe`; ids are stale after `close_image` or a GIMP restart. |
| **A destructive step went wrong** | GIMP 2.10 has no API undo — take a `checkpoint` *before* risky/automated ops and `restore_checkpoint` to recover. |
## Path to GIMP 3.x
If you later install GIMP 3, Python 3 GI plugins come back *and* the Script-Fu server
still exists — this bridge keeps working; you'd just gain the option of richer
in-process plugins.
## Contributing
Contributions are welcome — most are additive (a new pack, a recipe, or a tool in an
existing pack) with no core changes. See **[CONTRIBUTING.md](CONTRIBUTING.md)** for setup,
the test/dogfood loop, and the pack/recipe contracts. Bug reports and "this tool fought me
as an agent" friction reports are just as valuable as code.
## License
[AGPL-3.0](LICENSE).
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues