Skip to main content
Glama

mcp-image-gen

This is a small Node.js MCP server that gives an AI coding agent (Claude Code or any MCP client) one tool, generate_image.

I generate a lot of images from Claude Code sessions, and I run ComfyUI with Qwen-Image 2.1 on my own GPU. Cloud image quotas run out, and when the one I relied on did, my image work stopped. So this server tries the OpenAI Images API first (when it's configured) and falls back automatically to Qwen-Image 2.1 on a local ComfyUI server when the cloud is rate-limited or unavailable.

No npm dependencies, just Node.js built-ins (fetch, FormData, node:test).

Sample output: Qwen-Image 2.1 on a local ComfyUI, 1024x1024, 35 s end to end

Sample output of the local route, requested over MCP stdio with the prompt "a small red fox sitting in a snowy forest, soft morning light" (Qwen-Image 2.1 on ComfyUI, 1024x1024, 35 s).

How it works

flowchart LR
  C["MCP client (e.g. Claude Code)"] <-->|"stdio, JSON-RPC 2.0"| S["server.mjs"]
  S --> V["validate input"]
  V --> G{"backend"}
  G -->|"auto / openai"| O["OpenAI Images API<br/>/images/generations, /images/edits"]
  O -->|"429: pause cloud 15 min<br/>network, 401/403/404, 5xx"| Q
  O -->|"400 refusal: error, no fallback"| E["tool error"]
  G -->|"comfyui, or cloud skipped"| Q["ComfyUI: Qwen-Image 2.1<br/>/upload/image, /prompt, /history, /view"]
  O --> F["PNG files + file:/// links"]
  Q --> F

Related MCP server: 2Xapi.com GPT-image MCP Server

Quickstart

You need Node.js 20.11+. The local backend also needs a ComfyUI server with the Qwen-Image 2.1 nodes (TextEncodeQwenImage21, QwenImage21Cache) and the model files; the cloud backend needs an OpenAI API key.

git clone <this repo> && cd mcp-image-gen
npm test

# register with Claude Code (the key is read from your environment, not stored in the MCP config)
export OPENAI_API_KEY=...            # optional
claude mcp add image-gen -e COMFYUI_URL=http://127.0.0.1:8188 -- node /path/to/mcp-image-gen/src/server.mjs

Everything is configured through environment variables (see .env.example). These are the main ones:

Variable

Default

Meaning

OPENAI_API_KEY

(none)

Turns on the cloud backend. Without it, auto goes straight to ComfyUI.

OPENAI_IMAGE_MODEL

gpt-image-1

The model sent to the Images API. OpenAI's guide now lists newer GPT Image models, so set this to a current one.

COMFYUI_URL

http://127.0.0.1:8188

The ComfyUI server.

QWEN_UNET, QWEN_CLIP, QWEN_VAE

the Qwen-Image 2.1 file names

Model files in your ComfyUI install.

IMAGE_OUTPUT_DIR

~/Pictures/mcp-image-gen

Where images go when the call doesn't name a path.

The tool

generate_image { prompt, output?, references?, format?, quality?, variants?, backend? }

  • references: up to 10 image paths (PNG/JPEG/WebP, max 20 MB each) to keep a character, place or style. OpenAI gets them as image[] parts of /images/edits; ComfyUI gets them as uploads wired into the Qwen-Image 2.1 encoder.

  • format: portrait (default; 1024x1536 on OpenAI, 768x1376 on ComfyUI), square, landscape.

  • backend: auto (default), openai or comfyui.

  • The result names the backend, the real pixel size read from the PNG header, the time, and the saved paths with a file:/// link for each, plus a note when it fell back.

Tests and CI

npm test     # node --test: 23 tests, offline

The tests start local fake servers (a fake OpenAI and a fake ComfyUI, built on node:http), and every HTTP call goes to them, so there are no real API calls. test/server.test.mjs runs the real server process over stdio, end to end. .github/workflows/ci.yml runs the suite on Ubuntu and Windows with Node 20 and 22.

Security

These are the security rules I set for the server:

  • Transport is stdio only. The server opens no network port, talks only to the MCP client that started it, and makes outbound requests only to the configured OpenAI and ComfyUI URLs.

  • Inputs are validated before any request goes out: the prompt is required and at most 4,000 characters; format, quality and backend are enums; variants is an integer from 1 to 4; there are at most 10 references, each an existing regular file with an image extension and at most 20 MB; output must be a non-empty path without NUL bytes. The schema rejects unknown arguments (additionalProperties: false).

  • No secrets are logged, because nothing is logged: stdout carries only protocol messages. The API key is read from the environment, sent only in the Authorization header, and redacted from any upstream error text before it reaches the client (some APIs echo the key back in auth errors; a test checks the redaction).

  • Refusals are never routed around silently. A cloud refusal (HTTP 400, e.g. a content policy rejection) comes back as an error. It isn't retried on the local model unless the caller explicitly asks for backend: "comfyui".

  • The server writes images wherever the client asks (output). It trusts its MCP client, like any local tool with file access.

Design decisions

  • Classify cloud errors, then decide. rate-limited (429, quota) -> fall back and skip the cloud for 15 minutes, so the next calls don't wait on a known failure; unavailable (network, 401/403/404, 5xx) -> fall back for this call; refused -> error.

  • The local workflow is built in code. buildWorkflow generates the ComfyUI graph (API format). That way references are padded to the encoder's three inputs (repeating the last one), extra references get their own nodes, and an explicit EmptyLatentImage fixes the canvas size (without it, the canvas follows the first reference). One job per variant, with consecutive seeds.

  • Real size in the result. The reported size is read from the saved PNG header, not from the request.

  • The tool stays visible. _meta["anthropic/alwaysLoad"] keeps it in the model's base tool list, so the model calls it instead of answering "I created the image" without calling anything.

  • Zero dependencies, so the server starts instantly and has no supply-chain surface beyond Node.js.

Limitations

  • I haven't verified the OpenAI request/response shape against the live API in this version (see below), and the default model name may need changing for your account.

  • The ComfyUI backend needs the Qwen-Image 2.1 nodes and models; other models need another workflow. It polls /history every 2 seconds instead of using the WebSocket API.

  • No streaming or partial images, and no cancellation of a running ComfyUI job when the client disconnects.

  • Variants on ComfyUI run one after another.

How I built it

I built this with AI coding agents (Claude Code), which wrote most of the code. My part was the architecture (the backend order, the error classes and when to fall back, the security rules), the review gates and the verification.

I don't take an agent's "done" at face value, so this is what I checked, and how:

  • This repository: npm test runs 23 offline tests and they pass (fake OpenAI and ComfyUI servers; the stdio server is tested as a real child process). I also checked each critical rule by injecting a deliberate bug and confirming that a test fails: no fallback on refusal, key redaction, the rate-limit pause, reference padding, consecutive seeds, and the 401 classification.

  • The private predecessor of this server is the version I use every day (same tool, same fallback rules, a different cloud route). I verified it with a stdio test client, no model involved: real cloud text-to-image and reference-guided generations, a real local Qwen-Image 2.1 generation (21 s), and a fake cloud endpoint answering 429, after which the server switched to Qwen and the next request skipped the cloud. Then a real Claude Code session found the tool on its own and returned the file:/// link. That's also how I found out the alwaysLoad flag was necessary. The ComfyUI workflow graph (nodes, parameters, the EmptyLatentImage canvas fix, padding references to three) comes from my production setup.

  • Local route, verified on a real server (29.10.2026): I started this server as a stdio child process with no OpenAI key, and it answered initialize, tools/list and a generate_image call. With auto, it fell back to ComfyUI with the note "OpenAI not configured", and Qwen-Image 2.1 returned a 1024x1024 PNG in 35 s (the sample above).

  • Cloud route, not run yet: in this repo the predecessor's cloud route is replaced by the official OpenAI Images API, which has never been called with a real OpenAI account. Only the mocked tests cover it. I checked the request shape against OpenAI's image-generation guide on 29.10.2026 (repeated image[] parts, the three sizes, data[].b64_json). My assumptions about the OpenAI API: POST /v1/images/generations (JSON) and POST /v1/images/edits (multipart, repeated image[] parts), fields model, prompt, n, size (1024x1536, 1024x1024, 1536x1024), optional quality (low/medium/high), response data[].b64_json, errors as { error: { message, code } }, default model gpt-image-1.

  • CI: GitHub Actions runs npm test on Ubuntu and Windows with Node 20 and 22. The first run passed on 29.10.2026.

License

MIT, see LICENSE.

Available Tools

1 tool
generate_imageA

Generates a PNG image and saves it to disk. Tries the OpenAI Images API first (when OPENAI_API_KEY is set); when it is rate-limited or unavailable, falls back automatically to Qwen-Image 2.1 on the local ComfyUI server. Returns the file paths and the backend used.

Inputs: prompt (required), format, output, backend, +3 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4/5.0

    Scored across 1 tool

    Disambiguation5/5

    With a single tool there is no possibility of misselection or overlap. An agent's only choice is whether to call it at all, and the description makes the trigger conditions (generate a PNG from a prompt, with automatic backend fallback) unambiguous.

    Naming Consistency5/5

    The single name follows a clean verb_noun snake_case convention (generate_image) that would scale predictably if more tools were added. No style mixing is possible with one tool.

    Tool Count3/5

    One tool is thin even for a narrowly scoped server; the rubric treats 1-2 tools as borderline. It earns its place, but the surface feels underbuilt for a server whose apparent remit is image generation and management.

    Completeness3/5

    Generation is well covered, including a fallback backend, but there is no way to list, inspect, delete, or re-run previously generated images, and no way to check backend/API status. These are notable gaps an agent would hit when managing outputs, though core generation works.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers