Skip to main content
Glama
README.md
# nano-banana-mcp

**v1.1.0** — adds optional support for Nano Banana Pro (Gemini 3 Pro Image) alongside the default Gemini 2.5 Flash Image model. See [Model selection](#model-selection) below.

A Cloudflare Worker exposing Google's Gemini 2.5 Flash Image model ("nano banana") as an MCP tool, for use as a custom connector in Claude.ai.

Exposes two tools: `generate_image(prompt, aspect_ratio?, count?, seed?, temperature?, model?, resolution?)` and `edit_image(image, instruction, aspect_ratio?, seed?, temperature?, model?, resolution?)`.

Connector URL format (once deployed):
```
https://nano-banana-mcp.<subdomain>.workers.dev/<MCP_AUTH_TOKEN>/mcp
```

See DEPLOY.md for first-time setup.

## Parameters

### `generate_image`

| Param | Type | Required | Default | Notes |
| --- | --- | --- | --- | --- |
| `prompt` | string | yes | — | Description of the image. Style direction belongs here. |
| `aspect_ratio` | enum | no | `1:1` | One of `1:1`, `2:3`, `3:2`, `3:4`, `4:3`, `4:5`, `5:4`, `9:16`, `16:9`, `21:9`. |
| `count` | integer | no | `1` | 1–4. Each variation is a separate billed generation with its own URL. |
| `seed` | integer | no | random, always reported | int32. Same prompt + ratio + temperature + seed reproduces the same image. With `count > 1` the seed is incremented per variation (`seed`, `seed+1`, …). |
| `temperature` | number | no | model default | 0–2. Lower is more literal, higher more inventive. |
| `model` | enum | no | `normal` | `normal` or `pro`. See [Model selection](#model-selection). |
| `resolution` | enum | no | `1k` | `1k`, `2k`, or `4k`. Pro-only — see [Model selection](#model-selection). |

`num_images` is still accepted as a deprecated alias for `count`, so clients holding a cached copy of the old tool definition keep working.

### `edit_image`

| Param | Type | Required | Default | Notes |
| --- | --- | --- | --- | --- |
| `image` | string | yes | — | Public HTTPS URL, or the 8-character id from a previous result. |
| `instruction` | string | yes | — | The edit to perform, e.g. "make it dusk". |
| `aspect_ratio` | enum | no | source framing | Same values as above. Omit to leave the framing alone. |
| `seed` | integer | no | random, always reported | int32, as above. |
| `temperature` | number | no | model default | 0–2, as above. |
| `model` | enum | no | `normal` | `normal` or `pro`. See [Model selection](#model-selection). |
| `resolution` | enum | no | `1k` | `1k`, `2k`, or `4k`. Pro-only — see [Model selection](#model-selection). |

An invalid enum value or an out-of-range number comes back as an `isError` result naming the valid values, without spending a Gemini call.

## Model selection

Both tools accept an optional `model` parameter:

| `model` | Gemini model | Approx. cost | Notes |
| --- | --- | --- | --- |
| `normal` (default) | `gemini-2.5-flash-image` | ~$0.039/image | Same model and cost as before this option existed. Any call that omits `model` behaves exactly as it always has. |
| `pro` | `gemini-3-pro-image-preview` (Nano Banana Pro) | ~$0.134/image at 1K/2K, ~$0.24/image at 4K | ~3.4x the cost of `normal`. Better prompt adherence, text rendering, and multi-image consistency. Only use it when explicitly asked for. |

`resolution` (`1k` / `2k` / `4k`, default `1k`) only applies to `pro`, sent as `generationConfig.imageConfig.imageSize`. If `resolution` is passed while `model` is `normal` (or left at its default), it's ignored and the settings line notes `resolution dropped (normal model)` rather than silently discarding it.

### How the parameters reach Gemini

All of them go into `generationConfig` on the `generateContent` body — the aspect ratio specifically into `generationConfig.imageConfig.aspectRatio`:

```json
{
  "contents": [...],
  "generationConfig": {
    "temperature": 0.9,
    "seed": 4821,
    "responseModalities": ["TEXT", "IMAGE"],
    "imageConfig": { "aspectRatio": "16:9" }
  }
}
```

`temperature` is omitted from the body entirely when not supplied, so the model's own default applies. `seed` never is — see below. Earlier versions of this server instead appended `(aspect ratio 16:9)` to the prompt text, which left the ratio a suggestion the model was free to ignore — that is why 16:9 requests kept coming back square. `count` is handled as sequential `generateContent` calls rather than one call asking for N images, which the model answers inconsistently.

### Settings echo

Every image comes back as its own text content block: the URL on the first line, then the settings it was actually produced under, so the chat side can report them and reuse them in a follow-up request.

```
https://nano-banana-mcp.<subdomain>.workers.dev/<token>/img/8be62d09
variant 2/3 · 1376×768 (16:9) · seed 101 · temp 0.9 · model normal
```

On `pro`, the line also names the resolution and its approximate cost, e.g. `model pro · 2k · ~$0.134`.

**The seed is always present.** A caller who omits `seed` gets one generated by the Worker, which is sent to Gemini and reported back. Letting the model pick its own unreported seed would make the image impossible to reproduce, so refinement — "same image but at dusk" — would be impossible too. `temperature` reads `temp default` when it wasn't supplied, since there is no single number to name.

```
1376×768 (16:9) · seed 868960483 · temp default
```

The resolution is read from the returned image's own header rather than assumed from the requested ratio, so if the model ignores the aspect ratio the line says so:

```
1024×1024 (16:9) ⚠ model returned a different aspect ratio than the requested 16:9
```

When no `aspect_ratio` was requested — the usual case for `edit_image` — the ratio in the line is inferred from the returned pixels instead, and never carries the mismatch warning.

If a variation fails partway through a `count > 1` batch, the images already generated are still returned, with a trailing note naming the variation that failed and why.

## Image URLs

Since Claude.ai doesn't render raw base64 image blocks from custom MCP connectors, `generate_image` also stores each generated image in a Workers KV namespace (`IMAGES`) and returns a public URL alongside the base64 content:

```
https://nano-banana-mcp.<subdomain>.workers.dev/<MCP_AUTH_TOKEN>/img/<id>
```

- Each image is available at its URL for **24 hours**, after which it expires from KV and the route returns a 404.
- The URL is auth-gated the same way as `/mcp`: the token must match `MCP_AUTH_TOKEN`.

## Rendering

Tool results are plain MCP content blocks — one text block per image (URL + settings) followed by the raw image blocks — and the chat side decides how to display them. The server declares no `resources` capability and no MCP Apps extension, so no host-rendered widget is involved.

## Editing

`edit_image` edits an existing image with a natural-language instruction, using the same Gemini 2.5 Flash Image model. The source `image` argument accepts either:

- a public HTTPS URL to an image, or
- the 8-character id from a previous `generate_image`/`edit_image` result (the trailing segment of its returned `/img/<id>` URL).

If `aspect_ratio` is omitted, no `imageConfig` is sent at all and the source image's framing is preserved. Source images are capped at ~6 MB. Like `generate_image`, results are stored in KV and returned as URL(s) + raw image content, so edited images can themselves be passed back into `edit_image` by id for further edits.