Skip to main content
Glama

edit_image

Modify existing images using text prompts and optional reference images. Change specific elements, apply styles, or adjust composition while maintaining the original file structure.

Instructions

Edit a SPECIFIC existing image file, optionally using additional reference images. Use this when you have the exact file path of an image to modify.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
aspectRatioNoOptional aspect ratio for the edited image. Default is 1:1 (1024ร—1024).
imagePathYesFull file path to the main image file to edit
promptYesText describing the modifications to make to the existing image
referenceImagesNoOptional array of file paths to additional reference images to use during editing (e.g., for style transfer, adding elements, etc.)

Implementation Reference

  • The primary handler function that implements the 'edit_image' tool logic. It reads the input image, optionally reference images, constructs a prompt for the OpenRouter Gemini model, generates the edited image, saves it to disk, and returns the result with embedded image data.
    private async editImage(request: CallToolRequest): Promise<CallToolResult> {
      if (!this.ensureConfigured()) {
        throw new McpError(ErrorCode.InvalidRequest, "OpenRouter API token not configured. Use configure_openrouter_token first.");
      }
    
      const { imagePath, prompt, referenceImages, aspectRatio } = request.params.arguments as { 
        imagePath: string; 
        prompt: string; 
        referenceImages?: string[];
        aspectRatio?: AspectRatio;
      };
      
      try {
        // Prepare the main image
        const imageBuffer = await fs.readFile(imagePath);
        const mimeType = this.getMimeType(imagePath);
        const imageBase64 = imageBuffer.toString('base64');
        
        // Build content array for the message
        const contentParts: any[] = [];
        
        // Add the main image
        contentParts.push({
          type: "image_url",
          image_url: {
            url: `data:${mimeType};base64,${imageBase64}`,
          }
        });
        
        // Add reference images if provided
        if (referenceImages && referenceImages.length > 0) {
          for (const refPath of referenceImages) {
            try {
              const refBuffer = await fs.readFile(refPath);
              const refMimeType = this.getMimeType(refPath);
              const refBase64 = refBuffer.toString('base64');
              
              contentParts.push({
                type: "image_url",
                image_url: {
                  url: `data:${refMimeType};base64,${refBase64}`,
                }
              });
            } catch (error) {
              // Continue with other images, don't fail the entire operation
              continue;
            }
          }
        }
        
        // Add the text prompt
        contentParts.push({
          type: "text",
          text: prompt,
        });
        
        // Prepare the request payload
        const requestPayload: any = {
          model: "google/gemini-2.5-flash-image",
          messages: [
            {
              role: "user",
              content: contentParts,
            }
          ],
          modalities: ["image", "text"],
        };
    
        // Add aspect ratio if provided
        if (aspectRatio) {
          requestPayload.image_config = {
            aspect_ratio: aspectRatio,
          };
        }
    
        const response = await this.openai!.chat.completions.create(requestPayload);
        
        // Process response
        const content: any[] = [];
        const savedFiles: string[] = [];
        let textContent = "";
        
        // Get appropriate save directory
        const imagesDir = this.getImagesDirectory();
        await fs.mkdir(imagesDir, { recursive: true, mode: 0o755 });
        
        const message = response.choices[0]?.message;
        
        if (message) {
          // Process text content
          if (message.content && typeof message.content === 'string') {
            textContent = message.content;
          }
          
          // Process images
          if ((message as any).images && Array.isArray((message as any).images)) {
            for (const imageData of (message as any).images) {
              const imageUrl = imageData.image_url?.url;
              
              if (imageUrl && imageUrl.startsWith('data:image/')) {
                // Extract base64 data from data URL
                const matches = imageUrl.match(/^data:image\/(\w+);base64,(.+)$/);
                
                if (matches && matches[2]) {
                  const base64Data = matches[2];
                  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
                  const randomId = Math.random().toString(36).substring(2, 8);
                  const fileName = `edited-${timestamp}-${randomId}.png`;
                  const filePath = path.join(imagesDir, fileName);
                  
                  const imageBuffer = Buffer.from(base64Data, 'base64');
                  await fs.writeFile(filePath, imageBuffer);
                  savedFiles.push(filePath);
                  this.lastImagePath = filePath;
                  
                  // Add image to MCP response
                  content.push({
                    type: "image",
                    data: base64Data,
                    mimeType: "image/png",
                  });
                }
              }
            }
          }
        }
        
        // Build response
        let statusText = `๐ŸŽจ Image edited with OpenRouter!\n\nOriginal: ${imagePath}\nEdit prompt: "${prompt}"`;
        
        if (aspectRatio) {
          statusText += `\nAspect Ratio: ${aspectRatio}`;
        }
        
        if (referenceImages && referenceImages.length > 0) {
          statusText += `\n\nReference images used:\n${referenceImages.map(f => `- ${f}`).join('\n')}`;
        }
        
        if (textContent) {
          statusText += `\n\nDescription: ${textContent}`;
        }
        
        if (savedFiles.length > 0) {
          statusText += `\n\n๐Ÿ“ Edited image saved to:\n${savedFiles.map(f => `- ${f}`).join('\n')}`;
          statusText += `\n\n๐Ÿ’ก View the edited image by:`;
          statusText += `\n1. Opening the file at the path above`;
          statusText += `\n2. Clicking on "Called edit_image" in Cursor to expand the MCP call details`;
          statusText += `\n\n๐Ÿ”„ To continue editing, use: continue_editing`;
          statusText += `\n๐Ÿ“‹ To check current image info, use: get_last_image_info`;
        } else {
          statusText += `\n\nNote: No edited image was generated.`;
          statusText += `\n\n๐Ÿ’ก Tip: Try running the command again - sometimes the first call needs to warm up the model.`;
        }
        
        content.unshift({
          type: "text",
          text: statusText,
        });
        
        return { content };
        
      } catch (error) {
        throw new McpError(
          ErrorCode.InternalError,
          `Failed to edit image: ${error instanceof Error ? error.message : String(error)}`
        );
      }
    }
  • JSON schema defining the input parameters for the 'edit_image' tool, including required imagePath and prompt, optional referenceImages and aspectRatio.
    inputSchema: {
      type: "object",
      properties: {
        imagePath: {
          type: "string",
          description: "Full file path to the main image file to edit",
        },
        prompt: {
          type: "string",
          description: "Text describing the modifications to make to the existing image",
        },
        referenceImages: {
          type: "array",
          items: {
            type: "string"
          },
          description: "Optional array of file paths to additional reference images to use during editing (e.g., for style transfer, adding elements, etc.)",
        },
        aspectRatio: {
          type: "string",
          enum: ["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"],
          description: "Optional aspect ratio for the edited image. Default is 1:1 (1024ร—1024).",
        },
      },
      required: ["imagePath", "prompt"],
  • src/index.ts:97-126 (registration)
    Registration of the 'edit_image' tool in the ListToolsRequestSchema handler, providing name, description, and input schema.
    {
      name: "edit_image",
      description: "Edit a SPECIFIC existing image file, optionally using additional reference images. Use this when you have the exact file path of an image to modify.",
      inputSchema: {
        type: "object",
        properties: {
          imagePath: {
            type: "string",
            description: "Full file path to the main image file to edit",
          },
          prompt: {
            type: "string",
            description: "Text describing the modifications to make to the existing image",
          },
          referenceImages: {
            type: "array",
            items: {
              type: "string"
            },
            description: "Optional array of file paths to additional reference images to use during editing (e.g., for style transfer, adding elements, etc.)",
          },
          aspectRatio: {
            type: "string",
            enum: ["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"],
            description: "Optional aspect ratio for the edited image. Default is 1:1 (1024ร—1024).",
          },
        },
        required: ["imagePath", "prompt"],
      },
    },
  • src/index.ts:184-185 (registration)
    Dispatch registration in the CallToolRequestSchema switch statement that routes calls to the editImage handler method.
    case "edit_image":
      return await this.editImage(request);
  • Helper usage of the editImage handler within the continue_editing tool to reuse the editing logic with the last image path.
    return await this.editImage({
      method: "tools/call",
      params: {
        name: "edit_image",
        arguments: {
          imagePath: this.lastImagePath,
          prompt: prompt,
          referenceImages: referenceImages,
          aspectRatio: aspectRatio
        }
      }
    } as CallToolRequest);

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/WeiYu021/openrouter-image-MCP'

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