@chrischall/gemini-mcp 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +76 -3
- package/dist/bundle.js +523 -51
- package/dist/client.js +24 -0
- package/dist/inputs.js +37 -1
- package/dist/jobs.js +122 -2
- package/dist/library.js +151 -0
- package/dist/media-cleanup.js +4 -1
- package/dist/media-endpoint.js +109 -15
- package/dist/media-url.js +2 -2
- package/dist/put-endpoint.js +122 -0
- package/dist/storage/media.js +53 -4
- package/dist/tools/files.js +61 -3
- package/dist/tools/generate.js +75 -30
- package/dist/tools/interact.js +3 -2
- package/dist/tools/library.js +186 -0
- package/dist/tools/music.js +3 -2
- package/dist/tools/set.js +76 -12
- package/dist/tools/shared.js +202 -2
- package/dist/tools/uploads.js +69 -0
- package/dist/tools/video.js +3 -2
- package/dist/upload-url.js +138 -0
- package/dist/version.js +1 -1
- package/dist/zip.js +113 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"metadata": {
|
|
9
9
|
"description": "MCP server for Google Gemini image generation and editing — generate and edit images via the Gemini API",
|
|
10
|
-
"version": "1.
|
|
10
|
+
"version": "1.5.0"
|
|
11
11
|
},
|
|
12
12
|
"plugins": [
|
|
13
13
|
{
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"displayName": "Gemini Images",
|
|
16
16
|
"source": "./",
|
|
17
17
|
"description": "Generate and edit images with Google Gemini image models (Nano Banana / Nano Banana Pro) via natural language prompts",
|
|
18
|
-
"version": "1.
|
|
18
|
+
"version": "1.5.0",
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Chris Hall"
|
|
21
21
|
},
|
package/README.md
CHANGED
|
@@ -86,6 +86,19 @@ progress), two guards make re-issuing safe and unnecessary:
|
|
|
86
86
|
| `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 |
|
|
87
87
|
| `gemini_list_files` | List the files currently uploaded under this API key, with MIME types and expiry times |
|
|
88
88
|
| `gemini_delete_file` | Delete an uploaded file before its ~48h expiry (confirm-gated) |
|
|
89
|
+
| `gemini_sign_media` | *(hosted connector only)* Mint a fresh signed URL for generated media from its `r2_key` — an expired link is not a dead end |
|
|
90
|
+
| `gemini_get_upload_url` | *(hosted connector 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` |
|
|
91
|
+
| `gemini_save_character` / `gemini_list_characters` / `gemini_delete_character` | *(hosted connector only)* Persistent per-account character library: save a reference image + description under a name, then pass `characters: ["name"]` on generation tools. No expiry |
|
|
92
|
+
| `gemini_save_style` / `gemini_list_styles` / `gemini_delete_style` | *(hosted connector 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 |
|
|
93
|
+
|
|
94
|
+
Generation tools also share three throughput/latency controls: `async: true` (return a `job_id`
|
|
95
|
+
immediately), `max_wait_ms` (wait up to a budget, then hand back the `job_id` — fast results stay
|
|
96
|
+
in-band, slow batches never trip the host timeout), and `idempotency_key` (a retry returns the
|
|
97
|
+
recorded result instead of re-billing). On the hosted connector, a `gemini_image_set` result with
|
|
98
|
+
more than one image also carries a **`bundle_url`** — one signed URL for a zip of every image in
|
|
99
|
+
the set — and set links are signed for ~7 days instead of the default ~48h. (A set too large to
|
|
100
|
+
zip safely in Worker memory skips the bundle and says so via `bundle_skipped`; the per-image
|
|
101
|
+
links are unaffected.)
|
|
89
102
|
|
|
90
103
|
## Seeing your images (hosted connector)
|
|
91
104
|
|
|
@@ -96,13 +109,22 @@ something you can *open*. It does: **every result includes a URL**, with no conf
|
|
|
96
109
|
{
|
|
97
110
|
"images": ["https://connector.example.com/media/gen/2026-07-29/ab12cd34-a-cat.png?exp=…&sig=…"],
|
|
98
111
|
"media": [{ "url": "https://…", "r2_key": "gen/2026-07-29/ab12cd34-a-cat.png",
|
|
99
|
-
"expires_at": "2026-07-31T12:00:00.000Z"
|
|
112
|
+
"expires_at": "2026-07-31T12:00:00.000Z",
|
|
113
|
+
"curl_hint": "curl -sS -o a-cat.png \"https://…\"" }]
|
|
100
114
|
}
|
|
101
115
|
```
|
|
102
116
|
|
|
103
117
|
Those links need no auth header — the signature is in the URL — so they work in a browser, in
|
|
104
118
|
`curl`, and in a chat message. They expire (48h by default) and the objects behind them are
|
|
105
|
-
swept on a retention schedule.
|
|
119
|
+
swept on a retention schedule. The `r2_key` is the durable handle for that window:
|
|
120
|
+
|
|
121
|
+
- **`gemini_sign_media`** (hosted connector only) mints a fresh signed URL from an `r2_key`,
|
|
122
|
+
so an expired link never forces you to re-generate — and re-pay for — the image.
|
|
123
|
+
- **`gemini_upload_file` with `r2_key`** turns media the connector generated into a Files API
|
|
124
|
+
reference (the connector reads its own bucket — no HTTP round trip, no signature needed), so
|
|
125
|
+
a generated image can become the reference image for the next generation in one cheap call.
|
|
126
|
+
- Idempotent replays (`idempotency_key`) re-mint the URLs inside the recorded result before
|
|
127
|
+
returning it, so a reused result never carries a dead link.
|
|
106
128
|
|
|
107
129
|
**Why this matters:** MCP's inline image content blocks (`inline: true`) are visible to the
|
|
108
130
|
*assistant* but many chat clients never render them to the user, and the assistant cannot
|
|
@@ -140,7 +162,7 @@ safe form, and a markdown link is a reasonable enhancement where you know it ren
|
|
|
140
162
|
|
|
141
163
|
| Variable | Default | Effect |
|
|
142
164
|
|---|---|---|
|
|
143
|
-
| `MEDIA_TTL_DAYS` | `7` | Objects older than this are deleted by a daily cron
|
|
165
|
+
| `MEDIA_TTL_DAYS` | `7` | Objects older than this are deleted by a daily cron — generated media (`gen/`, legacy `media/`) and signed uploads (`up/`). The character/style library (`lib/`) is exempt: saved entries never expire |
|
|
144
166
|
| `MEDIA_URL_SECRET` | generated | HMAC key for `/media` links; rotate to revoke all outstanding URLs |
|
|
145
167
|
| `MEDIA_PUBLIC_BASE_URL` | unset | Serve from R2 directly instead of the Worker route |
|
|
146
168
|
|
|
@@ -157,7 +179,9 @@ Every tool that takes a reference image — `gemini_image_generate`, `gemini_ima
|
|
|
157
179
|
|---|---|---|
|
|
158
180
|
| `images_url` (`master_images_url`) | the **server** downloads the https URL | none |
|
|
159
181
|
| `images_file_uris` (`master_images_file_uris`) | a `files/<id>` reference, already uploaded | none |
|
|
182
|
+
| `images_r2_keys` (`master_images_r2_keys`) | the connector reads its **own store** (hosted connector only) | none |
|
|
160
183
|
| `images` | read off local disk (stdio builds only) | none |
|
|
184
|
+
| `characters` / `style` | saved library entries, attached by name (hosted connector only) | none |
|
|
161
185
|
| `images_base64` | **through the tool-call JSON** | **~14k tokens per JPEG** |
|
|
162
186
|
|
|
163
187
|
`images_base64` is the fallback of last resort. It costs roughly 14k tokens per modest photo,
|
|
@@ -218,6 +242,55 @@ straight to the Files API (nothing is buffered), so `Content-Length` is required
|
|
|
218
242
|
automatically with `--data-binary @file`. Accepted content types are `image/*`, `video/*` and
|
|
219
243
|
`audio/*`; an optional `?name=` or `X-Filename` header sets the display name.
|
|
220
244
|
|
|
245
|
+
### Signed upload URLs — no token at all (hosted connector)
|
|
246
|
+
|
|
247
|
+
`POST /upload` needs the MCP session's OAuth bearer token, which a sandboxed shell often does
|
|
248
|
+
not have. `gemini_get_upload_url` closes that gap: the authenticated MCP session mints a
|
|
249
|
+
short-lived (~10 min) signed **PUT** URL, and the shell uploads with zero auth headers — the
|
|
250
|
+
signature in the URL is the authorization, mirroring how `/media` downloads work.
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
# 1. tool call: gemini_get_upload_url { filename: "photo.jpg", content_type: "image/jpeg" }
|
|
254
|
+
# → { upload_url, r2_key, expires_at, curl_hint }
|
|
255
|
+
|
|
256
|
+
# 2. shell:
|
|
257
|
+
curl -sS -X PUT -H "Content-Type: image/jpeg" --data-binary @photo.jpg "$UPLOAD_URL"
|
|
258
|
+
# → { "r2_key": "up/<tenant>/2026-07-31/ab12cd34-photo.jpg", "size_bytes": 812345, ... }
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
The signature covers one tenant-scoped object key, the declared content type and the expiry;
|
|
262
|
+
uploads are capped at 15MB, enforced while reading the stream. Only **raster** image types are
|
|
263
|
+
accepted (jpeg/png/webp/gif/avif/heic/heif/bmp/tiff) — SVG is deliberately refused, because an
|
|
264
|
+
SVG is a scriptable document and `/media` serves from the connector's own origin. The returned
|
|
265
|
+
`r2_key` is then usable three ways: directly as `images_r2_keys` on any generation tool (the
|
|
266
|
+
server reads its own bucket — no bytes in the conversation), permanently via
|
|
267
|
+
`gemini_save_character`, or as a ~48h Files API reference via `gemini_upload_file({ r2_key })`.
|
|
268
|
+
Uploads themselves follow the media retention schedule (`up/` prefix, default 7 days).
|
|
269
|
+
|
|
270
|
+
### Character & style library (hosted connector)
|
|
271
|
+
|
|
272
|
+
Recurring subjects and styles can be saved once, per account, with **no expiry** (the retention
|
|
273
|
+
cron deliberately skips the library's `lib/` prefix):
|
|
274
|
+
|
|
275
|
+
```jsonc
|
|
276
|
+
// once:
|
|
277
|
+
{ "tool": "gemini_save_character", "name": "finn",
|
|
278
|
+
"description": "6-year-old boy, curly red hair", "image_r2_key": "up/…/photo.jpg" }
|
|
279
|
+
{ "tool": "gemini_save_style", "name": "bold-cartoon-sports",
|
|
280
|
+
"prompt_fragment": "bold cartoon style, thick outlines, saturated colors" }
|
|
281
|
+
|
|
282
|
+
// afterwards, on any generation:
|
|
283
|
+
{ "tool": "gemini_image_set",
|
|
284
|
+
"master_prompt": "Finn on a soccer field",
|
|
285
|
+
"scenes": ["kicking the ball", "celebrating a goal"],
|
|
286
|
+
"characters": ["finn"], "style": "bold-cartoon-sports" }
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Naming a character attaches its saved reference image and weaves its description into the
|
|
290
|
+
prompt; naming a style appends its fragment (and attaches its reference image, if it has one).
|
|
291
|
+
`gemini_image_set` passes character references to the master *and every scene call*, which is
|
|
292
|
+
what keeps the subject consistent across the set.
|
|
293
|
+
|
|
221
294
|
## Quick Start
|
|
222
295
|
|
|
223
296
|
```json
|