imgx-cli 0.6.0 → 0.7.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/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.0 (2026-02-27)
4
+
5
+ ### Added
6
+
7
+ - **Output format selection** — `--format` flag (CLI) and `output_format` parameter (MCP) to choose between `png`, `jpeg`, or `webp` output. Currently supported by OpenAI provider (`gpt-image-1`). Gemini provider outputs PNG regardless of format setting.
8
+ - `OUTPUT_FORMAT` capability added to provider capability system
9
+
10
+ ## 0.6.2 (2026-02-27)
11
+
12
+ ### Added
13
+
14
+ - **Image preview in MCP responses** — MCP tool results now include inline image data (base64) alongside file paths. Claude Desktop and other MCP clients can display generated/edited images directly without opening files manually.
15
+
16
+ ## 0.6.1 (2026-02-27)
17
+
18
+ ### Fixed
19
+
20
+ - Default output directory changed from process cwd to `~/Pictures/imgx` — fixes images being saved to AppData when used via MCP (Claude Desktop, etc.)
21
+
22
+ ## 0.6.0 (2026-02-27)
23
+
24
+ ### Added
25
+
26
+ - **OpenAI provider** — `gpt-image-1` model with generate and edit support
27
+ - Native `fetch` implementation (no `openai` npm dependency)
28
+ - Aspect ratio mapping to OpenAI size strings, resolution mapping to quality parameter
29
+ - Manual multipart/form-data construction for Node 18 compatibility
30
+ - `OPENAI_API_KEY` environment variable support
31
+ - `--provider` flag for `imgx config set api-key` — manage API keys per provider
32
+ - `imgx config set api-key <key> --provider openai`
33
+ - `imgx config list` now shows all configured provider keys
34
+
35
+ ### Changed
36
+
37
+ - CLI and MCP server now initialize both Gemini and OpenAI providers at startup
38
+ - `imgx providers` and error messages updated for multi-provider context
39
+ - Help text updated with OpenAI provider info and env var
40
+
3
41
  ## 0.5.2 (2026-02-26)
4
42
 
5
43
  ### Fixed
package/README.md CHANGED
@@ -48,16 +48,25 @@ Requires Node.js 18+.
48
48
 
49
49
  ## Setup
50
50
 
51
- Get a Gemini API key from [Google AI Studio](https://aistudio.google.com/apikey) (free tier available), then save it:
51
+ Set up at least one provider:
52
+
53
+ **Gemini** — get a key from [Google AI Studio](https://aistudio.google.com/apikey) (free tier available):
54
+
55
+ ```bash
56
+ imgx config set api-key YOUR_GEMINI_API_KEY --provider gemini
57
+ ```
58
+
59
+ **OpenAI** — get a key from [OpenAI Platform](https://platform.openai.com/api-keys):
52
60
 
53
61
  ```bash
54
- imgx config set api-key YOUR_GEMINI_API_KEY
62
+ imgx config set api-key YOUR_OPENAI_API_KEY --provider openai
55
63
  ```
56
64
 
57
- This stores the key in `~/.config/imgx/config.json` (Linux/macOS) or `%APPDATA%\imgx\config.json` (Windows). Alternatively, set an environment variable:
65
+ Keys are stored in `~/.config/imgx/config.json` (Linux/macOS) or `%APPDATA%\imgx\config.json` (Windows). Alternatively, set environment variables:
58
66
 
59
67
  ```bash
60
68
  export GEMINI_API_KEY="your-api-key"
69
+ export OPENAI_API_KEY="your-api-key"
61
70
  ```
62
71
 
63
72
  Environment variables take precedence over the config file.
@@ -99,6 +108,7 @@ imgx edit --last -p "Crop to 16:9" -o final.png
99
108
  | `--aspect-ratio` | `-a` | `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `2:3`, `3:2` |
100
109
  | `--resolution` | `-r` | `1K`, `2K`, `4K` |
101
110
  | `--count` | `-n` | Number of images to generate |
111
+ | `--format` | `-f` | Output format: `png`, `jpeg`, `webp` (OpenAI only) |
102
112
  | `--model` | `-m` | Model name |
103
113
  | `--provider` | | Provider name (default: `gemini`) |
104
114
  | `--output-dir` | `-d` | Output directory |
@@ -106,7 +116,8 @@ imgx edit --last -p "Crop to 16:9" -o final.png
106
116
  ### Configuration
107
117
 
108
118
  ```bash
109
- imgx config set api-key <key> # Save Gemini API key
119
+ imgx config set api-key <key> --provider gemini # Save Gemini API key
120
+ imgx config set api-key <key> --provider openai # Save OpenAI API key
110
121
  imgx config set model <name> # Set default model
111
122
  imgx config set output-dir <dir> # Set default output directory
112
123
  imgx config set aspect-ratio 16:9 # Set default aspect ratio
@@ -162,7 +173,8 @@ Environment variables override config file settings.
162
173
 
163
174
  | Variable | Description |
164
175
  |----------|-------------|
165
- | `GEMINI_API_KEY` | Gemini API key (overrides `imgx config set api-key`) |
176
+ | `GEMINI_API_KEY` | Gemini API key |
177
+ | `OPENAI_API_KEY` | OpenAI API key |
166
178
  | `IMGX_PROVIDER` | Default provider |
167
179
  | `IMGX_MODEL` | Default model |
168
180
  | `IMGX_OUTPUT_DIR` | Default output directory |
@@ -206,7 +218,7 @@ Add to your tool's MCP config. The `env` section is optional if you have already
206
218
  "imgx": {
207
219
  "command": "npx",
208
220
  "args": ["--package=imgx-cli", "-y", "imgx-mcp"],
209
- "env": { "GEMINI_API_KEY": "your-api-key" }
221
+ "env": { "GEMINI_API_KEY": "your-key", "OPENAI_API_KEY": "your-key" }
210
222
  }
211
223
  }
212
224
  }
@@ -224,7 +236,7 @@ Or install as a [Claude Code plugin](#install) for automatic MCP registration.
224
236
  "imgx": {
225
237
  "command": "npx",
226
238
  "args": ["--package=imgx-cli", "-y", "imgx-mcp"],
227
- "env": { "GEMINI_API_KEY": "your-api-key" }
239
+ "env": { "GEMINI_API_KEY": "your-key", "OPENAI_API_KEY": "your-key" }
228
240
  }
229
241
  }
230
242
  }
@@ -240,7 +252,7 @@ macOS / Linux:
240
252
  "imgx": {
241
253
  "command": "npx",
242
254
  "args": ["--package=imgx-cli", "-y", "imgx-mcp"],
243
- "env": { "GEMINI_API_KEY": "your-api-key" }
255
+ "env": { "GEMINI_API_KEY": "your-key", "OPENAI_API_KEY": "your-key" }
244
256
  }
245
257
  }
246
258
  }
@@ -254,7 +266,7 @@ Windows:
254
266
  "imgx": {
255
267
  "command": "cmd",
256
268
  "args": ["/c", "npx", "--package=imgx-cli", "-y", "imgx-mcp"],
257
- "env": { "GEMINI_API_KEY": "your-api-key" }
269
+ "env": { "GEMINI_API_KEY": "your-key", "OPENAI_API_KEY": "your-key" }
258
270
  }
259
271
  }
260
272
  }
@@ -270,11 +282,13 @@ Config file location: `%APPDATA%\Claude\claude_desktop_config.json` (Windows) or
270
282
  [mcp_servers.imgx]
271
283
  command = "npx"
272
284
  args = ["--package=imgx-cli", "-y", "imgx-mcp"]
273
- env = { GEMINI_API_KEY = "your-api-key" }
285
+ env = { GEMINI_API_KEY = "your-key", OPENAI_API_KEY = "your-key" }
274
286
  ```
275
287
 
276
288
  The same `npx` pattern works with Cursor, Windsurf, Continue.dev, Cline, Zed, and other MCP-compatible tools. On Windows, use `cmd /c npx` instead of `npx` directly.
277
289
 
290
+ Only include the API keys for providers you want to use. At least one is required.
291
+
278
292
  ## Architecture
279
293
 
280
294
  imgx separates **model-independent** and **model-dependent** concerns:
@@ -302,12 +316,14 @@ Each provider declares its supported capabilities. The CLI dynamically enables o
302
316
  | `MULTIPLE_OUTPUTS` | Generate multiple images per request |
303
317
  | `REFERENCE_IMAGES` | Use reference images for guidance |
304
318
  | `PERSON_CONTROL` | Control person generation in output |
319
+ | `OUTPUT_FORMAT` | Choose output format (PNG, JPEG, WebP) |
305
320
 
306
321
  ### Current providers
307
322
 
308
323
  | Provider | Models | Capabilities |
309
324
  |----------|--------|-------------|
310
- | Gemini | `gemini-3-pro-image-preview`, `gemini-2.5-flash-image` | All 7 capabilities |
325
+ | Gemini | `gemini-3-pro-image-preview`, `gemini-2.5-flash-image` | All 7 base capabilities |
326
+ | OpenAI | `gpt-image-1` | Generate, edit, aspect ratio, multi-output, output format |
311
327
 
312
328
  ## Development
313
329
 
@@ -39210,8 +39210,9 @@ function getApiKeyFromEnv() {
39210
39210
 
39211
39211
  // build/core/storage.js
39212
39212
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
39213
- import { dirname, resolve as resolve2 } from "node:path";
39213
+ import { dirname, join as join2, resolve as resolve2 } from "node:path";
39214
39214
  import { randomUUID } from "node:crypto";
39215
+ import { homedir as homedir2 } from "node:os";
39215
39216
  var MIME_TO_EXT = {
39216
39217
  "image/png": ".png",
39217
39218
  "image/jpeg": ".jpg",
@@ -39224,9 +39225,17 @@ function readImageAsBase64(filePath) {
39224
39225
  const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
39225
39226
  return { data: buffer.toString("base64"), mimeType };
39226
39227
  }
39228
+ function fallbackOutputDir(outputDir) {
39229
+ if (outputDir)
39230
+ return outputDir;
39231
+ const configured = resolveDefault("outputDir");
39232
+ if (configured)
39233
+ return configured;
39234
+ return join2(homedir2(), "Pictures", "imgx");
39235
+ }
39227
39236
  function saveImage(image, outputPath, outputDir) {
39228
39237
  const ext = MIME_TO_EXT[image.mimeType] || ".png";
39229
- const filePath = outputPath ? resolve2(outputPath) : resolve2(outputDir || ".", `imgx-${randomUUID().slice(0, 8)}${ext}`);
39238
+ const filePath = outputPath ? resolve2(outputPath) : resolve2(fallbackOutputDir(outputDir), `imgx-${randomUUID().slice(0, 8)}${ext}`);
39230
39239
  mkdirSync2(dirname(filePath), { recursive: true });
39231
39240
  writeFileSync2(filePath, image.data);
39232
39241
  return filePath;
@@ -39243,6 +39252,7 @@ var Capability;
39243
39252
  Capability2["REFERENCE_IMAGES"] = "REFERENCE_IMAGES";
39244
39253
  Capability2["PERSON_CONTROL"] = "PERSON_CONTROL";
39245
39254
  Capability2["STYLE_CONTROL"] = "STYLE_CONTROL";
39255
+ Capability2["OUTPUT_FORMAT"] = "OUTPUT_FORMAT";
39246
39256
  })(Capability || (Capability = {}));
39247
39257
 
39248
39258
  // build/providers/gemini/capabilities.js
@@ -39375,7 +39385,8 @@ var OPENAI_PROVIDER_INFO = {
39375
39385
  Capability.TEXT_TO_IMAGE,
39376
39386
  Capability.ASPECT_RATIO,
39377
39387
  Capability.IMAGE_EDITING,
39378
- Capability.MULTIPLE_OUTPUTS
39388
+ Capability.MULTIPLE_OUTPUTS,
39389
+ Capability.OUTPUT_FORMAT
39379
39390
  ]),
39380
39391
  aspectRatios: ["1:1", "3:2", "2:3", "16:9", "9:16", "4:3", "3:4"],
39381
39392
  resolutions: ["1K", "2K", "4K"]
@@ -39456,7 +39467,8 @@ var OpenAIProvider = class {
39456
39467
  prompt: input.prompt,
39457
39468
  n: input.count || 1,
39458
39469
  size: mapSize(input.aspectRatio),
39459
- quality: mapQuality(input.resolution)
39470
+ quality: mapQuality(input.resolution),
39471
+ ...input.outputFormat ? { output_format: input.outputFormat } : {}
39460
39472
  })
39461
39473
  });
39462
39474
  const json = await response.json();
@@ -39467,7 +39479,7 @@ var OpenAIProvider = class {
39467
39479
  error: json.error?.message || `HTTP ${response.status}`
39468
39480
  };
39469
39481
  }
39470
- return this.parseResponse(json);
39482
+ return this.parseResponse(json, input.outputFormat);
39471
39483
  } catch (err) {
39472
39484
  const msg = err instanceof Error ? err.message : String(err);
39473
39485
  return { success: false, images: [], error: msg };
@@ -39484,7 +39496,8 @@ var OpenAIProvider = class {
39484
39496
  prompt: input.prompt,
39485
39497
  n: String(input.count || 1),
39486
39498
  size: mapSize(input.aspectRatio),
39487
- quality: mapQuality(input.resolution)
39499
+ quality: mapQuality(input.resolution),
39500
+ ...input.outputFormat ? { output_format: input.outputFormat } : {}
39488
39501
  };
39489
39502
  const { body, contentType: ct } = buildMultipart(fields, [
39490
39503
  {
@@ -39511,20 +39524,22 @@ var OpenAIProvider = class {
39511
39524
  error: json.error?.message || `HTTP ${response.status}`
39512
39525
  };
39513
39526
  }
39514
- return this.parseResponse(json);
39527
+ return this.parseResponse(json, input.outputFormat);
39515
39528
  } catch (err) {
39516
39529
  const msg = err instanceof Error ? err.message : String(err);
39517
39530
  return { success: false, images: [], error: msg };
39518
39531
  }
39519
39532
  }
39520
- parseResponse(json) {
39533
+ parseResponse(json, outputFormat) {
39534
+ const mimeMap = { png: "image/png", jpeg: "image/jpeg", webp: "image/webp" };
39535
+ const mimeType = mimeMap[outputFormat || "png"] || "image/png";
39521
39536
  const images = [];
39522
39537
  if (json.data) {
39523
39538
  for (const item of json.data) {
39524
39539
  if (item.b64_json) {
39525
39540
  images.push({
39526
39541
  data: Buffer.from(item.b64_json, "base64"),
39527
- mimeType: "image/png"
39542
+ mimeType
39528
39543
  });
39529
39544
  }
39530
39545
  }
@@ -39581,7 +39596,8 @@ async function runGenerate(provider, args) {
39581
39596
  prompt: args.prompt,
39582
39597
  aspectRatio: args.aspectRatio,
39583
39598
  count: args.count,
39584
- resolution: args.resolution
39599
+ resolution: args.resolution,
39600
+ outputFormat: args.outputFormat
39585
39601
  }, args.model);
39586
39602
  if (!result.success || result.images.length === 0) {
39587
39603
  fail(result.error || "Generation failed");
@@ -39609,7 +39625,8 @@ async function runEdit(provider, args) {
39609
39625
  inputImage: args.inputImage,
39610
39626
  prompt: args.prompt,
39611
39627
  aspectRatio: args.aspectRatio,
39612
- resolution: args.resolution
39628
+ resolution: args.resolution,
39629
+ outputFormat: args.outputFormat
39613
39630
  }, args.model);
39614
39631
  if (!result.success || result.images.length === 0) {
39615
39632
  fail(result.error || "Edit failed");
@@ -39760,7 +39777,7 @@ function showAll() {
39760
39777
  }
39761
39778
 
39762
39779
  // build/cli/index.js
39763
- var VERSION2 = "0.6.0";
39780
+ var VERSION2 = "0.7.0";
39764
39781
  var HELP = `imgx v${VERSION2} \u2014 AI image generation and editing CLI
39765
39782
 
39766
39783
  Commands:
@@ -39785,6 +39802,7 @@ Options:
39785
39802
  -a, --aspect-ratio <ratio> Aspect ratio (e.g., 16:9, 1:1)
39786
39803
  -n, --count <number> Number of images to generate
39787
39804
  -r, --resolution <size> Resolution: 1K, 2K, 4K
39805
+ -f, --format <type> Output format: png, jpeg, webp (OpenAI only)
39788
39806
  -m, --model <model> Model name
39789
39807
  --provider <name> Provider: gemini, openai (default: gemini)
39790
39808
  -d, --output-dir <dir> Output directory
@@ -39852,6 +39870,7 @@ function main() {
39852
39870
  "aspect-ratio": { type: "string", short: "a" },
39853
39871
  count: { type: "string", short: "n" },
39854
39872
  resolution: { type: "string", short: "r" },
39873
+ format: { type: "string", short: "f" },
39855
39874
  model: { type: "string", short: "m" },
39856
39875
  provider: { type: "string" },
39857
39876
  "output-dir": { type: "string", short: "d" },
@@ -39886,6 +39905,7 @@ function main() {
39886
39905
  outputDir: values["output-dir"] || resolveDefault("outputDir") || void 0,
39887
39906
  aspectRatio: values["aspect-ratio"] || resolveDefault("aspectRatio") || void 0,
39888
39907
  resolution: values.resolution || resolveDefault("resolution") || void 0,
39908
+ outputFormat: values.format || void 0,
39889
39909
  model,
39890
39910
  count: values.count ? parseInt(values.count, 10) : void 0
39891
39911
  };
@@ -52131,6 +52131,28 @@ function resolveApiKey(providerName) {
52131
52131
  const config2 = loadConfig();
52132
52132
  return config2.providers?.[providerName]?.apiKey;
52133
52133
  }
52134
+ function loadProjectConfig() {
52135
+ try {
52136
+ const raw = readFileSync(resolve(".imgxrc"), "utf-8");
52137
+ return JSON.parse(raw);
52138
+ } catch {
52139
+ return {};
52140
+ }
52141
+ }
52142
+ function resolveDefault(key) {
52143
+ const envMap = {
52144
+ provider: process.env.IMGX_PROVIDER,
52145
+ model: process.env.IMGX_MODEL,
52146
+ outputDir: process.env.IMGX_OUTPUT_DIR
52147
+ };
52148
+ if (envMap[key])
52149
+ return envMap[key];
52150
+ const project = loadProjectConfig();
52151
+ if (project.defaults?.[key])
52152
+ return project.defaults[key];
52153
+ const config2 = loadConfig();
52154
+ return config2.defaults?.[key];
52155
+ }
52134
52156
  function saveLastOutput(filePaths) {
52135
52157
  const dir = configDir();
52136
52158
  mkdirSync(dir, { recursive: true });
@@ -69226,8 +69248,9 @@ function getApiKeyFromEnv() {
69226
69248
 
69227
69249
  // build/core/storage.js
69228
69250
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
69229
- import { dirname, resolve as resolve2 } from "node:path";
69251
+ import { dirname, join as join2, resolve as resolve2 } from "node:path";
69230
69252
  import { randomUUID } from "node:crypto";
69253
+ import { homedir as homedir2 } from "node:os";
69231
69254
  var MIME_TO_EXT = {
69232
69255
  "image/png": ".png",
69233
69256
  "image/jpeg": ".jpg",
@@ -69240,9 +69263,17 @@ function readImageAsBase64(filePath) {
69240
69263
  const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
69241
69264
  return { data: buffer.toString("base64"), mimeType };
69242
69265
  }
69266
+ function fallbackOutputDir(outputDir) {
69267
+ if (outputDir)
69268
+ return outputDir;
69269
+ const configured = resolveDefault("outputDir");
69270
+ if (configured)
69271
+ return configured;
69272
+ return join2(homedir2(), "Pictures", "imgx");
69273
+ }
69243
69274
  function saveImage(image, outputPath, outputDir) {
69244
69275
  const ext = MIME_TO_EXT[image.mimeType] || ".png";
69245
- const filePath = outputPath ? resolve2(outputPath) : resolve2(outputDir || ".", `imgx-${randomUUID().slice(0, 8)}${ext}`);
69276
+ const filePath = outputPath ? resolve2(outputPath) : resolve2(fallbackOutputDir(outputDir), `imgx-${randomUUID().slice(0, 8)}${ext}`);
69246
69277
  mkdirSync2(dirname(filePath), { recursive: true });
69247
69278
  writeFileSync2(filePath, image.data);
69248
69279
  return filePath;
@@ -69259,6 +69290,7 @@ var Capability;
69259
69290
  Capability2["REFERENCE_IMAGES"] = "REFERENCE_IMAGES";
69260
69291
  Capability2["PERSON_CONTROL"] = "PERSON_CONTROL";
69261
69292
  Capability2["STYLE_CONTROL"] = "STYLE_CONTROL";
69293
+ Capability2["OUTPUT_FORMAT"] = "OUTPUT_FORMAT";
69262
69294
  })(Capability || (Capability = {}));
69263
69295
 
69264
69296
  // build/providers/gemini/capabilities.js
@@ -69391,7 +69423,8 @@ var OPENAI_PROVIDER_INFO = {
69391
69423
  Capability.TEXT_TO_IMAGE,
69392
69424
  Capability.ASPECT_RATIO,
69393
69425
  Capability.IMAGE_EDITING,
69394
- Capability.MULTIPLE_OUTPUTS
69426
+ Capability.MULTIPLE_OUTPUTS,
69427
+ Capability.OUTPUT_FORMAT
69395
69428
  ]),
69396
69429
  aspectRatios: ["1:1", "3:2", "2:3", "16:9", "9:16", "4:3", "3:4"],
69397
69430
  resolutions: ["1K", "2K", "4K"]
@@ -69472,7 +69505,8 @@ var OpenAIProvider = class {
69472
69505
  prompt: input.prompt,
69473
69506
  n: input.count || 1,
69474
69507
  size: mapSize(input.aspectRatio),
69475
- quality: mapQuality(input.resolution)
69508
+ quality: mapQuality(input.resolution),
69509
+ ...input.outputFormat ? { output_format: input.outputFormat } : {}
69476
69510
  })
69477
69511
  });
69478
69512
  const json2 = await response.json();
@@ -69483,7 +69517,7 @@ var OpenAIProvider = class {
69483
69517
  error: json2.error?.message || `HTTP ${response.status}`
69484
69518
  };
69485
69519
  }
69486
- return this.parseResponse(json2);
69520
+ return this.parseResponse(json2, input.outputFormat);
69487
69521
  } catch (err) {
69488
69522
  const msg = err instanceof Error ? err.message : String(err);
69489
69523
  return { success: false, images: [], error: msg };
@@ -69500,7 +69534,8 @@ var OpenAIProvider = class {
69500
69534
  prompt: input.prompt,
69501
69535
  n: String(input.count || 1),
69502
69536
  size: mapSize(input.aspectRatio),
69503
- quality: mapQuality(input.resolution)
69537
+ quality: mapQuality(input.resolution),
69538
+ ...input.outputFormat ? { output_format: input.outputFormat } : {}
69504
69539
  };
69505
69540
  const { body, contentType: ct } = buildMultipart(fields, [
69506
69541
  {
@@ -69527,20 +69562,22 @@ var OpenAIProvider = class {
69527
69562
  error: json2.error?.message || `HTTP ${response.status}`
69528
69563
  };
69529
69564
  }
69530
- return this.parseResponse(json2);
69565
+ return this.parseResponse(json2, input.outputFormat);
69531
69566
  } catch (err) {
69532
69567
  const msg = err instanceof Error ? err.message : String(err);
69533
69568
  return { success: false, images: [], error: msg };
69534
69569
  }
69535
69570
  }
69536
- parseResponse(json2) {
69571
+ parseResponse(json2, outputFormat) {
69572
+ const mimeMap = { png: "image/png", jpeg: "image/jpeg", webp: "image/webp" };
69573
+ const mimeType = mimeMap[outputFormat || "png"] || "image/png";
69537
69574
  const images = [];
69538
69575
  if (json2.data) {
69539
69576
  for (const item of json2.data) {
69540
69577
  if (item.b64_json) {
69541
69578
  images.push({
69542
69579
  data: Buffer.from(item.b64_json, "base64"),
69543
- mimeType: "image/png"
69580
+ mimeType
69544
69581
  });
69545
69582
  }
69546
69583
  }
@@ -69561,9 +69598,17 @@ function initOpenAI() {
69561
69598
  }
69562
69599
 
69563
69600
  // build/mcp/server.js
69601
+ function buildImageContent(images, paths, extra) {
69602
+ const content = [];
69603
+ for (const img of images) {
69604
+ content.push({ type: "image", data: img.data.toString("base64"), mimeType: img.mimeType });
69605
+ }
69606
+ content.push({ type: "text", text: JSON.stringify({ success: true, filePaths: paths, ...extra }) });
69607
+ return content;
69608
+ }
69564
69609
  var server = new McpServer({
69565
69610
  name: "imgx",
69566
- version: "0.6.0"
69611
+ version: "0.7.0"
69567
69612
  });
69568
69613
  initGemini();
69569
69614
  initOpenAI();
@@ -69583,6 +69628,7 @@ server.tool("generate_image", "Generate an image from a text prompt", {
69583
69628
  aspect_ratio: external_exports3.enum(["1:1", "2:3", "3:2", "3:4", "4:3", "9:16", "16:9"]).optional().describe("Aspect ratio"),
69584
69629
  resolution: external_exports3.enum(["1K", "2K", "4K"]).optional().describe("Output resolution"),
69585
69630
  count: external_exports3.number().int().min(1).max(4).optional().describe("Number of images"),
69631
+ output_format: external_exports3.enum(["png", "jpeg", "webp"]).optional().describe("Output format"),
69586
69632
  model: external_exports3.string().optional().describe("Model name"),
69587
69633
  provider: external_exports3.string().optional().describe("Provider name")
69588
69634
  }, async (args) => {
@@ -69592,7 +69638,8 @@ server.tool("generate_image", "Generate an image from a text prompt", {
69592
69638
  prompt: args.prompt,
69593
69639
  aspectRatio: args.aspect_ratio,
69594
69640
  resolution: args.resolution,
69595
- count: args.count
69641
+ count: args.count,
69642
+ outputFormat: args.output_format
69596
69643
  };
69597
69644
  const result = await prov.generate(input, args.model);
69598
69645
  if (!result.success || result.images.length === 0) {
@@ -69605,9 +69652,7 @@ server.tool("generate_image", "Generate an image from a text prompt", {
69605
69652
  paths.push(saved);
69606
69653
  }
69607
69654
  saveLastOutput(paths);
69608
- return {
69609
- content: [{ type: "text", text: JSON.stringify({ success: true, filePaths: paths }) }]
69610
- };
69655
+ return { content: buildImageContent(result.images, paths) };
69611
69656
  } catch (err) {
69612
69657
  const msg = err instanceof Error ? err.message : String(err);
69613
69658
  return { content: [{ type: "text", text: `Error: ${msg}` }] };
@@ -69620,6 +69665,7 @@ server.tool("edit_image", "Edit an existing image with text instructions", {
69620
69665
  output_dir: external_exports3.string().optional().describe("Output directory"),
69621
69666
  aspect_ratio: external_exports3.enum(["1:1", "2:3", "3:2", "3:4", "4:3", "9:16", "16:9"]).optional().describe("Aspect ratio"),
69622
69667
  resolution: external_exports3.enum(["1K", "2K", "4K"]).optional().describe("Output resolution"),
69668
+ output_format: external_exports3.enum(["png", "jpeg", "webp"]).optional().describe("Output format"),
69623
69669
  model: external_exports3.string().optional().describe("Model name"),
69624
69670
  provider: external_exports3.string().optional().describe("Provider name")
69625
69671
  }, async (args) => {
@@ -69634,7 +69680,8 @@ server.tool("edit_image", "Edit an existing image with text instructions", {
69634
69680
  prompt: args.prompt,
69635
69681
  inputImage: args.input,
69636
69682
  aspectRatio: args.aspect_ratio,
69637
- resolution: args.resolution
69683
+ resolution: args.resolution,
69684
+ outputFormat: args.output_format
69638
69685
  };
69639
69686
  const result = await prov.edit(input, args.model);
69640
69687
  if (!result.success || result.images.length === 0) {
@@ -69642,9 +69689,7 @@ server.tool("edit_image", "Edit an existing image with text instructions", {
69642
69689
  }
69643
69690
  const saved = saveImage(result.images[0], args.output, args.output_dir);
69644
69691
  saveLastOutput([saved]);
69645
- return {
69646
- content: [{ type: "text", text: JSON.stringify({ success: true, filePaths: [saved] }) }]
69647
- };
69692
+ return { content: buildImageContent(result.images, [saved]) };
69648
69693
  } catch (err) {
69649
69694
  const msg = err instanceof Error ? err.message : String(err);
69650
69695
  return { content: [{ type: "text", text: `Error: ${msg}` }] };
@@ -69656,6 +69701,7 @@ server.tool("edit_last", "Edit the last generated/edited image with new text ins
69656
69701
  output_dir: external_exports3.string().optional().describe("Output directory"),
69657
69702
  aspect_ratio: external_exports3.enum(["1:1", "2:3", "3:2", "3:4", "4:3", "9:16", "16:9"]).optional().describe("Aspect ratio"),
69658
69703
  resolution: external_exports3.enum(["1K", "2K", "4K"]).optional().describe("Output resolution"),
69704
+ output_format: external_exports3.enum(["png", "jpeg", "webp"]).optional().describe("Output format"),
69659
69705
  model: external_exports3.string().optional().describe("Model name"),
69660
69706
  provider: external_exports3.string().optional().describe("Provider name")
69661
69707
  }, async (args) => {
@@ -69676,7 +69722,8 @@ server.tool("edit_last", "Edit the last generated/edited image with new text ins
69676
69722
  prompt: args.prompt,
69677
69723
  inputImage: lastPaths[0],
69678
69724
  aspectRatio: args.aspect_ratio,
69679
- resolution: args.resolution
69725
+ resolution: args.resolution,
69726
+ outputFormat: args.output_format
69680
69727
  };
69681
69728
  const result = await prov.edit(input, args.model);
69682
69729
  if (!result.success || result.images.length === 0) {
@@ -69684,9 +69731,7 @@ server.tool("edit_last", "Edit the last generated/edited image with new text ins
69684
69731
  }
69685
69732
  const saved = saveImage(result.images[0], args.output, args.output_dir);
69686
69733
  saveLastOutput([saved]);
69687
- return {
69688
- content: [{ type: "text", text: JSON.stringify({ success: true, filePaths: [saved], inputUsed: lastPaths[0] }) }]
69689
- };
69734
+ return { content: buildImageContent(result.images, [saved], { inputUsed: lastPaths[0] }) };
69690
69735
  } catch (err) {
69691
69736
  const msg = err instanceof Error ? err.message : String(err);
69692
69737
  return { content: [{ type: "text", text: `Error: ${msg}` }] };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imgx-cli",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "mcpName": "io.github.somacoffeekyoto/imgx",
5
5
  "description": "AI image generation and editing CLI with provider abstraction",
6
6
  "type": "module",