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

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