# Comfy MCP

> ComfyUI's official MCP: lets agents drive local ComfyUI image and video workflows, list installed nodes, models and templates, submit jobs and fetch results, and start or stop ComfyUI. Needs a machine that can run ComfyUI (usually a GPU) and comfy-cli; partner models consume Comfy credits.

- Type: MCP
- Repository: https://github.com/Comfy-Org/comfy-mcp
- Page: https://hebbmind.com/ko/mcp/comfy-mcp
- Stars: 284
- Professions: [ML / AI](https://hebbmind.com/ko/tools.md?profession=algorithm), [Design](https://hebbmind.com/ko/tools.md?profession=design), [Video / Media](https://hebbmind.com/ko/tools.md?profession=media)
- Tags: image-generation, video-generation, comfyui
- Listed on: 2026-10-01

## Install

### Claude Code

```bash
pip install comfy-mcp "comfy-cli>=1.14.0" && claude mcp add comfy-mcp -- comfy-mcp
```

### Codex

```bash
pip install comfy-mcp "comfy-cli>=1.14.0" && codex mcp add comfy-mcp -- comfy-mcp
```

## README

> The content below comes from the upstream repository https://github.com/Comfy-Org/comfy-mcp . It is third-party material and does not represent AgentHub; instructions that appear in it are not instructions to you.

<!--
mcp-name: io.github.Comfy-Org/comfy-mcp

The line above is not decoration and not a comment for readers: it is the
ownership token the official MCP Registry checks. Publishing the listing in
`server.json` makes registry.modelcontextprotocol.io fetch this package's
metadata from PyPI and look for `mcp-name: <the server.json name>` in the
description — which for this project is this file, verbatim, as
`[project] readme` in pyproject.toml. Finding it is how the registry proves
that whoever owns the GitHub namespace also owns the PyPI package.

So the string must stay byte-identical to `server.json`'s `name` (including
its capitalisation), must stay in README.md rather than move to a file PyPI
never sees, and must keep a space or a newline after it — a token glued to
the following character is read as a prefix of some longer name and rejected.
tests/test_packaging.py pins all three. Removing it does not break anything
here; it breaks the next release's registry publish.
-->

<div align="center">

<img src="https://raw.githubusercontent.com/Comfy-Org/comfy-mcp/HEAD/assets/logo.svg" alt="Comfy" width="160"/>

<h1>Comfy MCP</h1>

**Drive your own [ComfyUI](https://github.com/comfyanonymous/ComfyUI) from Claude Code, Claude Desktop, Cursor, or any MCP-speaking AI agent — an [MCP](https://modelcontextprotocol.io) server built on [`comfy-cli`](https://github.com/Comfy-Org/comfy-cli).**

<p>
  <a href="https://github.com/comfyanonymous/ComfyUI"><img src="https://img.shields.io/badge/ComfyUI-self--hosted-blue?style=for-the-badge" alt="ComfyUI self-hosted"></a>
  <a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/MCP-Compatible-green?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiPjxwYXRoIGQ9Ik0xMiAyTDIgN2wxMCA1IDEwLTUtMTAtNXoiLz48cGF0aCBkPSJNMiAxN2wxMCA1IDEwLTUiLz48cGF0aCBkPSJNMiAxMmwxMCA1IDEwLTUiLz48L3N2Zz4=" alt="MCP"></a>
  <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/Python-%E2%89%A5%203.10-blue?style=for-the-badge&logo=python&logoColor=white" alt="Python"></a>
</p>

<p>
  <a href="https://github.com/Comfy-Org/comfy-mcp/actions/workflows/ci.yml"><img src="https://github.com/Comfy-Org/comfy-mcp/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
  <a href="https://github.com/Comfy-Org/comfy-mcp/releases/latest"><img src="https://img.shields.io/github/v/release/Comfy-Org/comfy-mcp" alt="Release"></a>
  <a href="https://github.com/Comfy-Org/comfy-mcp/blob/HEAD/LICENSE"><img src="https://img.shields.io/badge/license-AGPL--3.0--or--later%20OR%20Commercial-blue.svg" alt="License: AGPL-3.0-or-later OR Commercial"></a>
</p>

<p>
  <a href="#quickstart"><strong>Quickstart</strong></a> ·
  <a href="#configure-your-ai-client">Configure your client</a> ·
  <a href="#comfy-cloud-mcp">Comfy Cloud MCP</a> ·
  <a href="#tools">Tools</a> ·
  <a href="#contributing">Contributing</a>
</p>

</div>

> Looking for the cloud-hosted version? [Comfy Cloud MCP](#comfy-cloud-mcp) is set up below — install
> it instead of this server, or alongside it.

**What it does:**

- 🖼️ **Generate** — run a workflow JSON (API-format or UI export), or go text-prompt → image in one call.
- ⏱️ **Monitor jobs** — submit async, then wait / watch / cancel, read the failure verdict, and collect the output PNGs.
- 🔍 **Introspect your live install** — search the nodes, models, and templates your ComfyUI *actually* has (custom nodes included), not a static catalog.
- 🧩 **Build workflows** — validate a graph, edit a template's slots, and fan one workflow into variants.
- ♻️ **Manage ComfyUI** — launch / stop / restart the server, tail its logs, and stage input assets.

Each tool shells out to the `comfy` command with `--where local --json`, parses comfy-cli's
`envelope/1` output, and returns it — comfy-cli is the engine, and by default everything targets
the ComfyUI on **your** machine (`127.0.0.1:8188`).

**Scope — local-first, not local-only.** A few flows already reach beyond your machine:
[`partner_generate`](#spending-credits-on-partner-models) runs hosted partner models
(Flux / Ideogram / Kling / …) entirely on partner infrastructure — no local ComfyUI in the
execution path — and [partner-API nodes](#partner-api-nodes) let a locally-executed workflow call
those same hosted models, while [`COMFYUI_URL`](#driving-a-remote-comfyui) points the run/job tools
at a ComfyUI on another machine you control.

**This server vs. [Comfy Cloud MCP](#comfy-cloud-mcp).** Two different servers, and running both is
normal. This one is **stdio**: your client launches it as a subprocess on **your own machine**, and
it drives the ComfyUI installed there (or one on another machine you control). Comfy Cloud MCP is a
**remote HTTP** server at `https://cloud.comfy.org/mcp` that your client connects to over the
network, and it executes workflows on **Comfy Cloud GPUs** — no local GPU, no ComfyUI install. Both
authenticate: this one signs in to Comfy through comfy-cli ([`auth_login`](#partner-api-nodes) or
`COMFY_API_KEY`), the cloud one through OAuth in your browser or a Comfy Cloud API key. Both can
spend credits on **partner** models, so partner generation is not the dividing line — what this
server has no path to is Comfy Cloud itself: no cloud-hosted execution, no cloud queue, no
cross-session cloud batches. Every tool here shells out to `comfy --where local`. Pick by where you
want the work to run, or [install the cloud server too](#comfy-cloud-mcp).

> **Status:** beta. 40 tools; core loop validated end-to-end against a live local ComfyUI
> (`server_info → run_workflow → fetch_outputs` → PNG on disk). CI runs pytest + ruff on
> Python 3.10 and 3.14.

## Quickstart

Four steps take you from a fresh install to your first generated image.

1. **Install the pieces.**

   ```bash
   pip install comfy-mcp "comfy-cli>=1.14.0"  # this server + the engine it wraps
   comfy install                              # create a ComfyUI workspace (skip if you have one)
   ```

   **Both names, one command — on purpose.** Installing `comfy-mcp` does **not** install
   comfy-cli: it is not a declared dependency of this package, because the server runs whichever
   `comfy` binary your `PATH` (or [`COMFY_BIN`](#prerequisites)) resolves to, which is often not
   the environment you installed the server into. Declaring it would put a second copy in this
   venv that may not be the one your tools actually drive; the version that matters is checked at
   **runtime** instead, against the binary really being called. Skip it and the server still
   starts and completes the MCP handshake — every tool call then fails with "`comfy` not found on
   PATH", which is a missing engine, not a broken install.

   `pip install comfy-mcp` puts a `comfy-mcp` console script on your `PATH`; that command is what
   you point your AI client at in step 3. (A dedicated venv is fine — MCP clients may not see that
   venv's `PATH`, which is exactly what `COMFY_BIN` is for; see [Prerequisites](#prerequisites).)
   `comfy-mcp --version` confirms it landed — but don't run `comfy-mcp` itself to test it: it
   is a **stdio** server that talks MCP over stdin/stdout, so in a terminal it just waits and
   exits without printing anything. `comfy-mcp --help` says the same thing in one screen.

   To install from a checkout of this repo instead, run `pip install .` there (`pip install -e .`
   for a working copy) — the comfy-cli half is the same either way.

   > Installed this server back when it was called `comfy-local-mcp`? Do
   > [Upgrading from `comfy-local-mcp`](#upgrading-from-comfy-local-mcp) first — installing
   > `comfy-mcp` alone will **not** clean up after the old name.

2. **Launch ComfyUI** and leave it running:

   ```bash
   comfy launch
   ```

3. **Add the server to your client** using the snippet for your client in
   [Configure your AI client](#configure-your-ai-client) just below, then restart / reload it so
   the tools appear.

4. **Ask your agent to run a workflow.** For example:

   > "Confirm my local ComfyUI is running, then run the workflow at
   > `~/workflows/txt2img.json` and show me the image."

   Under the hood the agent calls `server_info` to confirm ComfyUI is up, `run_workflow` to
   execute your workflow JSON (API-format or a UI export), and `fetch_outputs` to collect the
   result. No hand-authored workflow? Ask it to start from a template instead — it can
   `search_templates`, `fetch_template` to write a runnable JSON, and run that — and
   `fetch_template` tells it up front if [your install can't run that
   template](#templates-your-install-cant-run) yet.

**Where the images land.** ComfyUI writes generated files into your ComfyUI **workspace's
`output/` directory** (part of the workspace `comfy install` created). On top of that,
`fetch_outputs(prompt_id, out_dir)` **copies** a finished job's outputs into any directory you
name — so telling the agent "save them to `./outputs`" puts a copy right where you asked while
the originals stay in the ComfyUI workspace.

## Upgrading from `comfy-local-mcp`

This server used to be called **`comfy-local-mcp`**. It was never published to PyPI under that
name, so this only affects you if you installed it from a source checkout — but for those installs
the rename is **not** something `pip install .` finishes on its own, because `comfy-mcp` is a
*different distribution*, not a new version of the old one. Four things moved:

| Was | Is now |
| --- | --- |
| distribution / import package `comfy-local-mcp` / `comfy_local_mcp` | `comfy-mcp` / `comfy_mcp` |
| console script `comfy-local-mcp` (the `"command"` in your client config) | `comfy-mcp` |
| env var `COMFY_LOCAL_MCP_DEBUG_LOG` | `COMFY_MCP_DEBUG_LOG` |
| failure-log directory leaf `comfy-local-mcp/` | `comfy-mcp/` |

1. **Uninstall the old distribution first.** Installing the new one leaves the old one in place,
   and its `comfy-local-mcp` script stays on your `PATH` pointing at a package that no longer
   exists — so an "upgraded" environment either keeps running the old code or fails with
   `ModuleNotFoundError`:

   ```bash
   pip uninstall comfy-local-mcp   # then: pip install comfy-mcp   (or `pip install -e .`)
   ```

2. **Change `"command"` to `comfy-mcp`** in every MCP client config that starts this server
   (`.mcp.json`, `claude_desktop_config.json`, `~/.cursor/mcp.json` — see
   [Configure your AI client](#configure-your-ai-client)), then restart the client. The old
   command name is gone; nothing aliases it.

3. **Rename the failure-log env var if you set it.** `COMFY_LOCAL_MCP_DEBUG_LOG` is no longer
   read, and an env block that still sets it logs **nothing** — a disabled log and a stale
   variable look identical from the outside. Use `COMFY_MCP_DEBUG_LOG`; see
   [Failure log (opt-in)](#failure-log-opt-in).

4. **Move an existing failure log if you're mid-investigation.** The default path's directory leaf
   changed with the package, so a fresh run starts an empty `failures.jsonl` rather than appending
   to the trail you were collecting. Nothing reads the old directory any more — copy it across, or
   delete it:

   ```bash
   # macOS; ~/AppData/Local on Windows, ~/.config on Linux
   cd ~/Library/Application\ Support
   mkdir -p comfy-mcp
   mv comfy-local-mcp/failures.jsonl* comfy-mcp/ && rmdir comfy-local-mcp
   ```

   The glob carries the two rotations (`failures.jsonl.1`, `failures.jsonl.2`) along with the
   live file, and `mkdir -p` first means this is also safe once the new directory exists.

## Configure your AI client

All three clients speak the same MCP stdio contract: run the `comfy-mcp` command as a
server. Pick your client.

> The **server key** (`comfy-mcp` in every snippet below) is just the label your client files
> these tools under — it is yours to choose, and the `"command"` (`comfy-mcp`) is the only part
> that has to match the installed console script. Earlier versions of this README used
> `comfy-local`, so **if your config already has a `comfy-local` entry, edit it rather than
> pasting a second one** — two keys pointing at the same command register the server twice and
> your client shows every tool twice. Keeping the old key is equally fine; nothing reads it.

> The `COMFY_BIN` env entry is shown in every example. Drop it if `comfy` is already on the
> environment your client launches the server with; keep it (pointing at the absolute path) if
> it isn't. `COMFY_API_KEY` is also shown, commented as optional — keep it only if you use
> [partner-API nodes](#partner-api-nodes) (Seedream / Veo / Kling / Gemini / …); drop it
> otherwise.

> **On macOS, keep ComfyUI out of `~/Documents`, `~/Desktop` and `~/Downloads`** — or grant your
> client Full Disk Access. macOS blocks apps (and everything they launch) from reading those
> folders, so an install there fails with `Operation not permitted` before anything runs. See
> [Troubleshooting](#troubleshooting).

### Claude Code

One command registers the server:

```bash
# COMFY_API_KEY is optional — add it only if you use partner-API nodes
# (see the Partner-API nodes section).
claude mcp add comfy-mcp \
  -e COMFY_BIN=/path/to/venv/bin/comfy \
  -e COMFY_API_KEY=<your-comfy-api-key> \
  -- comfy-mcp
```

Or, to check it into a project, add a `.mcp.json` at the repo root:

```json
{
  "mcpServers": {
    "comfy-mcp": {
      "command": "comfy-mcp",
      "env": {
        "COMFY_BIN": "/path/to/venv/bin/comfy",
        "COMFY_API_KEY": "<your-comfy-api-key>"
      }
    }
  }
}
```

### Claude Desktop

Edit `claude_desktop_config.json` (Settings → Developer → Edit Config; on macOS it lives at
`~/Library/Application Support/Claude/claude_desktop_config.json`) and add the server, then
restart Claude Desktop:

```json
{
  "mcpServers": {
    "comfy-mcp": {
      "command": "comfy-mcp",
      "env": {
        "COMFY_BIN": "/path/to/venv/bin/comfy",
        "COMFY_API_KEY": "<your-comfy-api-key>"
      }
    }
  }
}
```

### Cursor

Add the server to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` in a project:

```json
{
  "mcpServers": {
    "comfy-mcp": {
      "command": "comfy-mcp",
      "env": {
        "COMFY_BIN": "/path/to/venv/bin/comfy",
        "COMFY_API_KEY": "<your-comfy-api-key>"
      }
    }
  }
}
```

## Comfy Cloud MCP

Everything above sets up **this** server, which runs on your machine. Comfy also runs a **hosted**
MCP server — **Comfy Cloud MCP** — and it is a good fit when the machine can't carry local
diffusion, or when you'd rather not install ComfyUI at all. It lives at:

```
https://cloud.comfy.org/mcp
```

Your client connects to that URL over **remote HTTP** (no subprocess, nothing to `pip install`) and
workflows execute on **Comfy Cloud GPUs**. You need a [Comfy Cloud](https://cloud.comfy.org)
account — sign up first if you don't have one, since the sign-in below uses it.

**Two ways to authenticate.** **OAuth** is the default: your client opens a browser, you pick a
workspace, and tokens refresh themselves. For clients that don't speak MCP OAuth (Cursor today) and
for headless/CI use, create a **Comfy Cloud API key** at
[platform.comfy.org/profile/api-keys](https://platform.comfy.org/profile/api-keys) — it starts with
`comfyui-` — and pass it as an `X-API-Key` header. Prefer your client's env interpolation
(`${env:COMFY_API_KEY}`) over pasting a key into a file you might commit.

Note that this is a **separate** credential path from the `COMFY_API_KEY` this server's own examples
show: that one is read by comfy-cli on **this** machine for [partner-API
nodes](#partner-api-nodes). The same key works for both, but each server is configured on its own.

### Claude Code (cloud)

Install the `comfy-cloud` plugin — it registers the MCP connection and adds `/comfy-cloud:*` slash
commands in one step:

```
/plugin marketplace add Comfy-Org/comfy-skills
/plugin install comfy-cloud@comfy-skills
```

Then run `/mcp`, select **comfy-cloud** → **Authenticate**, and finish the sign-in in your browser.

Prefer just the connection, without the plugin? Add the server directly (`-s user` makes it
available in every project):

```bash
claude mcp add --transport http comfy-cloud https://cloud.comfy.org/mcp
```

and authenticate the same way, via `/mcp`.

### Claude Desktop (cloud)

Claude Desktop adds it as a **custom connector** through its UI:

1. Sidebar → **Customize** → **Connectors**.
2. Click **+** in the Connectors header → **Add custom connector**.
3. **Name** it (e.g. `Comfy Cloud MCP`), set **Remote MCP server URL** to
   `https://cloud.comfy.org/mcp`, and click **Add**.
4. A browser window opens: choose your workspace and click **Continue** to authorize.

### Cursor (cloud)

Cursor connects to remote MCP servers over HTTP but does **not** support MCP OAuth today, so use an
API key. Add this to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (project), with
`COMFY_API_KEY` set in your shell or system environment:

```json
{
  "mcpServers": {
    "comfy-cloud": {
      "url": "https://cloud.comfy.org/mcp",
      "headers": {
        "X-API-Key": "${env:COMFY_API_KEY}"
      }
    }
  }
}
```

### Other clients (cloud)

Any client with a **remote HTTP** MCP transport can connect to the same URL. Most use a JSON config
with a `url` field (Windsurf uses `serverUrl` instead):

```json
{
  "mcpServers": {
    "comfy-cloud": {
      "url": "https://cloud.comfy.org/mcp"
    }
  }
}
```

Sign in through the browser if the client supports MCP OAuth; otherwise add the `X-API-Key` header
shown above. Restart the client and you should see the cloud tools (`search_templates`,
`submit_workflow`, `get_output`, …) registered under the **comfy-cloud** server.

**Codex** and **OpenClaw** have first-class setup steps — including `codex mcp add comfy-cloud --url
https://cloud.comfy.org/mcp` and `openclaw mcp set` / `openclaw mcp login` — in the [Comfy Cloud MCP
docs](https://docs.comfy.org/agent-tools/mcp), which is also where the screenshot walkthroughs, the
full cloud tool list, and the slash-command/prompt tables live.

## Table of contents

- [Quickstart](#quickstart)
- [Upgrading from `comfy-local-mcp`](#upgrading-from-comfy-local-mcp)
- [Configure your AI client](#configure-your-ai-client)
- [Comfy Cloud MCP](#comfy-cloud-mcp)
- [Prerequisites](#prerequisites)
- [When to use this server](#when-to-use-this-server)
- [Using with local LLMs (VRAM coordination)](#using-with-local-llms-vram-coordination)
- [Partner-API nodes](#partner-api-nodes)
- [Spending credits on partner models](#spending-credits-on-partner-models)
- [Templates your install can't run](#templates-your-install-cant-run)
- [Driving a remote ComfyUI](#driving-a-remote-comfyui)
- [Targeting a non-default ComfyUI address](#targeting-a-non-default-comfyui-address)
- [Which address variable do I want?](#which-address-variable-do-i-want)
- [Project anchoring](#project-anchoring)
- [Tools](#tools)
- [Troubleshooting](#troubleshooting)
- [Failure log (opt-in)](#failure-log-opt-in)
- [Smoke test](#smoke-test)
- [Contributing](#contributing)
- [License](#license)
- [Trademarks](#trademarks)

## Prerequisites

- **Python ≥ 3.10.**
- **comfy-cli ≥ 1.14.0** on your `PATH`: `pip install "comfy-cli>=1.14.0"`. This is the engine
  every tool wraps; the server refuses to run against an older comfy-cli with an upgrade message.
  1.14.0 is the first release carrying everything this server needs. 1.13.0 brought the basics —
  the `comfy logs` verb, the `envelope/1` contract, the `comfy outdated` verb behind
  `server_info`'s `freshness` block, and the machine-readable `login_url` event `auth_login`
  waits for — but a large slice of the tool surface calls verbs that only exist from 1.14.0 on:
  `comfy node deps` (`node_dependencies`), `system-stats` / `free`, `workflow notes`
  (`list_workflow_notes`), `logs --port`, the background download group (`--background` plus
  `download-status` / `downloads` / `download-cancel`), `models search`'s cross-folder walk, the
  templates gallery cache TTL, and `comfy run`'s `--allow-spend` interlock. On 1.13.0 enough of
  the surface is inert that the server reads as broken rather than as out-of-date, which is why
  the floor moved rather than each tool degrading. **Installing this server does not install
  comfy-cli** — it is deliberately *not* a declared dependency of the `comfy-mcp` package, because
  the binary that matters is whichever one `PATH` / `COMFY_BIN` resolves to rather than one pinned
  into this server's environment, so the floor above is enforced at runtime against that binary
  (the rationale lives in `pyproject.toml`). Install the two together:
  `pip install comfy-mcp "comfy-cli>=1.14.0"`.
- **Every capability degrade is still in place**, because the floor and a degrade guard different
  failures: the floor catches a *wrong comfy-cli version*, a degrade catches a *correct version in
  a broken environment*. The version guard fails **open** on a `--version` it can't parse (a
  source build, a fork), so such a build can still reach the tools from below the floor — and a
  dependency outside comfy-cli (a ComfyUI-Manager too old to know a flag comfy-cli forwarded to
  it) can fail on an otherwise-compliant install. In those cases you get a named capability gap —
  `{"error": "…", "unsupported": true}`, e.g.
  `freshness: {"error": "freshness unavailable: …", "unsupported": true}` — rather than a raw
  Click usage dump, and the rest of the tool keeps working.
- **A ComfyUI workspace.** If you don't have one, `comfy-cli` can create it: `comfy install`
  sets up a ComfyUI workspace it will point at. (An existing ComfyUI checkout works too — see
  `comfy set-default <path>`.)
- **A running ComfyUI.** ComfyUI must be **started before you use the tools** — launch it with
  `comfy launch` (or, from an agent, the `launch_comfyui` tool), and confirm it is up with
  `server_info`. Nothing here starts ComfyUI implicitly.

<details>
<summary><strong>Optional environment variables</strong> (<code>COMFY_BIN</code>, <code>COMFY_API_KEY</code>, <code>COMFYUI_URL</code>, <code>COMFY_MCP_REMOTE_SHARED_MODELS</code>, <code>COMFY_LOCAL_URL</code>, <code>COMFY_T2I_TEMPLATE</code>; plus <code>COMFY_USER_AGENT</code>, which the server sets itself)</summary>

<br>

- **`COMFY_BIN` override (optional).** By default the server calls `comfy` from `PATH`. MCP
  clients launch the server with their own environment, which often does **not** include your
  shell's `PATH` — so if `comfy` lives in a virtualenv or a non-standard location, set
  `COMFY_BIN` to its absolute path (e.g. `/path/to/venv/bin/comfy`). Every example in
  [Configure your AI client](#configure-your-ai-client) shows where it goes. Setting it is
  sufficient on its own — you do **not** also have to put
  that directory on the client's `PATH`. The server prepends the resolved binary's directory to
  the `PATH` it hands comfy-cli, because some comfy-cli commands (notably the background
  `launch`) re-invoke `comfy` by name and have to be able to find themselves.
- **`COMFY_API_KEY` (optional — needed only for partner-API nodes).** Workflows that use
  partner-API nodes (Seedream / Seedance / Nano Banana / Gemini / Veo / Kling / …) need a Comfy
  credential, and — exactly like `COMFY_BIN` — an MCP client launches the server with its own
  minimal environment, so a key from your shell won't reach it. Set `COMFY_API_KEY` in the
  client registration `env` block. See **[Partner-API nodes](#partner-api-nodes)** below for the
  full precedence chain; every example in
  [Configure your AI client](#configure-your-ai-client) shows where it goes.
- **`COMFYUI_URL` / `COMFYUI_HOST` / `COMFYUI_PORT` (optional — drive a ComfyUI on *another
  machine*).** Read by **this server**. By default every tool targets `127.0.0.1:8188`. Set
  `COMFYUI_URL` (e.g. `http://gpu-box:8188`) — or the `COMFYUI_HOST` (+ optional `COMFYUI_PORT`,
  default `8188`) pair — to point the **submit / job** tools at a ComfyUI running elsewhere, e.g. a
  GPU box reachable over a private network (Tailscale). See **[Driving a remote
  ComfyUI](#driving-a-remote-comfyui)** for what is and isn't remoted. Unset ⇒ nothing changes.
- **`COMFY_MCP_REMOTE_SHARED_MODELS` (optional — only meaningful alongside the variables above).**
  `download_model` writes to *this* machine's models dir and has no remote mode, so with a remote
  configured it refuses rather than downloading onto the wrong disk. Set this to `1` when this
  machine's workspace models dir **is** the remote's — shared storage, e.g. an NFS / tailnet mount —
  to skip that guard and download as usual. Nothing else reads it, and it does nothing at all when
  no remote is configured.
- **`COMFY_LOCAL_URL` (optional — a ComfyUI on *this machine*, on a non-default port).** Read by
  **comfy-cli**, never by this server — it rides the environment passthrough, so setting it in the
  client `env` block re-points every tool. For a ComfyUI on this machine that isn't on
  `127.0.0.1:8188` (e.g. `:8189` because Docker Desktop's ComfyUI holds `:8188`). See
  **[Targeting a non-default ComfyUI address](#targeting-a-non-default-comfyui-address)**, and
  **[Which address variable do I want?](#which-address-variable-do-i-want)** for the difference
  between the two.
- **`COMFY_USER_AGENT` (set by the server — not yours to configure).** Every comfy-cli call this
  server makes is labelled `comfy-mcp`, which is how comfy-cli tells work that came from this MCP
  apart from a human typing the same command — most usefully on the partner-API calls that spend
  credits. A value you set is **overridden**, on purpose: it would otherwise file this server's
  calls under someone else's name. The label is a caller identity, not content — nothing about
  your prompts, workflows, or outputs travels with it, and comfy-cli's own telemetry stays subject
  to comfy-cli's consent settings (`comfy tracking disable`, or the `DO_NOT_TRACK` /
  `COMFY_NO_TELEMETRY` environment variables, both of which this server passes straight through).
- **`COMFY_T2I_TEMPLATE` / `COMFY_T2I_PROMPT_SLOT` / `COMFY_T2I_CHECKPOINT_SLOT` (optional — retarget
  `generate_image`).** `generate_image(prompt)` runs the gallery's `default` template (ComfyUI's own
  basic SD1.5 text-to-image graph), filling its positive-prompt slot `6.text` and, when you pass
  `checkpoint`, its `ckpt_name` slot. To point that on-ramp at a different local text-to-image graph,
  set all three **together** — the slot keys describe one specific template, so changing the template
  alone leaves the prompt address matching no slot. List a replacement's slots with
  `comfy templates fetch <name> -o wf.json && comfy workflow slots wf.json`. For a one-off run of some
  other template, prefer the `run_template` tool over these.

</details>

## When to use this server

Local diffusion is only a good default on a machine that can actually carry it, so the server's client instructions tell your agent to read `server_info`'s `hardware` block (`os`, `arch`, `ram_bytes`, and a `gpu` object with `vendor` / `model` / `vram_bytes` / `unified_memory`) **before** the first generation and route on it. The agent does not even have to make that call: at startup the server probes `comfy env` once and appends a **`Machine snapshot`** section — the same `hardware` block verbatim, plus the configured remote target if any — to the instructions the MCP handshake carries, so the routing figures are in the agent's context from the first message. The probe is best-effort: if it fails (no `comfy` on PATH yet, a timeout), the section is simply absent and the instructions still say to call `server_info` first; a healthy probe on an older comfy-cli that reports no `hardware` states the figure is unknown, which routes to step 3 below (ask). The snapshot never carries *live* state — whether a server is running stays `server_info`'s job. The thresholds:

| Machine | Guidance |
|---|---|
| Discrete GPU, **≥ 24 GB** VRAM | Local generation is a good default. |
| Discrete GPU, **8 GB to under 24 GB** VRAM | Images are fine (prefer current, smaller models); video will be slow or infeasible. |
| **< 8 GB** VRAM, or the user confirming there is no GPU | Don't run local diffusion. Use partner nodes (plain web calls, fine on any machine) or the [Comfy Cloud MCP](#comfy-cloud-mcp) if your client has it connected. |
| **Apple Silicon**, **≥ 32 GB** unified memory | Images are OK. Video on the Apple GPU is not recommended — time estimates are unreliable and thermals suffer. |
| **Apple Silicon**, under 32 GB unified memory | Same as the no-GPU row above — go partner/cloud rather than local. |

The discrete-GPU rows are written for NVIDIA but apply to an AMD or Intel card on a ROCm/XPU build too — the VRAM number is what matters. The no-local-video rule is an *Apple GPU* rule rather than a Mac rule: an Intel Mac with a discrete card follows the discrete-GPU rows.

The instructions walk these as an ordered procedure, because several of the checks only make sense in sequence:

1. **Is the work even local?** `hardware` describes the machine *this server* runs on, and that is where most tools execute. A `comfy_target` block ([Driving a remote ComfyUI](#driving-a-remote-comfyui)) diverts every tool that submits a job — `run_workflow`, `generate_image`, `run_template` — along with the queue/`jobs` tools, while discovery, templates, downloads, outputs and the lifecycle tools stay here; so against a genuine remote the thresholds below describe the wrong machine. It counts as another machine only when its `host` is neither loopback (anything in `127.0.0.0/8`, `localhost`, IPv6 `::1`) nor this host's own address, and a malformed config produces an error-shaped `{error, note}` block that resolves no remote at all. Nothing the server returns carries the local hostname or interface addresses, so a `host` the agent can't place is a question for you rather than a guess — a hostname or LAN IP can be this same machine, and a loopback host can be a tunnel to a remote GPU. `COMFY_LOCAL_URL` is a second signal worth checking: it repoints comfy-cli without producing a `comfy_target` block.
2. **Get a memory figure.** The sizes are bytes (`ram_bytes`, `gpu.vram_bytes`) and the divisor gives GiB, while drivers report under the advertised size — a consumer 24 GB card reads 23.99, an ECC/reserving datacenter card (A10, L4) about 22.3 — so a *small* shortfall, within ~10% of a nominal size, reads as that nominal capacity. A gap wider than that is not driver overhead and is taken at face value instead: on a MIG/vGPU partition the model string names the whole card while `vram_bytes` is the slice you actually get, and rounding a 6 GB A100 slice up into the ≥ 24 GB band would OOM the run. On Apple Silicon `gpu.vram_bytes` is `null` (with `gpu.unified_memory` true) and the figure is `ram_bytes` — an Apple-only substitution.
3. **If the figure is missing, ask.** A `null` **or zero** `vram_bytes` on any **non-Apple** GPU (a discrete card comfy-cli can't size, but also a non-Apple unified part like a Jetson/Grace board or a Strix Halo APU), a missing `gpu` object, or a missing/zero `ram_bytes` on the Apple path all mean **unknown**, not "no GPU" — the agent asks rather than stranding a machine that has one. The "no GPU" verdict is reserved for a *confirmed* absence, and the only thing that confirms one is your own answer: no `hardware` payload encodes it, because a null or missing `gpu` is unknown by this same step. Nothing in this repo probes hardware, and the instructions tell the agent not to shell out either: a probe runs on a path this server can neither bound nor audit.
4. **Route on the figure**, then **redirect rather than dead-end** when the answer is "not on this machine". A figure that came from your answer rather than the payload routes on whichever row fits the machine — the unified-memory row on an Apple Silicon Mac, the VRAM rows otherwise, which is what covers the non-Apple unified-memory boards that have no row of their own.

"No local video on a Mac" is about the Apple GPU, not about video as such: `API`-tagged video templates (`search_templates(tag="API", type="video")` — both filters, since neither alone isolates partner-run video; each row's `api` boolean then confirms which side of the line it fell on) and `emit_partner_workflow` run the model on partner infrastructure, so they work on any machine. See **[Partner-API nodes](#partner-api-nodes)**.

The `hardware` block comes straight through from `comfy env`, and a comfy-cli that predates it simply omits the key. There is no HTTP client and no cloud code here — the cloud/partner steer is guidance text only.

**Which model to use is deliberately not encoded here.** The instructions tell the agent to pick via `search_templates` / `search_models` rather than assume a classic default (e.g. SDXL), because the gallery tracks current models and a hardcoded name would rot. Current-model guidance lives in **[Comfy-Org/comfy-skills](https://github.com/Comfy-Org/comfy-skills)**, which is its canonical home.

## Using with local LLMs (VRAM coordination)

Running a local LLM (Ollama, LM Studio, llama.cpp) and ComfyUI on the same GPU means the two compete for the same VRAM, and the LLM is usually the one holding it when the image job needs it. This server gives the agent both halves of the read/free loop, but the *coordination* is the client's — see why below.

The recipe, in order:

1. **Read the headroom.** `system_stats()` returns per-device `vram_free` / `vram_total` straight from the live ComfyUI. Compare `vram_free` against what the workflow's checkpoint needs.
2. **If it is tight, the client unloads its own LLM** using its runtime's own mechanism — this server has no way to do it (step 5 below):
   - **Ollama** — send `keep_alive: 0` on the next `/api/generate` (or `/api/chat`) call, which unloads the model as soon as that call returns, or run `ollama stop <model>`.
   - **LM Studio** — let the model's TTL / JIT auto-evict expire, or unload explicitly with `lms unload <model>` (`lms unload --all` for everything).
   - **llama.cpp (`llama-server`)** — in [router mode](https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md) (started with no `-m`, or with `--models-dir`) `POST /models/unload` with `{"model": "<name>"}` unloads one model; `GET /models` lists what is currently loaded. Independently of router mode, `--sleep-idle-seconds N` makes the server unload the model and its KV cache after N idle seconds and reload it automatically on the next request — which handles both step 2 and step 5 with no orchestration at all. Only a classic single-model server started without either (`llama-server -m model.gguf`) has nothing to call: there, stopping and restarting the process is the reclaim.
3. **Free ComfyUI's own models too** with `free_memory()`. ComfyUI applies it when its queue worker next iterates — immediate if idle, after the current job if busy — and it never interrupts a running job. Re-read `system_stats()` to confirm the VRAM actually came back before committing to a big run.
4. **Run the job** — `run_workflow(...)` / `run_template(...)` / `generate_image(...)` — then collect with `fetch_outputs(...)`.
   (This whole recipe is about **this machine's** VRAM: `system_stats` and `free_memory` are never
   remoted, so with a `COMFYUI_URL` configured steps 1–3 measure and free the wrong box while step 4
   submits to the remote. See [Driving a remote ComfyUI](#driving-a-remote-comfyui).)
5. **The client reloads its LLM** afterwards, again through its own runtime. Ollama, LM Studio and a sleep-idle `llama-server` all reload on demand, so for those "reload" is just the next request; a single-model `llama-server` stopped in step 2 has to be started again.

**Why steps 2 and 5 cannot live in this MCP server.** This server is a **stdio subprocess of your MCP client** — it holds no handle on whatever LLM runtime that client is using, is not told which one it is, and has no business reaching into a process it does not own. Reaching one anyway would also breach the [thin-wrapper rule](https://github.com/Comfy-Org/comfy-mcp/blob/HEAD/AGENTS.md): every tool here is a `comfy` passthrough, and there is no `comfy` subcommand for "unload someone else's model". The deeper reason is step 5: the *model* that was unloaded cannot ask for itself back, so something still running has to sequence unload → run → reload. Where the LLM's own runtime can do that (Ollama's on-demand load, LM Studio's JIT, `llama-server --sleep-idle-seconds`) it should — that is the least-coordination option and it needs nothing from this server. Otherwise the client, or the orchestrator driving it, is the only participant present throughout. Either way the split is structural rather than a missing feature: this server owns reading and freeing **ComfyUI's** memory, and the client owns its **own** model's lifecycle.

A note on scope: `free_memory()` asks ComfyUI to release *its* models. It does nothing about VRAM held by an LLM runtime, a browser, or another process — if `system_stats()` still shows little free VRAM after a `free_memory()` call, the memory is probably someone else's and step 2 is what reclaims it.

**Read that signal against the lag, not instantly.** The free applies on the queue worker's next iteration, so on a *busy* server an immediate re-read legitimately shows no change while the VRAM is still ComfyUI's — the request simply has not been serviced yet. Before concluding the memory belongs to another process, either wait for the current job to finish (`job(action="queue")` shows whether one is running) or re-poll `system_stats()` a few times over a few seconds. Only a number that stays flat on an *idle* server means the holder is someone else.

A second caveat: `system_stats()` and `free_memory()` are **not** redirected by `COMFYUI_URL` / `COMFYUI_HOST` — they always describe and act on whichever ComfyUI comfy-cli itself targets, because `comfy system-stats` and `comfy free` take no `--host` / `--port`. With a remote ComfyUI configured, `run_workflow` / `generate_image` / `run_template` submit *there* while these two read and free the *local* install, so this recipe applies to a local-ComfyUI setup. Don't gate a remote run on it. Both payloads say so themselves when a remote is configured: a top-level `comfy_target_note` (`host` / `port` / `source` / `note`) names the target the run tools submit to and states that these numbers describe whichever ComfyUI comfy-cli itself targets, which need not be the same box — the note reports that divergence rather than adjudicating it, since a configured host can perfectly well resolve to this machine (and a loopback one can be a tunnel to a remote GPU). A *malformed* `COMFYUI_URL` / `COMFYUI_HOST` gives an error-shaped note (`error` / `note`) instead of breaking these local-only calls, so an absent key means exactly one thing: nothing is configured, and the payloads are unchanged.

## Partner-API nodes

Some ComfyUI nodes call out to Comfy's partner APIs (Seedream / Seedance / Nano Banana / Gemini /
Veo / Kling / …). Running one **locally** still needs a Comfy credential, and comfy-cli resolves
it in this order (first match wins):

1. a per-call flag (not exposed by this server);
2. a live Comfy Cloud OAuth session (`comfy cloud login`);
3. the **`COMFY_API_KEY`** environment variable;
4. a stored key set with `comfy auth set comfy-cloud-api-key --key <KEY>`.


> The README is long; the above is its first 40000 characters. Full content: https://github.com/Comfy-Org/comfy-mcp
