@falldownthesystem/imagen-mcp 0.2.0 → 0.3.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/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # imagen-mcp
2
2
 
3
- Minimal MCP server for OpenAI GPT Image 2.5 and Krea 3. Generate and edit tools for each provider.
3
+ Minimal MCP server for OpenAI GPT Image 2.6 and Krea 3. Generate and edit tools for each provider.
4
4
 
5
5
  | Provider | Tools | Models |
6
6
  |---|---|---|
7
- | OpenAI | `generate_image`, `edit_image` | `flare` (default, `gpt-image-2.5-flare`): fast, general purpose. `sunburst` (`gpt-image-2.5-sunburst`): slower, higher precision. |
7
+ | OpenAI | `generate_image`, `edit_image` | `flare` (default, `gpt-image-2.6-flare`): fast, good layouts. `sunburst` (`gpt-image-2.6-sunburst`): more detail, more consistent across edits. |
8
8
  | Krea | `krea_generate_image`, `krea_edit_image` | Krea 3: fast, instruction-following editor. Early-access beta. |
9
9
 
10
10
  The server registers a provider's tools only when that provider's API key is set.
@@ -66,22 +66,26 @@ claude mcp add imagen -e OPENAI_API_KEY=sk-... -e KREA_API_KEY=... -- npx -y @fa
66
66
 
67
67
  ### `generate_image`
68
68
 
69
- Text to image. Required: `prompt`.
69
+ Text to image, animated GIF, or SVG. Required: `prompt`.
70
70
 
71
71
  | Option | Values | Default | Notes |
72
72
  |---|---|---|---|
73
+ | `output_format` | `png`, `jpeg`, `webp`, `gif`, `svg` | `png` | Also sets the file extension. `gif` is an animation: describe the subject, the motion, and how it loops, and `size` sets each frame. `svg` is a static vector file (paths, shapes, colors); it needs `n` = 1. `gif` and `svg` reject `output_compression`. |
74
+ | `frame_count` | 2 to 16 | 9 | `gif` only. Frames, frame size, and `n` share one pixel budget, so use a small `size` for many frames. |
75
+ | `frame_duration_ms` | 20 to 2000 | 200 | `gif` only. Delay per frame, rounded to 10 ms. |
73
76
  | `moderation` | `auto`, `low` | `low` | OpenAI content filter strictness. `low` refuses fewer borderline prompts; OpenAI usage policies still apply. The API default is `auto`; this tool sends `low` unless set. |
74
77
 
75
78
  ### `edit_image`
76
79
 
77
- Reference images to image. Required: `prompt`, `images`. The model regenerates the whole picture guided by the references and the prompt. Without a mask, the prompt alone decides what changes.
80
+ Reference images to image. Required: `prompt`, `images`. The model regenerates the whole picture guided by the references and the prompt. Without a mask or markup, the prompt alone decides what changes. Output is `png`, `jpeg`, or `webp`; the edit endpoint does not make GIF or SVG.
78
81
 
79
82
  | Option | Values | Default | Notes |
80
83
  |---|---|---|---|
81
84
  | `images` | 1 to 16 file paths | required | `png`, `jpeg`, or `webp`. The first image is the main subject; extra images supply elements to combine, such as a logo or a style reference. Relative paths resolve against the server working directory. |
82
- | `mask` | file path | none | PNG with an alpha channel, same width and height as the first image. Fully transparent pixels mark the region the model may repaint; opaque pixels mark what to keep. Use for inpainting one area or filling an extended canvas. It is guidance, not a hard pixel lock. |
85
+ | `mask` | file path | none | PNG with an alpha channel, same width and height as the first image. Fully transparent pixels mark the region the model may repaint; opaque pixels mark what to keep. Use for inpainting one area or filling an extended canvas. It is guidance, not a hard pixel lock. Cannot be combined with `markup`. |
86
+ | `markup` | file path | none | Transparent PNG annotation layer, same width and height as the first image, at most 50 MB. Draw circles, arrows, or scribbles where the change goes, and say in the prompt what to change and to remove the marks. Applies to the first image; other images stay references. Cannot be combined with `mask`. |
83
87
 
84
- There is no `input_fidelity` option. The GPT Image 2.5 models always process reference images at high fidelity, and the API rejects the parameter. Faces, logos, text, and textures from the references are preserved by default.
88
+ There is no `input_fidelity` option. The GPT Image 2.6 models always process reference images at high fidelity, and the API rejects the parameter. Faces, logos, text, and textures from the references are preserved by default.
85
89
 
86
90
  ### Shared options
87
91
 
@@ -90,18 +94,19 @@ Only the options you set are sent to the API. The default column shows what appl
90
94
  | Option | Values | Default | Notes |
91
95
  |---|---|---|---|
92
96
  | `prompt` | text, up to 32000 chars | required | |
93
- | `model` | `flare`, `sunburst` | `flare` | `flare` (`gpt-image-2.5-flare`) is fast and general purpose. `sunburst` (`gpt-image-2.5-sunburst`) is slower with higher precision. Same token price. `IMAGEN_DEFAULT_MODEL` env changes the default. |
97
+ | `model` | `flare`, `sunburst` | `flare` | `flare` (`gpt-image-2.6-flare`) is fast and lays out posters and product shots well. `sunburst` (`gpt-image-2.6-sunburst`) gives more detail and keeps characters and products more consistent across edits at `medium` or `high` quality. Same token price. `IMAGEN_DEFAULT_MODEL` env changes the default. |
94
98
  | `size` | `auto`, `1024x1024`, `1536x1024`, `1024x1536`, `WIDTHxHEIGHT` | `auto` | Custom sizes: multiples of 16, ratio 1:3 to 3:1, long edge up to 3840. Overrides `aspect_ratio`. |
95
99
  | `aspect_ratio` | `1:1`, `3:2`, `2:3`, `4:3`, `3:4`, `16:9`, `9:16`, `21:9`, `9:21` | none | Combined with `long_edge` to compute a size. |
96
100
  | `long_edge` | 256 to 3840 | 1536 | Only used with `aspect_ratio`. |
97
101
  | `quality` | `low`, `medium`, `high`, `xhigh`, `max`, `auto` | `auto` | Higher tiers add detail and cost more output tokens. `auto` lets the model choose and may pick an expensive tier. Set it explicitly to control cost. |
102
+ | `complexity` | `low`, `medium`, `high` | `high` | How much the model plans the scene before it renders. Separate from `quality`, no extra fee. `low` is faster and suits focused edits such as a new background or sign. `high` helps with dense text, complex layouts, and many objects, but can add latency and unwanted changes. |
98
103
  | `background` | `transparent`, `opaque`, `auto` | `auto` | `transparent` gives an alpha background for stickers, logos, and cut-outs. Needs `png` or `webp`. |
99
- | `output_format` | `png`, `jpeg`, `webp` | `png` | Also sets the file extension. |
104
+ | `output_format` | `png`, `jpeg`, `webp` | `png` | Also sets the file extension. `generate_image` also takes `gif` and `svg`. |
100
105
  | `output_compression` | 0 to 100 | 100 | `jpeg` and `webp` only. Works like JPEG quality: lower is smaller with more artifacts. |
101
106
  | `n` | 1 to 10 | 1 | Number of separate variations generated from the same prompt in one call. Each costs its own output tokens. |
102
107
  | `output_dir` | directory path | `IMAGEN_OUTPUT_DIR` env, else `./output` | Relative to the server working directory. |
103
108
  | `file_name` | base name without extension | timestamp plus a slug of the prompt | With `n` greater than 1, a `-1`, `-2` suffix is added. |
104
- | `return_image` | boolean | `false` | Also return each image inline as MCP image content, so the client shows it and the model can inspect it. Adds about 1.4 MB of context per 1024x1024 png. The file is saved to disk either way. |
109
+ | `return_image` | boolean | `false` | Also return each image inline as MCP image content, so the client shows it and the model can inspect it. Adds about 1.4 MB of context per 1024x1024 png. SVG files are never returned inline. The file is saved to disk either way. |
105
110
 
106
111
  The result lists the saved file paths, the model, the size, and token usage.
107
112
 
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ if (!openaiKey && !kreaKey) {
17
17
  console.error("imagen-mcp: set OPENAI_API_KEY, KREA_API_KEY, or both.");
18
18
  process.exit(1);
19
19
  }
20
- const server = new McpServer({ name: "imagen-mcp", version: "0.2.0" });
20
+ const server = new McpServer({ name: "imagen-mcp", version: "0.3.0" });
21
21
  // Tools are registered per configured provider so clients never see a tool that can only fail.
22
22
  if (openaiKey)
23
23
  registerOpenAITools(server);
package/dist/openai.js CHANGED
@@ -18,20 +18,51 @@ const outputSchema = z.object({
18
18
  })
19
19
  .optional(),
20
20
  });
21
+ const generateInput = {
22
+ ...commonInput,
23
+ output_format: z
24
+ .enum(["png", "jpeg", "webp", "gif", "svg"])
25
+ .optional()
26
+ .describe("Also the file extension. gif = animation (size is per frame). svg = static vector (n must be 1). gif/svg ignore output_compression. Default png."),
27
+ frame_count: z.number().int().min(2).max(16).optional().describe("gif only: frames per animation. Keep size small for many frames. Default 9."),
28
+ frame_duration_ms: z
29
+ .number()
30
+ .int()
31
+ .min(20)
32
+ .max(2000)
33
+ .optional()
34
+ .describe("gif only: delay per frame in ms, rounded to 10. Default 200."),
35
+ moderation: z
36
+ .enum(["auto", "low"])
37
+ .optional()
38
+ .describe("OpenAI content filter. low refuses fewer borderline prompts. Default low."),
39
+ };
21
40
  function apiParams(input) {
22
41
  return {
23
42
  model: resolveModel(input.model),
24
43
  prompt: input.prompt,
25
44
  size: resolveSize(input),
26
45
  quality: input.quality,
46
+ complexity: input.complexity,
27
47
  background: input.background,
28
- output_format: input.output_format,
29
48
  output_compression: input.output_compression,
30
49
  n: input.n,
31
50
  };
32
51
  }
33
- function saveResult(result, input, model, size) {
34
- const ext = input.output_format ?? "png";
52
+ // The API rejects these combinations, so fail before spending a request on them.
53
+ function checkGenerateFormat(input) {
54
+ const format = input.output_format ?? "png";
55
+ if (format !== "gif" && (input.frame_count !== undefined || input.frame_duration_ms !== undefined)) {
56
+ throw new Error("frame_count and frame_duration_ms need output_format gif.");
57
+ }
58
+ if ((format === "gif" || format === "svg") && input.output_compression !== undefined) {
59
+ throw new Error(`output_compression is not supported with output_format ${format}.`);
60
+ }
61
+ if (format === "svg" && (input.n ?? 1) !== 1) {
62
+ throw new Error("output_format svg needs n = 1.");
63
+ }
64
+ }
65
+ function saveResult(result, input, ext, model, size) {
35
66
  const images = (result.data ?? []).map((image, i) => {
36
67
  if (!image.b64_json)
37
68
  throw new Error(`Image ${i + 1} has no b64_json payload.`);
@@ -49,24 +80,25 @@ export function registerOpenAITools(server) {
49
80
  const client = new OpenAI();
50
81
  server.registerTool("generate_image", {
51
82
  title: "Generate image",
52
- description: "Generate images from a text prompt with OpenAI GPT Image 2.5. Saves files to disk and returns paths and token usage.",
53
- inputSchema: z.object({
54
- ...commonInput,
55
- moderation: z
56
- .enum(["auto", "low"])
57
- .optional()
58
- .describe("OpenAI content filter. low refuses fewer borderline prompts. Default low."),
59
- }),
83
+ description: "Generate images, animated GIFs, or SVG vector graphics from a text prompt with OpenAI GPT Image 2.6. For a GIF, describe the subject, the motion, and how it loops. Saves files to disk and returns paths and token usage.",
84
+ inputSchema: z.object(generateInput),
60
85
  outputSchema,
61
86
  annotations,
62
87
  }, async (input) => {
88
+ checkGenerateFormat(input);
63
89
  const params = apiParams(input);
64
- const result = await client.images.generate({ ...params, moderation: input.moderation ?? "low" });
65
- return saveResult(result, input, params.model, params.size);
90
+ const result = await client.images.generate({
91
+ ...params,
92
+ output_format: input.output_format,
93
+ frame_count: input.frame_count,
94
+ frame_duration_ms: input.frame_duration_ms,
95
+ moderation: input.moderation ?? "low",
96
+ });
97
+ return saveResult(result, input, input.output_format ?? "png", params.model, params.size);
66
98
  });
67
99
  server.registerTool("edit_image", {
68
100
  title: "Edit image",
69
- description: "Edit, restyle, or combine 1-16 reference images with a text prompt using OpenAI GPT Image 2.5. The whole picture is regenerated; without a mask the prompt decides what changes. Reference details are always kept at high fidelity (no input_fidelity option). Saves files to disk and returns paths and token usage.",
101
+ description: "Edit, restyle, or combine 1-16 reference images with a text prompt using OpenAI GPT Image 2.6. The whole picture is regenerated; without a mask or markup the prompt decides what changes. Reference details are always kept at high fidelity (no input_fidelity option). Output is png, jpeg, or webp only. Saves files to disk and returns paths and token usage.",
70
102
  inputSchema: z.object({
71
103
  ...commonInput,
72
104
  images: z
@@ -77,17 +109,25 @@ export function registerOpenAITools(server) {
77
109
  mask: z
78
110
  .string()
79
111
  .optional()
80
- .describe("Path of a PNG with alpha, same size as first image. Transparent pixels = region to repaint (guidance, not a hard lock). Default none."),
112
+ .describe("Path of a PNG with alpha, same size as first image. Transparent pixels = region to repaint (guidance, not a hard lock). Not with markup. Default none."),
113
+ markup: z
114
+ .string()
115
+ .optional()
116
+ .describe("Path of a transparent PNG annotation layer, same size as first image, with circles, arrows, or scribbles that mark where to change. The prompt says what to change and to remove the marks. Max 50 MB. Not with mask. Default none."),
81
117
  }),
82
118
  outputSchema,
83
119
  annotations,
84
120
  }, async (input) => {
121
+ if (input.mask && input.markup)
122
+ throw new Error("Use mask or markup, not both.");
85
123
  const params = apiParams(input);
86
124
  const result = await client.images.edit({
87
125
  ...params,
126
+ output_format: input.output_format,
88
127
  image: await Promise.all(input.images.map(loadImage)),
89
128
  mask: input.mask ? await loadImage(input.mask) : undefined,
129
+ markup: input.markup ? await loadImage(input.markup) : undefined,
90
130
  });
91
- return saveResult(result, input, params.model, params.size);
131
+ return saveResult(result, input, input.output_format ?? "png", params.model, params.size);
92
132
  });
93
133
  }
package/dist/output.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import * as z from "zod/v4";
4
- export const MIME = { png: "image/png", jpeg: "image/jpeg", webp: "image/webp" };
4
+ // svg is left out on purpose: MCP image content is raster, so saved SVGs are returned as file paths only.
5
+ export const MIME = { png: "image/png", jpeg: "image/jpeg", webp: "image/webp", gif: "image/gif" };
5
6
  export const outputInput = {
6
7
  output_dir: z.string().optional().describe("Save directory. Default IMAGEN_OUTPUT_DIR env or ./output (relative to server cwd)."),
7
8
  file_name: z.string().optional().describe("Base name without extension. Default timestamp + prompt slug."),
package/dist/params.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as z from "zod/v4";
2
2
  import { outputInput } from "./output.js";
3
3
  export const MODELS = {
4
- flare: "gpt-image-2.5-flare",
5
- sunburst: "gpt-image-2.5-sunburst",
4
+ flare: "gpt-image-2.6-flare",
5
+ sunburst: "gpt-image-2.6-sunburst",
6
6
  };
7
7
  const ASPECT_RATIOS = {
8
8
  "1:1": 1,
@@ -29,7 +29,7 @@ export const commonInput = {
29
29
  model: z
30
30
  .enum(["flare", "sunburst"])
31
31
  .optional()
32
- .describe("flare = fast, general purpose. sunburst = slower, higher precision. Same price. Default flare."),
32
+ .describe("flare = fast, good layouts. sunburst = more detail, more consistent across edits at medium/high quality. Same price. Default flare."),
33
33
  size: z
34
34
  .string()
35
35
  .regex(/^(auto|\d+x\d+)$/)
@@ -50,6 +50,10 @@ export const commonInput = {
50
50
  .enum(["low", "medium", "high", "xhigh", "max", "auto"])
51
51
  .optional()
52
52
  .describe("Higher = more detail, more output tokens. auto lets the model choose (may be costly). Default auto."),
53
+ complexity: z
54
+ .enum(["low", "medium", "high"])
55
+ .optional()
56
+ .describe("How much the model plans the scene before rendering; separate from quality, no extra fee. low = faster, suits focused edits. high = dense text, complex layouts, many objects. Default high."),
53
57
  background: z
54
58
  .enum(["transparent", "opaque", "auto"])
55
59
  .optional()
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@falldownthesystem/imagen-mcp",
3
- "version": "0.2.0",
4
- "description": "Minimal MCP server for OpenAI GPT Image 2.5 (flare and sunburst) and Krea 3: generate and edit images",
3
+ "version": "0.3.0",
4
+ "description": "Minimal MCP server for OpenAI GPT Image 2.6 (flare and sunburst) and Krea 3: generate and edit images",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "imagen-mcp": "dist/index.js"
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@modelcontextprotocol/server": "^2.3.1",
41
- "openai": "^7.34.0",
41
+ "openai": "^7.51.0",
42
42
  "zod": "^4.6.7"
43
43
  },
44
44
  "devDependencies": {