@chrischall/gemini-mcp 1.9.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "metadata": {
9
9
  "description": "MCP server for Google Gemini image generation and editing — generate and edit images via the Gemini API",
10
- "version": "1.9.0"
10
+ "version": "1.10.0"
11
11
  },
12
12
  "plugins": [
13
13
  {
@@ -15,7 +15,7 @@
15
15
  "displayName": "Gemini Images",
16
16
  "source": "./",
17
17
  "description": "Generate and edit images with Google Gemini image models (Nano Banana / Nano Banana Pro) via natural language prompts",
18
- "version": "1.9.0",
18
+ "version": "1.10.0",
19
19
  "author": {
20
20
  "name": "Chris Hall"
21
21
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gemini-mcp",
3
3
  "displayName": "Gemini Images",
4
- "version": "1.9.0",
4
+ "version": "1.10.0",
5
5
  "description": "MCP server for Google Gemini image generation and editing — generate and edit images via the Gemini API",
6
6
  "author": {
7
7
  "name": "Chris Hall",
package/dist/bundle.js CHANGED
@@ -32868,7 +32868,7 @@ async function loadStdioDotenv() {
32868
32868
  }
32869
32869
 
32870
32870
  // src/version.ts
32871
- var VERSION = "1.9.0";
32871
+ var VERSION = "1.10.0";
32872
32872
 
32873
32873
  // src/errors.ts
32874
32874
  init_dist();
@@ -33813,10 +33813,13 @@ function timeoutRiskHint(opts) {
33813
33813
  return lead + `If the host gives up mid-generation the image is still written to disk (and gemini_interact leaves an <image>.json sidecar with the interaction id) \u2014 no rerun needed, just look in the output dir. For a faster in-band result use a Flash model, drop image_size to 1K/2K, or \u2014 in Claude Code \u2014 set MCP_TOOL_TIMEOUT.`;
33814
33814
  }
33815
33815
  function buildMeta(model, seed, opts) {
33816
- const meta3 = { model, seed };
33816
+ const meta3 = reportShape({ model, seed }, opts);
33817
+ if (opts.image_size) meta3.image_size = opts.image_size;
33818
+ return meta3;
33819
+ }
33820
+ function reportShape(meta3, opts) {
33817
33821
  if (opts.aspect_ratio) meta3.aspect_ratio = opts.aspect_ratio;
33818
33822
  if (opts.orientation) meta3.orientation = opts.orientation;
33819
- if (opts.image_size) meta3.image_size = opts.image_size;
33820
33823
  return meta3;
33821
33824
  }
33822
33825
  async function emitMedia(named, kind, opts, meta3, onWritten) {
@@ -36194,7 +36197,7 @@ function registerInteractTools(server, client2) {
36194
36197
  }
36195
36198
  });
36196
36199
  client2.session.lastInteractionId = r.id;
36197
- const meta3 = { model, interaction_id: r.id };
36200
+ const meta3 = reportShape({ model, interaction_id: r.id }, { ...args, aspect_ratio: aspectRatio });
36198
36201
  if (chainRecovered) meta3.chain_recovered = chainRecovered;
36199
36202
  else if (previousInteractionId) meta3.previous_interaction_id = previousInteractionId;
36200
36203
  if (continuedFromSidecar) meta3.continued_from_sidecar = true;
@@ -36306,7 +36309,7 @@ function registerVideoTools(server, client2) {
36306
36309
  onInteractionStarted: ctx.reportInteraction
36307
36310
  }));
36308
36311
  client2.session.lastVideoInteractionId = r.id;
36309
- const meta3 = { model, interaction_id: r.id };
36312
+ const meta3 = reportShape({ model, interaction_id: r.id }, { ...args, aspect_ratio: aspectRatio });
36310
36313
  if (previousInteractionId) meta3.previous_interaction_id = previousInteractionId;
36311
36314
  if (r.text) meta3.text = r.text;
36312
36315
  if (report) meta3.image_inputs = report;
@@ -5,7 +5,7 @@ import { ChainedRequest404Error } from '../client.js';
5
5
  import { slugify, baseName, resolveImagePath, writeSidecar, resolveOutputDir, readImageAsInline } from '../images.js';
6
6
  import { resolveImageInputs } from '../inputs.js';
7
7
  import { findInteractionImages, latestInteractionId } from '../sidecar.js';
8
- import { emit, resolveAspectRatio, orientationSchema, ASPECT_RATIOS, IMAGE_SIZES, MODEL_CHOICE_GUIDE, resolveVideoInput, videoPathSchema, timeoutMsSchema, timeoutRiskHint, idempotencyKeySchema, asyncSchema, maxWaitMsSchema, withProgressHeartbeat, assertLocalInputsAvailable, imagesUrlSchema, imagesFileUrisSchema } from './shared.js';
8
+ import { emit, reportShape, resolveAspectRatio, orientationSchema, ASPECT_RATIOS, IMAGE_SIZES, MODEL_CHOICE_GUIDE, resolveVideoInput, videoPathSchema, timeoutMsSchema, timeoutRiskHint, idempotencyKeySchema, asyncSchema, maxWaitMsSchema, withProgressHeartbeat, assertLocalInputsAvailable, imagesUrlSchema, imagesFileUrisSchema } from './shared.js';
9
9
  import { fingerprintRequest } from '../jobs.js';
10
10
  import { previewLocalInputsUnlessConfirmed, schemaConfirm } from './_confirm.js';
11
11
  // The last-interaction id and the written-outputs set used to live here, at
@@ -259,7 +259,9 @@ export function registerInteractTools(server, client) {
259
259
  }
260
260
  });
261
261
  client.session.lastInteractionId = r.id;
262
- const meta = { model, interaction_id: r.id };
262
+ // `aspectRatio` is the RESOLVED value (see resolveAspectRatio above), so
263
+ // an `orientation: 'portrait'` request can be seen to have become 9:16.
264
+ const meta = reportShape({ model, interaction_id: r.id }, { ...args, aspect_ratio: aspectRatio });
263
265
  // On recovery the prior id was NOT used — reporting it as
264
266
  // previous_interaction_id would misrepresent what the model saw.
265
267
  if (chainRecovered)
@@ -446,15 +446,29 @@ export function timeoutRiskHint(opts) {
446
446
  }
447
447
  /** Build the result metadata echoed to the caller (omitting unset optionals). */
448
448
  export function buildMeta(model, seed, opts) {
449
- const meta = { model, seed };
450
- // The RESOLVED ratio, and the orientation only when one was asked for — so a
451
- // caller who said "portrait" can see both what they asked and what it became.
449
+ const meta = reportShape({ model, seed }, opts);
450
+ if (opts.image_size)
451
+ meta.image_size = opts.image_size;
452
+ return meta;
453
+ }
454
+ /**
455
+ * Record the shape a generation produced, into any meta object.
456
+ *
457
+ * The RESOLVED ratio, and the `orientation` only when one was actually asked
458
+ * for — so a caller who said "portrait" sees both what they asked and what it
459
+ * became, while a bare `21:9` is not handed back a word nobody used.
460
+ *
461
+ * It lives apart from `buildMeta` because two tools cannot use that function:
462
+ * `gemini_interact` and `gemini_video_generate` build their metas around
463
+ * `interaction_id` and chain state rather than a seed, and so reported no shape
464
+ * at all until #166. Sharing the rule rather than copying it is what keeps
465
+ * those hand-rolled metas from drifting again.
466
+ */
467
+ export function reportShape(meta, opts) {
452
468
  if (opts.aspect_ratio)
453
469
  meta.aspect_ratio = opts.aspect_ratio;
454
470
  if (opts.orientation)
455
471
  meta.orientation = opts.orientation;
456
- if (opts.image_size)
457
- meta.image_size = opts.image_size;
458
472
  return meta;
459
473
  }
460
474
  /**
@@ -3,7 +3,7 @@ import { McpToolError } from '@chrischall/mcp-utils';
3
3
  import { slugify, baseName } from '../images.js';
4
4
  import { resolveImageInputs } from '../inputs.js';
5
5
  import { DEFAULT_VIDEO_MODEL } from '../models.js';
6
- import { emitMedia, resolveAspectRatio, orientationSchema, timeoutMsSchema, idempotencyKeySchema, asyncSchema, maxWaitMsSchema, withProgressHeartbeat, assertLocalInputsAvailable, imagesUrlSchema, imagesFileUrisSchema } from './shared.js';
6
+ import { emitMedia, reportShape, resolveAspectRatio, orientationSchema, timeoutMsSchema, idempotencyKeySchema, asyncSchema, maxWaitMsSchema, withProgressHeartbeat, assertLocalInputsAvailable, imagesUrlSchema, imagesFileUrisSchema } from './shared.js';
7
7
  import { fingerprintRequest } from '../jobs.js';
8
8
  import { previewLocalInputsUnlessConfirmed, schemaConfirm } from './_confirm.js';
9
9
  /** omni's own aspect-ratio enum — NOT the image tools' ASPECT_RATIOS. */
@@ -100,7 +100,9 @@ export function registerVideoTools(server, client) {
100
100
  onInteractionStarted: ctx.reportInteraction,
101
101
  }));
102
102
  client.session.lastVideoInteractionId = r.id;
103
- const meta = { model, interaction_id: r.id };
103
+ // The RESOLVED ratio: omni takes only 16:9/9:16, so a caller who asked
104
+ // in words can see which of the two their request became.
105
+ const meta = reportShape({ model, interaction_id: r.id }, { ...args, aspect_ratio: aspectRatio });
104
106
  if (previousInteractionId)
105
107
  meta.previous_interaction_id = previousInteractionId;
106
108
  if (r.text)
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.9.0'; // x-release-please-version
2
+ export const VERSION = '1.10.0'; // x-release-please-version
package/mint.yaml ADDED
@@ -0,0 +1,57 @@
1
+ version: 1
2
+ name: Gemini Images
3
+ slug: gemini
4
+ summary: >-
5
+ Generate and edit images with Google Gemini image models (Nano Banana /
6
+ Nano Banana Pro) via natural language prompts
7
+ env:
8
+ - name: GEMINI_API_KEY
9
+ secret: true
10
+ required: true
11
+ help: >-
12
+ Your Google Gemini API key (aistudio.google.com/apikey)
13
+ - name: GEMINI_IMAGE_MODEL
14
+ required: false
15
+ help: >-
16
+ Override the default image model (default: gemini-3.1-flash-image)
17
+ - name: GEMINI_OUTPUT_DIR
18
+ required: false
19
+ help: >-
20
+ Default directory for generated images (default: current working
21
+ directory)
22
+ - name: GEMINI_INPUT_DIR
23
+ required: false
24
+ help: >-
25
+ Directory to resolve bare input-image filenames against
26
+ - name: GEMINI_TIMEOUT_MS
27
+ required: false
28
+ help: >-
29
+ Upstream request timeout in ms (default: 60000, or 120000 for 4K output)
30
+ - name: GEMINI_CHAIN_RETRY_MS
31
+ required: false
32
+ help: >-
33
+ Retry budget in milliseconds for a chained request that 404s while the
34
+ upstream result settles (default 120000).
35
+ - name: GEMINI_HEARTBEAT_MS
36
+ required: false
37
+ help: >-
38
+ How often, in milliseconds, to emit a progress notification while a
39
+ generation is in flight. Hosts reset their tools/call timeout on progress,
40
+ so this lets a long 4K/Pro generation outlive the default budget.
41
+ egress:
42
+ allow:
43
+ # Only hosts the SERVER process actually fetches. Hosts that appear
44
+ # solely in a URL this server BUILDS and returns are excluded.
45
+ - generativelanguage.googleapis.com
46
+ #
47
+ # NOTE — this server also reaches at least one host determined at
48
+ # RUNTIME, which cannot appear as a literal here:
49
+ # - a signed upload URL returned by the API (often storage.googleapis.com / s3)
50
+ # - a media/CDN host supplied at runtime
51
+ # Confirm the concrete host(s) against a live run and add them, or
52
+ # the isolated tier will block the call.
53
+ #
54
+ # Also: the images_url argument makes this server fetch an ARBITRARY
55
+ # caller-supplied https host. That cannot be enumerated in advance, so on
56
+ # the isolated tier either allow the hosts you intend to pass, or expect
57
+ # image-by-URL to be blocked there.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrischall/gemini-mcp",
3
- "version": "1.9.0",
3
+ "version": "1.10.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>",
@@ -33,7 +33,8 @@
33
33
  ".claude-plugin",
34
34
  "skills/",
35
35
  ".mcp.json",
36
- "server.json"
36
+ "server.json",
37
+ "mint.yaml"
37
38
  ],
38
39
  "scripts": {
39
40
  "build": "tsc && npm run bundle",
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.0",
9
+ "version": "1.10.0",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@chrischall/gemini-mcp",
14
- "version": "1.9.0",
14
+ "version": "1.10.0",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },