@chrischall/gemini-mcp 1.14.2 → 2.1.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/dist/bundle.js +54545 -33843
- package/dist/tools/files.js +12 -12
- package/dist/tools/generate.js +4 -4
- package/dist/tools/interact.js +2 -2
- package/dist/tools/jobs.js +2 -2
- package/dist/tools/library.js +8 -8
- package/dist/tools/music.js +2 -2
- package/dist/tools/set.js +2 -2
- package/dist/tools/shared.js +3 -3
- package/dist/tools/uploads.js +2 -2
- package/dist/tools/usage.js +2 -2
- package/dist/tools/video.js +2 -2
- package/dist/version.js +1 -1
- package/package.json +6 -5
- package/server.json +2 -2
package/dist/tools/files.js
CHANGED
|
@@ -89,7 +89,7 @@ export function registerFileTools(server, client) {
|
|
|
89
89
|
: ' To upload a local file without base64: mint a signed PUT URL with gemini_get_upload_url, PUT the bytes to it, ' +
|
|
90
90
|
'then pass the returned r2_key here.'),
|
|
91
91
|
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
92
|
-
inputSchema: {
|
|
92
|
+
inputSchema: z.object({
|
|
93
93
|
url: z
|
|
94
94
|
.string()
|
|
95
95
|
.url()
|
|
@@ -126,7 +126,7 @@ export function registerFileTools(server, client) {
|
|
|
126
126
|
.describe('Override the detected MIME type (sniffed from the bytes / taken from the server response otherwise)'),
|
|
127
127
|
display_name: z.string().min(1).optional().describe('Human-readable name recorded against the upload'),
|
|
128
128
|
confirm: schemaConfirm,
|
|
129
|
-
},
|
|
129
|
+
}),
|
|
130
130
|
}, async (args, extra) => {
|
|
131
131
|
const sources = [args.url && 'url', args.data_base64 && 'data_base64', args.r2_key && 'r2_key', args.path && 'path'].filter(Boolean);
|
|
132
132
|
if (sources.length !== 1) {
|
|
@@ -184,9 +184,9 @@ export function registerFileTools(server, client) {
|
|
|
184
184
|
description: 'List files, images and photos currently uploaded to the Gemini Files API under this API key, with their reusable `file_uri` (`files/<id>`) references, ' +
|
|
185
185
|
'MIME types and expiry times. Retention is ~48h, so an entry that has vanished has expired rather than failed.',
|
|
186
186
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
187
|
-
inputSchema: {
|
|
187
|
+
inputSchema: z.object({
|
|
188
188
|
page_size: z.number().int().positive().max(100).optional().describe('Maximum files to return (1-100, default 100)'),
|
|
189
|
-
},
|
|
189
|
+
}),
|
|
190
190
|
}, async (args) => {
|
|
191
191
|
const files = await client.listFiles(args.page_size ?? 100);
|
|
192
192
|
return minifiedResult({
|
|
@@ -216,9 +216,9 @@ export function registerFileTools(server, client) {
|
|
|
216
216
|
'end — re-sign it instead of re-generating (and re-paying for) the image. Keywords: expired link, refresh url, ' +
|
|
217
217
|
're-sign, media url, download again.',
|
|
218
218
|
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
219
|
-
inputSchema: {
|
|
219
|
+
inputSchema: z.object({
|
|
220
220
|
r2_key: z.string().min(1).describe('The `media[].r2_key` from an earlier generation result'),
|
|
221
|
-
},
|
|
221
|
+
}),
|
|
222
222
|
}, async (args) => {
|
|
223
223
|
const sink = client.mediaSink;
|
|
224
224
|
if (!sink?.resign) {
|
|
@@ -260,9 +260,9 @@ export function registerFileTools(server, client) {
|
|
|
260
260
|
'Costs image tokens on the turns you use it, which is why it is a separate call and not part of every result. ' +
|
|
261
261
|
'Keywords: see the image, view, look at, verify output, check the result, preview.',
|
|
262
262
|
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
263
|
-
inputSchema: {
|
|
263
|
+
inputSchema: z.object({
|
|
264
264
|
r2_key: z.string().min(1).describe('The `media[].r2_key` from a generation result or gemini_list_recent_media'),
|
|
265
|
-
},
|
|
265
|
+
}),
|
|
266
266
|
}, async (args) => {
|
|
267
267
|
const key = args.r2_key.trim();
|
|
268
268
|
const { bytes, mimeType } = await client.readStoredMedia(key);
|
|
@@ -287,14 +287,14 @@ export function registerFileTools(server, client) {
|
|
|
287
287
|
'to find what already exists BEFORE re-running (and re-paying for) it. Keywords: lost result, orphaned job, ' +
|
|
288
288
|
'missing image, what did I generate, recover, recent media.',
|
|
289
289
|
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
290
|
-
inputSchema: {
|
|
290
|
+
inputSchema: z.object({
|
|
291
291
|
limit: z.number().int().positive().max(100).optional().describe('How many to return (default 20, newest first)'),
|
|
292
292
|
since_day: z
|
|
293
293
|
.string()
|
|
294
294
|
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
295
295
|
.optional()
|
|
296
296
|
.describe('Only media stored on or after this UTC day, e.g. "2026-08-18"'),
|
|
297
|
-
},
|
|
297
|
+
}),
|
|
298
298
|
}, async (args) => {
|
|
299
299
|
const sink = client.mediaSink;
|
|
300
300
|
if (!sink?.listRecent) {
|
|
@@ -338,10 +338,10 @@ export function registerFileTools(server, client) {
|
|
|
338
338
|
description: 'Delete an uploaded file, image or photo (by file_uri) from the Gemini Files API before its ~48h expiry. Any tool call still referencing it ' +
|
|
339
339
|
'will then fail with a generic 404, so delete only references you are finished with.',
|
|
340
340
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
341
|
-
inputSchema: {
|
|
341
|
+
inputSchema: z.object({
|
|
342
342
|
file_uri: z.string().min(1).describe('The `files/<id>` reference (or full uri) to delete'),
|
|
343
343
|
confirm: schemaConfirm,
|
|
344
|
-
},
|
|
344
|
+
}),
|
|
345
345
|
}, async (args) => {
|
|
346
346
|
// Deleting is a remote mutation with no undo — confirm-gated per the
|
|
347
347
|
// fleet convention (unlike writing a generated image, which is local).
|
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
|
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
22
|
-
inputSchema: {
|
|
22
|
+
inputSchema: z.object({
|
|
23
23
|
prompt: z.string().min(1).describe('Text prompt describing the image'),
|
|
24
24
|
count: z.number().int().positive().max(8).optional().describe('Number of independent images (default 1)'),
|
|
25
25
|
filename: z.string().optional().describe('Base filename for the output image (extension stripped; default: slugified prompt)'),
|
|
@@ -34,7 +34,7 @@ export function registerGenerateTools(server, client) {
|
|
|
34
34
|
video_path: videoPathSchema,
|
|
35
35
|
confirm: schemaConfirm,
|
|
36
36
|
...sharedImageSchema,
|
|
37
|
-
},
|
|
37
|
+
}),
|
|
38
38
|
}, async (args, extra) => {
|
|
39
39
|
// Local-path / clipboard / disk-upload inputs simply do not exist on the
|
|
40
40
|
// hosted connector. Fail here — before the confirm gate previews files and
|
|
@@ -161,7 +161,7 @@ export function registerGenerateTools(server, client) {
|
|
|
161
161
|
'and avoids re-processing the full image each round; use gemini_image_edit for one-off edits or composing multiple distinct inputs. ' +
|
|
162
162
|
'Gemini over-preserves the input; there is no edit-strength control — for large structural changes, reroll with a different `seed` or more forceful wording.',
|
|
163
163
|
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
164
|
-
inputSchema: {
|
|
164
|
+
inputSchema: z.object({
|
|
165
165
|
prompt: z.string().min(1).describe('Instruction describing the edit or composition'),
|
|
166
166
|
images: z.array(z.string().min(1)).optional().describe('Paths to input image file(s) (1 = edit, 2+ = compose)'),
|
|
167
167
|
images_url: imagesUrlSchema('Input images'),
|
|
@@ -173,7 +173,7 @@ export function registerGenerateTools(server, client) {
|
|
|
173
173
|
filename: z.string().optional().describe('Base filename for the output image (extension stripped; default: slugified prompt)'),
|
|
174
174
|
confirm: schemaConfirm,
|
|
175
175
|
...sharedImageSchema,
|
|
176
|
-
},
|
|
176
|
+
}),
|
|
177
177
|
}, async (args, extra) => {
|
|
178
178
|
assertLocalInputsAvailable(client.mediaSink, args);
|
|
179
179
|
// Saved characters count as input images for the required-input check —
|
package/dist/tools/interact.js
CHANGED
|
@@ -91,7 +91,7 @@ export function registerInteractTools(server, client) {
|
|
|
91
91
|
recoveryDescription +
|
|
92
92
|
'Output is JPEG.',
|
|
93
93
|
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
94
|
-
inputSchema: {
|
|
94
|
+
inputSchema: z.object({
|
|
95
95
|
input: z.string().min(1).describe('Text prompt or editing instruction'),
|
|
96
96
|
previous_interaction_id: z
|
|
97
97
|
.string()
|
|
@@ -161,7 +161,7 @@ export function registerInteractTools(server, client) {
|
|
|
161
161
|
.optional()
|
|
162
162
|
.describe('Use the image currently on the macOS system clipboard as an input (downscaled to JPEG)'),
|
|
163
163
|
confirm: schemaConfirm,
|
|
164
|
-
},
|
|
164
|
+
}),
|
|
165
165
|
}, async (args, extra) => {
|
|
166
166
|
assertLocalInputsAvailable(client.mediaSink, args);
|
|
167
167
|
// Every sidecar-backed recovery below reads the output dir off disk. A
|
package/dist/tools/jobs.js
CHANGED
|
@@ -24,10 +24,10 @@ export function registerJobTools(server, client) {
|
|
|
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
26
|
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
27
|
-
inputSchema: {
|
|
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
29
|
output_dir: z.string().optional().describe('Where to write media recovered from a killed job (default: $GEMINI_OUTPUT_DIR or cwd)'),
|
|
30
|
-
},
|
|
30
|
+
}),
|
|
31
31
|
}, async (args) => {
|
|
32
32
|
try {
|
|
33
33
|
return await client.session.jobs.getResult(args.job_id);
|
package/dist/tools/library.js
CHANGED
|
@@ -63,7 +63,7 @@ export function registerLibraryTools(server, client) {
|
|
|
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
65
|
annotations: { readOnlyHint: false, openWorldHint: false },
|
|
66
|
-
inputSchema: {
|
|
66
|
+
inputSchema: z.object({
|
|
67
67
|
name: NAME_FIELD,
|
|
68
68
|
description: z
|
|
69
69
|
.string()
|
|
@@ -75,7 +75,7 @@ export function registerLibraryTools(server, client) {
|
|
|
75
75
|
.optional()
|
|
76
76
|
.describe('Reference image already in this connector\'s store: an `r2_key` from gemini_get_upload_url + PUT, or from a generation result\'s media[].r2_key'),
|
|
77
77
|
image_url: z.string().url().optional().describe('Reference image as a public https URL the SERVER fetches'),
|
|
78
|
-
},
|
|
78
|
+
}),
|
|
79
79
|
}, async (args, extra) => {
|
|
80
80
|
const image = (await withProgressHeartbeat(extra, 'Loading the reference image', () => resolveLibraryImage(client, args, true)));
|
|
81
81
|
const record = await library.saveCharacter({ name: args.name, description: args.description, image });
|
|
@@ -93,7 +93,7 @@ export function registerLibraryTools(server, client) {
|
|
|
93
93
|
description: 'List this account\'s saved characters (persistent reference library): names, descriptions and timestamps. ' +
|
|
94
94
|
USE_CHARACTERS_HINT,
|
|
95
95
|
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
96
|
-
inputSchema: {},
|
|
96
|
+
inputSchema: z.object({}),
|
|
97
97
|
}, async () => {
|
|
98
98
|
const characters = await library.listCharacters();
|
|
99
99
|
return minifiedResult({
|
|
@@ -113,7 +113,7 @@ export function registerLibraryTools(server, client) {
|
|
|
113
113
|
server.registerTool('gemini_delete_character', {
|
|
114
114
|
description: 'Delete a saved character from this account\'s reference library. Generation calls naming it will then fail until it is re-saved.',
|
|
115
115
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
116
|
-
inputSchema: { name: NAME_FIELD, confirm: schemaConfirm },
|
|
116
|
+
inputSchema: z.object({ name: NAME_FIELD, confirm: schemaConfirm }),
|
|
117
117
|
}, async (args) => {
|
|
118
118
|
const gate = previewUnlessConfirmed(args.confirm, 'Delete a saved character (its reference image and description)', 'DELETE', `characters/${args.name}`);
|
|
119
119
|
if (gate)
|
|
@@ -129,7 +129,7 @@ export function registerLibraryTools(server, client) {
|
|
|
129
129
|
'optionally a reference image) that a single word then applies to any generation. No expiry; re-saving a name replaces it. ' +
|
|
130
130
|
USE_STYLE_HINT,
|
|
131
131
|
annotations: { readOnlyHint: false, openWorldHint: false },
|
|
132
|
-
inputSchema: {
|
|
132
|
+
inputSchema: z.object({
|
|
133
133
|
name: NAME_FIELD,
|
|
134
134
|
prompt_fragment: z
|
|
135
135
|
.string()
|
|
@@ -137,7 +137,7 @@ export function registerLibraryTools(server, client) {
|
|
|
137
137
|
.describe('The style text appended to generation prompts, e.g. "bold cartoon style, thick outlines, saturated colors"'),
|
|
138
138
|
image_r2_key: z.string().min(1).optional().describe('Optional style reference image already in this connector\'s store (an r2_key)'),
|
|
139
139
|
image_url: z.string().url().optional().describe('Optional style reference image as a public https URL the SERVER fetches'),
|
|
140
|
-
},
|
|
140
|
+
}),
|
|
141
141
|
}, async (args, extra) => {
|
|
142
142
|
const image = await withProgressHeartbeat(extra, 'Loading the style reference image', () => resolveLibraryImage(client, args, false));
|
|
143
143
|
const record = await library.saveStyle({ name: args.name, promptFragment: args.prompt_fragment, image });
|
|
@@ -154,7 +154,7 @@ export function registerLibraryTools(server, client) {
|
|
|
154
154
|
server.registerTool('gemini_list_styles', {
|
|
155
155
|
description: 'List this account\'s saved style presets (persistent reference library). ' + USE_STYLE_HINT,
|
|
156
156
|
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
157
|
-
inputSchema: {},
|
|
157
|
+
inputSchema: z.object({}),
|
|
158
158
|
}, async () => {
|
|
159
159
|
const styles = await library.listStyles();
|
|
160
160
|
return minifiedResult({
|
|
@@ -172,7 +172,7 @@ export function registerLibraryTools(server, client) {
|
|
|
172
172
|
server.registerTool('gemini_delete_style', {
|
|
173
173
|
description: 'Delete a saved style preset from this account\'s reference library.',
|
|
174
174
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
175
|
-
inputSchema: { name: NAME_FIELD, confirm: schemaConfirm },
|
|
175
|
+
inputSchema: z.object({ name: NAME_FIELD, confirm: schemaConfirm }),
|
|
176
176
|
}, async (args) => {
|
|
177
177
|
const gate = previewUnlessConfirmed(args.confirm, 'Delete a saved style preset', 'DELETE', `styles/${args.name}`);
|
|
178
178
|
if (gate)
|
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
|
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
33
|
-
inputSchema: {
|
|
33
|
+
inputSchema: z.object({
|
|
34
34
|
prompt: z.string().min(1).describe('Description of the music: mood, genre, instruments, tempo, structure, or lyrics'),
|
|
35
35
|
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.`),
|
|
36
36
|
images: z.array(z.string().min(1)).optional().describe('Optional reference image path(s) to condition the music'),
|
|
@@ -47,7 +47,7 @@ export function registerMusicTools(server, client) {
|
|
|
47
47
|
async: asyncSchema,
|
|
48
48
|
max_wait_ms: maxWaitMsSchema,
|
|
49
49
|
confirm: schemaConfirm,
|
|
50
|
-
},
|
|
50
|
+
}),
|
|
51
51
|
}, async (args, extra) => {
|
|
52
52
|
assertLocalInputsAvailable(client.mediaSink, args);
|
|
53
53
|
const model = args.model ?? DEFAULT_MUSIC_MODEL;
|
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
|
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
16
|
-
inputSchema: {
|
|
16
|
+
inputSchema: z.object({
|
|
17
17
|
master_prompt: z.string().min(1).describe('Prompt for the master/reference image'),
|
|
18
18
|
scenes: z.array(z.string().min(1)).min(1).max(8).optional().describe('Per-image prompts (1-8); each references the master'),
|
|
19
19
|
count: z.number().int().positive().max(8).optional().describe('Number of variations of master_prompt (when scenes omitted)'),
|
|
@@ -28,7 +28,7 @@ export function registerSetTools(server, client) {
|
|
|
28
28
|
master_images_base64: imagesBase64Schema('Reference images for the master generation', 'master_images'),
|
|
29
29
|
confirm: schemaConfirm,
|
|
30
30
|
...sharedImageSchema,
|
|
31
|
-
},
|
|
31
|
+
}),
|
|
32
32
|
}, async (args, extra) => {
|
|
33
33
|
assertLocalInputsAvailable(client.mediaSink, args);
|
|
34
34
|
// `scenes` is min(1) at the schema, so a non-undefined value is non-empty.
|
package/dist/tools/shared.js
CHANGED
|
@@ -356,8 +356,8 @@ const HEARTBEAT_DEFAULT_MS = 10_000;
|
|
|
356
356
|
* `fn`'s result/rejection passes through untouched.
|
|
357
357
|
*/
|
|
358
358
|
export async function withProgressHeartbeat(extra, message, fn) {
|
|
359
|
-
const progressToken = extra?._meta?.progressToken;
|
|
360
|
-
const send = extra?.
|
|
359
|
+
const progressToken = extra?.mcpReq?._meta?.progressToken;
|
|
360
|
+
const send = extra?.mcpReq?.notify;
|
|
361
361
|
const env = Number(readEnvVar('GEMINI_HEARTBEAT_MS'));
|
|
362
362
|
const intervalMs = Number.isFinite(env) ? env : HEARTBEAT_DEFAULT_MS;
|
|
363
363
|
const debug = !!readEnvVar('GEMINI_DEBUG');
|
|
@@ -376,7 +376,7 @@ export async function withProgressHeartbeat(extra, message, fn) {
|
|
|
376
376
|
const reason = progressToken === undefined
|
|
377
377
|
? 'no progressToken from host (host cannot extend its tools/call timeout; long calls will hit it)'
|
|
378
378
|
: !send
|
|
379
|
-
? '
|
|
379
|
+
? 'no mcpReq.notify on the request extra (SDK v2 renamed v1\'s sendNotification; without it there is no channel to send progress on)'
|
|
380
380
|
: 'heartbeat disabled (GEMINI_HEARTBEAT_MS=0)';
|
|
381
381
|
console.error(`[gemini-mcp] heartbeat inactive: ${reason} — ${message}`);
|
|
382
382
|
}
|
package/dist/tools/uploads.js
CHANGED
|
@@ -52,7 +52,7 @@ export function registerUploadUrlTools(server, client) {
|
|
|
52
52
|
`${wholeMb(UPLOAD_MAX_BYTES)}MB. The PUT must send exactly the Content-Type this URL was minted for — the signature ` +
|
|
53
53
|
'covers it, so a mismatched header is rejected.',
|
|
54
54
|
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
55
|
-
inputSchema: {
|
|
55
|
+
inputSchema: z.object({
|
|
56
56
|
filename: z
|
|
57
57
|
.string()
|
|
58
58
|
.min(1)
|
|
@@ -64,7 +64,7 @@ export function registerUploadUrlTools(server, client) {
|
|
|
64
64
|
// serves from the connector's own origin (see RASTER_IMAGE_TYPE_PATTERN).
|
|
65
65
|
.regex(RASTER_IMAGE_TYPE_PATTERN, 'must be a raster image MIME type like image/jpeg (SVG is not accepted)')
|
|
66
66
|
.describe('MIME type of the bytes that will be PUT — raster images only (jpeg/png/webp/gif/avif/heic/heif/bmp/tiff; not SVG). The PUT must send exactly this Content-Type.'),
|
|
67
|
-
},
|
|
67
|
+
}),
|
|
68
68
|
}, async (args) => {
|
|
69
69
|
// `step` names the operation in the message, and `surfaceToolErrors`
|
|
70
70
|
// (wired in index.ts) prefixes the tool and flattens the cause chain —
|
package/dist/tools/usage.js
CHANGED
|
@@ -34,12 +34,12 @@ export function registerUsageTools(server, client) {
|
|
|
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
36
|
annotations: toolAnnotations({ readOnly: true }),
|
|
37
|
-
inputSchema: {
|
|
37
|
+
inputSchema: z.object({
|
|
38
38
|
reset: z
|
|
39
39
|
.boolean()
|
|
40
40
|
.optional()
|
|
41
41
|
.describe('Zero the running total after reporting it, so the next call measures from here. Use it to bracket a workflow without arithmetic.'),
|
|
42
|
-
},
|
|
42
|
+
}),
|
|
43
43
|
}, async (args) => {
|
|
44
44
|
const usage = client.session.usageTotal;
|
|
45
45
|
const calls = client.session.billedCalls;
|
package/dist/tools/video.js
CHANGED
|
@@ -38,7 +38,7 @@ export function registerVideoTools(server, client) {
|
|
|
38
38
|
'Output is written to disk as MP4 (video has no inline MCP block). Video runs long — give it a `max_wait_ms` budget ' +
|
|
39
39
|
'(or `async: true` + gemini_get_result on a local install), or raise `timeout_ms`. Needs a funded account.',
|
|
40
40
|
annotations: { readOnlyHint: false, openWorldHint: true },
|
|
41
|
-
inputSchema: {
|
|
41
|
+
inputSchema: z.object({
|
|
42
42
|
prompt: z.string().min(1).describe('Description of the video to generate (or the edit instruction when task=edit)'),
|
|
43
43
|
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.'),
|
|
44
44
|
orientation: orientationSchema,
|
|
@@ -65,7 +65,7 @@ export function registerVideoTools(server, client) {
|
|
|
65
65
|
async: asyncSchema,
|
|
66
66
|
max_wait_ms: maxWaitMsSchema,
|
|
67
67
|
confirm: schemaConfirm,
|
|
68
|
-
},
|
|
68
|
+
}),
|
|
69
69
|
}, async (args, extra) => {
|
|
70
70
|
assertLocalInputsAvailable(client.mediaSink, args);
|
|
71
71
|
let previousInteractionId = args.previous_interaction_id;
|
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 = '1.
|
|
2
|
+
export const VERSION = '2.1.0'; // x-release-please-version
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chrischall/gemini-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.1.0",
|
|
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 --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 --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,12 +46,13 @@
|
|
|
46
46
|
"typecheck": "tsc -p tsconfig.test.json"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@chrischall/mcp-utils": "^0.
|
|
50
|
-
"@modelcontextprotocol/
|
|
49
|
+
"@chrischall/mcp-utils": "^1.0.0",
|
|
50
|
+
"@modelcontextprotocol/server": "^2.0.0",
|
|
51
51
|
"dotenv": "^17.4.0",
|
|
52
|
-
"zod": "^4.
|
|
52
|
+
"zod": "^4.6.2"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
+
"@modelcontextprotocol/client": "^2.0.0",
|
|
55
56
|
"@types/node": "^26.0.0",
|
|
56
57
|
"@vitest/coverage-v8": "^5.0.0",
|
|
57
58
|
"esbuild": "^0.28.0",
|
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": "1.
|
|
9
|
+
"version": "2.1.0",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "@chrischall/gemini-mcp",
|
|
14
|
-
"version": "1.
|
|
14
|
+
"version": "2.1.0",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|