一键导入
comfyui-node-registry
Authoring & publishing ComfyUI custom nodes to the Comfy Registry — node structure, pyproject.toml spec, comfy-cli publishing, and CI
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Authoring & publishing ComfyUI custom nodes to the Comfy Registry — node structure, pyproject.toml spec, comfy-cli publishing, and CI
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Drive the LTX Director (Timeline) node — its Add Image/Text/Audio buttons are DOM-only and cannot be clicked by an agent; edit the hidden timeline_data JSON widget instead. Load when a workflow contains LTXDirector / LTXDirectorGuide / PromptRelayEncodeTimeline, or when asked to add, move, retime, or remove timeline segments (image / text / audio / motion).
Train a character/identity LoRA locally on FLUX.1-dev via the comfyui-mcp train_* tools (GPU Docker + ostris ai-toolkit). Use when the user wants to train a LoRA of a person/character from their photos on the local GPU — covers dataset prep, launch, monitoring, and using the result in ComfyUI. For WAN/Z-Image training via the ai-toolkit UI see ai-toolkit-trainer.
Core ComfyUI knowledge — workflow format, node types, pipeline patterns, and MCP tool usage
Discover Civitai models with the BUILT-IN search_civitai_models tool and install/generate them locally — find a checkpoint/LoRA/embedding on Civitai, download it into ComfyUI, and use its trigger words. Optionally pair the official Civitai MCP for community features (images browsing, posting, collections).
Run the ComfyUI agent locally for FREE — no subscription, no API key, fully offline — using our gemma4 models fine-tuned on the comfyui-mcp tool suite via Ollama. Use when the user asks about running locally, running for free, offline use, avoiding API costs, Ollama setup, or which local model to pick.
Debug a WRONG or imperfect render (not a hard error) by inspecting inputs and intermediate steps with run-to-node — render one branch up to an output, preview-tap latents/masks/preprocessor maps, localize the first bad stage, then fix. Use when a final image/video comes out wrong — artifacts, wrong subject/pose/composition/color, blur, a ControlNet/IPAdapter/mask/LoRA not taking, a two-stage refiner or upscale degrading the result — rather than the run failing with an error (for errors/OOM/missing nodes use the troubleshooting skill).
| name | comfyui-node-registry |
| description | Authoring & publishing ComfyUI custom nodes to the Comfy Registry — node structure, pyproject.toml spec, comfy-cli publishing, and CI |
This skill covers writing a ComfyUI custom node pack and publishing it to the Comfy Registry (registry.comfy.org), the public catalog that powers ComfyUI-Manager. For using existing nodes in workflows, see the comfyui-core skill instead.
A node pack is a Python package placed under ComfyUI/custom_nodes/<name>/. The package __init__.py must export NODE_CLASS_MAPPINGS and NODE_DISPLAY_NAME_MAPPINGS; WEB_DIRECTORY is optional (only if the pack ships frontend JS).
ComfyUI/custom_nodes/my-node-pack/
├── __init__.py # exports the mappings ComfyUI scans for
├── nodes.py # node class definitions
├── pyproject.toml # registry metadata (required to publish)
├── requirements.txt # optional Python deps
├── .comfyignore # optional — exclude files from the published archive
├── LICENSE
├── README.md
└── web/js/ # optional frontend extension (see WEB_DIRECTORY)
__init__.pyfrom .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
# Optional: serve frontend JS/CSS from this folder (path relative to __init__.py)
WEB_DIRECTORY = "./web/js"
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
nodes.py)class ImageSelector:
CATEGORY = "example" # menu path where the node appears
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"images": ("IMAGE",),
"mode": (["brightest", "reddest", "greenest", "bluest"],),
},
"optional": {
"threshold": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
"count": ("INT", {"default": 1, "min": 1, "max": 64}),
"label": ("STRING", {"default": "", "multiline": False}),
},
}
RETURN_TYPES = ("IMAGE",) # tuple of output data types
RETURN_NAMES = ("image",) # optional friendly output names
FUNCTION = "choose_image" # name of the method ComfyUI calls
OUTPUT_NODE = False # True for terminal nodes (e.g. SaveImage)
def choose_image(self, images, mode, threshold=0.5, count=1, label=""):
import torch
brightness = [torch.mean(img.flatten()).item() for img in images]
best = brightness.index(max(brightness))
return (images[best].unsqueeze(0),) # MUST return a tuple
NODE_CLASS_MAPPINGS = {
"ImageSelector": ImageSelector, # globally unique class_type key
}
NODE_DISPLAY_NAME_MAPPINGS = {
"ImageSelector": "Image Selector", # label shown in the UI
}
| Member | Required | Purpose |
|---|---|---|
INPUT_TYPES | yes | @classmethod returning {"required": {...}, "optional": {...}}. Each input is (TYPE,) or (TYPE, {opts}). |
RETURN_TYPES | yes | Tuple of output type strings (e.g. ("IMAGE", "MASK")). Single output still needs a trailing comma. |
FUNCTION | yes | String name of the method to execute. |
CATEGORY | yes | Menu path string for the Add Node menu. |
RETURN_NAMES | no | Friendly names for outputs (defaults to lowercased types). |
OUTPUT_NODE | no | True marks a terminal node that produces a result (save/preview). |
Input type widgets — the options dict drives the UI widget:
("INT", {"default": 0, "min": 0, "max": 100, "step": 1})("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.1})("STRING", {"default": "", "multiline": True})(["a", "b", "c"],) — a literal list becomes a dropdown("IMAGE",), ("LATENT",), ("MODEL",) etc. — typed connections (no widget)The executing method must return a tuple matching RETURN_TYPES, even for a single output (return (result,)).
pyproject.toml — Registry MetadataRequired to publish. comfy node init scaffolds this file. See references/pyproject.toml for a fully-commented example.
[project]
name = "my-node-pack" # unique & IMMUTABLE; lowercase, <100 chars, no "ComfyUI" prefix
description = "What this node pack does"
version = "1.0.0" # semantic version X.Y.Z — bump to publish a new version
license = { file = "LICENSE" } # or { text = "MIT License" }
requires-python = ">=3.10"
dependencies = [
"comfyui-frontend-package<=1.21.6", # optional — pin frontend if you ship UI
]
classifiers = [
"Operating System :: OS Independent",
]
[project.urls]
Repository = "https://github.com/you/my-node-pack" # REQUIRED — must be a valid repo URL
Documentation = "https://github.com/you/my-node-pack/wiki"
"Bug Tracker" = "https://github.com/you/my-node-pack/issues"
[tool.comfy]
PublisherId = "your-publisher-id" # the id after @ on your registry profile
DisplayName = "My Node Pack" # human-friendly name in the registry
Icon = "https://raw.githubusercontent.com/you/my-node-pack/main/icon.png" # square, max 400x400px
requires-comfyui = ">=1.0.0" # optional ComfyUI version constraint
| Field | Section | Notes |
|---|---|---|
name | [project] | Unique and immutable once published. <100 chars; alphanumeric + - _ .; no consecutive special chars; can't start with a number/special char; case-insensitive. Don't prefix with "ComfyUI". |
version | [project] | Semantic X.Y.Z — X breaking, Y backwards-compatible feature, Z bug fix. Each value is published once and is immutable. |
description | [project] | Short summary (recommended). |
license | [project] | { file = "LICENSE" } or { text = "MIT License" }. |
requires-python | [project] | e.g. ">=3.10" (recommended). |
dependencies | [project] | PEP 508 requirement strings; can pin comfyui-frontend-package. |
Repository | [project.urls] | Required — valid GitHub repo URL. |
PublisherId | [tool.comfy] | Required — your publisher id (after the @ on your profile). |
DisplayName | [tool.comfy] | Friendly registry name (optional). |
Icon | [tool.comfy] | URL to a square image, max 400×400px; SVG/PNG/JPG/GIF. |
Banner | [tool.comfy] | URL to a 21:9 banner image; SVG/PNG/JPG/GIF (optional). |
requires-comfyui | [tool.comfy] | ComfyUI version range using < > <= >= ~= != (optional). |
includes | [tool.comfy] | Array forcing extra folders into the published archive. |
.comfyignore — uses .gitignore syntax; files listed are excluded from the published archive. Use it to drop tests, examples, large assets, and dev files.[tool.comfy].includes — the inverse: force-includes folders that would otherwise be skipped (e.g. a bundled web/dist).@ on your profile page. It is globally unique and cannot be changed — use it for PublisherId in pyproject.toml.Install comfy-cli (requires Python 3.10+; a virtualenv is recommended):
pip install comfy-cli
| Command | What it does |
|---|---|
comfy node init | Scaffolds pyproject.toml with registry metadata in the current node pack folder. Fill in the required fields (esp. PublisherId and Repository). |
comfy node publish | Validates and uploads the current version to the registry. Prompts for your API key. Prints the registry URL on success. |
Version immutability: once a version is published it cannot be modified or overwritten. To ship changes, bump version and publish again. To pull a bad version, deprecate it on the website (More Actions > Deprecate), which prompts users to upgrade rather than deleting it.
Automate publishing on every version bump. Add the API key as a repo secret named REGISTRY_ACCESS_TOKEN (Settings > Secrets and variables > Actions), then create .github/workflows/publish_action.yml:
name: Publish to Comfy registry
on:
workflow_dispatch:
push:
branches:
- main
paths:
- "pyproject.toml"
jobs:
publish-node:
name: Publish Custom Node to registry
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Publish Custom Node
uses: Comfy-Org/publish-node-action@main
with:
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
main but only when pyproject.toml changes (i.e. when you bump version). workflow_dispatch allows manual runs.main, update the branches: list.pyproject.toml and publishes it — so the typical flow is: bump version, commit, push to main, done.If your pack adds custom UI (widgets, sidebar tabs, menu items), set WEB_DIRECTORY in __init__.py and ship JS there. New frontend extensions should target the modern @comfyorg/extension-api rather than poking at legacy globals; pull in @comfyorg/comfyui-frontend-types for TypeScript types (npm install -D @comfyorg/comfyui-frontend-types). For the full frontend authoring workflow (defineExtension/defineNode/defineWidget and the defineSidebarTab/defineCommand/defineSetting shell APIs), see the sibling comfyui-frontend-extensions skill.
FUNCTION method must return (value,), not return value, even for one output.RETURN_TYPES without a comma — ("IMAGE") is a string, not a tuple. Write ("IMAGE",).INPUT_TYPES not a @classmethod — ComfyUI calls it on the class; missing the decorator breaks node loading.version instead; deprecate bad ones on the website.name after publishing — it's immutable and globally unique. Pick a good name (no "ComfyUI" prefix) up front.[project.urls].Repository — it's required; publishing fails without a valid repo URL.PublisherId — use the id after the @ on your profile, not your display name.NODE_CLASS_MAPPINGS — the key is the class_type stored in workflow JSON. Changing it breaks every saved workflow that used the node.REGISTRY_ACCESS_TOKEN secret; never in pyproject.toml or the repo.