UNPKG

imagegen-mcp-server

Version:

MCP server for AI image generation with OpenAI DALL-E, Google Imagen, Gemini, and Flux via Replicate

119 lines (118 loc) 4.51 kB
import Replicate from "replicate"; import { saveBase64Image } from "../utils/fs.js"; const DEFAULT_MODEL = "black-forest-labs/flux-1.1-pro"; // Available Replicate models export const REPLICATE_MODELS = { "flux-1.1-pro": "black-forest-labs/flux-1.1-pro", "qwen-image": "qwen/qwen-image", "seedream-4": "bytedance/seedream-4", }; export async function generateImageReplicate(args) { const apiKey = process.env.REPLICATE_API_TOKEN; if (!apiKey) { throw new Error("Missing REPLICATE_API_TOKEN environment variable"); } const replicate = new Replicate({ auth: apiKey }); const format = (args.format ?? "png").toLowerCase(); const ext = format === "jpeg" ? "jpg" : format; const mimeType = format === "png" ? "image/png" : format === "jpg" || format === "jpeg" ? "image/jpeg" : "image/webp"; // Flux 1.1 supported aspect ratios and sizes const getFluxDimensions = (size, width, height) => { if (size) { const [w, h] = size.split("x").map(Number); if (w && h) return { width: w, height: h }; } if (width && height) { return { width, height }; } // Default to 1024x1024 return { width: 1024, height: 1024 }; }; const dimensions = getFluxDimensions(args.size, args.width, args.height); const model = args.model || process.env.REPLICATE_MODEL || DEFAULT_MODEL; // Build input parameters for Flux 1.1 const input = { prompt: args.prompt, width: dimensions.width, height: dimensions.height, output_format: format === "jpg" ? "jpg" : format, output_quality: 95, }; // Add optional parameters if provided if (args.seed !== undefined) { input.seed = args.seed; } // Flux 1.1 Pro supports safety tolerance input.safety_tolerance = 2; try { const output = await replicate.run(model, { input }); // Handle streaming response from Replicate let imageUrl; if (output instanceof ReadableStream) { // Convert stream to array buffer const response = new Response(output); const arrayBuffer = await response.arrayBuffer(); const base64 = Buffer.from(arrayBuffer).toString("base64"); // Save image directly from stream const outputDir = process.env.OUTPUT_DIR || "outputs"; const name = (args.filenameHint || "flux-image") .toLowerCase() .replace(/[^a-z0-9-_]+/g, "-") .replace(/-+/g, "-") .replace(/^-|-$/g, ""); const filename = `${Date.now()}-${name || "image"}.${ext}`; const path = saveBase64Image(base64, outputDir, filename); return { path, mimeType, base64: args.returnBase64 ? base64 : undefined, provider: "replicate", model, width: dimensions.width, height: dimensions.height, }; } else if (Array.isArray(output)) { imageUrl = output[0]; } else if (typeof output === "string") { imageUrl = output; } else { throw new Error("Unexpected output format from Replicate"); } // Download the image and convert to base64 const response = await fetch(imageUrl); if (!response.ok) { throw new Error(`Failed to download image: ${response.statusText}`); } const arrayBuffer = await response.arrayBuffer(); const base64 = Buffer.from(arrayBuffer).toString("base64"); // Save image to disk const outputDir = process.env.OUTPUT_DIR || "outputs"; const name = (args.filenameHint || "flux-image") .toLowerCase() .replace(/[^a-z0-9-_]+/g, "-") .replace(/-+/g, "-") .replace(/^-|-$/g, ""); const filename = `${Date.now()}-${name || "image"}.${ext}`; const path = saveBase64Image(base64, outputDir, filename); return { path, mimeType, base64: args.returnBase64 ? base64 : undefined, provider: "replicate", model, width: dimensions.width, height: dimensions.height, }; } catch (error) { throw new Error(`Replicate API error: ${error.message}`); } }