Generate and edit images with Google Gemini (Nano Banana / Nano Banana Pro) via the Gemini API
- ✓Open-source license (MIT)
- ✓Actively maintained (<30d)
- ✓Clear description
- ✓Documented (README)
git clone https://github.com/chrischall/gemini-mcp{
"mcpServers": {
"gemini-mcp": {
"command": "node",
"args": ["/path/to/gemini-mcp/dist/index.js"]
}
}
}1 items in this repository
Generate and edit images with Google Gemini image models via MCP. Use when the user asks to generate, create, or edit images using Gemini or Nano Banana models, wants to produce a consistent set of images from a prompt, or needs to compose/blend multiple images. Triggers on phrases like "generate an image of", "edit this image with Gemini", "create a set of consistent images", "use Nano Banana to make", or any request to produce images via the Gemini image API. Requires the @chrischall/gemini-mcp package installed and the gemini server registered (see Setup below).
MCP Servers overview
# gemini-mcp
[](https://github.com/chrischall/gemini-mcp/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/@chrischall/gemini-mcp)
[](LICENSE)
MCP server for Google Gemini media generation. Exposes thirteen tools to Claude over stdio: list available models, generate/edit/compose images, generate a consistent set of images from a master prompt, multi-turn image refinement (Interactions API), **video** generation (omni), **music** generation (Lyria), an async result poll for long generations, and Files API upload/list/delete for reusable image references. Output is written to disk by default (path returned) or returned inline as base64. Built on the Gemini v1beta API (`generativelanguage.googleapis.com`) using the Nano Banana / Nano Banana Pro (images), omni (video), and Lyria (music) model families.
Developed and maintained by AI (Claude Code).
## Environment Variables
| Variable | Required | Description |
|---|---|---|
| `GEMINI_API_KEY` | Yes | Your Google Gemini API key ([aistudio.google.com/apikey](https://aistudio.google.com/apikey)) |
| `GEMINI_IMAGE_MODEL` | No | Override the default image model (default: `gemini-3.1-flash-image`) |
| `GEMINI_OUTPUT_DIR` | No | Default directory for generated images (default: current working directory) |
| `GEMINI_INPUT_DIR` | No | Directory to resolve bare input-image filenames against (so `images: ["foo.jpg"]` works) |
| `GEMINI_TIMEOUT_MS` | No | Upstream request timeout in ms (default: `60000`, or `120000` for `image_size: "4K"`); each generation tool also takes a per-call `timeout_ms` |
| `GEMINI_HEARTBEAT_MS` | No | Progress-notification cadence in ms while a generation runs (default: `10000`; `0` disables) — keeps MCP hosts that reset their timeout on progress from timing out long generations |
| `GEMINI_CHAIN_RETRY_MS` | No | How long to wait out interactions-store lag when a chained call 404s (default: `120000`; `0` disables retrying) |
### Long generations and client timeouts
4K / Pro-model generations can outrun an MCP host's own `tools/call` timeout (error `-32001`).
The server sends `notifications/progress` heartbeats so hosts that reset their timeout on
progress wait it out. If the host still gives up, the server-side generation usually completes
anyway: the image is written to the output dir, `gemini_interact` also writes an
`<image>.json` sidecar recording the `interaction_id`, and `continue_last: true` resumes the
interaction the lost response belonged to.
### When a chained call 404s
A 404 on a request carrying `previous_interaction_id` is **not** proof the chain expired. The
only 404 body observed live is generic — `"Requested entity was not found."` — and never names
*which* entity. An unknown or renamed **model id**, and an expired Files API **`files/…` uri**
(~48h TTL), return exactly the same thing. So the server no longer asserts a cause it can't
establish: the upstream text is surfaced verbatim, and `gemini_interact` runs an experiment to
find out which it was.
Most often the id isn't missing at all — it just isn't *visible* yet. The interactions store is
eventually consistent, and a freshly created id can 404 while the same id resolves fine minutes
later; heavy turns (4K, Pro, `thinking_level: high`) are the likeliest to hit it, which is
exactly the turn you most want to chain from. So a chained 404 is retried with exponential
backoff for up to **120s** (`GEMINI_CHAIN_RETRY_MS`) before anything is declared broken. The
404 generates nothing and isn't billed, so the wait costs only time.
After that budget is spent, the tool looks up that id's sidecar, re-attaches the image it
produced, and re-issues the request **without** the chain:
- **The re-issue succeeds** → the chain really was the problem, and you get your image anyway,
reported as `chain_recovered: { expired_interaction_id, reanchored_on }`. The 404'd attempt
generates nothing, so this costs the one generation you'd have paid for re-anchoring manually.
- **The re-issue 404s too** → the interaction id was never the cause. You get told exactly that,
with the upstream text, and pointed at the model id and any `files/…` uri instead of being
sent to chase an interaction that was fine all along.
- **No sidecar matches the dead id** → the original error, rather than a guess. Re-anchoring on
the wrong picture would silently corrupt the edit.
Separately, `continue_last` no longer dies with the server process: with no in-memory id it
resumes from the newest `<image>.json` sidecar in the output dir and reports
`continued_from_sidecar: true`. That case was never an expired chain at all — the interaction
was alive upstream the whole time; only our memory of its id was gone.
For hosts whose timeout can't be tamed (e.g. Claude Desktop, a fixed ~30s cap that ignores
progress), two guards make re-issuing safe and unnecessary:
- **`async: true`** returns a `job_id` immediately instead of the image, so the call can't
time out at all; poll `gemini_get_result` with the `job_id` until it's `done`.
- **`idempotency_key`** makes a repeat call idempotent — a retry with the same key returns the
recorded result (`reused: true`) instead of billing a second generation. (Even without a key,
two identical in-flight calls are deduplicated automatically.)
## Tools
| Tool | Description |
|------|-------------|
| `gemini_list_models` | List available Gemini image models and the current default |
| `gemini_image_generate` | Generate image(s) from a text prompt |
| `gemini_image_edit` | One-off edits or multi-image composition with a text instruction (for a series of edits, use `gemini_interact`) |
| `gemini_image_set` | Generate a master image plus N consistent images referencing it |
| `gemini_interact` | Preferred tool for iterative refinement: multi-turn generation/editing via the Interactions API — chain the returned `interaction_id` via `previous_interaction_id` (or `continue_last: true`) |
| `gemini_video_generate` | Generate a short video (text→video, image→video, two-still interpolation, `edit` or `extend`) via the Gemini omni model; `resolution` 360p/720p/1080p/4k is the cost lever; written to disk as MP4 |
| `gemini_music_generate` | Generate music from a text prompt via a Lyria model — `lyria-3-clip-preview` (30s, default), `lyria-3.5` (full-length, vocals) or `lyria-3-pro-preview`; single-turn; written to disk as MP3 |
| `gemini_get_result` | Fetch an async generation started with `async: true` by its `job_id` (status `running` → `done` result). Lets a long generation outlive a host's `tools/call` timeout |
| `gemini_token_usage` | Token usage and an estimated USD cost for this session so far. Call it before and after a workflow and subtract to attribute that workflow's spend. Priced per call against each call's own model from a dated rate card (`GEMINI_RATE_CARD` overrides it); there is no account-balance endpoint to read, so this is how spend is attributed |
| `gemini_upload_file` | Upload an image (or video/audio) to the Gemini Files API once — from a `url`, `data_base64`, or a local `path` — and get a reusable `files/<id>` reference |
| `gemini_list_files` | List the files currently uploaded under this API key, with MIME types and expiry times |
| `gemini_delete_file` | Delete an uploaded file before its ~48h expiry (confirm-gated) |
| `gemini_sign_media` | *(hosted deployments only)* Mint a fresh signed URL for generated media from its `r2_key` — an expired link is not a dead end |
| `gemini_view_media` | *(hosted deployments only)* Return a generated image as an inline image block from its `r2_key`, so a model can actually see what it made before refining it |
| `gemini_get_upload_url` | *(hosted deployments only)* Mint a short-lived signed PUT URL so a shell can upload a reference image with no auth header; the PUT returns an `r2_key` usable in `images_r2_keys`, `gemini_save_character`, or `gemini_upload_file` |
| `gemini_save_character` / `gemini_list_characters` / `gemini_delete_character` | *(hosted deployments only)* Persistent per-account character library: save a reference image + description under a name, then pass `characters: ["name"]` on generation tools. No expiry |
| `gemini_save_style` / `gemini_list_styles` / `gemini_delete_style` | *(hosted deployments only)* Persistent per-account style presets: a reusable prompt fragment (optionally with a reference image), applied by passing `style: "name"` on generation tools. No expiry |
Generation tools also share three throughput/latency controls: `async: true` (return a `job_id`
immediately), `max_wait_ms` (wait up to a budget, then hand back the `job_id` — fast results stay
in-band, slow batches never trip the host timeout), and `idempotency_key` (a retry returns the
recorded result instead of re-billing). On a hosted deployment, a `gemini_image_set` result with
more than one image also carries a **`bundle_url`** — one signed URL for a zip of every image in
the set — and set links are signed for ~7 days instead of the default ~48h. (A set too large to
zip safely in memory skips the bundle and says so via `bundle_skipped`; the per-image
links are unaffected.)
## Seeing your images (hosted)
On a hosted deployment there is no filesystem, so a generated image has to come back as
something you can *open*. It does: **every result includes a URL**, with no configuration.
```jsonc
{
"images": ["https://mcp.nullnet.app/b/<account>/gemini/gen/2026-07-29/ab12cd34-a-cat.png?exp=…&sig=…"],
"media": [{ "url": "https://…", "r2_key": "gen/2026-07-29/ab12cd34-a-cat.png",
"expires_at": "2026-07-31T12:00:00.000Z",
"curl_hint": "curl -sS -o a-cat.png \"https://…\"" }]
}
```
Those links need no auth header — the signature is in the URL — so they work in a browser, in
`curl`, and in a chatWhat people ask about gemini-mcp
What is chrischall/gemini-mcp?
+
chrischall/gemini-mcp is mcp servers for the Claude AI ecosystem. Generate and edit images with Google Gemini (Nano Banana / Nano Banana Pro) via the Gemini API It has 0 GitHub stars and its last recorded update is dated 2026-09-20.
How do I install gemini-mcp?
+
You can install gemini-mcp by cloning the repository (https://github.com/chrischall/gemini-mcp) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.
Is chrischall/gemini-mcp safe to use?
+
Our security agent has analyzed chrischall/gemini-mcp and assigned a Trust Score of 87/100 (tier: Trusted). See the full breakdown of passed checks and flags on this page.
Who maintains chrischall/gemini-mcp?
+
chrischall/gemini-mcp is maintained by chrischall. The last recorded GitHub activity is dated 2026-09-20, with 1 open issues.
Are there alternatives to gemini-mcp?
+
Yes. On ClaudeWave you can browse similar mcp servers at /categories/mcp, sorted by popularity or recent activity.
Deploy gemini-mcp to your cloud
Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.
Maintain this repo? Add a badge to your README
Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.
[](https://claudewave.com/repo/chrischall-gemini-mcp)<a href="https://claudewave.com/repo/chrischall-gemini-mcp"><img src="https://claudewave.com/api/badge/chrischall-gemini-mcp" alt="Featured on ClaudeWave: chrischall/gemini-mcp" width="320" height="64" /></a>More MCP Servers
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl! Don't be shy, join here: https://discord.gg/EMgGbDceNQ
The fastest path to AI-powered full stack observability, even for lean teams.