Skip to main content
Glama

gemini_generate_image

Generate images with Google Gemini AI while maintaining consistent styles across sessions using reference images and aspect ratio controls.

Instructions

Generate images using Gemini's image generation capabilities. Supports session-based image consistency for maintaining style/character across multiple generations.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
promptYesDescription of the image to generate
aspect_ratioNoAspect ratio for the generated image. Overrides session setting if provided.
output_pathNoOptional path where to save the generated image. If not provided, saves to ~/Documents/nanobanana_generated/
conversation_idNoSession ID for maintaining image history and consistency across generations
use_image_historyNoIf true, includes previous generated images from this session for style/character consistency
reference_imagesNoArray of file paths to reference images for style/character consistency
enable_google_searchNoEnable Google Search for real-world reference grounding

Implementation Reference

  • The primary handler for the 'gemini_generate_image' MCP tool, which orchestrates image generation by gathering context, preparing reference images, calling the Gemini API, and saving the resulting file.
    case "gemini_generate_image": {
      const {
        prompt,
        aspect_ratio,
        output_path,
        conversation_id = "default",
        use_image_history = false,
        reference_images = [],
      } = args as any;
    
      try {
        // 대화 컨텍스트 가져오기/생성
        const context = getOrCreateContext(conversation_id);
    
        // Validate directly passed aspect_ratio
        if (aspect_ratio && !VALID_ASPECT_RATIOS.includes(aspect_ratio as AspectRatio)) {
          return {
            content: [{
              type: "text",
              text: `Invalid aspect ratio: ${aspect_ratio}. Valid: ${VALID_ASPECT_RATIOS.join(", ")}`,
            }],
            isError: true,
          };
        }
    
        // Priority: direct param > session setting
        const effectiveAspectRatio = aspect_ratio ?? context.aspectRatio;
    
        // aspectRatio 필수 체크 (둘 다 없으면 에러)
        if (effectiveAspectRatio === null) {
          return {
            content: [{
              type: "text",
              text: `Error: Aspect ratio not specified. Either pass aspect_ratio parameter or call set_aspect_ratio first.\nValid ratios: ${VALID_ASPECT_RATIOS.join(", ")}`,
            }],
            isError: true,
          };
        }
    
        // contents 구성: 참조 이미지 + 히스토리 이미지 + 프롬프트
        const parts: GeminiImageRequestPart[] = [];
        const failedReferenceImages: Array<{ path: string; reason: string }> = [];
    
        // 1. 수동 지정 참조 이미지 추가
        if (reference_images && reference_images.length > 0) {
          for (const imgPath of reference_images) {
            try {
              let resolvedPath = imgPath;
              if (!path.isAbsolute(resolvedPath)) {
                resolvedPath = path.join(process.cwd(), resolvedPath);
              }
              const base64 = await imageToBase64(resolvedPath);
              parts.push({
                inlineData: {
                  mimeType: "image/png",
                  data: base64,
                },
              });
            } catch (error) {
              failedReferenceImages.push({
                path: imgPath,
                reason: error instanceof Error ? error.message : String(error),
              });
            }
          }
        }
    
        // 2. 히스토리 이미지 추가 (일관성 유지용)
        if (use_image_history && context.imageHistory.length > 0) {
          const recentImages = context.imageHistory.slice(-MAX_REFERENCE_IMAGES);
          for (const img of recentImages) {
            parts.push({
              inlineData: {
                mimeType: img.mimeType,
                data: img.base64Data,
              },
            });
          }
        }
    
        // 3. 프롬프트 추가 (히스토리 이미지가 있으면 일관성 유지 지시 추가)
        let finalPrompt = prompt;
        if (use_image_history && context.imageHistory.length > 0) {
          finalPrompt = `${prompt}\n\nIMPORTANT: Maintain visual consistency with the provided reference images (same style, character appearance, color palette).`;
        }
        parts.push({ text: finalPrompt });
    
        // REST API 직접 호출 (세션 모델 우선, 없으면 환경 변수 기본값)
        const effectiveModel = context.selectedModel ?? IMAGE_MODEL;
        const apiResponse = await callGeminiImageAPI(parts, effectiveAspectRatio, effectiveModel);
    
        if (apiResponse.error) {
          return {
            content: [{
              type: "text",
              text: `Image generation failed: ${apiResponse.error}\n${apiResponse.textResponse}`,
            }],
            isError: true,
          };
        }
    
        if (!apiResponse.imageData) {
          return {
            content: [{
              type: "text",
              text: `Image generation failed.\nPrompt: "${prompt}"\n` +
                    (apiResponse.textResponse ? `Model response: ${apiResponse.textResponse}` : 'No image returned from model'),
            }],
            isError: true,
          };
        }
    
        // Determine output path - always ensure PNG extension
        let finalPath = output_path;
        if (!finalPath) {
          const homeDir = os.homedir();
          const tempDir = path.join(homeDir, 'Documents', 'nanobanana_generated');
          await fs.mkdir(tempDir, { recursive: true });
          const filename = `generated_${Date.now()}.png`;
          finalPath = path.join(tempDir, filename);
        } else {
          if (!path.isAbsolute(finalPath)) {
            finalPath = path.join(process.cwd(), finalPath);
          }
          if (!finalPath.toLowerCase().endsWith('.png')) {
            finalPath = finalPath.replace(/\.[^/.]+$/, '') + '.png';
          }
        }
    
        // Save image
        const buffer = Buffer.from(apiResponse.imageData, 'base64');
        await saveImageFromBuffer(buffer, finalPath);
    
        // 생성된 이미지를 히스토리에 저장
        addImageToHistory(context, {
          id: generateImageId(),
          filePath: finalPath,
          base64Data: apiResponse.imageData,
          mimeType: "image/png",
          prompt: prompt,
          timestamp: Date.now(),
          type: "generated",
        });
    
        let successText = `Image generated successfully!\n` +
              `Prompt: "${prompt}"\n` +
              `Saved to: ${finalPath}\n` +
              `Session: ${conversation_id} (history: ${context.imageHistory.length} images)`;
    
        if (failedReferenceImages.length > 0) {
          successText += `\n\nWarning: ${failedReferenceImages.length} reference image(s) could not be loaded:\n`;
          successText += failedReferenceImages.map(f => `  - ${f.path}: ${f.reason}`).join('\n');
        }
    
        if (apiResponse.textResponse) {
          successText += `\n\nModel response: ${apiResponse.textResponse}`;
        }
    
        return {
          content: [
            ...(RETURN_PATH_ONLY ? [] : [{ type: "image", data: apiResponse.imageData, mimeType: "image/png" }]),
            { type: "text", text: successText },

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pistachiomatt/nanobanana-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server