@chrischall/gemini-mcp 1.4.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.
@@ -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.4.0"
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.4.0",
18
+ "version": "1.5.0",
19
19
  "author": {
20
20
  "name": "Chris Hall"
21
21
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gemini-mcp",
3
3
  "displayName": "Gemini Images",
4
- "version": "1.4.0",
4
+ "version": "1.5.0",
5
5
  "description": "MCP server for Google Gemini image generation and editing — generate and edit images via the Gemini API",
6
6
  "author": {
7
7
  "name": "Chris Hall",
package/README.md CHANGED
@@ -87,6 +87,18 @@ progress), two guards make re-issuing safe and unnecessary:
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
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.)
90
102
 
91
103
  ## Seeing your images (hosted connector)
92
104
 
@@ -150,7 +162,7 @@ safe form, and a markdown link is a reasonable enhancement where you know it ren
150
162
 
151
163
  | Variable | Default | Effect |
152
164
  |---|---|---|
153
- | `MEDIA_TTL_DAYS` | `7` | Objects older than this are deleted by a daily cron (both the current `gen/` prefix and the legacy `media/` one) |
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 |
154
166
  | `MEDIA_URL_SECRET` | generated | HMAC key for `/media` links; rotate to revoke all outstanding URLs |
155
167
  | `MEDIA_PUBLIC_BASE_URL` | unset | Serve from R2 directly instead of the Worker route |
156
168
 
@@ -167,7 +179,9 @@ Every tool that takes a reference image — `gemini_image_generate`, `gemini_ima
167
179
  |---|---|---|
168
180
  | `images_url` (`master_images_url`) | the **server** downloads the https URL | none |
169
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 |
170
183
  | `images` | read off local disk (stdio builds only) | none |
184
+ | `characters` / `style` | saved library entries, attached by name (hosted connector only) | none |
171
185
  | `images_base64` | **through the tool-call JSON** | **~14k tokens per JPEG** |
172
186
 
173
187
  `images_base64` is the fallback of last resort. It costs roughly 14k tokens per modest photo,
@@ -228,6 +242,55 @@ straight to the Files API (nothing is buffered), so `Content-Length` is required
228
242
  automatically with `--data-binary @file`. Accepted content types are `image/*`, `video/*` and
229
243
  `audio/*`; an optional `?name=` or `X-Filename` header sets the display name.
230
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
+
231
294
  ## Quick Start
232
295
 
233
296
  ```json