Skip to main content
Glama
nickbaumann98

EverArt Forge MCP Server

generate_image

Create images for web projects using AI models, supporting multiple formats including SVG, PNG, JPG, and WebP with responsive design integration.

Instructions

Generate images using EverArt Models, optimized for web development. Supports web project paths, responsive formats, and inline preview. Available models:

  • 5000:FLUX1.1: Standard quality

  • 9000:FLUX1.1-ultra: Ultra high quality

  • 6000:SD3.5: Stable Diffusion 3.5

  • 7000:Recraft-Real: Photorealistic style

  • 8000:Recraft-Vector: Vector art style (SVG format)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
promptYesText description of desired image
modelNoModel ID (5000:FLUX1.1, 9000:FLUX1.1-ultra, 6000:SD3.5, 7000:Recraft-Real, 8000:Recraft-Vector)5000
formatNoOutput format (svg, png, jpg, webp). Note: Vector format (svg) is only available with Recraft-Vector (8000) model.svg
output_pathNoOptional: Custom output path for the generated image. If not provided, image will be saved in the default storage directory.
web_project_pathNoPath to web project root folder for storing images in appropriate asset directories.
project_typeNoWeb project type to determine appropriate asset directory structure (e.g., 'react', 'vue', 'html', 'next').
asset_pathNoOptional subdirectory within the web project's asset structure for storing generated images.
image_countNoNumber of images to generate

Implementation Reference

  • src/index.ts:365-418 (registration)
    Registration of the 'generate_image' tool in the ListToolsRequestSchema handler, including name, description, and detailed inputSchema for parameters like prompt, model, format, paths, etc.
    { name: "generate_image", description: "Generate images using EverArt Models, optimized for web development. " + "Supports web project paths, responsive formats, and inline preview. " + "Available models:\n" + "- 5000:FLUX1.1: Standard quality\n" + "- 9000:FLUX1.1-ultra: Ultra high quality\n" + "- 6000:SD3.5: Stable Diffusion 3.5\n" + "- 7000:Recraft-Real: Photorealistic style\n" + "- 8000:Recraft-Vector: Vector art style (SVG format)", inputSchema: { type: "object", properties: { prompt: { type: "string", description: "Text description of desired image", }, model: { type: "string", description: "Model ID (5000:FLUX1.1, 9000:FLUX1.1-ultra, 6000:SD3.5, 7000:Recraft-Real, 8000:Recraft-Vector)", default: "5000", }, format: { type: "string", description: "Output format (svg, png, jpg, webp). Note: Vector format (svg) is only available with Recraft-Vector (8000) model.", default: "svg" }, output_path: { type: "string", description: "Optional: Custom output path for the generated image. If not provided, image will be saved in the default storage directory.", }, web_project_path: { type: "string", description: "Path to web project root folder for storing images in appropriate asset directories.", }, project_type: { type: "string", description: "Web project type to determine appropriate asset directory structure (e.g., 'react', 'vue', 'html', 'next').", }, asset_path: { type: "string", description: "Optional subdirectory within the web project's asset structure for storing generated images.", }, image_count: { type: "number", description: "Number of images to generate", default: 1, }, }, required: ["prompt"], }, },
  • Main handler logic for 'generate_image': validates inputs, creates generation via EverArt client API, polls for completion, saves image using saveImage helper, opens in viewer, and returns formatted success response with inline details.
    case "generate_image": { try { const args = request.params.arguments as any; // Validate required parameters if (!args.prompt || typeof args.prompt !== 'string' || args.prompt.trim() === '') { return errorResponse({ type: EverArtErrorType.VALIDATION_ERROR, message: "Prompt is required and must be a non-empty string." }); } const prompt = args.prompt; // Use 'let' instead of 'const' for model since we might need to modify it let modelInput = args.model || "5000"; const image_count = args.image_count || 1; const output_path = args.output_path; const web_project_path = args.web_project_path; const project_type = args.project_type; const asset_path = args.asset_path; // Enhanced validation if (image_count < 1 || image_count > 10) { return errorResponse({ type: EverArtErrorType.VALIDATION_ERROR, message: "image_count must be between 1 and 10" }); } // Validate model - extract the numeric ID if a combined format was provided const validModels = ["5000", "6000", "7000", "8000", "9000"]; // Handle model IDs in the format "8000:Recraft-Vector" if (modelInput.includes(":")) { const originalModel = modelInput; modelInput = modelInput.split(":")[0]; console.log(`Received combined model ID format: ${originalModel}, using base ID: ${modelInput}`); } if (!validModels.includes(modelInput)) { return errorResponse({ type: EverArtErrorType.VALIDATION_ERROR, message: `Invalid model ID: ${modelInput}. Valid models are: ${validModels.join(", ")}` }); } // Now we have the validated model ID const format = args.format || (modelInput === "8000" ? "svg" : "png"); // Validate format const supportedFormats = ["svg", "png", "jpg", "jpeg", "webp"]; if (!supportedFormats.includes(format.toLowerCase())) { return errorResponse({ type: EverArtErrorType.VALIDATION_ERROR, message: `Unsupported format: ${format}. Supported formats are: ${supportedFormats.join(", ")}` }); } // Validate model/format compatibility if (!validateModelFormatCompatibility(modelInput, format)) { return errorResponse({ type: EverArtErrorType.VALIDATION_ERROR, message: `Format '${format}' is not compatible with model '${modelInput}'. SVG format is only available with Recraft-Vector (8000) model.` }); } // Generate image with retry logic let generation; let retryCount = 0; while (retryCount < MAX_RETRIES) { try { generation = await client.v1.generations.create( modelInput, prompt, "txt2img", { imageCount: image_count, height: 1024, width: 1024, // Add extra fields for specific models if needed ...(modelInput === "8000" ? { variant: "vector" } : {}), }, ); break; } catch (error) { if (retryCount >= MAX_RETRIES - 1) throw error; // Exponential backoff const delay = INITIAL_RETRY_DELAY * Math.pow(2, retryCount); await new Promise(r => setTimeout(r, delay)); retryCount++; } } if (!generation) { throw new Error("Failed to create generation after multiple attempts"); } // Enhanced polling with better timeout handling const completedGen = await client.v1.generations.fetchWithPolling( generation[0].id, { maxAttempts: 30, // Increased from default interval: 3000 // Check every 3 seconds } ); const imgUrl = completedGen.image_url; if (!imgUrl) { throw new Error("No image URL in the completed generation"); } // Save image locally with specified format and path const filepath = await saveImage( imgUrl, prompt, modelInput, format, output_path, web_project_path, project_type, asset_path ); // Open in default viewer try { await open(filepath); } catch (openError) { console.warn("Could not open the image in default viewer:", openError); // Continue without throwing - this is a non-critical error } // Model name mapping for user-friendly display const modelNames: Record<string, string> = { "5000": "FLUX1.1 (Standard quality)", "9000": "FLUX1.1-ultra (Ultra high quality)", "6000": "Stable Diffusion 3.5", "7000": "Recraft-Real (Photorealistic)", "8000": "Recraft-Vector (Vector art)" }; // Read the image file for inline display let imageData: string | undefined; try { const imageContent = await fs.readFile(filepath); imageData = imageContent.toString('base64'); } catch (error) { console.warn("Unable to read image for inline display:", error); // Continue without inline display if reading fails } // Calculate relative web path if applicable let webRelativePath: string | undefined; if (web_project_path && filepath.startsWith(web_project_path)) { webRelativePath = filepath.slice(web_project_path.length); if (!webRelativePath.startsWith('/')) webRelativePath = '/' + webRelativePath; } return { content: [ { type: "text", text: `✅ Image generated and saved successfully!\n\n` + `Generation details:\n` + `• Model: ${modelNames[modelInput] || modelInput}\n` + `• Prompt: "${prompt}"\n` + `• Format: ${format.toUpperCase()}\n` + `• Saved to: ${filepath}` + (webRelativePath ? `\n• Web relative path: ${webRelativePath}` : ``) }, { type: "text", text: `View the image at: file://${filepath}` } ], }; } catch (error: unknown) { console.error("Detailed error:", error); // Categorize errors for better user feedback if (error instanceof Error) { if (error.message.includes("SVG format")) { return errorResponse({ type: EverArtErrorType.FORMAT_ERROR, message: error.message }); } else if (error.message.includes("Failed to fetch image")) { return errorResponse({ type: EverArtErrorType.NETWORK_ERROR, message: "Failed to download the generated image. Please check your internet connection and try again." }); } else if (error.message.includes("rate limit")) { return errorResponse({ type: EverArtErrorType.API_ERROR, message: "EverArt API rate limit reached. Please try again later." }); } else if (error.message.includes("unauthorized") || error.message.includes("authentication")) { return errorResponse({ type: EverArtErrorType.AUTHENTICATION_ERROR, message: "API authentication failed. Please check your EverArt API key." }); } } // Generic error handling const errorMessage = error instanceof Error ? error.message : "Unknown error"; return errorResponse({ type: EverArtErrorType.UNKNOWN_ERROR, message: errorMessage }); } }
  • Key helper function saveImage: fetches image from URL, handles format-specific processing (SVG optimization, raster conversion with sharp), supports web project paths, retries on fetch errors, and saves to filesystem.
    async function saveImage(imageUrl: string, prompt: string, model: string, format: string = "svg", outputPath?: string, webProjectPath?: string, projectType?: string, assetPath?: string): Promise<string> { // Validate format format = format.toLowerCase(); const supportedFormats = ['svg', 'png', 'jpg', 'jpeg', 'webp']; if (!supportedFormats.includes(format)) { throw new Error(`Unsupported format: ${format}. Supported formats are: ${supportedFormats.join(', ')}`); } // Validate model/format compatibility if (!validateModelFormatCompatibility(model, format)) { throw new Error(`Format '${format}' is not compatible with model '${model}'. SVG format is only available with Recraft-Vector (8000) model.`); } let filepath: string; try { // Handle web project paths if specified let projectBasePath: string | undefined; if (webProjectPath) { projectBasePath = await processWebProjectPath(webProjectPath, projectType, assetPath); } // Generate a standardized file name for web assets const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); const sanitizedPrompt = prompt.slice(0, 20).replace(/[^a-zA-Z0-9]/g, "_").toLowerCase(); const filename = `${sanitizedPrompt}_${model}.${format}`; if (outputPath) { // If outputPath is provided, ensure it has the correct extension const ext = path.extname(outputPath); if (!ext) { // If no extension provided, append the format filepath = `${outputPath}.${format}`; } else if (ext.slice(1).toLowerCase() !== format.toLowerCase()) { // If extension doesn't match format, warn but use the specified format console.warn(`Warning: File extension ${ext} doesn't match specified format ${format}`); filepath = outputPath.slice(0, -ext.length) + `.${format}`; } else { filepath = outputPath; } // Ensure the directory exists await fs.mkdir(path.dirname(filepath), { recursive: true }); } else if (projectBasePath) { // Web project path takes precedence over default filepath = path.join(projectBasePath, filename); } else { // Default behavior: save to STORAGE_DIR with timestamp filepath = path.join(STORAGE_DIR, `${timestamp}_${model}_${sanitizedPrompt}.${format}`); } // Fetch the image with retries let response; let retryCount = 0; while (retryCount < MAX_RETRIES) { try { // @ts-ignore - node-fetch doesn't support timeout in RequestInit, but this works at runtime response = await fetch(imageUrl, { timeout: 30000 }); if (response.ok) break; // If we got a 429 (rate limit), wait longer before retrying if (response.status === 429) { const retryAfter = parseInt(response.headers.get('retry-after') || '5', 10); await new Promise(r => setTimeout(r, retryAfter * 1000)); } else { throw new Error(`Failed to fetch image: ${response.statusText} (${response.status})`); } } catch (error) { if (retryCount >= MAX_RETRIES - 1) throw error; // Exponential backoff const delay = INITIAL_RETRY_DELAY * Math.pow(2, retryCount); await new Promise(r => setTimeout(r, delay)); } retryCount++; } if (!response || !response.ok) { throw new Error(`Failed to fetch image after ${MAX_RETRIES} attempts`); } const buffer = await response.arrayBuffer(); const content = Buffer.from(buffer); if (format === "svg") { // For SVG, optimize and save const svgString = content.toString('utf-8'); const result = optimize(svgString, { multipass: true, plugins: [ 'preset-default', 'removeDimensions', 'removeViewBox', 'cleanupIds', ], }); await fs.writeFile(filepath, result.data); } else { // For raster formats, convert using sharp with better error handling try { const image = sharp(content); switch (format.toLowerCase()) { case "png": await image.png({ quality: 90 }).toFile(filepath); break; case "jpg": case "jpeg": await image.jpeg({ quality: 90 }).toFile(filepath); break; case "webp": await image.webp({ quality: 90 }).toFile(filepath); break; default: throw new Error(`Unsupported format: ${format}`); } } catch (error) { throw new Error(`Image processing failed: ${(error as Error).message}`); } } return filepath; } catch (error) { throw new Error(`Failed to save image: ${(error as Error).message}`); } }
  • Helper function to validate model-format compatibility, specifically restricting SVG to Recraft-Vector model (8000).
    function validateModelFormatCompatibility(model: string, format: string): boolean { // SVG is only supported by Recraft-Vector (8000) if (format.toLowerCase() === 'svg' && model !== '8000') { return false; } return true; }
  • Input schema definition for generate_image tool, specifying properties, descriptions, defaults, and required fields.
    type: "object", properties: { prompt: { type: "string", description: "Text description of desired image", }, model: { type: "string", description: "Model ID (5000:FLUX1.1, 9000:FLUX1.1-ultra, 6000:SD3.5, 7000:Recraft-Real, 8000:Recraft-Vector)", default: "5000", }, format: { type: "string", description: "Output format (svg, png, jpg, webp). Note: Vector format (svg) is only available with Recraft-Vector (8000) model.", default: "svg" }, output_path: { type: "string", description: "Optional: Custom output path for the generated image. If not provided, image will be saved in the default storage directory.", }, web_project_path: { type: "string", description: "Path to web project root folder for storing images in appropriate asset directories.", }, project_type: { type: "string", description: "Web project type to determine appropriate asset directory structure (e.g., 'react', 'vue', 'html', 'next').", }, asset_path: { type: "string", description: "Optional subdirectory within the web project's asset structure for storing generated images.", }, image_count: { type: "number", description: "Number of images to generate", default: 1, }, }, required: ["prompt"],

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/nickbaumann98/everart-forge-mcp'

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