Edit image
edit_imageEdit existing images using a text prompt and optional mask. Supports multiple OpenAI models. Results saved to disk with file paths returned.
Instructions
Edit one or more existing images using a text prompt and optional mask. Supports gpt-image-1.5, gpt-image-1, gpt-image-1-mini, and dall-e-2. Results are saved to disk and file paths are returned.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| prompt | Yes | Description of the desired edit. | |
| images | Yes | Absolute paths to input image files (png/jpg/webp). Up to 16 for GPT Image. | |
| mask | No | Absolute path to a mask image. Transparent pixels indicate areas to edit. Must match the first input image's dimensions. | |
| model | No | Model to use. DALL·E 3 does not support edits. Defaults to env DALLE_DEFAULT_MODEL or gpt-image-1.5. | |
| size | No | ||
| quality | No | ||
| n | No | ||
| background | No | ||
| output_format | No | ||
| output_compression | No | ||
| input_fidelity | No | GPT Image only. 'high' preserves more of the original image. | |
| user | No | ||
| output_dir | No | ||
| filename_prefix | No | ||
| return_image_content | No |
Implementation Reference
- src/tools/edit.ts:57-130 (handler)The main handler function `registerEditTool` which registers the 'edit_image' MCP tool via `server.registerTool(...)`. The handler reads inputs (prompt, images, mask), validates model supports edit, builds ImageEditParams, calls OpenAI images.edit(), saves results to disk, and returns file paths.
export function registerEditTool(server: McpServer): void { server.registerTool( "edit_image", { title: "Edit image", description: "Edit one or more existing images using a text prompt and optional mask. Supports gpt-image-1.5, gpt-image-1, gpt-image-1-mini, and dall-e-2. Results are saved to disk and file paths are returned.", inputSchema, }, async (args) => { try { const model = (args.model ?? defaultModel()) as Model; const info = MODELS[model]; if (!info.supportsEdit) { return errorContent(new Error(`Model '${model}' does not support image edits.`)); } const uploads = await Promise.all(args.images.map((p) => readImageAsUpload(p))); const params: ImageEditParams = { model, prompt: args.prompt, image: uploads.length === 1 ? uploads[0]! : uploads, }; if (args.mask) params.mask = await readImageAsUpload(args.mask); if (args.size) params.size = args.size as ImageEditParams["size"]; if (args.quality) params.quality = args.quality as ImageEditParams["quality"]; if (args.n !== undefined) params.n = args.n; if (args.user) params.user = args.user; if (info.family === "gpt-image") { if (args.background) params.background = args.background; if (args.output_format) params.output_format = args.output_format; if (args.output_compression !== undefined) params.output_compression = args.output_compression; if (args.input_fidelity) params.input_fidelity = args.input_fidelity; } else { params.response_format = "b64_json"; } const client = getOpenAI(); const response = await client.images.edit(params); const items = response.data ?? []; if (items.length === 0) { return errorContent(new Error("OpenAI returned no images.")); } const outDir = resolveOutputDir(args.output_dir); const seed = `${Date.now()}_edit_${args.prompt}`; const saved = await Promise.all( items.map(async (item, i) => { const extracted = await extractImage(item, response.output_format ?? args.output_format ?? null); return saveImage(extracted, outDir, args.filename_prefix ?? "edit", seed, i); }), ); const lines: string[] = [ `Edited ${saved.length} image${saved.length === 1 ? "" : "s"} with ${model}.`, `Source: ${args.images.join(", ")}${args.mask ? ` (mask: ${args.mask})` : ""}`, `Saved to: ${outDir}`, "", ...saved.map((s, i) => ` [${i}] ${s.path} (${s.mime}, ${s.bytes} bytes)`), ]; if (response.usage) { lines.push("", `Usage: ${JSON.stringify(response.usage)}`); } return { content: buildContent(lines.join("\n"), saved, args.return_image_content === true), }; } catch (err) { return errorContent(err); } }, ); } - src/tools/edit.ts:15-55 (schema)Zod input schema for the 'edit_image' tool, defining all parameters: prompt, images, mask, model, size, quality, n, background, output_format, output_compression, input_fidelity, user, output_dir, filename_prefix, return_image_content.
const inputSchema = { prompt: z.string().min(1).max(32000).describe("Description of the desired edit."), images: z .array(z.string()) .min(1) .max(16) .describe("Absolute paths to input image files (png/jpg/webp). Up to 16 for GPT Image."), mask: z .string() .optional() .describe( "Absolute path to a mask image. Transparent pixels indicate areas to edit. Must match the first input image's dimensions.", ), model: z .enum(["gpt-image-1.5", "gpt-image-1", "gpt-image-1-mini", "dall-e-2"]) .optional() .describe("Model to use. DALL·E 3 does not support edits. Defaults to env DALLE_DEFAULT_MODEL or gpt-image-1.5."), size: z .enum([ "auto", "1024x1024", "1536x1024", "1024x1536", "512x512", "256x256", ]) .optional(), quality: z.enum(["auto", "low", "medium", "high", "standard"]).optional(), n: z.number().int().min(1).max(10).optional(), background: z.enum(["transparent", "opaque", "auto"]).optional(), output_format: z.enum(["png", "jpeg", "webp"]).optional(), output_compression: z.number().int().min(0).max(100).optional(), input_fidelity: z .enum(["high", "low"]) .optional() .describe("GPT Image only. 'high' preserves more of the original image."), user: z.string().optional(), output_dir: z.string().optional(), filename_prefix: z.string().optional(), return_image_content: z.boolean().optional(), }; - src/server.ts:21-21 (registration)Registration call: `registerEditTool(server)` invoked during server creation, which wires the edit_image tool into the MCP server.
registerEditTool(server); - src/models.ts:19-19 (helper)The `supportsEdit` boolean field in the ModelInfo interface, used by the edit handler to reject models that don't support editing (e.g., dall-e-3).
supportsEdit: boolean; - src/models.ts:37-37 (helper)Example model config showing `supportsEdit: true` for gpt-image-1.5; similar entries exist for gpt-image-1, gpt-image-1-mini, and dall-e-2.
supportsEdit: true,