@chrischall/gemini-mcp 2.3.3 → 2.3.4
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 +2 -2
- package/README.md +1 -1
- package/dist/bundle.js +721 -182
- package/dist/client.js +146 -39
- package/dist/dotenv.js +3 -4
- package/dist/fetch-image.js +10 -4
- package/dist/images.js +15 -5
- package/dist/index.js +6 -4
- package/dist/inputs.js +1 -1
- package/dist/library.js +9 -4
- package/dist/session.js +21 -15
- package/dist/sidecar.js +31 -7
- package/dist/tenant.js +4 -9
- package/dist/tools/_confirm.js +9 -0
- package/dist/tools/files.js +3 -1
- package/dist/tools/generate.js +2 -2
- package/dist/tools/interact.js +6 -6
- package/dist/tools/jobs.js +2 -2
- package/dist/tools/library.js +4 -2
- package/dist/tools/models.js +2 -2
- package/dist/tools/music.js +2 -2
- package/dist/tools/set.js +1 -1
- package/dist/tools/shared.js +1 -1
- package/dist/tools/usage.js +1 -1
- package/dist/tools/video.js +5 -7
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/server.json +38 -3
- package/skills/gemini-mcp/SKILL.md +2 -2
package/dist/tools/files.js
CHANGED
|
@@ -89,7 +89,7 @@ export function registerFileTools(server, client) {
|
|
|
89
89
|
? ' ' + confirmNote('A local `path` is confirmed first')
|
|
90
90
|
: ' To upload a local file without base64: mint a signed PUT URL with gemini_get_upload_url, PUT the bytes to it, ' +
|
|
91
91
|
'then pass the returned r2_key here.'),
|
|
92
|
-
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
92
|
+
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
|
|
93
93
|
inputSchema: z.object({
|
|
94
94
|
url: z
|
|
95
95
|
.string()
|
|
@@ -168,6 +168,7 @@ export function registerFileTools(server, client) {
|
|
|
168
168
|
body: { inputs },
|
|
169
169
|
target: resolved,
|
|
170
170
|
payload: { inputs, display_name: args.display_name },
|
|
171
|
+
args,
|
|
171
172
|
confirmToken: args.confirmToken,
|
|
172
173
|
});
|
|
173
174
|
if (gate)
|
|
@@ -380,6 +381,7 @@ export function registerFileTools(server, client) {
|
|
|
380
381
|
path: `/v1beta/${args.file_uri}`,
|
|
381
382
|
target: args.file_uri,
|
|
382
383
|
payload: { file_uri: args.file_uri },
|
|
384
|
+
args,
|
|
383
385
|
confirmToken: args.confirmToken,
|
|
384
386
|
});
|
|
385
387
|
if (gate)
|
package/dist/tools/generate.js
CHANGED
|
@@ -19,7 +19,7 @@ export function registerGenerateTools(server, client) {
|
|
|
19
19
|
description: 'Generate image(s) from a text prompt with a Gemini image model (Nano Banana / Nano Banana Pro). ' +
|
|
20
20
|
'If the result will likely be refined iteratively, prefer gemini_interact (multi-turn) as the entry point. ' +
|
|
21
21
|
confirmNote('Local file inputs are confirmed first'),
|
|
22
|
-
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
22
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
23
23
|
inputSchema: z.object({
|
|
24
24
|
prompt: z.string().min(1).describe('Text prompt describing the image'),
|
|
25
25
|
count: z.number().int().positive().max(8).optional().describe('Number of independent images (default 1)'),
|
|
@@ -166,7 +166,7 @@ export function registerGenerateTools(server, client) {
|
|
|
166
166
|
'and avoids re-processing the full image each round; use gemini_image_edit for one-off edits or composing multiple distinct inputs. ' +
|
|
167
167
|
'Gemini over-preserves the input; there is no edit-strength control — for large structural changes, reroll with a different `seed` or more forceful wording. ' +
|
|
168
168
|
confirmNote('Local file inputs are confirmed first'),
|
|
169
|
-
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
169
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
170
170
|
inputSchema: z.object({
|
|
171
171
|
prompt: z.string().min(1).describe('Instruction describing the edit or composition'),
|
|
172
172
|
images: z.array(z.string().min(1)).optional().describe('Paths to input image file(s) (1 = edit, 2+ = compose). Local paths must be inside GEMINI_REFERENCE_DIR (else GEMINI_UPLOAD_DIR) when set.'),
|
package/dist/tools/interact.js
CHANGED
|
@@ -11,10 +11,10 @@ import { fingerprintRequest } from '../jobs.js';
|
|
|
11
11
|
import { attachCost } from '../pricing.js';
|
|
12
12
|
import { confirmLocalInputs, confirmNote, confirmTokenParam } from './_confirm.js';
|
|
13
13
|
// The last-interaction id and the written-outputs set used to live here, at
|
|
14
|
-
// module scope
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
14
|
+
// module scope, which leaked across users on the retired multi-user Worker:
|
|
15
|
+
// `continue_last` from user B resumed user A's interaction. Both now live on
|
|
16
|
+
// `client.session` (src/session.ts) — one per client, and one client per
|
|
17
|
+
// single-user process today (see the invariant there).
|
|
18
18
|
/** Shared warning for the reference-image params. */
|
|
19
19
|
const NEW_REFERENCES_ONLY = 'NEW reference images only (e.g. a style or target photo). When chaining with previous_interaction_id, ' +
|
|
20
20
|
"do NOT re-attach the prior turn's output — the interaction already contains it, and re-sending it " +
|
|
@@ -91,7 +91,7 @@ export function registerInteractTools(server, client) {
|
|
|
91
91
|
recoveryDescription +
|
|
92
92
|
'Output is JPEG. ' +
|
|
93
93
|
confirmNote('Local file inputs are confirmed first'),
|
|
94
|
-
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
94
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
95
95
|
inputSchema: z.object({
|
|
96
96
|
input: z.string().min(1).describe('Text prompt or editing instruction'),
|
|
97
97
|
previous_interaction_id: z
|
|
@@ -133,7 +133,7 @@ export function registerInteractTools(server, client) {
|
|
|
133
133
|
output_dir: z
|
|
134
134
|
.string()
|
|
135
135
|
.optional()
|
|
136
|
-
.describe('Directory to write images to (default: $GEMINI_OUTPUT_DIR or
|
|
136
|
+
.describe('Directory to write images to (default: $GEMINI_OUTPUT_DIR or ~/Downloads/gemini-mcp)'),
|
|
137
137
|
inline: z
|
|
138
138
|
.boolean()
|
|
139
139
|
.optional()
|
package/dist/tools/jobs.js
CHANGED
|
@@ -23,10 +23,10 @@ export function registerJobTools(server, client) {
|
|
|
23
23
|
'A killed video/music job started with `background: true` is recovered from its upstream interaction when it finished there.',
|
|
24
24
|
// NOT read-only any more: recovering a killed job writes the media it
|
|
25
25
|
// pulls back, exactly as the generation tool would have.
|
|
26
|
-
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
26
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
27
27
|
inputSchema: z.object({
|
|
28
28
|
job_id: z.string().min(1).describe('The job_id returned by a generation tool called with async: true'),
|
|
29
|
-
output_dir: z.string().optional().describe('Where to write media recovered from a killed job (default: $GEMINI_OUTPUT_DIR or
|
|
29
|
+
output_dir: z.string().optional().describe('Where to write media recovered from a killed job (default: $GEMINI_OUTPUT_DIR or ~/Downloads/gemini-mcp)'),
|
|
30
30
|
}),
|
|
31
31
|
}, async (args) => {
|
|
32
32
|
try {
|
package/dist/tools/library.js
CHANGED
|
@@ -62,7 +62,7 @@ export function registerLibraryTools(server, client) {
|
|
|
62
62
|
'description. Saved characters have NO expiry — unlike uploads (~retention window) and Files API references (~48h) ' +
|
|
63
63
|
'— so a name saved once keeps working in every later session. Re-saving a name replaces it. ' +
|
|
64
64
|
USE_CHARACTERS_HINT,
|
|
65
|
-
annotations: { readOnlyHint: false, openWorldHint:
|
|
65
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
66
66
|
inputSchema: z.object({
|
|
67
67
|
name: NAME_FIELD,
|
|
68
68
|
description: z
|
|
@@ -124,6 +124,7 @@ export function registerLibraryTools(server, client) {
|
|
|
124
124
|
path: `characters/${args.name}`,
|
|
125
125
|
target: args.name,
|
|
126
126
|
payload: { name: args.name },
|
|
127
|
+
args,
|
|
127
128
|
confirmToken: args.confirmToken,
|
|
128
129
|
});
|
|
129
130
|
if (gate)
|
|
@@ -138,7 +139,7 @@ export function registerLibraryTools(server, client) {
|
|
|
138
139
|
description: 'Save a named style preset to this account\'s persistent reference library: a reusable prompt fragment (and ' +
|
|
139
140
|
'optionally a reference image) that a single word then applies to any generation. No expiry; re-saving a name replaces it. ' +
|
|
140
141
|
USE_STYLE_HINT,
|
|
141
|
-
annotations: { readOnlyHint: false, openWorldHint:
|
|
142
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
142
143
|
inputSchema: z.object({
|
|
143
144
|
name: NAME_FIELD,
|
|
144
145
|
prompt_fragment: z
|
|
@@ -193,6 +194,7 @@ export function registerLibraryTools(server, client) {
|
|
|
193
194
|
path: `styles/${args.name}`,
|
|
194
195
|
target: args.name,
|
|
195
196
|
payload: { name: args.name },
|
|
197
|
+
args,
|
|
196
198
|
confirmToken: args.confirmToken,
|
|
197
199
|
});
|
|
198
200
|
if (gate)
|
package/dist/tools/models.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { minifiedResult } from '@chrischall/mcp-utils';
|
|
1
|
+
import { minifiedResult, toolAnnotations } from '@chrischall/mcp-utils';
|
|
2
2
|
export function registerModelTools(server, client) {
|
|
3
3
|
server.registerTool('gemini_list_models', {
|
|
4
4
|
description: 'List the Gemini image-generation models available to your API key (Nano Banana / Nano Banana Pro family), and the current default model.',
|
|
5
|
-
annotations: {
|
|
5
|
+
annotations: toolAnnotations({ readOnly: true, openWorld: true }),
|
|
6
6
|
}, async () => {
|
|
7
7
|
const models = await client.listModels();
|
|
8
8
|
return minifiedResult({ default: client.defaultModel(), models });
|
package/dist/tools/music.js
CHANGED
|
@@ -30,7 +30,7 @@ export function registerMusicTools(server, client) {
|
|
|
30
30
|
'cannot be refined by a follow-up call, so put the whole brief in the prompt. Runs long — give it a `max_wait_ms` ' +
|
|
31
31
|
'budget (or `async: true` + gemini_get_result on a local install), or raise `timeout_ms`. Needs a funded account. ' +
|
|
32
32
|
confirmNote('Local file inputs are confirmed first'),
|
|
33
|
-
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
33
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
34
34
|
inputSchema: z.object({
|
|
35
35
|
prompt: z.string().min(1).describe('Description of the music: mood, genre, instruments, tempo, structure, or lyrics'),
|
|
36
36
|
model: z.enum(MUSIC_MODELS).optional().describe(`Lyria model (default: ${DEFAULT_MUSIC_MODEL} — 30s, $0.04). lyria-3.5 and lyria-3-pro-preview run minutes-long at $0.08.`),
|
|
@@ -40,7 +40,7 @@ export function registerMusicTools(server, client) {
|
|
|
40
40
|
images_base64: imagesBase64Schema(),
|
|
41
41
|
from_clipboard: z.boolean().optional().describe('Use the image currently on the macOS clipboard as a reference'),
|
|
42
42
|
filename: z.string().optional().describe('Base filename for the output audio (extension stripped; default: slugified prompt)'),
|
|
43
|
-
output_dir: z.string().optional().describe('Directory to write audio to (default: $GEMINI_OUTPUT_DIR or
|
|
43
|
+
output_dir: z.string().optional().describe('Directory to write audio to (default: $GEMINI_OUTPUT_DIR or ~/Downloads/gemini-mcp)'),
|
|
44
44
|
inline: z.boolean().optional().describe('Return base64 audio inline instead of writing to disk. A 30s track is ~1.9MB of base64 — the default hands back a path instead'),
|
|
45
45
|
background: z.boolean().optional().describe('Run the generation on Google\'s side and poll it, so a killed job can be recovered by gemini_get_result. Off by default — see gemini_video_generate'),
|
|
46
46
|
timeout_ms: timeoutMsSchema,
|
package/dist/tools/set.js
CHANGED
|
@@ -13,7 +13,7 @@ export function registerSetTools(server, client) {
|
|
|
13
13
|
description: 'Generate a consistent SET of images: a master image from master_prompt, then one image per scene that references the master so the subject/style stays consistent. Provide `scenes` (explicit per-image prompts) OR `count` (variations of the master). ' +
|
|
14
14
|
'Scene generations run in parallel (reference_mode "master", the default). On the hosted connector: saved `characters` and a saved `style` can seed the whole set by name, multi-image results include a `bundle_url` zip of every image (one curl instead of N), and `max_wait_ms` returns a pollable job handle if the batch runs long. ' +
|
|
15
15
|
confirmNote('Local file inputs are confirmed first'),
|
|
16
|
-
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
16
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
17
17
|
inputSchema: z.object({
|
|
18
18
|
master_prompt: z.string().min(1).describe('Prompt for the master/reference image'),
|
|
19
19
|
scenes: z.array(z.string().min(1)).min(1).max(8).optional().describe('Per-image prompts (1-8); each references the master'),
|
package/dist/tools/shared.js
CHANGED
|
@@ -300,7 +300,7 @@ export const sharedImageSchema = {
|
|
|
300
300
|
.describe('Exact output aspect ratio. For a plain landscape/portrait/square request, `orientation` is the shorthand; this wins if both are given.'),
|
|
301
301
|
orientation: orientationSchema,
|
|
302
302
|
image_size: z.enum(IMAGE_SIZES).optional().describe('Output resolution (512 = 0.5K, Flash-only)'),
|
|
303
|
-
output_dir: z.string().optional().describe('Directory to write images to (default: $GEMINI_OUTPUT_DIR or
|
|
303
|
+
output_dir: z.string().optional().describe('Directory to write images to (default: $GEMINI_OUTPUT_DIR or ~/Downloads/gemini-mcp)'),
|
|
304
304
|
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
305
|
seed: z.number().int().optional().describe('Seed for reproducible generation; random if omitted'),
|
|
306
306
|
thinking_level: z.enum(['minimal', 'high']).optional().describe('Reasoning depth (Gemini 3 models); higher can help complex/structural edits'),
|
package/dist/tools/usage.js
CHANGED
|
@@ -33,7 +33,7 @@ export function registerUsageTools(server, client) {
|
|
|
33
33
|
description: 'Token usage for this session so far — what every generation has cost in tokens, added up. ' +
|
|
34
34
|
'Call it before and after a workflow and subtract to get that workflow\'s cost; call it after a ' +
|
|
35
35
|
'single generation for that call\'s. Reports tokens AND an estimated USD cost, priced per call against each call\'s own model and stamped with the date its rates were read (override with GEMINI_RATE_CARD). Note there is no account-balance endpoint to query — Google Cloud is post-paid and its billing data lags by hours — so this is the accurate way to attribute spend to a call.',
|
|
36
|
-
annotations: toolAnnotations({ readOnly: true }),
|
|
36
|
+
annotations: toolAnnotations({ readOnly: true, openWorld: false }),
|
|
37
37
|
inputSchema: z.object({
|
|
38
38
|
reset: z
|
|
39
39
|
.boolean()
|
package/dist/tools/video.js
CHANGED
|
@@ -23,11 +23,9 @@ const VIDEO_RESOLUTIONS = ['360p', '720p', '1080p', '4k'];
|
|
|
23
23
|
// escape hatch for a video model that rejects delivery outright.
|
|
24
24
|
const VIDEO_DELIVERY = ['inline', 'uri'];
|
|
25
25
|
// The most-recent video interaction id (for continue_last) lives on
|
|
26
|
-
// `client.session`, NOT at module scope —
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
// not registered on the Worker today, but the hazard is identical, so it is
|
|
30
|
-
// scoped the same way. It stays separate from the image interact memory: a
|
|
26
|
+
// `client.session`, NOT at module scope — on any host serving several users
|
|
27
|
+
// from one process, a module-level id would let one user's continue_last
|
|
28
|
+
// resume another user's interaction (see the invariant in src/session.ts). It stays separate from the image interact memory: a
|
|
31
29
|
// "last video" and a "last image" are distinct chains. See src/session.ts.
|
|
32
30
|
export function registerVideoTools(server, client) {
|
|
33
31
|
server.registerTool('gemini_video_generate', {
|
|
@@ -38,7 +36,7 @@ export function registerVideoTools(server, client) {
|
|
|
38
36
|
'Output is written to disk as MP4 (video has no inline MCP block). Video runs long — give it a `max_wait_ms` budget ' +
|
|
39
37
|
'(or `async: true` + gemini_get_result on a local install), or raise `timeout_ms`. Needs a funded account. ' +
|
|
40
38
|
confirmNote('Local file inputs are confirmed first'),
|
|
41
|
-
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
39
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
42
40
|
inputSchema: z.object({
|
|
43
41
|
prompt: z.string().min(1).describe('Description of the video to generate (or the edit instruction when task=edit)'),
|
|
44
42
|
aspect_ratio: z.enum(VIDEO_ASPECT_RATIOS).optional().describe('Exact output aspect ratio (omni: 16:9 or 9:16). `orientation` is the plain-language shorthand; this wins if both are given.'),
|
|
@@ -51,7 +49,7 @@ export function registerVideoTools(server, client) {
|
|
|
51
49
|
images_base64: imagesBase64Schema(),
|
|
52
50
|
from_clipboard: z.boolean().optional().describe('Use the image currently on the macOS clipboard as a reference'),
|
|
53
51
|
filename: z.string().optional().describe('Base filename for the output video (extension stripped; default: slugified prompt)'),
|
|
54
|
-
output_dir: z.string().optional().describe('Directory to write the video to (default: $GEMINI_OUTPUT_DIR or
|
|
52
|
+
output_dir: z.string().optional().describe('Directory to write the video to (default: $GEMINI_OUTPUT_DIR or ~/Downloads/gemini-mcp)'),
|
|
55
53
|
model: z
|
|
56
54
|
.string()
|
|
57
55
|
.regex(/^[\w.-]+$/, 'must be a bare model id (letters, digits, ".", "_", "-")')
|
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.
|
|
2
|
+
export const VERSION = '2.3.4'; // x-release-please-version
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chrischall/gemini-mcp",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.4",
|
|
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>",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"typecheck": "tsc -p tsconfig.test.json"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@chrischall/mcp-utils": "^
|
|
49
|
+
"@chrischall/mcp-utils": "^3.0.0",
|
|
50
50
|
"@modelcontextprotocol/server": "^2.3.0",
|
|
51
51
|
"dotenv": "^18.0.2",
|
|
52
52
|
"zod": "^4.6.5"
|
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.
|
|
9
|
+
"version": "2.3.4",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "@chrischall/gemini-mcp",
|
|
14
|
-
"version": "2.3.
|
|
14
|
+
"version": "2.3.4",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
{
|
|
20
20
|
"name": "GEMINI_API_KEY",
|
|
21
21
|
"description": "Your Google Gemini API key (aistudio.google.com/apikey)",
|
|
22
|
-
"isRequired":
|
|
22
|
+
"isRequired": false,
|
|
23
23
|
"format": "string",
|
|
24
24
|
"isSecret": true
|
|
25
25
|
},
|
|
@@ -57,6 +57,41 @@
|
|
|
57
57
|
"isRequired": false,
|
|
58
58
|
"format": "string",
|
|
59
59
|
"isSecret": false
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"name": "GEMINI_TIMEOUT_MS",
|
|
63
|
+
"description": "Upstream request timeout in ms (default: 60000, or 120000 for image_size: \"4K\"); each generation tool also takes a per-call timeout_ms",
|
|
64
|
+
"isRequired": false,
|
|
65
|
+
"format": "string",
|
|
66
|
+
"isSecret": false
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"name": "GEMINI_HEARTBEAT_MS",
|
|
70
|
+
"description": "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",
|
|
71
|
+
"isRequired": false,
|
|
72
|
+
"format": "string",
|
|
73
|
+
"isSecret": false
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"name": "GEMINI_CHAIN_RETRY_MS",
|
|
77
|
+
"description": "How long to wait out interactions-store lag when a chained call 404s (default: 120000; 0 disables retrying)",
|
|
78
|
+
"isRequired": false,
|
|
79
|
+
"format": "string",
|
|
80
|
+
"isSecret": false
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"name": "GEMINI_DEBUG",
|
|
84
|
+
"description": "When set, emit heartbeat diagnostics to stderr. Off by default.",
|
|
85
|
+
"isRequired": false,
|
|
86
|
+
"format": "string",
|
|
87
|
+
"isSecret": false
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"name": "GEMINI_RATE_CARD",
|
|
91
|
+
"description": "Override/extend the shipped USD-per-1M-token rates used by gemini_token_usage's cost estimate, e.g. {\"gemini-3-pro-image\":{\"input\":2,\"text_output\":12,\"image_output\":120}}. Malformed JSON is ignored (a bad card must not fail generations).",
|
|
92
|
+
"isRequired": false,
|
|
93
|
+
"format": "string",
|
|
94
|
+
"isSecret": false
|
|
60
95
|
}
|
|
61
96
|
]
|
|
62
97
|
}
|
|
@@ -70,7 +70,7 @@ Note: Image generation requires a billing-enabled Google Cloud project.
|
|
|
70
70
|
|---|---|---|
|
|
71
71
|
| `GEMINI_API_KEY` | Yes | Your Google Gemini API key |
|
|
72
72
|
| `GEMINI_IMAGE_MODEL` | No | Override the default image model (default: `gemini-3.1-flash-image`) |
|
|
73
|
-
| `GEMINI_OUTPUT_DIR` | No | Default directory for saved images (default:
|
|
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
|
|
|
76
76
|
## Tools
|
|
@@ -318,7 +318,7 @@ Condensed from Google's official Nano Banana prompting guide. Core rule:
|
|
|
318
318
|
- **`search_types`** (`gemini_interact` only): `["web_search", "image_search"]` picks the grounding search types (setting it implies `google_search`). **`image_search`** (gemini-3.1-flash-image only) pulls web images via Google Image Search as *visual* references — useful for real-world subjects (a specific butterfly species, a landmark, a product). ⚠️ Two catches: Google ToS require **displaying the returned `grounding.search_suggestions` HTML chips** to the user, and image_search won't depict real people from web images.
|
|
319
319
|
- **`video_url`** (a public YouTube URL, on `gemini_image_generate` / `gemini_interact`) generates an image from a video reference — **requires a Flash model** (e.g. `model: "gemini-3.1-flash-image"`). For a **local video file**, use **`video_path`** instead: the file is uploaded to the Gemini Files API (streamed from disk, 2 GB max), waited to `ACTIVE`, and referenced by its `files/…` uri. The result metadata echoes `video_file` (`{uri, name, expires}`, ~48h retention) — reuse that uri as `video_url` in later calls to skip re-uploading.
|
|
320
320
|
- **`gemini_interact`** is the multi-turn path: it returns an `interaction_id`; thread it back via `previous_interaction_id` for conversational refinement. Output is **JPEG only**. (The Interactions API is GA as of 2026-07; it uses a different request shape than the `generate`/`edit`/`set` tools.)
|
|
321
|
-
- `output_dir` per-call overrides `$GEMINI_OUTPUT_DIR` overrides
|
|
321
|
+
- `output_dir` per-call overrides `$GEMINI_OUTPUT_DIR` overrides `~/Downloads/gemini-mcp`. `inline: true` returns bytes (with a metadata text block) instead of writing.
|
|
322
322
|
- `count` and `scenes` are mutually exclusive in `gemini_image_set`; `reference_mode: "chain"` references the previous image instead of the master.
|
|
323
323
|
- Aspect ratios: `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `2:3`, `3:2`, … · Image sizes: `512` (0.5K, Flash only), `1K`, `2K`, `4K`. `4K` is the max native output — true 18×24 in @ 300 DPI (5400×7200) needs an external upscale step.
|
|
324
324
|
- All generated images carry a **SynthID** watermark (Google).
|