Skip to main content
Glama

merge_images

Combine multiple images into one by arranging them horizontally, vertically, or in a grid layout with customizable spacing and background color.

Instructions

Merge multiple images into a single image by arranging them horizontally, vertically, or in a grid.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
input_pathsYesOrdered list of absolute paths to the images to merge (minimum 2)
layoutNoArrangement: horizontal (side by side), vertical (stacked), or grid (auto columns)horizontal
gapNoGap in pixels between images (default 0)
backgroundNoBackground/gap fill color as hex (default #ffffff)#ffffff
output_pathYesAbsolute path for the output file (format inferred from extension)

Implementation Reference

  • The handler function that performs the logic of merging multiple images based on input parameters (layout, gap, etc.) using the sharp library.
      async ({ input_paths, layout = "horizontal", gap = 0, background = "#ffffff", output_path }) => {
        try {
          // Load metadata for all images
          const metas = await Promise.all(input_paths.map(async (p) => {
            await fs.access(p);
            const meta = await sharp(p).metadata();
            return { path: p, width: meta.width, height: meta.height };
          }));
    
          // Parse background color
          let bg = { r: 255, g: 255, b: 255, alpha: 1 };
          const hex = background.replace('#', '');
          if (hex.length === 6) {
            bg = { r: parseInt(hex.slice(0,2),16), g: parseInt(hex.slice(2,4),16), b: parseInt(hex.slice(4,6),16), alpha: 1 };
          }
    
          let canvasW, canvasH, positions;
    
          if (layout === "horizontal") {
            canvasH = Math.max(...metas.map(m => m.height));
            canvasW = metas.reduce((sum, m) => sum + m.width, 0) + gap * (metas.length - 1);
            positions = [];
            let x = 0;
            for (const m of metas) {
              positions.push({ x, y: Math.floor((canvasH - m.height) / 2) });
              x += m.width + gap;
            }
          } else if (layout === "vertical") {
            canvasW = Math.max(...metas.map(m => m.width));
            canvasH = metas.reduce((sum, m) => sum + m.height, 0) + gap * (metas.length - 1);
            positions = [];
            let y = 0;
            for (const m of metas) {
              positions.push({ x: Math.floor((canvasW - m.width) / 2), y });
              y += m.height + gap;
            }
          } else {
            // grid: auto columns = ceil(sqrt(n))
            const cols = Math.ceil(Math.sqrt(metas.length));
            const rows = Math.ceil(metas.length / cols);
            const cellW = Math.max(...metas.map(m => m.width));
            const cellH = Math.max(...metas.map(m => m.height));
            canvasW = cols * cellW + gap * (cols - 1);
            canvasH = rows * cellH + gap * (rows - 1);
            positions = metas.map((m, i) => {
              const col = i % cols;
              const row = Math.floor(i / cols);
              return {
                x: col * (cellW + gap) + Math.floor((cellW - m.width) / 2),
                y: row * (cellH + gap) + Math.floor((cellH - m.height) / 2),
              };
            });
          }
    
          // Build composite layers
          const composites = await Promise.all(metas.map(async (m, i) => ({
            input: await sharp(m.path).toBuffer(),
            left: positions[i].x,
            top: positions[i].y,
          })));
    
          const outExt = path.extname(output_path).slice(1).toLowerCase() || "png";
          const formatMap = { jpg: "jpeg" };
          const fmt = formatMap[outExt] || outExt;
    
          await sharp({
            create: { width: canvasW, height: canvasH, channels: 4, background: bg },
          })
            .composite(composites)
            .toFormat(fmt)
            .toFile(output_path);
    
          const stat = await fs.stat(output_path);
          return {
            content: [{ type: "text", text: JSON.stringify({ success: true, output_path, width: canvasW, height: canvasH, size_bytes: stat.size }) }],
          };
        } catch (err) {
          return { isError: true, content: [{ type: "text", text: `Error: ${err.message}` }] };
        }
      }
    );
  • index.js:227-236 (registration)
    Registration of the "merge_images" tool with schema definitions for input arguments using Zod.
    server.tool(
      "merge_images",
      "Merge multiple images into a single image by arranging them horizontally, vertically, or in a grid.",
      {
        input_paths: z.array(z.string()).min(2).describe("Ordered list of absolute paths to the images to merge (minimum 2)"),
        layout: z.enum(["horizontal", "vertical", "grid"]).optional().default("horizontal").describe("Arrangement: horizontal (side by side), vertical (stacked), or grid (auto columns)"),
        gap: z.number().int().min(0).optional().default(0).describe("Gap in pixels between images (default 0)"),
        background: z.string().optional().default("#ffffff").describe("Background/gap fill color as hex (default #ffffff)"),
        output_path: z.string().describe("Absolute path for the output file (format inferred from extension)"),
      },

Tool Definition Quality

Score is being calculated. Check back soon.

Install Server

Other Tools

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/sonic0002/imagic-mcp'

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