@chrischall/gemini-mcp 2.3.4 → 2.4.1

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.
@@ -119,10 +119,10 @@ export function registerLibraryTools(server, client) {
119
119
  tool: 'gemini_delete_character',
120
120
  action: 'character.delete',
121
121
  message: 'Review and confirm this deletion:',
122
- description: 'Delete a saved character (its reference image and description)',
123
- method: 'DELETE',
124
- path: `characters/${args.name}`,
122
+ summary: 'Delete a saved character (its reference image and description)',
123
+ account: undefined,
125
124
  target: args.name,
125
+ request: { method: 'DELETE', path: `characters/${args.name}` },
126
126
  payload: { name: args.name },
127
127
  args,
128
128
  confirmToken: args.confirmToken,
@@ -189,10 +189,10 @@ export function registerLibraryTools(server, client) {
189
189
  tool: 'gemini_delete_style',
190
190
  action: 'style.delete',
191
191
  message: 'Review and confirm this deletion:',
192
- description: 'Delete a saved style preset',
193
- method: 'DELETE',
194
- path: `styles/${args.name}`,
192
+ summary: 'Delete a saved style preset',
193
+ account: undefined,
195
194
  target: args.name,
195
+ request: { method: 'DELETE', path: `styles/${args.name}` },
196
196
  payload: { name: args.name },
197
197
  args,
198
198
  confirmToken: args.confirmToken,
@@ -14,7 +14,10 @@ import { buildZip } from '../zip.js';
14
14
  const DEFAULT_SINK = createDiskSink();
15
15
  /** Supported output aspect ratios (Gemini image API). */
16
16
  export const ASPECT_RATIOS = ['1:1', '2:3', '3:2', '3:4', '4:3', '4:5', '5:4', '9:16', '16:9', '21:9', '1:4', '4:1', '1:8', '8:1'];
17
- /** Supported output resolutions. `512` is Flash-only (0.5K); `1K`/`2K`/`4K` work on all image models. */
17
+ /**
18
+ * Supported output resolutions. `512` (0.5K) is gemini-3.1-flash-image only — Nano Banana 2.1, the
19
+ * default, documents 1K/2K/4K — while `1K`/`2K`/`4K` work on all image models.
20
+ */
18
21
  export const IMAGE_SIZES = ['512', '1K', '2K', '4K'];
19
22
  /**
20
23
  * The words people actually use for shape, as a shorthand over
@@ -77,7 +80,7 @@ export const orientationSchema = z
77
80
  * When to pick which Nano Banana model — shared by every tool's `model` param
78
81
  * so the guidance can't drift between them.
79
82
  */
80
- export const MODEL_CHOICE_GUIDE = 'gemini-3.1-flash-image (Nano Banana 2, the default) is the generalist: fast, 4K, reliable text, strong ' +
83
+ export const MODEL_CHOICE_GUIDE = 'gemini-nano-banana-2.1 (Nano Banana 2.1, the default) is the generalist: fast, 4K, reliable text, strong ' +
81
84
  'multi-reference consistency. gemini-3-pro-image (Pro) is for the hardest work — best world knowledge, brand ' +
82
85
  'precision. gemini-3.1-flash-lite-image (Lite) is cheapest: 1K only, no search grounding.';
83
86
  /**
@@ -299,7 +302,7 @@ export const sharedImageSchema = {
299
302
  .optional()
300
303
  .describe('Exact output aspect ratio. For a plain landscape/portrait/square request, `orientation` is the shorthand; this wins if both are given.'),
301
304
  orientation: orientationSchema,
302
- image_size: z.enum(IMAGE_SIZES).optional().describe('Output resolution (512 = 0.5K, Flash-only)'),
305
+ image_size: z.enum(IMAGE_SIZES).optional().describe('Output resolution (512 = 0.5K, gemini-3.1-flash-image only)'),
303
306
  output_dir: z.string().optional().describe('Directory to write images to (default: $GEMINI_OUTPUT_DIR or ~/Downloads/gemini-mcp)'),
304
307
  inline: z.boolean().optional().describe('Return the image as an inline image block you can SEE, instead of a path (stdio) or link (hosted). The default costs nothing to carry and hands back a reference you can reuse; use this when you need to check the result yourself. On the hosted connector gemini_view_media does the same for an image you already have'),
305
308
  seed: z.number().int().optional().describe('Seed for reproducible generation; random if omitted'),
@@ -544,7 +547,7 @@ export async function emitMedia(named, kind, opts, meta, onWritten) {
544
547
  const unavailable = persisted.some((p) => p.unavailable)
545
548
  ? {
546
549
  media_url_unavailable: 'The media was stored but no public URL could be generated for it, so the values above are bare object keys, not links. ' +
547
- 'This is a connector misconfiguration: set MEDIA_PUBLIC_BASE_URL, or use inline: true to receive the bytes directly.',
550
+ 'This is a hosting misconfiguration — the host did not provide a blob store (mcp-host injects MCP_BLOB_BASE_URL and MCP_BLOB_SIGNING_KEY) — so ask the server operator, or use inline: true to receive the bytes directly.',
548
551
  }
549
552
  : {};
550
553
  const detailed = persisted.filter((p) => p.key !== undefined);
@@ -86,7 +86,7 @@ export function registerUploadUrlTools(server, client) {
86
86
  '(the signature in the URL is the auth, and it covers the content type). On a 2xx, reference the `r2_key` from ' +
87
87
  'THIS result — not the PUT response: images_r2_keys on generation tools (master_images_r2_keys on ' +
88
88
  'gemini_image_set), gemini_save_character to keep it with no expiry, or gemini_upload_file({ r2_key }) for a ' +
89
- '~48h files/… reference. Uploads themselves follow the media retention schedule (default 7 days).',
89
+ '~48h files/… reference. Uploads themselves follow the host\'s blob-store retention schedule.',
90
90
  });
91
91
  });
92
92
  }
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Single source of the server version. release-please bumps the literal below. */
2
- export const VERSION = '2.3.4'; // x-release-please-version
2
+ export const VERSION = '2.4.1'; // x-release-please-version
package/mint.yaml CHANGED
@@ -13,16 +13,28 @@ env:
13
13
  - name: GEMINI_IMAGE_MODEL
14
14
  required: false
15
15
  help: >-
16
- Override the default image model (default: gemini-3.1-flash-image)
16
+ Override the default image model (default: gemini-nano-banana-2.1)
17
17
  - name: GEMINI_OUTPUT_DIR
18
18
  required: false
19
19
  help: >-
20
- Default directory for generated images (default: current working
21
- directory)
20
+ Default directory for generated images (default: ~/Downloads/gemini-mcp,
21
+ not the cwd). When set, a per-call output_dir must be inside it.
22
22
  - name: GEMINI_INPUT_DIR
23
23
  required: false
24
24
  help: >-
25
25
  Directory to resolve bare input-image filenames against
26
+ - name: GEMINI_UPLOAD_DIR
27
+ required: false
28
+ help: >-
29
+ Directories (path.delimiter-separated) that local files uploaded to the
30
+ Gemini Files API must come from. Mostly a local-install knob: on a hosted
31
+ server, uploads come from gemini_get_upload_url instead.
32
+ - name: GEMINI_REFERENCE_DIR
33
+ required: false
34
+ help: >-
35
+ Directories (path.delimiter-separated) that local reference images must
36
+ come from (else GEMINI_UPLOAD_DIR). Unset on a hosted server, only
37
+ $MCP_DATA_DIR/uploads is allowed, so leave it unset there.
26
38
  - name: GEMINI_TIMEOUT_MS
27
39
  required: false
28
40
  help: >-
@@ -38,6 +50,10 @@ env:
38
50
  How often, in milliseconds, to emit a progress notification while a
39
51
  generation is in flight. Hosts reset their tools/call timeout on progress,
40
52
  so this lets a long 4K/Pro generation outlive the default budget.
53
+ - name: GEMINI_DEBUG
54
+ required: false
55
+ help: >-
56
+ When set, emit heartbeat diagnostics to stderr. Off by default.
41
57
  - name: GEMINI_RATE_CARD
42
58
  required: false
43
59
  help: >-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrischall/gemini-mcp",
3
- "version": "2.3.4",
3
+ "version": "2.4.1",
4
4
  "mcpName": "io.github.chrischall/gemini-mcp",
5
5
  "description": "Gemini image-generation MCP server for Claude — developed and maintained by AI (Claude Code)",
6
6
  "author": "Claude Code (AI) <https://www.anthropic.com/claude>",
@@ -38,7 +38,7 @@
38
38
  ],
39
39
  "scripts": {
40
40
  "build": "tsc && npm run bundle",
41
- "bundle": "esbuild src/index.ts --bundle --alias:zod/v4=./node_modules/zod/v4/index.cjs --platform=node --format=esm --external:dotenv --outfile=dist/bundle.js",
41
+ "bundle": "esbuild src/index.ts --bundle --alias:zod/v4=./node_modules/zod/v4/index.cjs --platform=node --format=esm --external:dotenv --banner:js=\"import { createRequire as __createRequire } from 'module'; import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const require = __createRequire(import.meta.url); const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename);\" --outfile=dist/bundle.js",
42
42
  "dev": "node dist/index.js",
43
43
  "test": "npm run typecheck && vitest run",
44
44
  "test:watch": "vitest",
@@ -46,9 +46,10 @@
46
46
  "typecheck": "tsc -p tsconfig.test.json"
47
47
  },
48
48
  "dependencies": {
49
- "@chrischall/mcp-utils": "^3.0.0",
49
+ "@chrischall/mcp-utils": "^3.0.3",
50
50
  "@modelcontextprotocol/server": "^2.3.0",
51
51
  "dotenv": "^18.0.2",
52
+ "undici": "^8.12.2",
52
53
  "zod": "^4.6.5"
53
54
  },
54
55
  "devDependencies": {
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/chrischall/gemini-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "2.3.4",
9
+ "version": "2.4.1",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@chrischall/gemini-mcp",
14
- "version": "2.3.4",
14
+ "version": "2.4.1",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },
@@ -25,14 +25,14 @@
25
25
  },
26
26
  {
27
27
  "name": "GEMINI_IMAGE_MODEL",
28
- "description": "Override the default image model (default: gemini-3.1-flash-image)",
28
+ "description": "Override the default image model (default: gemini-nano-banana-2.1)",
29
29
  "isRequired": false,
30
30
  "format": "string",
31
31
  "isSecret": false
32
32
  },
33
33
  {
34
34
  "name": "GEMINI_OUTPUT_DIR",
35
- "description": "Default directory for generated images (default: current working directory)",
35
+ "description": "Default directory for generated images (default: ~/Downloads/gemini-mcp). When set, a per-call output_dir must be inside it.",
36
36
  "isRequired": false,
37
37
  "format": "string",
38
38
  "isSecret": false
@@ -69,7 +69,7 @@ Note: Image generation requires a billing-enabled Google Cloud project.
69
69
  | Variable | Required | Description |
70
70
  |---|---|---|
71
71
  | `GEMINI_API_KEY` | Yes | Your Google Gemini API key |
72
- | `GEMINI_IMAGE_MODEL` | No | Override the default image model (default: `gemini-3.1-flash-image`) |
72
+ | `GEMINI_IMAGE_MODEL` | No | Override the default image model (default: `gemini-nano-banana-2.1`) |
73
73
  | `GEMINI_OUTPUT_DIR` | No | Default directory for saved images (default: ~/Downloads/gemini-mcp) |
74
74
  | `GEMINI_INPUT_DIR` | No | Directory to resolve bare input-image filenames against (e.g. point at Cowork's `uploads/` folder so `images: ["house.jpg"]` works) |
75
75
 
@@ -84,7 +84,8 @@ Note: Image generation requires a billing-enabled Google Cloud project.
84
84
 
85
85
  | Model | When to use | Reference-image caps (of 14 max) |
86
86
  |-------|-------------|----------------------------------|
87
- | `gemini-3.1-flash-image` (Nano Banana 2) | The versatile generalist workhorse for all tasks — balances speed with state-of-the-art 4K generation, world knowledge, and reliable text rendering; excels at multi-reference-image processing and consistency. Only model with video input + image_search grounding | 10 objects + 4 characters + 3 style refs |
87
+ | `gemini-nano-banana-2.1` (Nano Banana 2.1, **default**) | The versatile generalist workhorse for all tasks — an update to Nano Banana 2 with better visual quality, prompt adherence, character consistency and text rendering; 1K/2K/4K, $0.0336 per 1K image | up to 10 objects + 4 characters |
88
+ | `gemini-3.1-flash-image` (Nano Banana 2, **deprecated** 2026-10-06, no shutdown date yet) | The previous generalist workhorse — balances speed with state-of-the-art 4K generation, world knowledge, and reliable text rendering; excels at multi-reference-image processing and consistency. Only model with video input + image_search grounding | 10 objects + 4 characters + 3 style refs |
88
89
  | `gemini-3-pro-image` (Nano Banana Pro) | The premium choice for the most complex visual tasks — highest world knowledge, advanced localization, accurate brand consistency, precision creative control | 6 objects + 5 characters |
89
90
  | `gemini-3.1-flash-lite-image` (Nano Banana 2 Lite) | The fastest/cheapest for simple tasks — 1K output only, no Google Search grounding | 14 objects (no character consistency) |
90
91
 
@@ -95,7 +96,7 @@ Note: Image generation requires a billing-enabled Google Cloud project.
95
96
  | `gemini_image_edit(prompt, images_url?, images_file_uris?, images?, images_base64?, google_search?, seed?, filename?, model?, aspect_ratio?, image_size?, thinking_level?, output_dir?, inline?)` | Edit or compose input image(s) with a text instruction. Requires ≥1 input from any of the four reference forms |
96
97
  | `gemini_image_set(master_prompt, scenes? \| count?, reference_mode?, master_images_url?, master_images_file_uris?, master_images?, master_images_base64?, google_search?, seed?, basename?, model?, thinking_level?, ...)` | Master image (optionally seeded from a reference photo) plus N consistent images referencing it. `master_images_url` / `master_images_file_uris` are resolved **once** and passed to the master *and* every scene |
97
98
 
98
- ### Reference images — four ways in, one that costs context
99
+ ### Reference images — several ways in, one that costs context
99
100
 
100
101
  These apply to **every** tool that takes a reference image: the four image tools, plus
101
102
  `gemini_video_generate` (reference stills) and `gemini_music_generate`.
@@ -104,7 +105,9 @@ These apply to **every** tool that takes a reference image: the four image tools
104
105
  |---|---|---|
105
106
  | `images_url` (`master_images_url`) | the **server** downloads the https URL | none |
106
107
  | `images_file_uris` (`master_images_file_uris`) | a `files/<id>` reference from `gemini_upload_file` | none |
108
+ | `images_r2_keys` (`master_images_r2_keys`) | the server reads its **own store** — an `r2_key` from `gemini_get_upload_url` or a generation result (hosted deployments only) | none |
107
109
  | `images` | read off local disk (stdio builds only) | none |
110
+ | `characters` / `style` | saved library entries, attached by name (hosted deployments only) | none |
108
111
  | `images_base64` | **through the tool-call JSON** | **~14k tokens per JPEG** |
109
112
 
110
113
  **Reach for `images_base64` last.** It costs ~14k tokens per modest photo, and a truncated file
@@ -129,17 +132,32 @@ not an error.
129
132
  | `gemini_list_files(page_size?)` | List current uploads with MIME types and expiry |
130
133
  | `gemini_delete_file(file_uri, confirmToken?)` | Delete an upload before its ~48h expiry (confirmed first — see Notes) |
131
134
 
132
- On the **hosted hosted deployment** there is also `POST /upload`, behind the same OAuth token as
133
- `/mcp` — the zero-base64 path for an agent with a shell:
135
+ ### Hosted deployment only
136
+
137
+ A hosted deployment has no filesystem, so these tools stand in for it. There is no `POST /upload`
138
+ endpoint (it went with the retired Worker connector): an agent with a shell uploads through a
139
+ signed URL instead.
140
+
141
+ | Tool | Description |
142
+ |------|-------------|
143
+ | `gemini_get_upload_url(filename, content_type)` | Mint a ~10-minute signed **PUT** URL. `curl -X PUT -H "Content-Type: <type>" --data-binary @file "<upload_url>"` needs no auth header. The result's `r2_key` goes to `images_r2_keys`, `gemini_save_character`, or `gemini_upload_file({ r2_key })` |
144
+ | `gemini_sign_media(r2_key)` | Mint a fresh signed link for stored media (links live at most 24h) — an expired link is not a reason to re-generate |
145
+ | `gemini_view_media(r2_key)` | Return a stored image inline so you can look at what was made before refining it |
146
+ | `gemini_list_recent_media(limit?, since_day?)` | Recent stored media with prompt, model and interaction id — how a lost result or turn is found again |
147
+ | `gemini_save_character` / `gemini_list_characters` / `gemini_delete_character` | Per-account character library (reference image + description, no expiry); attach with `characters: ["name"]` |
148
+ | `gemini_save_style` / `gemini_list_styles` / `gemini_delete_style` | Per-account style presets (prompt fragment, optional reference image, no expiry); attach with `style: "name"` |
134
149
 
135
150
  ```bash
136
- curl -X POST https://<hosted deployment>/upload \
137
- -H "Authorization: Bearer $ACCESS_TOKEN" \
138
- -H "Content-Type: image/jpeg" \
139
- --data-binary @photo.jpg
140
- # → {"file_uri":"files/abc123", ...} then: images_file_uris: ["files/abc123"]
151
+ # 1. tool: gemini_get_upload_url { filename: "photo.jpg", content_type: "image/jpeg" }
152
+ # → { upload_url, r2_key, expires_at, curl_hint }
153
+ # 2. shell:
154
+ curl -sS -X PUT -H "Content-Type: image/jpeg" --data-binary @photo.jpg "$UPLOAD_URL"
155
+ # 3. tool: gemini_image_edit { prompt: "make it winter", images_r2_keys: ["<r2_key from step 1>"] }
141
156
  ```
142
157
 
158
+ Uploads are raster images only, capped at 15MB, and follow the host's retention schedule; save
159
+ anything you want to keep with `gemini_save_character`.
160
+
143
161
  ### Multi-turn (Interactions API)
144
162
  | Tool | Description |
145
163
  |------|-------------|
@@ -182,15 +200,16 @@ gemini_image_edit(prompt: "make the background blue", images_url: ["https://exam
182
200
  → the server fetches the URL itself; returns path to edited PNG
183
201
  ```
184
202
 
185
- **Reuse one photo across many generations (hosted hosted deployment, agent with a shell):**
203
+ **Reuse one photo across many generations (hosted deployment, agent with a shell):**
186
204
  ```
187
- $ curl -X POST https://<hosted deployment>/upload -H "Authorization: Bearer $TOKEN" \
188
- -H "Content-Type: image/jpeg" --data-binary @photo.jpg
189
- → {"file_uri": "files/abc123"}
190
-
191
- gemini_image_edit(prompt: "make it winter", images_file_uris: ["files/abc123"])
192
- gemini_image_edit(prompt: "make it sunrise", images_file_uris: ["files/abc123"])
193
- → two edits, one upload, zero image bytes in context. Valid ~48h.
205
+ gemini_get_upload_url(filename: "photo.jpg", content_type: "image/jpeg")
206
+ → { upload_url, r2_key: "up/…/photo.jpg" }
207
+ $ curl -sS -X PUT -H "Content-Type: image/jpeg" --data-binary @photo.jpg "$UPLOAD_URL"
208
+
209
+ gemini_image_edit(prompt: "make it winter", images_r2_keys: ["up/…/photo.jpg"])
210
+ gemini_image_edit(prompt: "make it sunrise", images_r2_keys: ["up/…/photo.jpg"])
211
+ → two edits, one upload, zero image bytes in context. To keep the photo past the
212
+ upload retention window, gemini_save_character it and pass characters: ["name"].
194
213
  ```
195
214
 
196
215
  **Generate a consistent set (master + scenes):**