.bat you drop next to ComfyUI that clones the right
custom nodes and downloads every model to the right folder. The community —
Aitrepreneur especially — has made
these for nearly every new model, and they’re genuinely great. You go from
“new model dropped” to “generating” in one double-click.
We love the UX. We do not love what it’s made of.
The problem with a folder of scripts
A hand-written installer is a pile of duplicated, unvalidated facts:- Windows-only. The
.batdoesn’t run on RunPod, so there’s a parallel.sh— the same URLs and folders, copy-pasted, free to drift apart. - No validation. Nothing checks that
https://huggingface.co/.../ideogram4_fp8_scaled.safetensorsstill exists. When a repo gets restructured or a file renamed, the URL 404s — or worse, returns a tiny HTML error page that your downloader cheerfully saves asmodel.safetensors. You find out 30 GB and one failed generation later. - It’s a script, so you can’t reason about it. Want to know what models a
pack pulls, or reuse them from an agent? You’re grepping
curllines.
Make it a manifest
So in comfyui-mcp a pack is data:manifest.yaml isn’t a new format. It’s a ComfyManifest —
the exact shape the server’s existing apply_manifest tool already consumes to
install nodes + models on a live ComfyUI. So one file drives two things:
- MCP-native install — from a Claude session:
apply_manifest --path packs/anima/manifest.yaml, idempotent, with the same engine that powers the rest of the server. - The double-click scripts —
npm run packs:genreads the manifest and emitsinstall-windows.batandinstall-runpod.sh. The beloved UX survives; the duplication doesn’t. Edit the manifest, rebuild, done.
fix-ltxvideo-kornia.bat and .sh, so the Linux user
isn’t a second-class citizen.
The silent killer: requirements.txt into the right Python
Agit clone of a custom node is only half an install. Most nodes have a
requirements.txt, and if you don’t install it, the node imports fine in your
shell but silently fails to register in ComfyUI — it just never shows up in
the node list, with no error you’d notice. We hit this repeatedly:
- DWPose (controlnet_aux) vanished because
scikit-imagewasn’t installed — even though it’s right there in the node’srequirements.txt. - vrgamedevgirl’s grain/sharpen nodes vanished because
librosawas missing — also in itsrequirements.txt.
pip: list. That’s a band-aid;
the real bug was that the generated installers cloned nodes but never ran their
requirements.txt. The fix is two lines in the generator, and it matters more
than it sounds:
- Resolve each node’s
requirements.txton clone — exactly what ComfyUI-Manager does, so the double-click scripts reach parity with the MCP path (which already delegates to Manager). - Target the ComfyUI venv, not system Python. Installing a node’s deps into the
wrong interpreter is the other way a node “installs” but won’t load. The scripts
now resolve
.venv/Scripts/python.exe(Windows) or.venv/bin/python(Linux), then the portablepython_embeded, then$PYTHON— and echo which one they chose so it’s auditable.
pip: list stays as an explicit net for deps no node lists (and for
already-cloned nodes, since the requirements step only fires on a fresh clone). But
the default is now: clone a node, its Python deps land in the same venv ComfyUI
actually runs — and it shows up.
The part that matters: links can’t rot unnoticed
Generating scripts is nice. The reason this is worth doing is CI. Every push runs.github/workflows/packs.yml on a Linux runner:
-
Schema — every manifest validates against the real
manifestSchema. -
Offline dry-run — each
install-runpod.shruns withgitandcurlstubbed: it must stage every node + model path, and a second run must be a no-op (idempotent). -
The good one — every model URL, for real.
packs:check-urlssends aHEAD(or a 1-byte rangedGET) to every model URL and asserts two things: the link resolves, and the payload size is sane for the model type. A diffusion model that comes back at 4 KB isn’t a model — it’s an error page, and the build goes red:No multi-gigabyte downloads — just enough to know the link is alive and pointing at something the right size. Gated repos (HuggingFace 🔒) answer401/403; we treat that as valid-but-gated, not a failure. And because a single CDN hiccup shouldn’t fail a build, the check retries transient errors before giving up.
.bat files tells you nothing
until a user runs it. A manifest with a URL gate tells you, on every commit,
the moment a model link dies — so the pack on the shelf is one you can trust.
Adding a pack is one file
Point the builder at an upstream installer (the.bat is the ground truth for
exact URLs → folders), drop the resulting manifest.yaml, pack.yaml, and
workflow.json into packs/<name>/, run npm run packs:gen, and CI does the
rest. The shelf so far: ANIMA, Ideogram 4, LTX-2.3, ERNIE,
WAN (animate / longer-videos / transparent), Qwen (image / image-edit)
Z-Image (turbo / base / xy-plot), plus an artokun-flow WAN Animate pack
derived straight from its workflow (no upstream installer) — each one a
manifest, each one validated on every push.
Share what you build
Because a pack is just three small files —manifest.yaml, pack.yaml, and
workflow.json — it’s trivial to pass around. If you build or derive one
locally, send it upstream: open an issue or PR on
comfyui-mcp with those files.
Every contributed pack is reviewed for safety — model URLs, custom-node sources,
no surprises — before it merges, and from then on CI keeps its links honest for
everyone. The bigger the userbase, the more this becomes a shared,
always-validated catalog of battle-tested setups rather than a drawer of one-off
scripts.
The difference between a folder of scripts and a validated catalog is small to
build and large to live with. Your installers stop being write-once artifacts
that rot in a drawer, and start being something CI keeps honest for you.
Browse them in
packs/,
or file a model you want packed at
artokun/comfyui-mcp.