Skip to main content
Glama
README.md
# comfyui-mcp

MCP server exposing a local (or LAN) ComfyUI instance's HTTP API as tools, so an
LLM client (Claude Desktop, Claude Code, etc.) can queue generations, inspect the
queue/history, upload reference images, browse installed models/nodes, and pull
back generated images -- without hand-rolling `curl` calls every time.

Built with Python + FastMCP, stdio transport (local subprocess, talks to ComfyUI
over HTTP on `127.0.0.1:8188` by default).

## Requirements

- Python 3.10+
- A running ComfyUI instance reachable over HTTP (default assumes the same
  machine, default port)

## Install

```bash
cd comfyui-mcp   # this directory
python -m venv .venv
.venv\Scripts\activate        # Windows
# source .venv/bin/activate   # macOS/Linux
pip install -r requirements.txt
```

## Configure

Environment variables (optional -- sane defaults for a local install):

| Variable | Default | Purpose |
|---|---|---|
| `COMFYUI_BASE_URL` | `http://127.0.0.1:8188` | Where ComfyUI's API is reachable |
| `COMFYUI_HTTP_TIMEOUT` | `30` | Per-request HTTP timeout in seconds (not the job-wait timeout, which is a per-tool-call parameter) |
| `COMFYUI_MOUNT_DIR` | `E:\Claude\Projects\Krea2\comfy_out` | Where `comfyui_save_output` writes renders — a folder the client/sandbox can see |
| `COMFYUI_LORA_DIR` | *(unset)* | LoRA `.safetensors` directory, for `comfyui_get_lora_metadata` (ComfyUI's API exposes LoRA names but not their path) |
| `COMFYUI_MODELS_DIR` | *(unset)* | Parent `models/` dir; `COMFYUI_LORA_DIR` falls back to `<this>/loras` |

The last three power the filesystem-backed tools. They assume the MCP server
process runs on the same (Windows) host as ComfyUI, which is the intended setup.

Copy `.env.example` to `.env` and edit if your ComfyUI runs on a different
host/port, or just set the variables in your MCP client config (see below).

## Register with an MCP client

**Claude Code:**
```bash
claude mcp add comfyui -- python /absolute/path/to/comfyui-mcp/comfyui_mcp/server.py
```

**Claude Desktop** (`claude_desktop_config.json`):
```json
{
  "mcpServers": {
    "comfyui": {
      "command": "python",
      "args": ["/absolute/path/to/comfyui-mcp/comfyui_mcp/server.py"],
      "env": {
        "COMFYUI_BASE_URL": "http://127.0.0.1:8188"
      }
    }
  }
}
```

Use the venv's Python interpreter path (`.venv/Scripts/python.exe` on Windows,
`.venv/bin/python` on macOS/Linux) if you don't want to rely on a global install.

## Tools

| Tool | Purpose |
|---|---|
| `comfyui_generate_image` | High-level text-to-image: builds a standard txt2img graph and submits it (checkpoint, prompts, size, sampler, steps, cfg, seed, etc.) |
| `comfyui_queue_prompt` | Submit any raw API-format node graph (img2img, ControlNet, LoRA, custom nodes -- anything exported from ComfyUI's "Save (API Format)") |
| `comfyui_get_queue` | List running/pending jobs |
| `comfyui_manage_queue` | Clear pending queue, or remove specific pending jobs |
| `comfyui_get_history` | Get a finished job's result, or list recent history |
| `comfyui_manage_history` | Clear history, or remove specific entries |
| `comfyui_interrupt` | Stop the currently running job |
| `comfyui_free_memory` | Unload models / free VRAM |
| `comfyui_upload_image` | Upload a reference image (for img2img, ControlNet, etc.) |
| `comfyui_upload_mask` | Upload an inpainting mask tied to a reference image |
| `comfyui_view_image` | Fetch actual image bytes for a filename (the only tool that returns image content); optional `max_dim` returns a downscaled thumbnail |
| `comfyui_save_output` | Copy a finished render out of ComfyUI into `COMFYUI_MOUNT_DIR` so the client/sandbox can reach, present, or edit it |
| `comfyui_get_lora_metadata` | Read a LoRA's trigger words / base model / training resolution from its safetensors header (no model load) |
| `comfyui_list_templates` | List server-side workflow templates and the slots each accepts |
| `comfyui_run_template` | Run a named template, filling in only its slots (no hand-retyped graphs) |
| `comfyui_get_progress` | Live progress/ETA (node, step, percent) for a running job, via ComfyUI's websocket |
| `comfyui_get_node_info` | Browse/inspect node types and their input/output schemas |
| `comfyui_list_models` | List model folder types, or filenames within one (checkpoints, loras, vae, ...) |
| `comfyui_list_embeddings` | List installed textual-inversion embeddings |
| `comfyui_get_system_stats` | Python/ComfyUI version + per-device VRAM/RAM |

## Design notes

- **Endpoint shapes were verified against ComfyUI's actual `server.py`**
  (fetched from `comfyanonymous/ComfyUI` master), not assumed, since the API
  has no formal versioned spec.
- **Images are references, not inline bytes, by default.** `comfyui_generate_image`
  and `comfyui_get_history` return `{filename, subfolder, type}` for each output
  image; call `comfyui_view_image` to actually fetch and view one. This keeps
  responses small when a batch produces several images, most of which the agent
  won't need to inspect.
- **Large ComfyUI payloads are summarized by default.** `/object_info` (all node
  schemas) and `/queue`/`/history` (which embed full node graphs) can be tens to
  hundreds of KB. `comfyui_get_queue`, `comfyui_get_history`, and
  `comfyui_get_node_info` default to condensed output with a `summary=false` /
  explicit `node_class` escape hatch for full detail.
- **`wait` + polling for completion; websocket for progress.** Job *completion*
  is detected by polling `/history/{prompt_id}` (it only populates once a job
  finishes) up to a per-call `timeout` — this maps cleanly onto a synchronous
  tool call, and `wait=false` returns immediately with a `prompt_id` to check
  later via `comfyui_get_history`. Live *progress* (node/step/percent) can't be
  polled from `/history`, so a single background subscription to ComfyUI's `/ws`
  websocket caches the latest progress per `prompt_id`, surfaced by
  `comfyui_get_progress`. The websocket is best-effort: if it can't connect (or
  `websockets` isn't installed), progress reports `"unknown"` and nothing else
  breaks.
- **The filesystem bridge (`comfyui_save_output`, `comfyui_get_lora_metadata`).**
  These are the only tools that touch the host filesystem. ComfyUI's output/ dir
  isn't visible to the client's sandbox, and its API exposes LoRA names but not
  their on-disk paths — so both tools read/write local paths directly. Every
  access is confined to a configured root (`COMFYUI_MOUNT_DIR` / `COMFYUI_LORA_DIR`)
  and validated against directory traversal. `comfyui_save_output` fetches bytes
  through the existing `/view` endpoint rather than reading ComfyUI's output dir
  directly, so it works without knowing where ComfyUI stores files.
- **Server-side workflow templates.** `comfyui_run_template` renders a stored
  API graph (`comfyui_mcp/templates/*.json`) by substituting typed *slots* —
  `"{{seed}}"` as a whole value keeps its int/bool/null type; `"{{x}}"` inside a
  longer string is spliced as text. A graph that references an undeclared slot is
  rejected before submission, rather than shipping a literal `{{typo}}`. Add your
  own canonical graphs (Krea2 base, film-grain tail, caption stack) as new files
  in that directory.
- **No response_format (json/markdown) toggle.** Every tool returns plain JSON.
  For a single-user local tool this is simpler and more predictable than
  maintaining two output shapes per tool; revisit if this ever needs to serve a
  chat-facing UI that benefits from markdown.
- **Newer `/api/jobs` endpoint intentionally not used.** ComfyUI's current
  master branch has a richer job-status API (`/api/jobs/{id}` with explicit
  pending/in_progress/completed/failed status), but it's very recent and may
  not exist on older ComfyUI installs. `/history` + `/queue` are long-standing
  and work everywhere, so they're used for polling instead. Worth revisiting if
  you're always running latest ComfyUI.

## Testing

Run the unit tests:

```bash
.venv\Scripts\python.exe -m pytest tests/ -q
```

They cover the pure logic in the newer tools without needing a live ComfyUI:
the safetensors metadata reader, template loading + slot substitution (including
type preservation and undeclared-slot rejection), path-traversal safety, the
`http`→`ws` URL derivation, the progress tracker's websocket-message handling,
the `prompt`/`slots` JSON-string coercion, and image downscaling. All 20 tools
also register cleanly with FastMCP.

**Exercise the filesystem/networked tools against a real ComfyUI instance**
before relying on them — checkpoint/sampler/scheduler names are install-specific
(use `comfyui_list_models` and `comfyui_get_node_info(node_class="KSampler")`),
and `comfyui_save_output` / `comfyui_get_lora_metadata` depend on the
`COMFYUI_MOUNT_DIR` / `COMFYUI_LORA_DIR` paths being correct for your machine.