@chrischall/gemini-mcp 0.7.0 → 0.7.2
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 +56 -11
- package/dist/client.js +35 -4
- package/dist/images.js +4 -2
- package/dist/tools/interact.js +45 -6
- package/dist/tools/shared.js +4 -1
- package/dist/version.js +1 -1
- 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": "0.7.
|
|
10
|
+
"version": "0.7.2"
|
|
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": "0.7.
|
|
18
|
+
"version": "0.7.2",
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Chris Hall"
|
|
21
21
|
},
|
package/dist/bundle.js
CHANGED
|
@@ -32510,7 +32510,7 @@ var init_clipboard = __esm({
|
|
|
32510
32510
|
init_dist();
|
|
32511
32511
|
|
|
32512
32512
|
// src/version.ts
|
|
32513
|
-
var VERSION = "0.7.
|
|
32513
|
+
var VERSION = "0.7.2";
|
|
32514
32514
|
|
|
32515
32515
|
// src/tools/models.ts
|
|
32516
32516
|
init_dist();
|
|
@@ -32544,6 +32544,8 @@ var UPLOAD_BASE_URL = "https://generativelanguage.googleapis.com/upload/v1beta";
|
|
|
32544
32544
|
var FILE_MAX_BYTES = 2 * 1024 ** 3;
|
|
32545
32545
|
var FILE_POLL_INTERVAL_MS = 2e3;
|
|
32546
32546
|
var FILE_POLL_MAX_ATTEMPTS = 150;
|
|
32547
|
+
var CHAIN_404_RETRIES = 2;
|
|
32548
|
+
var CHAIN_404_RETRY_MS = 2e3;
|
|
32547
32549
|
var GeminiClient = class {
|
|
32548
32550
|
apiKey;
|
|
32549
32551
|
configError;
|
|
@@ -32756,9 +32758,28 @@ var GeminiClient = class {
|
|
|
32756
32758
|
if (opts.searchTypes?.length) searchTool.search_types = opts.searchTypes;
|
|
32757
32759
|
body.tools = [searchTool];
|
|
32758
32760
|
}
|
|
32759
|
-
|
|
32760
|
-
|
|
32761
|
-
|
|
32761
|
+
let data;
|
|
32762
|
+
for (let attempt = 0; ; attempt++) {
|
|
32763
|
+
try {
|
|
32764
|
+
data = await this.interactionsApi.fetchJson("POST", "/interactions", {
|
|
32765
|
+
body
|
|
32766
|
+
});
|
|
32767
|
+
break;
|
|
32768
|
+
} catch (err) {
|
|
32769
|
+
if (!(opts.previousInteractionId && err instanceof ApiError && err.status === 404)) throw err;
|
|
32770
|
+
if (attempt < CHAIN_404_RETRIES) {
|
|
32771
|
+
await this.sleep(CHAIN_404_RETRY_MS * (attempt + 1));
|
|
32772
|
+
continue;
|
|
32773
|
+
}
|
|
32774
|
+
throw new McpToolError(
|
|
32775
|
+
`Previous interaction "${opts.previousInteractionId}" was not found (retried ${CHAIN_404_RETRIES}\xD7 \u2014 the interactions store can lag briefly after a turn completes). If the id is old: interactions are retained 55 days on the paid tier (1 day on the free tier) and are scoped to the API key that created them.`,
|
|
32776
|
+
{
|
|
32777
|
+
hint: "Retry once more in a few seconds; if it keeps failing, start a new chain: call gemini_interact again with the last output image passed via `images` (or `images_base64`) plus the same instruction, then chain the NEW interaction id.",
|
|
32778
|
+
cause: err
|
|
32779
|
+
}
|
|
32780
|
+
);
|
|
32781
|
+
}
|
|
32782
|
+
}
|
|
32762
32783
|
const images = [];
|
|
32763
32784
|
const textParts = [];
|
|
32764
32785
|
const searchQueries = [];
|
|
@@ -32871,7 +32892,7 @@ function resolveInputPath(p, kind) {
|
|
|
32871
32892
|
const inputDir = readEnvVar("GEMINI_INPUT_DIR");
|
|
32872
32893
|
if (inputDir) {
|
|
32873
32894
|
const candidate = join3(inputDir, p);
|
|
32874
|
-
if (existsSync(candidate)) return candidate;
|
|
32895
|
+
if (existsSync(candidate)) return resolve(candidate);
|
|
32875
32896
|
}
|
|
32876
32897
|
const cwd = resolve(p);
|
|
32877
32898
|
if (existsSync(cwd)) return cwd;
|
|
@@ -32990,7 +33011,7 @@ function buildMeta(model, seed, opts) {
|
|
|
32990
33011
|
if (opts.image_size) meta3.image_size = opts.image_size;
|
|
32991
33012
|
return meta3;
|
|
32992
33013
|
}
|
|
32993
|
-
async function emit(named, opts, meta3) {
|
|
33014
|
+
async function emit(named, opts, meta3, onWritten) {
|
|
32994
33015
|
if (opts.inline) {
|
|
32995
33016
|
const imageBlocks = named.map((n) => ({ type: "image", data: n.image.base64, mimeType: n.image.mimeType }));
|
|
32996
33017
|
if (meta3 && Object.keys(meta3).length > 0) {
|
|
@@ -33003,6 +33024,7 @@ async function emit(named, opts, meta3) {
|
|
|
33003
33024
|
const dir = resolveOutputDir(opts.output_dir);
|
|
33004
33025
|
const images = [];
|
|
33005
33026
|
for (const n of named) images.push(await writeImage(dir, n.base, n.image.base64, n.image.mimeType));
|
|
33027
|
+
onWritten?.(images);
|
|
33006
33028
|
return textResult({ images, ...meta3 });
|
|
33007
33029
|
}
|
|
33008
33030
|
|
|
@@ -33168,6 +33190,21 @@ function registerSetTools(server) {
|
|
|
33168
33190
|
init_zod();
|
|
33169
33191
|
init_dist();
|
|
33170
33192
|
var lastInteractionId;
|
|
33193
|
+
var writtenOutputs = /* @__PURE__ */ new Set();
|
|
33194
|
+
var NEW_REFERENCES_ONLY = "NEW reference images only (e.g. a style or target photo). When chaining with previous_interaction_id, do NOT re-attach the prior turn's output \u2014 the interaction already contains it, and re-sending it anchors the model against your edit.";
|
|
33195
|
+
function splitReattachedOutputs(images) {
|
|
33196
|
+
const kept = [];
|
|
33197
|
+
const dropped = [];
|
|
33198
|
+
for (const p of images) {
|
|
33199
|
+
let resolved;
|
|
33200
|
+
try {
|
|
33201
|
+
resolved = resolveImagePath(p);
|
|
33202
|
+
} catch {
|
|
33203
|
+
}
|
|
33204
|
+
(resolved && writtenOutputs.has(resolved) ? dropped : kept).push(p);
|
|
33205
|
+
}
|
|
33206
|
+
return { kept, dropped };
|
|
33207
|
+
}
|
|
33171
33208
|
function registerInteractTools(server) {
|
|
33172
33209
|
server.registerTool(
|
|
33173
33210
|
"gemini_interact",
|
|
@@ -33178,8 +33215,8 @@ function registerInteractTools(server) {
|
|
|
33178
33215
|
input: external_exports.string().min(1).describe("Text prompt or editing instruction"),
|
|
33179
33216
|
previous_interaction_id: external_exports.string().optional().describe("ID from a prior gemini_interact call \u2014 continues that multi-turn conversation"),
|
|
33180
33217
|
continue_last: external_exports.boolean().optional().describe("Continue from the most recent interaction this server created (convenience for previous_interaction_id; an explicit id wins)"),
|
|
33181
|
-
images: external_exports.array(external_exports.string().min(1)).optional().describe(
|
|
33182
|
-
images_base64: external_exports.array(external_exports.string().min(1)).optional().describe(
|
|
33218
|
+
images: external_exports.array(external_exports.string().min(1)).optional().describe(`Paths to reference input images. ${NEW_REFERENCES_ONLY}`),
|
|
33219
|
+
images_base64: external_exports.array(external_exports.string().min(1)).optional().describe(`Reference images as base64 strings or data URIs. ${NEW_REFERENCES_ONLY}`),
|
|
33183
33220
|
model: external_exports.string().optional().describe(`Model id override (default: server default; see gemini_list_models). ${MODEL_CHOICE_GUIDE}`),
|
|
33184
33221
|
aspect_ratio: external_exports.enum(ASPECT_RATIOS).optional().describe("Output aspect ratio"),
|
|
33185
33222
|
image_size: external_exports.enum(IMAGE_SIZES).optional().describe("Output resolution (512 = 0.5K, Flash-only)"),
|
|
@@ -33206,7 +33243,12 @@ function registerInteractTools(server) {
|
|
|
33206
33243
|
}
|
|
33207
33244
|
previousInteractionId = lastInteractionId;
|
|
33208
33245
|
}
|
|
33209
|
-
|
|
33246
|
+
let images = args.images;
|
|
33247
|
+
let dropped = [];
|
|
33248
|
+
if (previousInteractionId && images?.length) {
|
|
33249
|
+
({ kept: images, dropped } = splitReattachedOutputs(images));
|
|
33250
|
+
}
|
|
33251
|
+
const inputs = await gatherImageInputs({ images, images_base64: args.images_base64, from_clipboard: args.from_clipboard });
|
|
33210
33252
|
const video = await resolveVideoInput(args);
|
|
33211
33253
|
const r = await client.interact({
|
|
33212
33254
|
input: args.input,
|
|
@@ -33225,7 +33267,8 @@ function registerInteractTools(server) {
|
|
|
33225
33267
|
const model = resolveModel(args.model, readEnvVar("GEMINI_IMAGE_MODEL"));
|
|
33226
33268
|
const meta3 = { model, interaction_id: r.id };
|
|
33227
33269
|
if (previousInteractionId) meta3.previous_interaction_id = previousInteractionId;
|
|
33228
|
-
meta3.
|
|
33270
|
+
if (dropped.length) meta3.dropped_previous_output = dropped;
|
|
33271
|
+
meta3.hint = `To refine this image, call gemini_interact again with previous_interaction_id: "${r.id}" (or continue_last: true) \u2014 do NOT re-attach the returned image as an input; the interaction already contains it.`;
|
|
33229
33272
|
if (r.text) meta3.text = r.text;
|
|
33230
33273
|
if (r.grounding) meta3.grounding = r.grounding;
|
|
33231
33274
|
if (video.videoFileMeta) meta3.video_file = video.videoFileMeta;
|
|
@@ -33234,7 +33277,9 @@ function registerInteractTools(server) {
|
|
|
33234
33277
|
image,
|
|
33235
33278
|
base: r.images.length > 1 ? `${slug}-${String(i + 1).padStart(2, "0")}` : slug
|
|
33236
33279
|
}));
|
|
33237
|
-
return emit(named, args, meta3)
|
|
33280
|
+
return emit(named, args, meta3, (paths) => {
|
|
33281
|
+
for (const p of paths) writtenOutputs.add(p);
|
|
33282
|
+
});
|
|
33238
33283
|
}
|
|
33239
33284
|
);
|
|
33240
33285
|
}
|
package/dist/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { basename, dirname, join } from 'node:path';
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
|
-
import { loadDotenvSafely, readEnvVar, McpToolError, createApiClient, formatApiError, fileBlob } from '@chrischall/mcp-utils';
|
|
3
|
+
import { loadDotenvSafely, readEnvVar, McpToolError, ApiError, createApiClient, formatApiError, fileBlob } from '@chrischall/mcp-utils';
|
|
4
4
|
import { resolveModel, filterImageModels } from './models.js';
|
|
5
5
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
6
|
await loadDotenvSafely({ path: join(__dirname, '..', '.env'), override: false });
|
|
@@ -21,6 +21,13 @@ const FILE_MAX_BYTES = 2 * 1024 ** 3;
|
|
|
21
21
|
// in ~1s (verified); longer videos take proportionally longer to process.
|
|
22
22
|
const FILE_POLL_INTERVAL_MS = 2_000;
|
|
23
23
|
const FILE_POLL_MAX_ATTEMPTS = 150;
|
|
24
|
+
// The interactions store is eventually consistent: a freshly returned id can
|
|
25
|
+
// 404 for a short window right after its turn completes (observed live
|
|
26
|
+
// 2026-07-06 — an id that 404'd on an immediate chain resolved fine minutes
|
|
27
|
+
// later). A chained 404 generated nothing (no cost), so retry before giving up:
|
|
28
|
+
// attempts wait 2s then 4s (~6s total).
|
|
29
|
+
const CHAIN_404_RETRIES = 2;
|
|
30
|
+
const CHAIN_404_RETRY_MS = 2_000;
|
|
24
31
|
export class GeminiClient {
|
|
25
32
|
apiKey;
|
|
26
33
|
configError;
|
|
@@ -256,9 +263,33 @@ export class GeminiClient {
|
|
|
256
263
|
searchTool.search_types = opts.searchTypes;
|
|
257
264
|
body.tools = [searchTool];
|
|
258
265
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
266
|
+
let data;
|
|
267
|
+
for (let attempt = 0;; attempt++) {
|
|
268
|
+
try {
|
|
269
|
+
data = await this.interactionsApi.fetchJson('POST', '/interactions', {
|
|
270
|
+
body,
|
|
271
|
+
});
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
// A 404 on a chained call means the referenced interaction wasn't
|
|
276
|
+
// found. Two causes: (a) store lag right after the id was created —
|
|
277
|
+
// retry with backoff; (b) genuinely gone — interactions are retained
|
|
278
|
+
// 55 days (paid tier) / 1 day (free tier) and are scoped to the API
|
|
279
|
+
// key's project. The chain is recoverable from the last output file,
|
|
280
|
+
// so the exhausted-retries error says how.
|
|
281
|
+
if (!(opts.previousInteractionId && err instanceof ApiError && err.status === 404))
|
|
282
|
+
throw err;
|
|
283
|
+
if (attempt < CHAIN_404_RETRIES) {
|
|
284
|
+
await this.sleep(CHAIN_404_RETRY_MS * (attempt + 1));
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
throw new McpToolError(`Previous interaction "${opts.previousInteractionId}" was not found (retried ${CHAIN_404_RETRIES}× — the interactions store can lag briefly after a turn completes). If the id is old: interactions are retained 55 days on the paid tier (1 day on the free tier) and are scoped to the API key that created them.`, {
|
|
288
|
+
hint: 'Retry once more in a few seconds; if it keeps failing, start a new chain: call gemini_interact again with the last output image passed via `images` (or `images_base64`) plus the same instruction, then chain the NEW interaction id.',
|
|
289
|
+
cause: err,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
262
293
|
// Only surface the `model_output` step — that's the caller-facing result.
|
|
263
294
|
// `thought` steps hold internal reasoning (and, with includeThoughts, draft
|
|
264
295
|
// "thinking" images); collecting those would leak reasoning into `text` and
|
package/dist/images.js
CHANGED
|
@@ -62,7 +62,9 @@ export async function writeImage(dir, base, base64, mimeType) {
|
|
|
62
62
|
* 1. Absolute path that exists → returned as-is.
|
|
63
63
|
* 2. `GEMINI_INPUT_DIR` env var is set → look for `join(inputDir, p)`.
|
|
64
64
|
* 3. Relative to cwd → `resolve(p)`.
|
|
65
|
-
*
|
|
65
|
+
* Always returns an absolute path (callers compare resolved paths — e.g. the
|
|
66
|
+
* interact re-attach guard — so a relative GEMINI_INPUT_DIR must not leak
|
|
67
|
+
* through). Throws a helpful `McpToolError` (labelled `kind`) if none are found.
|
|
66
68
|
*/
|
|
67
69
|
function resolveInputPath(p, kind) {
|
|
68
70
|
if (isAbsolute(p) && existsSync(p))
|
|
@@ -73,7 +75,7 @@ function resolveInputPath(p, kind) {
|
|
|
73
75
|
// single-user MCP — the caller already has the user's own filesystem access.
|
|
74
76
|
const candidate = join(inputDir, p);
|
|
75
77
|
if (existsSync(candidate))
|
|
76
|
-
return candidate;
|
|
78
|
+
return resolve(candidate);
|
|
77
79
|
}
|
|
78
80
|
const cwd = resolve(p);
|
|
79
81
|
if (existsSync(cwd))
|
package/dist/tools/interact.js
CHANGED
|
@@ -2,12 +2,40 @@ import { z } from 'zod';
|
|
|
2
2
|
import { McpToolError, readEnvVar } from '@chrischall/mcp-utils';
|
|
3
3
|
import { resolveModel } from '../models.js';
|
|
4
4
|
import { client } from '../client.js';
|
|
5
|
-
import { slugify, baseName, gatherImageInputs } from '../images.js';
|
|
5
|
+
import { slugify, baseName, gatherImageInputs, resolveImagePath } from '../images.js';
|
|
6
6
|
import { emit, ASPECT_RATIOS, IMAGE_SIZES, MODEL_CHOICE_GUIDE, resolveVideoInput, videoPathSchema } from './shared.js';
|
|
7
7
|
// The most recent interaction id this server process created — what
|
|
8
8
|
// `continue_last: true` resumes. In-memory only: an MCP server lives for the
|
|
9
9
|
// host session, so "last" means "last in this session".
|
|
10
10
|
let lastInteractionId;
|
|
11
|
+
// Absolute paths of every image this tool has written this session. Used to
|
|
12
|
+
// drop a chained call's re-attached prior output: the interaction state
|
|
13
|
+
// already contains that image, and re-sending it as a fresh reference anchors
|
|
14
|
+
// the model against the requested edit (observed in the wild — callers pass
|
|
15
|
+
// the last result path back via `images` despite the description saying not to).
|
|
16
|
+
const writtenOutputs = new Set();
|
|
17
|
+
/** Shared warning for the reference-image params. */
|
|
18
|
+
const NEW_REFERENCES_ONLY = 'NEW reference images only (e.g. a style or target photo). When chaining with previous_interaction_id, ' +
|
|
19
|
+
"do NOT re-attach the prior turn's output — the interaction already contains it, and re-sending it " +
|
|
20
|
+
'anchors the model against your edit.';
|
|
21
|
+
/**
|
|
22
|
+
* On a chained call, split `images` into genuinely new references vs paths
|
|
23
|
+
* this server itself generated (which the interaction already contains).
|
|
24
|
+
* Unresolvable paths are kept so `gatherImageInputs` surfaces its usual error.
|
|
25
|
+
*/
|
|
26
|
+
function splitReattachedOutputs(images) {
|
|
27
|
+
const kept = [];
|
|
28
|
+
const dropped = [];
|
|
29
|
+
for (const p of images) {
|
|
30
|
+
let resolved;
|
|
31
|
+
try {
|
|
32
|
+
resolved = resolveImagePath(p);
|
|
33
|
+
}
|
|
34
|
+
catch { /* kept; loading will error actionably */ }
|
|
35
|
+
(resolved && writtenOutputs.has(resolved) ? dropped : kept).push(p);
|
|
36
|
+
}
|
|
37
|
+
return { kept, dropped };
|
|
38
|
+
}
|
|
11
39
|
export function registerInteractTools(server) {
|
|
12
40
|
server.registerTool('gemini_interact', {
|
|
13
41
|
description: 'Preferred tool for iterative or multi-step refinement of a single image — ' +
|
|
@@ -30,11 +58,11 @@ export function registerInteractTools(server) {
|
|
|
30
58
|
images: z
|
|
31
59
|
.array(z.string().min(1))
|
|
32
60
|
.optional()
|
|
33
|
-
.describe(
|
|
61
|
+
.describe(`Paths to reference input images. ${NEW_REFERENCES_ONLY}`),
|
|
34
62
|
images_base64: z
|
|
35
63
|
.array(z.string().min(1))
|
|
36
64
|
.optional()
|
|
37
|
-
.describe(
|
|
65
|
+
.describe(`Reference images as base64 strings or data URIs. ${NEW_REFERENCES_ONLY}`),
|
|
38
66
|
model: z
|
|
39
67
|
.string()
|
|
40
68
|
.optional()
|
|
@@ -90,7 +118,12 @@ export function registerInteractTools(server) {
|
|
|
90
118
|
}
|
|
91
119
|
previousInteractionId = lastInteractionId;
|
|
92
120
|
}
|
|
93
|
-
|
|
121
|
+
let images = args.images;
|
|
122
|
+
let dropped = [];
|
|
123
|
+
if (previousInteractionId && images?.length) {
|
|
124
|
+
({ kept: images, dropped } = splitReattachedOutputs(images));
|
|
125
|
+
}
|
|
126
|
+
const inputs = await gatherImageInputs({ images, images_base64: args.images_base64, from_clipboard: args.from_clipboard });
|
|
94
127
|
const video = await resolveVideoInput(args);
|
|
95
128
|
const r = await client.interact({
|
|
96
129
|
input: args.input,
|
|
@@ -110,7 +143,11 @@ export function registerInteractTools(server) {
|
|
|
110
143
|
const meta = { model, interaction_id: r.id };
|
|
111
144
|
if (previousInteractionId)
|
|
112
145
|
meta.previous_interaction_id = previousInteractionId;
|
|
113
|
-
|
|
146
|
+
if (dropped.length)
|
|
147
|
+
meta.dropped_previous_output = dropped;
|
|
148
|
+
meta.hint =
|
|
149
|
+
`To refine this image, call gemini_interact again with previous_interaction_id: "${r.id}" (or continue_last: true) — ` +
|
|
150
|
+
'do NOT re-attach the returned image as an input; the interaction already contains it.';
|
|
114
151
|
if (r.text)
|
|
115
152
|
meta.text = r.text;
|
|
116
153
|
if (r.grounding)
|
|
@@ -122,6 +159,8 @@ export function registerInteractTools(server) {
|
|
|
122
159
|
image,
|
|
123
160
|
base: r.images.length > 1 ? `${slug}-${String(i + 1).padStart(2, '0')}` : slug,
|
|
124
161
|
}));
|
|
125
|
-
|
|
162
|
+
// writeImage returns already-resolved absolute paths.
|
|
163
|
+
return emit(named, args, meta, (paths) => { for (const p of paths)
|
|
164
|
+
writtenOutputs.add(p); });
|
|
126
165
|
});
|
|
127
166
|
}
|
package/dist/tools/shared.js
CHANGED
|
@@ -85,8 +85,10 @@ export function buildMeta(model, seed, opts) {
|
|
|
85
85
|
* return their paths as a text result. `inline` wins when true.
|
|
86
86
|
* Optional `meta` is merged into the disk-path JSON result, or prepended as a
|
|
87
87
|
* text block in inline mode (only if meta has keys).
|
|
88
|
+
* Optional `onWritten` receives the absolute paths written in disk mode (never
|
|
89
|
+
* called inline) — lets a tool remember its own outputs.
|
|
88
90
|
*/
|
|
89
|
-
export async function emit(named, opts, meta) {
|
|
91
|
+
export async function emit(named, opts, meta, onWritten) {
|
|
90
92
|
if (opts.inline) {
|
|
91
93
|
const imageBlocks = named.map((n) => ({ type: 'image', data: n.image.base64, mimeType: n.image.mimeType }));
|
|
92
94
|
if (meta && Object.keys(meta).length > 0) {
|
|
@@ -100,5 +102,6 @@ export async function emit(named, opts, meta) {
|
|
|
100
102
|
const images = [];
|
|
101
103
|
for (const n of named)
|
|
102
104
|
images.push(await writeImage(dir, n.base, n.image.base64, n.image.mimeType));
|
|
105
|
+
onWritten?.(images);
|
|
103
106
|
return textResult({ images, ...meta });
|
|
104
107
|
}
|
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 = '0.7.
|
|
2
|
+
export const VERSION = '0.7.2'; // x-release-please-version
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chrischall/gemini-mcp",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
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>",
|
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": "0.7.
|
|
9
|
+
"version": "0.7.2",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "@chrischall/gemini-mcp",
|
|
14
|
-
"version": "0.7.
|
|
14
|
+
"version": "0.7.2",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|