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
JavaScript
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}`);
}
}