日本語 | 中文 | Español | Français | हिन्दी | Italiano | Português (BR)
Drive ComfyUI from Python. No node graph.
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_imageis 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_imagerejects 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6e3526029e3a39d33ece861a1e4f41a9c1ae75414fdf65a4f563886b5c3e9531
|
|
| MD5 |
454faea6fc063be0c0762c8a8aa5a0a3
|
|
| BLAKE2b-256 |
27629f73eaf9cb63cc045b163c323a8ea81cfd040d23681be353f37493dfe637
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
comfy_headless-3.0.1.tar.gz -
Subject digest:
6e3526029e3a39d33ece861a1e4f41a9c1ae75414fdf65a4f563886b5c3e9531 - Sigstore transparency entry: 2551137266
- Sigstore integration time:
-
Permalink:
mcp-tool-shop-org/comfy-headless@98f63e78c2c456b0a088d577dd64d1bd3ae194b7 -
Branch / Tag:
refs/tags/v3.0.1 - Owner: https://github.com/mcp-tool-shop-org
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@98f63e78c2c456b0a088d577dd64d1bd3ae194b7 -
Trigger Event:
release
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
041c106b12115dd7e9e1e4cf41e3bc0a5a10f52301fc0658de384e8b569c5daf
|
|
| MD5 |
4711dfb5fb62aa84a17a982492933db9
|
|
| BLAKE2b-256 |
45be533bd0d01a8d003e66833664fcbcaea9a5c4525764d15e3e30e5a3991bf9
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
comfy_headless-3.0.1-py3-none-any.whl -
Subject digest:
041c106b12115dd7e9e1e4cf41e3bc0a5a10f52301fc0658de384e8b569c5daf - Sigstore transparency entry: 2551137312
- Sigstore integration time:
-
Permalink:
mcp-tool-shop-org/comfy-headless@98f63e78c2c456b0a088d577dd64d1bd3ae194b7 -
Branch / Tag:
refs/tags/v3.0.1 - Owner: https://github.com/mcp-tool-shop-org
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@98f63e78c2c456b0a088d577dd64d1bd3ae194b7 -
Trigger Event:
release
-
Statement type: