generate_image
Create images from text descriptions using Google Gemini AI, with options for aspect ratios, visual references, styles, and watermarks to generate custom visual content.
Instructions
Create a new image using Google Gemini AI from a text description, optionally providing reference images to guide the result. Use the edit_image tool when you need to modify an existing asset.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| description | Yes | Detailed description of the image to generate. For better social media results, include details about colors, style and composition. | |
| images | No | Optional array of image file paths to use as visual context (absolute or relative). | |
| watermarkPosition | No | Optional watermark position when using `watermarkPath`. | bottom-right |
| aspectRatio | No | Aspect ratio preset (square/landscape/portrait). | square |
| style | No | Additional style for the image (optional). Examples: "minimalist", "colorful", "professional", "artistic" | |
| outputPath | No | Path where to save the image (optional). If not specified, saves in current directory. Can be a folder or complete path with filename. | |
| watermarkPath | No | Path to watermark image file to overlay in a corner (optional) |
Implementation Reference
- src/tools/generateImage.ts:53-85 (handler)The main execution handler for the 'generate_image' tool. Validates arguments, generates image data using GeminiService, saves the image using ImageService, and returns the file path.export async function handleGenerateImage( args: GenerateImageArgs, geminiService: GeminiService, imageService: ImageService ) { if (!args.description || !args.description.trim()) { throw invalidParams('Description is required to generate an image'); } try { const imageData = await geminiService.generateImage(args); const filePath = await imageService.saveImage(imageData, { outputPath: args.outputPath, description: args.description, watermarkPath: args.watermarkPath, watermarkPosition: args.watermarkPosition }); return { content: [ { type: 'text', text: filePath, }, ], }; } catch (error) { throw ensureMcpError(error, ErrorCode.InternalError, 'Failed to generate image', { stage: 'generate_image.tool', }); } }
- src/tools/generateImage.ts:12-50 (schema)The input schema definition for the 'generate_image' tool, defining parameters like description, images, aspectRatio, style, etc.inputSchema: { type: 'object', properties: { description: { type: 'string', description: 'Detailed description of the image to generate. For better social media results, include details about colors, style and composition.', }, images: { type: 'array', items: { type: 'string' }, description: 'Optional array of image file paths to use as visual context (absolute or relative).', }, watermarkPosition: { type: 'string', enum: ['top-left', 'top-right', 'bottom-left', 'bottom-right'], description: 'Optional watermark position when using `watermarkPath`.', default: 'bottom-right', }, aspectRatio: { type: 'string', enum: ['square', 'landscape', 'portrait'], description: 'Aspect ratio preset (square/landscape/portrait).', default: 'square', }, style: { type: 'string', description: 'Additional style for the image (optional). Examples: "minimalist", "colorful", "professional", "artistic"', }, outputPath: { type: 'string', description: 'Path where to save the image (optional). If not specified, saves in current directory. Can be a folder or complete path with filename.', }, watermarkPath: { type: 'string', description: 'Path to watermark image file to overlay in a corner (optional)', }, }, required: ['description'], },
- src/index.ts:58-62 (registration)Registration of the 'generate_image' tool in the MCP server's listTools request handler, where the tools array includes generateImageTool.this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [generateImageTool, editImageTool], }; });
- src/index.ts:66-69 (registration)Dispatch/handling registration in the MCP server's callTool request handler, routing 'generate_image' calls to the handleGenerateImage function.if (request.params.name === 'generate_image') { const args = request.params.arguments as unknown as GenerateImageArgs; return await handleGenerateImage(args, this.geminiService, this.imageService); }
- src/services/gemini.ts:61-124 (helper)Core helper function in GeminiService that constructs the prompt, assembles image parts, calls Google Gemini API for image generation, and extracts the base64 image data.private async _generateImageInternal(args: GenerateImageArgs, helperPath: string | null): Promise<ImageData> { // Build optimized prompt for image generation let fullPrompt = `${args.description}`; // Add style if specified if (args.style) { fullPrompt += ` The style should be ${args.style}.`; } if (helperPath) { fullPrompt += '. Use the white image only as a guide for the aspect ratio.'; } const model = this.genAI.getGenerativeModel({ model: 'gemini-3-pro-image-preview', safetySettings: this.getSafetySettings() }); // If images are provided as context, attach them as inline parts const parts: any[] = [{ text: fullPrompt }]; if (args.images) { for (const userImage of args.images) { parts.push(await toInlinePart(userImage)); } } if (helperPath) { parts.push(await toInlinePart(helperPath)); } let response; try { response = await model.generateContent(parts); } catch (error) { throw ensureMcpError(error, ErrorCode.InternalError, 'Gemini image generation request failed', { stage: 'GeminiService.generateContent', }); } // Extract image from response const candidate = response.response.candidates?.[0]; if (!candidate?.content?.parts) { const finishReason = candidate?.finishReason ?? 'unknown'; throw internalError(`Gemini finish reason: ${String(finishReason)}`, { reason: 'emptyCandidate', finishReason, }); } for (const part of candidate.content.parts) { if (part.inlineData?.data && part.inlineData?.mimeType) { return { base64: part.inlineData.data, mimeType: part.inlineData.mimeType, }; } } throw internalError('Gemini response did not contain image data', { reason: 'missingInlineData', }); }