Skip to main content

日本語 | 中文 | Español | Français | हिन्दी | Italiano | Português (BR)

comfy-headless

Drive ComfyUI from Python. No node graph.

CI Codecov PyPI version MIT License Landing Page


What this is

comfy-headless builds ComfyUI API-format graphs and runs them. You call a Python function; it emits the JSON node graph, POSTs it to ComfyUI, polls for completion, and hands you the output paths.

That framing matters, because it tells you what can go wrong. The library's entire job is emitting node names and input keys that the target ComfyUI actually has. When ComfyUI renames or drops a node, a graph referencing the old name is rejected at submit with an opaque error — and nothing warns you first.

v3.0 is the release that took that seriously. Every node type this library emits was audited against the live ComfyUI catalog. Nine no longer existed. They're gone, the graphs that used them are rebuilt on verified nodes, and the library can now tell you what a server is missing before you spend a run on it.

Problem What comfy-headless does
The node interface is a lot Presets and a clean Python API
Prompt engineering is hard Optional AI enhancement via local Ollama
Video generation is fiddly 24 presets across 9 model families
"Which settings do I use?" Recommendations sized to your VRAM
Graphs fail with cryptic errors Dependency check names the node and the pack

Quick start

pip install comfy-headless[standard]
from comfy_headless import ComfyClient

client = ComfyClient()                       # defaults to http://localhost:8188
result = client.generate_image("a beautiful sunset over mountains")
print(result["images"])

generate_image returns a dict with success, prompt_id, images, error, seed and preset. Every generation call in this library returns a dict — there is no result object to unwrap.

Install

pip install comfy-headless              # core only, ~2MB
pip install comfy-headless[standard]    # + AI enhancement + WebSocket (recommended)
pip install comfy-headless[full]        # everything
Extra Adds
ai Prompt analysis and enhancement via local Ollama
websocket Real-time progress over WebSocket
ui Gradio web interface
health System health monitoring
validation Pydantic config validation
observability OpenTelemetry tracing
standard ai + websocket
full All of the above

Requires Python 3.10+ and a running ComfyUI instance.

Check what's active at runtime:

from comfy_headless import FEATURES, list_missing_features

print(FEATURES)                 # {'ai': True, 'websocket': True, 'health': False, ...}
print(list_missing_features())  # {'health': 'pip install comfy-headless[health]', ...}

Images

result = client.generate_image(
    "a cyberpunk street at night",
    negative_prompt="blurry, low quality",
    preset="hd",          # overrides width/height/steps/cfg when set
    seed=42,
)

Eight image presets: draft, fast, quality, hd, portrait, landscape, cinematic, square.

Batch a list of prompts:

result = client.generate_batch(
    ["a red fox", "a snowy owl", "a grey wolf"],
    preset="fast",
)

AI prompt enhancement

Requires the [ai] extra and a local Ollama. These are module-level functions, not client methods:

from comfy_headless import enhance_prompt, analyze_prompt

result = enhance_prompt("a cat", style="balanced")
print(result.enhanced)   # .original .enhanced .negative .additions .reasoning

analysis = analyze_prompt("a cyberpunk city at night")
print(analysis.intent, analysis.styles, analysis.suggested_preset)

Video

from comfy_headless import list_video_presets, get_recommended_preset

print(list_video_presets())                  # 24 presets
print(get_recommended_preset(vram_gb=16))    # picks one that fits

result = client.generate_video(
    "a slow pan across a mountain range",
    preset="ltx_quality",
)
print(result["videos"])

Selection is by preset, not by model — generate_video has no model argument. Any of frames, fps, steps, cfg, width, height can be passed to override the preset.

Image input

Image-to-video, and anything else taking a source image, needs that image to exist inside ComfyUI first. Upload it, then pass the name the server gives back:

ref = client.upload_image("reference.png")
# {"name": "reference.png", "subfolder": "", "type": "input", "ref": "reference.png"}

result = client.generate_video(
    "a cat walking through a garden",
    preset="wan_14b",
    init_image=ref["name"],
)

Read name back from the response rather than reusing the filename you sent — ComfyUI renames on collision, so the two are not always equal. ref is the same value already joined with any subfolder, which is exactly what the graph needs.

Changed in 3.0: init_image is a server-side filename. Earlier versions accepted base64 data and smuggled it through a third-party node that does not exist on a stock ComfyUI install. See the CHANGELOG.

Model families

Family Min VRAM Quality Speed Extra nodes Best for
Wan 2.1/2.2 6 GB Great Medium Low VRAM, efficiency
AnimateDiff Lightning 6 GB Fair Fastest AnimateDiff-Evolved 4-step drafts
AnimateDiff 8 GB Good Fast AnimateDiff-Evolved, Frame Interp. Quick previews
LTX-Video 12 GB Excellent Fast The safe default
Mochi 12 GB Excellent Slow Text adherence, long clips
SVD 12 GB Good Medium Animating a still
Hunyuan 1.5 14 GB Best Slow Highest quality
CogVideoX 16 GB Good Slow CogVideoX Wrapper Legacy
Hunyuan 1.0 24 GB Great Slow Frame Interpolation Superseded by 1.5

Six of the nine families run on stock ComfyUI core nodes — no wrapper pack for the model itself. Only AnimateDiff (both variants) and CogVideoX need one. Video output uses Video Helper Suite; frame interpolation uses Frame Interpolation.

VRAM figures are the floor for that family's default resolution, read from VIDEO_MODEL_INFO — not ceilings. More memory buys longer clips and higher resolutions from the same family.

Check before you run

Rather than discovering a missing node at submit time:

workflow = client.build_video_workflow("a cat walking")

report = client.check_workflow_dependencies(workflow)
print(report["missing_packs"])     # what this server is missing
print(report["required_packs"])    # what this graph needs

# or raise MissingNodePackError, naming the class and the pack that provides it
client.require_workflow_dependencies(workflow)

Configuration

Environment variables use the COMFY_HEADLESS_ prefix with __ section delimiters:

Variable Default
COMFY_HEADLESS_COMFYUI__URL http://localhost:8188
COMFY_HEADLESS_OLLAMA__URL http://localhost:11434
COMFY_HEADLESS_OLLAMA__MODEL qwen2.5:7b
COMFY_HEADLESS_COMFYUI__TIMEOUT_READ read timeout, seconds
COMFY_HEADLESS_LOGGING__LEVEL log level

Or pass the URL directly: ComfyClient("http://192.168.1.50:8188").

Web UI

comfy-headless                 # launching the UI is the default action
Flag Meaning
--port / -p UI port (default 7861)
--share Public Gradio share link
--url ComfyUI server URL
--version / -v Print version
--check Feature availability
--diagnose Full diagnostics

Six tabs: Image, Video, Queue & History, Workflows, Models, Settings. Theme is Ocean Mist — soft teal accents on warm neutral backgrounds.

Programmatically (requires [ui]):

from comfy_headless import launch
launch(port=7861, share=False)

Progress

Blocking calls take an on_progress callback:

client.generate_image("a fox", on_progress=lambda pct, msg: print(f"{pct:.0%} {msg}"))

For real-time updates over WebSocket (requires [websocket]):

import asyncio
from comfy_headless import ComfyWSClient

async def main():
    async with ComfyWSClient() as ws:
        prompt_id = await ws.queue_prompt(workflow)
        return await ws.wait_for_completion(prompt_id)

asyncio.run(main())

Errors

Every exception carries a structured code, message and hint:

from comfy_headless import (
    ComfyHeadlessError,       # base
    ComfyUIConnectionError,   # cannot reach ComfyUI
    ComfyUIOfflineError,      # ComfyUI not responding
    GenerationTimeoutError,
    GenerationFailedError,
    ValidationError,
    UploadError,              # new in 3.0
    MissingNodePackError,     # new in 3.0
)

try:
    client.generate_image("test")
except ComfyUIOfflineError:
    print("Start ComfyUI first")

How it works

your call ─→ build API-format graph ─→ POST /prompt ─→ poll /history ─→ GET /view
                     │
                     └─ validated against GET /object_info

The library talks to seven ComfyUI routes — /system_stats, /object_info, /queue, /history, /prompt, /interrupt, /view — plus /upload/image and /upload/mask for binary input.

/object_info is the authority on what a given server can run. It is a live endpoint, not a versioned artifact: there is no core-node registry to pin against. So the library validates emitted graphs against the target server's actual catalog rather than assuming a fixed node set. check_workflow_dependencies() is that check, and it is load-bearing infrastructure rather than a convenience.

Useful escape hatches when you want the graph itself:

workflow = client.build_txt2img_workflow("a fox")   # the raw API-format dict
prompt_id = client.queue_prompt(workflow)           # submit it yourself
client.wait_for_completion(prompt_id)

Docs

Full handbook: mcp-tool-shop-org.github.io/comfy-headless — getting started, usage, configuration, API reference, video models, architecture.

Security & data scope

  • Data touched: connects to a local or remote ComfyUI instance over HTTP/WebSocket. Sends workflow JSON and uploaded images, receives generated media. Optionally connects to a local Ollama for prompt intelligence. Writes output to temp directories with automatic cleanup.
  • Data NOT touched: no telemetry, no analytics, no external APIs beyond the ComfyUI and Ollama endpoints you configure. Secrets are masked in all log output via SecretValue.
  • Permissions required: network access to your ComfyUI server and optional Ollama server; file write for output and temp directories.
  • Uploads: upload_image rejects subfolder traversal attempts. Uploaded files land in ComfyUI's input directory on whichever server you point at — treat that server as trusted.

See SECURITY.md for vulnerability reporting.

Scorecard

Category Score
A. Security 10/10
B. Error Handling 10/10
C. Operator Docs 10/10
D. Shipping Hygiene 10/10
E. Identity (soft) 10/10
Overall 50/50

Assessed with @mcptoolshop/shipcheck

Related

Part of MCP Tool Shop — open-source ML tooling for local hardware.

Contributing

Issues and pull requests welcome — see CONTRIBUTING.md. Useful areas: additional model families, workflow templates, docs, bug fixes.

If you add a node type, verify it exists in the live ComfyUI catalog first, and declare its pack if it isn't core. That rule is why this release exists.

License

MIT — see LICENSE.


Built by MCP Tool Shop

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

comfy_headless-3.0.1.tar.gz (265.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

comfy_headless-3.0.1-py3-none-any.whl (170.8 kB view details)

Uploaded Python 3

File details

Details for the file comfy_headless-3.0.1.tar.gz.

File metadata

  • Download URL: comfy_headless-3.0.1.tar.gz
  • Upload date:
  • Size: 265.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for comfy_headless-3.0.1.tar.gz
Algorithm Hash digest
SHA256 6e3526029e3a39d33ece861a1e4f41a9c1ae75414fdf65a4f563886b5c3e9531
MD5 454faea6fc063be0c0762c8a8aa5a0a3
BLAKE2b-256 27629f73eaf9cb63cc045b163c323a8ea81cfd040d23681be353f37493dfe637

See more details on using hashes here.

Provenance

The following attestation bundles were made for comfy_headless-3.0.1.tar.gz:

Publisher: publish.yml on mcp-tool-shop-org/comfy-headless

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file comfy_headless-3.0.1-py3-none-any.whl.

File metadata

  • Download URL: comfy_headless-3.0.1-py3-none-any.whl
  • Upload date:
  • Size: 170.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for comfy_headless-3.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 041c106b12115dd7e9e1e4cf41e3bc0a5a10f52301fc0658de384e8b569c5daf
MD5 4711dfb5fb62aa84a17a982492933db9
BLAKE2b-256 45be533bd0d01a8d003e66833664fcbcaea9a5c4525764d15e3e30e5a3991bf9

See more details on using hashes here.

Provenance

The following attestation bundles were made for comfy_headless-3.0.1-py3-none-any.whl:

Publisher: publish.yml on mcp-tool-shop-org/comfy-headless

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

3.1.0

2 files

This release

3.0.1 This release

2 files

2.5.3

2 files

2.5.2

2 files

2.5.1

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page