Skip to main content
Glama

generate_image

Destructive

Generate images using AI with support for MeiGen, local ComfyUI, or OpenAI-compatible APIs. Accepts prompts and optional reference images for style guidance.

Instructions

Generate an image using AI. Supports MeiGen platform, local ComfyUI, or OpenAI-compatible APIs. Tip: get prompts from get_inspiration() or enhance_prompt(), and use gallery image URLs as referenceImages for style guidance. For Midjourney V8.1, an optional style reference can be passed by appending --sref <code> at the end of the prompt — only when the user provides a Midjourney style code (numeric or text). Do NOT pass URLs or local paths via --sref; for any image-based reference, use the referenceImages parameter instead.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
promptYesThe image generation prompt
modelNoModel name. For OpenAI-compatible providers: any model ID your endpoint supports. For MeiGen: use model IDs from list_models.
sizeNoImage size for OpenAI-compatible providers: "1024x1024", "1536x1024", "auto". MeiGen/ComfyUI: use aspectRatio instead.
aspectRatioNoAspect ratio for MeiGen provider. Use "auto" (recommended, default when omitted) to let MeiGen infer the best ratio from the prompt content. Explicit values: "1:1", "3:4", "4:3", "16:9", "9:16", "21:9", "2:3", "3:2", "4:5", "5:4", etc. (model-dependent). ComfyUI: use comfyui_workflow modify to adjust dimensions before generating.
resolutionNoResolution tier. MeiGen: "1K" / "2K" / "3K" / "4K" — each model supports a subset (list_models reports resolutions when applicable). OpenAI: not used (use size instead).
qualityNoImage quality. MeiGen gpt-image-2: "low" / "medium" / "high". OpenAI-compatible providers also accept "high".
referenceImagesNoImage references for style/content guidance. Accepts both public URLs (http/https) and local file paths. Local files are automatically compressed and uploaded when needed. For ComfyUI: local files are passed directly to the workflow (requires LoadImage node). Sources: gallery URLs from search_gallery/get_inspiration, URLs from previous generate_image results, or local file paths.
providerNoWhich provider to use. Auto-detected from configuration if not specified.
workflowNoComfyUI workflow name to use (from comfyui_workflow list). Uses default workflow if not specified.
negativePromptNoNegative prompt for OpenAI-compatible providers. ComfyUI: use comfyui_workflow modify to set negative prompt in the workflow before generating.

Implementation Reference

  • Main handler: registerGenerateImage that registers the 'generate_image' tool on the MCP server. Dispatches to three providers (OpenAI, MeiGen, ComfyUI) based on config, handles reference image resolution, semaphore-based concurrency control, and error classification.
    export function registerGenerateImage(server: McpServer, apiClient: MeiGenApiClient, config: MeiGenConfig) {
      server.tool(
        'generate_image',
        'Generate an image using AI. Supports MeiGen platform, local ComfyUI, or OpenAI-compatible APIs. Tip: get prompts from get_inspiration() or enhance_prompt(), and use gallery image URLs as referenceImages for style guidance. For Midjourney V8.1, an optional style reference can be passed by appending `--sref <code>` at the end of the prompt — only when the user provides a Midjourney style code (numeric or text). Do NOT pass URLs or local paths via --sref; for any image-based reference, use the referenceImages parameter instead.',
        generateImageSchema,
        { readOnlyHint: false, destructiveHint: true },
        async ({ prompt, model, size, aspectRatio, resolution, quality, referenceImages, provider: requestedProvider, workflow, negativePrompt }, extra) => {
          const availableProviders = getAvailableProviders(config)
    
          if (availableProviders.length === 0) {
            return {
              content: [{
                type: 'text' as const,
                text: 'No image generation providers configured. Get a MeiGen API token at https://www.meigen.ai (sign in → Settings → API Keys), then set MEIGEN_API_TOKEN in your environment or MCP config and restart the host. Claude Code users can run /meigen:setup for guided configuration. Alternative providers: OPENAI_API_KEY (any OpenAI-compatible API) or ComfyUI workflow import.',
              }],
              isError: true,
            }
          }
    
          // Determine which provider to use
          let providerType: ProviderType
          if (requestedProvider) {
            if (!availableProviders.includes(requestedProvider)) {
              return {
                content: [{
                  type: 'text' as const,
                  text: `Provider "${requestedProvider}" is not configured. Available: ${availableProviders.join(', ')}`,
                }],
                isError: true,
              }
            }
            providerType = requestedProvider
          } else {
            providerType = getDefaultProvider(config)!
          }
    
          try {
            // Auto-upload local reference images for API providers (ComfyUI handles local files natively)
            const resolvedRefs = providerType !== 'comfyui'
              ? await resolveReferenceImages(referenceImages, config, (msg) => notify(extra, msg))
              : referenceImages
    
            switch (providerType) {
              case 'openai': {
                await sharedApiSemaphore.acquire()
                try {
                  return await generateWithOpenAI(config, prompt, model, size, quality, resolvedRefs)
                } finally {
                  sharedApiSemaphore.release()
                }
              }
              case 'meigen': {
                await sharedApiSemaphore.acquire()
                try {
                  return await generateWithMeiGen(apiClient, prompt, model, aspectRatio, resolution, quality, resolvedRefs, extra)
                } finally {
                  sharedApiSemaphore.release()
                }
              }
              case 'comfyui': {
                await comfyuiSemaphore.acquire()
                try {
                  return await generateWithComfyUI(config, prompt, workflow, referenceImages, extra)
                } finally {
                  comfyuiSemaphore.release()
                }
              }
              default:
                return {
                  content: [{ type: 'text' as const, text: `Unknown provider: ${providerType}` }],
                  isError: true,
                }
            }
          } catch (error) {
            const message = error instanceof Error ? error.message : String(error)
            const guidance = classifyError(message)
            return {
              content: [{
                type: 'text' as const,
                text: `Image generation failed: ${message}\n\n${guidance}`,
              }],
              isError: true,
            }
          }
        }
      )
    }
  • Input schema (generateImageSchema) defining all tool parameters: prompt, model, size, aspectRatio, resolution, quality, referenceImages, provider, workflow, negativePrompt — each with Zod validation and descriptions.
    export const generateImageSchema = {
      prompt: z.string().trim().min(1, 'Prompt cannot be empty').describe('The image generation prompt'),
      model: z.string().optional()
        .describe('Model name. For OpenAI-compatible providers: any model ID your endpoint supports. For MeiGen: use model IDs from list_models.'),
      size: z.string().optional()
        .describe('Image size for OpenAI-compatible providers: "1024x1024", "1536x1024", "auto". MeiGen/ComfyUI: use aspectRatio instead.'),
      aspectRatio: z.string().optional()
        .describe('Aspect ratio for MeiGen provider. Use "auto" (recommended, default when omitted) to let MeiGen infer the best ratio from the prompt content. Explicit values: "1:1", "3:4", "4:3", "16:9", "9:16", "21:9", "2:3", "3:2", "4:5", "5:4", etc. (model-dependent). ComfyUI: use comfyui_workflow modify to adjust dimensions before generating.'),
      resolution: z.string().optional()
        .describe('Resolution tier. MeiGen: "1K" / "2K" / "3K" / "4K" — each model supports a subset (list_models reports resolutions when applicable). OpenAI: not used (use size instead).'),
      quality: z.string().optional()
        .describe('Image quality. MeiGen gpt-image-2: "low" / "medium" / "high". OpenAI-compatible providers also accept "high".'),
      referenceImages: z.array(z.string()).optional()
        .describe('Image references for style/content guidance. Accepts both public URLs (http/https) and local file paths. Local files are automatically compressed and uploaded when needed. For ComfyUI: local files are passed directly to the workflow (requires LoadImage node). Sources: gallery URLs from search_gallery/get_inspiration, URLs from previous generate_image results, or local file paths.'),
      provider: z.enum(['openai', 'meigen', 'comfyui']).optional()
        .describe('Which provider to use. Auto-detected from configuration if not specified.'),
      workflow: z.string().optional()
        .describe('ComfyUI workflow name to use (from comfyui_workflow list). Uses default workflow if not specified.'),
      negativePrompt: z.string().optional()
        .describe('Negative prompt for OpenAI-compatible providers. ComfyUI: use comfyui_workflow modify to set negative prompt in the workflow before generating.'),
    }
  • src/server.ts:274-275 (registration)
    Registration call in the main server setup that wires registerGenerateImage into the MCP server instance.
    // Image generation (requires API Key, MeiGen Token, or ComfyUI workflow)
    registerGenerateImage(server, apiClient, config)
  • Provider-specific handler: generateWithOpenAI — uses OpenAIProvider to generate, saves locally, and tracks recent generations.
    async function generateWithOpenAI(
      config: MeiGenConfig,
      prompt: string,
      model?: string,
      size?: string,
      quality?: string,
      referenceImages?: string[],
    ) {
      const provider = new OpenAIProvider(config.openaiApiKey!, config.openaiBaseUrl, config.openaiModel)
      const result = await provider.generate({ prompt, model, size, quality, referenceImages })
    
      const savedPath = saveImageLocally(result.imageBase64, result.mimeType)
    
      addRecentGeneration({ prompt, provider: 'openai', model: model || config.openaiModel })
    
      const lines = [`Image generated successfully.`]
      lines.push(`- Provider: OpenAI-compatible (${model || config.openaiModel})`)
      if (referenceImages?.length) lines.push(`- Reference images: ${referenceImages.length} used`)
      if (savedPath) lines.push(`- Saved to: ${savedPath}`)
    
      return {
        content: [{ type: 'text' as const, text: lines.join('\n') }],
      }
    }
  • Provider-specific handler: generateWithMeiGen — submits via apiClient.generateImage, polls for completion via waitForGeneration, downloads/saves result, and tracks recent generations. Handles multi-candidate results.
    async function generateWithMeiGen(
      apiClient: MeiGenApiClient,
      prompt: string,
      model: string | undefined,
      aspectRatio: string | undefined,
      resolution: string | undefined,
      quality: string | undefined,
      referenceImages: string[] | undefined,
      extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
    ) {
      // 1. Submit generation request
      // model / resolution / quality 不强制填充默认值;缺省时由 MeiGen 后端按 DB 决定
      const genResponse = await apiClient.generateImage({
        prompt,
        modelId: model,
        aspectRatio: aspectRatio || 'auto',
        resolution,
        quality,
        referenceImages,
      })
    
      if (!genResponse.generationId) {
        throw new Error('No generation ID returned')
      }
    
      // Notify: generation submitted
      await notify(extra, 'Image generation submitted, waiting for result...')
    
      // 2. Poll until completed (with progress notifications)
      const status = await apiClient.waitForGeneration(
        genResponse.generationId,
        300_000,
        async (elapsedMs) => {
          await notify(extra, `Still generating... (${Math.round(elapsedMs / 1000)}s elapsed)`)
        },
      )
    
      if (status.status === 'failed') {
        throw new Error(status.error || 'Generation failed')
      }
    
      // Detect video model misuse early — give a clear redirect instead of cryptic "no image URL"
      if (status.mediaType === 'video') {
        throw new Error('This model produces video. Use the generate_video tool with the same model id.')
      }
    
      // Use imageUrls array if available (e.g., V8.1 returns 4 candidates), fall back to imageUrl
      const allImageUrls = status.imageUrls?.length ? status.imageUrls : (status.imageUrl ? [status.imageUrl] : [])
    
      if (allImageUrls.length === 0) {
        throw new Error('No image URL in completed generation')
      }
    
      // Download first image for local save
      const imageRes = await fetch(allImageUrls[0])
      if (!imageRes.ok) {
        throw new Error(`Failed to download generated image: ${imageRes.status}`)
      }
      const buffer = await imageRes.arrayBuffer()
      const base64 = Buffer.from(buffer).toString('base64')
      const mimeType = imageRes.headers.get('content-type') || 'image/jpeg'
    
      const savedPath = saveImageLocally(base64, mimeType)
    
      // 优先用后端返回的 modelId(反映真实使用的模型,包含 is_default 解析结果);
      // 若后端未回传(旧版 backend),用用户显式传入的 model,再 fallback 到占位
      const actualModel = genResponse.modelId || model || 'meigen-default'
    
      addRecentGeneration({ prompt, provider: 'meigen', model: actualModel, aspectRatio })
    
      const lines = [`Image generated successfully.`]
      lines.push(`- Provider: MeiGen (model: ${actualModel})`)
    
      if (allImageUrls.length > 1) {
        lines.push(`- ${allImageUrls.length} candidate images returned:`)
        allImageUrls.forEach((url, i) => lines.push(`  ${i + 1}. ${url}`))
      } else {
        lines.push(`- Image URL: ${allImageUrls[0]}`)
      }
    
      if (savedPath) lines.push(`- Saved to: ${savedPath}`)
      lines.push(`\nYou can use any Image URL as referenceImages for follow-up generation.`)
    
      return {
        content: [{ type: 'text' as const, text: lines.join('\n') }],
      }
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already provide destructiveHint=true, so the description is not required to reiterate destructiveness. However, it adds valuable behavioral details: automatic compression/upload of local files, provider differences, and correct handling of --sref vs referenceImages. There is no contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three focused sentences plus a targeted Midjourney tip. Every sentence adds value without redundancy. The structure is front-loaded with purpose, then usage guidance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 10 parameters and no output schema, the description covers core purpose, parameter usage across providers, and integration with sibling tools. Minor gaps remain (e.g., error handling, rate limits), but overall it provides sufficient context for an AI agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, baseline 3. The description adds meaningful context beyond schema descriptions, e.g., sourcing referenceImages from gallery or previous results, and directing ComfyUI negative prompt handling to comfyui_workflow modify.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description starts with 'Generate an image using AI' and explicitly lists supported platforms (MeiGen, ComfyUI, OpenAI-compatible APIs). This clearly identifies the tool's function and distinguishes it from siblings like generate_video and comfyui_workflow.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description advises using get_inspiration() or enhance_prompt() for prompts and explains how to use referenceImages. It also gives specific guidance for Midjourney --sref usage. While it doesn't explicitly state when not to use this tool, the context is clear and helpful.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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/jau123/MeiGen-AI-Design-MCP'

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