flux_generate
Generate images from text prompts using FLUX AI models, saving files to specified directories for text-to-image creation, variations, inpainting, and edge-guided generation.
Instructions
Generate an image with a FLUX model via Replicate and save files to download_path
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| prompt | Yes | Text prompt describing the image | |
| download_path | Yes | Directory to save generated images | |
| model | No | FLUX model to use | black-forest-labs/flux-1.1-pro-ultra |
| image_path | No | Local path or URL to input image (for image-accepting models) | |
| mask_path | No | Local path or URL to mask for inpainting (Fill model) | |
| aspect_ratio | No | Aspect ratio (e.g., '1:1', '16:9', '3:4') | |
| seed | No | Random seed for reproducibility | |
| raw | No | Enable raw realism mode (Ultra model) | |
| num_outputs | No | Number of images to generate | |
| output_quality | No | Quality setting (model-dependent) | |
| go_fast | No | Speed vs quality tradeoff | |
| strength | No | Variation strength (Redux model) | |
| num_inference_steps | No | Inference steps (Fill model) | |
| guidance | No | Guidance scale (Fill model) | |
| output_format | No | Output image format (png, jpeg, or webp) | png |
Implementation Reference
- index.js:317-460 (handler)The main handler for the 'flux_generate' tool. It authenticates with Replicate API, builds model inputs based on args and model metadata, runs the generation, downloads the output images securely, and returns paths and URLs.if (name === "flux_generate") { try { const apiToken = process.env.REPLICATE_API_TOKEN; if (!apiToken) { throw new Error("REPLICATE_API_TOKEN environment variable is not set"); } const replicate = new Replicate({ auth: apiToken }); const modelId = args.model || "black-forest-labs/flux-1.1-pro-ultra"; const meta = FLUX_MODELS[modelId]; if (!meta) { throw new Error(`Unknown model: ${modelId}`); } // Build input const input = { prompt: args.prompt }; // Set output format (default to png for better compatibility) const outputFormat = args.output_format || "png"; input.output_format = outputFormat; // Add optional parameters if they exist for this model const optionalParams = [ "aspect_ratio", "seed", "raw", "num_outputs", "output_quality", "go_fast", "strength", "num_inference_steps", "guidance", ]; for (const param of optionalParams) { if (args[param] !== undefined && meta.inputs[param]) { input[param] = args[param]; } } // Handle image input for models that accept it if (meta.accepts_image) { if (!args.image_path) { throw new Error(`Model ${modelId} requires image_path`); } // Validate image_path to prevent SSRF // Only allow HTTPS URLs or local file paths (Replicate will handle validation) if (args.image_path.startsWith("http://")) { throw new Error("HTTP URLs not allowed for security reasons. Use HTTPS."); } input.image = args.image_path; } // Handle mask for Fill model if (modelId.endsWith("/flux-fill-pro") && args.mask_path) { // Same validation for mask paths if (args.mask_path.startsWith("http://")) { throw new Error("HTTP URLs not allowed for security reasons. Use HTTPS."); } input.mask = args.mask_path; } // Run the model const output = await replicate.run(modelId, { input }); // Download results - VALIDATE PATH FIRST const downloadPath = validateDownloadPath(args.download_path); await fs.mkdir(downloadPath, { recursive: true }); const timestamp = new Date() .toISOString() .replace(/[:.]/g, "-") .slice(0, 19); const baseName = `${meta.display.replace(/[^a-z0-9]/gi, "").toLowerCase()}_${timestamp}`; const savedFiles = []; const urls = Array.isArray(output) ? output : [output]; for (let i = 0; i < urls.length; i++) { const urlObj = urls[i]; // Convert to string if it's a URL object const url = typeof urlObj === 'string' ? urlObj : String(urlObj); // Use the requested output format for file extension const ext = outputFormat === "jpeg" ? ".jpg" : `.${outputFormat}`; const filepath = path.join(downloadPath, `${baseName}_${i + 1}${ext}`); await downloadFile(url, filepath); savedFiles.push(filepath); } return { content: [ { type: "text", text: JSON.stringify( { model: modelId, saved: savedFiles, urls: urls, }, null, 2 ), }, ], }; } catch (error) { // Sanitize error message to avoid leaking sensitive information let safeMessage = "An error occurred while generating the image."; // Only expose safe, user-actionable error messages if (error.message.includes("REPLICATE_API_TOKEN")) { safeMessage = "API token is not configured. Please set REPLICATE_API_TOKEN."; } else if (error.message.includes("Unknown model")) { safeMessage = error.message; // Safe to expose model validation errors } else if (error.message.includes("requires image_path")) { safeMessage = error.message; // Safe to expose parameter validation errors } else if (error.message.includes("Download path must be")) { safeMessage = "Invalid download path. Path must be within home directory or /tmp."; } else if (error.message.includes("HTTP URLs not allowed")) { safeMessage = "Only HTTPS URLs are allowed for security reasons."; } else if (error.message.includes("NSFW")) { safeMessage = "Content was flagged by safety filters. Please try a different prompt."; } else if (error.message.includes("Only Replicate CDN")) { safeMessage = "Invalid image source. Only Replicate CDN URLs are allowed."; } // Log full error server-side for debugging (not sent to client) console.error("FLUX MCP Error:", error); return { content: [ { type: "text", text: `Error: ${safeMessage}`, }, ], isError: true, }; } }
- index.js:218-286 (schema)Input schema defining parameters for flux_generate tool, including required prompt and download_path, optional model selection from FLUX models, and model-specific inputs.inputSchema: { type: "object", properties: { prompt: { type: "string", description: "Text prompt describing the image", }, download_path: { type: "string", description: "Directory to save generated images", }, model: { type: "string", description: "FLUX model to use", enum: Object.keys(FLUX_MODELS), default: "black-forest-labs/flux-1.1-pro-ultra", }, image_path: { type: "string", description: "Local path or URL to input image (for image-accepting models)", }, mask_path: { type: "string", description: "Local path or URL to mask for inpainting (Fill model)", }, aspect_ratio: { type: "string", description: "Aspect ratio (e.g., '1:1', '16:9', '3:4')", }, seed: { type: "number", description: "Random seed for reproducibility", }, raw: { type: "boolean", description: "Enable raw realism mode (Ultra model)", }, num_outputs: { type: "number", description: "Number of images to generate", }, output_quality: { type: "number", description: "Quality setting (model-dependent)", }, go_fast: { type: "boolean", description: "Speed vs quality tradeoff", }, strength: { type: "number", description: "Variation strength (Redux model)", }, num_inference_steps: { type: "number", description: "Inference steps (Fill model)", }, guidance: { type: "number", description: "Guidance scale (Fill model)", }, output_format: { type: "string", description: "Output image format (png, jpeg, or webp)", enum: ["png", "jpeg", "webp"], default: "png", }, }, required: ["prompt", "download_path"],
- index.js:214-288 (registration)Tool registration in ListTools handler: defines name, description, and references input schema for flux_generate.{ name: "flux_generate", description: "Generate an image with a FLUX model via Replicate and save files to download_path", inputSchema: { type: "object", properties: { prompt: { type: "string", description: "Text prompt describing the image", }, download_path: { type: "string", description: "Directory to save generated images", }, model: { type: "string", description: "FLUX model to use", enum: Object.keys(FLUX_MODELS), default: "black-forest-labs/flux-1.1-pro-ultra", }, image_path: { type: "string", description: "Local path or URL to input image (for image-accepting models)", }, mask_path: { type: "string", description: "Local path or URL to mask for inpainting (Fill model)", }, aspect_ratio: { type: "string", description: "Aspect ratio (e.g., '1:1', '16:9', '3:4')", }, seed: { type: "number", description: "Random seed for reproducibility", }, raw: { type: "boolean", description: "Enable raw realism mode (Ultra model)", }, num_outputs: { type: "number", description: "Number of images to generate", }, output_quality: { type: "number", description: "Quality setting (model-dependent)", }, go_fast: { type: "boolean", description: "Speed vs quality tradeoff", }, strength: { type: "number", description: "Variation strength (Redux model)", }, num_inference_steps: { type: "number", description: "Inference steps (Fill model)", }, guidance: { type: "number", description: "Guidance scale (Fill model)", }, output_format: { type: "string", description: "Output image format (png, jpeg, or webp)", enum: ["png", "jpeg", "webp"], default: "png", }, }, required: ["prompt", "download_path"], }, },
- index.js:17-103 (helper)FLUX_MODELS configuration object used to validate model selection, determine accepted inputs, and provide metadata for flux_generate handler.const FLUX_MODELS = { "black-forest-labs/flux-1.1-pro-ultra": { display: "FLUX1.1 Pro Ultra", kind: "text-to-image", notes: [ "Highest quality, up to ~4MP; 'raw' mode for realism.", "Use when you need best composition/large output.", ], inputs: { prompt: { required: true, type: "string" }, raw: { required: false, type: "boolean" }, aspect_ratio: { required: false, type: "string" }, seed: { required: false, type: "integer" }, output_quality: { required: false, type: "number" }, go_fast: { required: false, type: "boolean" }, }, accepts_image: false, }, "black-forest-labs/flux-pro": { display: "FLUX1.1 Pro", kind: "text-to-image", notes: [ "Fast, reliable, commercial-grade default when Ultra not required.", ], inputs: { prompt: { required: true, type: "string" }, aspect_ratio: { required: false, type: "string" }, seed: { required: false, type: "integer" }, }, accepts_image: false, }, "black-forest-labs/flux-redux-dev": { display: "FLUX.1 Redux [dev]", kind: "image-variation", notes: [ "Variations/restyling while preserving key elements; mix image + text.", ], inputs: { image: { required: true, type: "file_or_url" }, prompt: { required: true, type: "string" }, strength: { required: false, type: "number" }, seed: { required: false, type: "integer" }, num_outputs: { required: false, type: "integer" }, }, accepts_image: true, }, "black-forest-labs/flux-fill-pro": { display: "FLUX.1 Fill [pro]", kind: "inpainting/outpainting", notes: ["Professional in/outpainting; provide mask for areas to change."], inputs: { image: { required: true, type: "file_or_url" }, mask: { required: false, type: "file_or_url" }, prompt: { required: true, type: "string" }, num_inference_steps: { required: false, type: "integer" }, guidance: { required: false, type: "number" }, seed: { required: false, type: "integer" }, }, accepts_image: true, }, "black-forest-labs/flux-depth-dev": { display: "FLUX.1 Depth [dev]", kind: "depth-guided editing", notes: [ "Structure-preserving edits/style transfer using depth; supply an image.", ], inputs: { image: { required: true, type: "file_or_url" }, prompt: { required: true, type: "string" }, seed: { required: false, type: "integer" }, }, accepts_image: true, }, "black-forest-labs/flux-canny-pro": { display: "FLUX.1 Canny [pro]", kind: "edge-guided generation", notes: [ "Control structure/composition with edges; ideal for sketches/wireframes → detailed images.", ], inputs: { image: { required: true, type: "file_or_url" }, prompt: { required: true, type: "string" }, seed: { required: false, type: "integer" }, }, accepts_image: true, }, };
- index.js:106-131 (helper)validateDownloadPath helper: securely validates and resolves the download directory path to prevent directory traversal attacks.function validateDownloadPath(userPath) { // Expand ~ to home directory const expandedPath = userPath.replace(/^~/, process.env.HOME || ""); // Resolve to absolute path and normalize const absolutePath = path.resolve(expandedPath); // Security check: Ensure path doesn't escape to parent directories const homePath = process.env.HOME || ""; const allowedPaths = [ homePath, "/tmp", path.join(process.cwd(), "downloads"), ]; // Check if the resolved path is within allowed directories const isAllowed = allowedPaths.some(allowedPath => { return absolutePath.startsWith(path.resolve(allowedPath)); }); if (!isAllowed) { throw new Error(`Download path must be within home directory, /tmp, or project downloads folder. Got: ${absolutePath}`); } return absolutePath; }