Skip to content
Automation
Skill

/comfyui-frontend-extensions

Authoring ComfyUI v2 frontend extensions with @comfyorg/extension-api, covering defineNode/defineExtension/defineWidget, shell UI (sidebar tabs, commands, hotkeys), typed events, and handles. Use when writing or editing ComfyUI web-UI extension code (custom node JS, sidebar

From plugin
comfyui-mcp
88442 skills4 agents11 commands1 MCP

Claude Code

$ npx -y skills add artokun/comfyui-mcp --skill comfyui-frontend-extensions --agent claude-code

SKILL.md

comfyui-frontend-extensions.SKILL.md
name: comfyui-frontend-extensions
description: Authoring ComfyUI v2 frontend extensions with @comfyorg/extension-api, covering defineNode/defineExtension/defineWidget, shell UI (sidebar tabs, commands, hotkeys), typed events, and handles. Use when writing or editing ComfyUI web-UI extension code (custom node JS, sidebar panels, widgets).

ComfyUI v2 Frontend Extension API

The v2 extension API is the published npm package `@comfyorg/extension-api`. It replaces the legacy `app.registerExtension()` / `nodeType.prototype` monkey-patching model with a typed, tree-shakeable, import-based API.

> If you are converting an existing v1 extension, read > [`references/migrate-v1-to-v2.md`](references/migrate-v1-to-v2.md) for a > pattern-by-pattern mapping.

Mental model

| v1 (legacy) | v2 (`@comfyorg/extension-api`) | |-------------|-------------------------------| | One giant `app.registerExtension({...})` call | One `defineX` per concern, each independently disposable | | `window.app` / `app.*` globals | Direct `import` from the package; no `window.app` at module-eval time | | `nodeType.prototype.onExecuted = ...` patching | `node.on('executed', fn)` on a `NodeHandle` | | Mutate `widget.value`, assign `widget.callback` | `widget.setValue(v)` / `widget.on('valueChange', fn)` | | `api.addEventListener('execution_start', fn)` | `execution.on('start', fn)` (typed namespaces) | | Manual `removeEventListener` bookkeeping | Every subscription returns `Unsubscribe`; every `defineX` returns `DisposableHandle` |

Core principles baked into the API:

  • **Import, don't reach for globals.** `import { defineNode } from '@comfyorg/extension-api'`. No `window.app` dependency at module evaluation time.
  • **Read via getters, write via command-dispatch setters.** `getValue()` reads; `setValue()` dispatches an undo-able, serializable command. Read-only invariants (set at construction) are `readonly` accessors (`node.type`, `widget.name`).
  • **Observe via typed `on(...)` subscriptions.** Each returns an `Unsubscribe` cleanup function. No Vue refs/signals are ever exposed; Vue reactivity is the internal engine only.
  • **Everything is disposable.** Every `defineX` returns a `DisposableHandle` with an idempotent, synchronous `dispose()`.

Registration entry points

All imported from `@comfyorg/extension-api`:

| Function | Purpose | Returns | |----------|---------|---------| | `defineNode(opts)` | **Primary entry** — react to node lifecycle (replaces prototype patching) | `NodeExtensionOptions` | | `defineExtension(opts)` | App-scoped lifecycle (`init`/`setup`) + shell UI host | `ExtensionOptions` | | `defineWidget(opts)` | Register a custom widget type (DOM via `mount`) | `WidgetExtensionOptions` | | `defineSidebarTab(opts)` | Add a left-sidebar tab (Vue or custom) | `DisposableHandle` | | `defineBottomPanelTab(opts)` | Add a bottom-panel tab | `DisposableHandle` | | `defineToolbarButton(opts)` | Add an action-bar button | `DisposableHandle` | | `defineCommand(opts)` | Register an invokable command | `DisposableHandle` | | `defineHotkey(opts)` | Bind a key combo to a command id | `DisposableHandle` | | `defineSetting(opts)` | Add a settings-menu entry | `DisposableHandle` | | `defineAboutBadge(opts)` | Add a badge to the About page | `DisposableHandle` |

Imperative carve-outs (fire-and-forget, not `defineX`, no handle): `toast`, `notify`.

A single extension file typically exports a default `defineExtension`/`defineNode` result and calls the shell-UI `defineX` functions inside `setup()` or at module scope. They queue safely before the app boots.

`defineNode` — the primary entry point

Reacts to node lifecycle. `nodeCreated` fires once per node instance (typed in, pasted, duplicated, or loaded without an existing workflow). `loadedGraphNode` fires once when a node is restored from a saved workflow (widget values already populated). Exactly one of them fires per node entity, never both.

import { defineNode, onNodeMounted, onNodeRemoved } from '@comfyorg/extension-api'

export default defineNode({
  name: 'my-org.executed-logger',
  // Filter to specific comfyClass names. Omit to receive every node type.
  nodeTypes: ['KSampler', 'KSamplerAdvanced'],

  // MUST be synchronous. Runs inside a Vue EffectScope; everything registered
  // here (subscriptions, onNodeMounted) auto-disposes when the node is removed.
  nodeCreated(node) {
    // Read-only invariants
    console.log(node.type, node.comfyClass, node.id)

    // Subscribe to backend execution completion (replaces onExecuted patching)
    node.on('executed', (e) => {
      console.log('output:', e.output) // Record<string, unknown>
    })

    // Lifecycle hooks — call SYNCHRONOUSLY (never after an await)
    onNodeMounted(() => {
      // Node fully mounted; DOM/canvas ready.
    })
    onNodeRemoved(() => {
      // Cleanup: abort fetches, close sockets. Does NOT fire on subgraph promotion.
    })
  },

  loadedGraphNode(node) {
    // Node restored from a saved workflow; widget values are already set.
  }
})

`NodeHandle` surface (Phase A)

| Member | Kind | Notes | |--------|------|-------| | `id: string` | readonly | Opaque token. Compare with `node.equals(other)`, never by slicing. | | `equals(other)` | method | Canonical identity comparison. | | `type: string` | readonly | LiteGraph node type. | | `comfyClass: string` | readonly | Backend class name. | | `getProperty<T>(key)` / `getProperties()` / `setProperty(key, v)` | methods | Per-instance props (migration shim — prefer widget values). | | `getInputs()` / `getOutputs()` | methods | `ReadonlyArray<Readonly<SlotInfo>>` — frozen views. | | `on('executed', fn)` | method | Execution complete → `NodeExecutedEvent { output }`. | | `on('removed', fn)` | method | Node deleted (not subgraph promotion). | | `on('configured', fn)` | method | Loaded from saved workflow (after widget values restored). | | `on('beforeSerialize', fn)` | method | **Deprecated** — use widget-level `before

Read more
@artokun
Ships withcomfyui-mcp

<img src="docs/images/demo-poster.jpg" width="760" alt="The Agent panel driving ComfyUI end to end. Click to watch the demo"> The Agent panel driving ComfyUI end to end. It reads what is installed locally, wires the graph, frees VRAM, and runs the render.

88442 skills4 agents1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
884
Star history of artokun/comfyui-mcp on GitHub, a chart by star-history.com

Other skills on comfyui-mcp.

ai-toolkit-trainer

ai-toolkit-trainer

Train custom LoRAs with ostris AI-Toolkit. Covers WAN 2.2/2.1 (people, styles, video motion) and Z-Image (Turbo & Base, low-VRAM…

@artokun@artokunInstall Skill
anima-base

anima-base

Anime/illustration text-to-image (ANIMA 1.0, ~2B Cosmos DiT). Use for anime, manga, illustrated characters; accepts Danbooru tags…

@artokun@artokunInstall Skill
anima-lora-trainer

anima-lora-trainer

Train a custom anime LoRA on the ANIMA base model with Citron's local Gradio trainer (kohya sd-scripts), <6GB VRAM,…

@artokun@artokunInstall Skill
civitai

civitai

Discover Civitai models with the BUILT-IN download_model action:"search_civitai" and install/generate them locally. Find a…

@artokun@artokunInstall Skill
color-correction

color-correction

Diagnose and fix video/image color OBJECTIVELY with the get_image (action:"analyze_color") tool (scopes/stats such as black/white…

@artokun@artokunInstall Skill
comfyui-core

comfyui-core

Core ComfyUI knowledge covering workflow format, node types, pipeline patterns, and MCP tool usage

@artokun@artokunInstall Skill

More skills from other plugins.

agent-builder

agent-builder

Load immediately after an Agent intent, before the first agent_builder_* call. Governs how you create, configure, test, and…

n8n11d209k
@n8n-io@n8n-ioInstall Skill
browser-use

browser-use

Direct browser control via CDP for web interaction: automation, scraping, testing, screenshots, and site/app work. Also covers…

@browser-use@browser-useInstall Skill
agent-browser

agent-browser

Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling…

@vercel-labs-2@vercel-labs-2Install Skill
add-anydoc

add-anydoc

Add local office-document-to-Markdown conversion to NanoClaw agent containers with the pinned Firecrawl AnyDoc CLI. Use when…

@nanocoai@nanocoaiInstall Skill

Questions about comfyui-frontend-extensions.

Which coding agents can use comfyui-frontend-extensions?
comfyui-frontend-extensions installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.