> ## Documentation Index
> Fetch the complete documentation index at: https://comfyui-mcp.artokun.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Nodes

> Discover, install, update, snapshot, bisect, scaffold, and publish custom node packs.

<Info>5 tools. Generated from the live MCP tool schemas — do not edit by hand; run `npm run docs:gen`.</Info>

<Tip>**You don't type these calls.** Ask your agent for what you want in ordinary English — it chooses the tool and fills in the arguments. The JSON on this page is what it sends. New here? Start with [Using the tools](/docs/using-tools).</Tip>

## search\_custom\_nodes

Discover ComfyUI custom node PACKS in the public ComfyUI Registry (registry.comfy.org). Read-only and network-only: queries the hosted registry over HTTP and does NOT require a running ComfyUI or COMFYUI\_PATH. This searches node PACKS, not models (use download\_model action:"search") and not local installs (use list\_local\_models action:"list"). To actually install what you find, or to manage packs already installed, use install\_custom\_node. Driven by the `action` parameter:

* action:"search" — Search by keyword; `query` required. Returns a ranked list of packs with id, name, author, install count, and latest version. The keyword search ranks a fixed window of packs client-side, so when it matches nothing the query is also tried as an exact registry id automatically (e.g. 'comfyui kjnodes' → 'comfyui-kjnodes'). Pass a returned id to action:"details" for full info, or to install\_custom\_node (action:"install").
* action:"details" — Full details for ONE pack by its exact registry id: description, author, license, repository, install count, latest version, the node types it provides, and recent version changelogs. Look up the id via action:"search" first.

<Tip>**In plain terms:** Finding a node pack in the public registry, before you install anything. Read-only and network-only — it does not need a running ComfyUI. Installing what you find is a different tool: `install_custom_node`.</Tip>

### Parameters

<ParamField path="action" type="enum" required>
  Which registry lookup to perform. "search" requires `query` (and takes optional `limit`/`page`); "details" requires `id`. Options: `action:"search"`, `action:"details"`.
</ParamField>

<ParamField path="query" type="string">
  action:"search" — REQUIRED. Keyword(s) to match against pack name/description, e.g. 'impact', 'controlnet aux'.
</ParamField>

<ParamField path="limit" type="integer">
  action:"search" — max results to return (default 10).
</ParamField>

<ParamField path="page" type="integer">
  action:"search" — page number for pagination (default 1).
</ParamField>

<ParamField path="id" type="string">
  action:"details" — REQUIRED. Exact registry pack id (the 'id' field from action:"search"), e.g. 'comfyui-impact-pack'.
</ParamField>

### Examples

**You say:** Is there a node pack for face detailing?

```json theme={null}
{
  "tool": "search_custom_nodes",
  "arguments": {
    "action": "search",
    "query": "face detailer",
    "limit": 5
  }
}
```

**You get back:** Matching packs from the registry with their ids, authors and descriptions. The id is what install\_custom\_node wants.

**You say:** Tell me more about that Impact Pack before I install it.

```json theme={null}
{
  "tool": "search_custom_nodes",
  "arguments": {
    "action": "details",
    "id": "comfyui-impact-pack"
  }
}
```

**You get back:** The pack's description, author, license, repository, install count, the node types it provides, and its recent version changelogs.

***

## install\_custom\_node

Install, repair, enable/disable and remove ComfyUI custom node packs on this ComfyUI. To FIND a pack in the public registry first, use search\_custom\_nodes. Driven by the `action` parameter:

* action:"install" — Install a pack by registry id, git URL, or name. Local installs prefer official comfy-cli when available; remote or CLI-unavailable installs use the ComfyUI-Manager HTTP API. A ComfyUI restart may be required. Targeting the comfyui-mcp sidebar panel pack ('comfyui-agent-panel' / 'comfyui-mcp-panel') is routed through the verified install\_comfyui(action:'panel') path (the version is re-read from disk afterwards) and is REFUSED while the panel is version-pinned.
* action:"update" — Update an installed pack, or pass id:'all' to update every installed pack. Local operations prefer official comfy-cli; remote operations use Manager HTTP. Targeting the sidebar panel pack is routed through the verified install\_comfyui(action:'panel') path. While the panel is version-pinned, BOTH a direct panel target and 'all' are REFUSED — 'all' would move the pinned panel too; clear the pin with install\_comfyui(action:'panel')(action='unpin') or update other packs individually.
* action:"reinstall" — Reinstall a pack. Local operations prefer official comfy-cli; remote operations use Manager HTTP. A ComfyUI restart may be required. A panel target is routed through the verified install\_comfyui(action:'panel') path and is REFUSED while the panel is version-pinned.
* action:"fix" — Repair a pack's install and Python dependencies, or pass id:'all' to repair every pack. Local operations prefer official comfy-cli; remote single-pack repairs use Manager HTTP. REFUSES the sidebar panel pack — 'fix' has no verified on-disk check, so use install\_comfyui(action:'panel') for the panel — and refuses 'all' while the panel is version-pinned.
* action:"uninstall" — Uninstall a pack (removes it). IRREVERSIBLE through this tool — for a cleanup audit prefer action:"disable", which is reversible. The pack must be one ComfyUI-Manager tracks: an id that resolves nowhere is REFUSED before anything is queued (a drained queue would otherwise read exactly like a success), and a pack that is on disk but unmanaged is named so you can remove its directory yourself. After the queue drains the installed-pack list is re-read and the pack must be GONE before anything claims 'uninstalled'. A ComfyUI restart is required to unload it fully. REFUSES the sidebar panel pack.
* action:"disable" — Disable an installed pack WITHOUT removing it — the reversible first step of a cleanup (re-enable with action:"enable"; action:"uninstall" removes a pack outright). Uses the ComfyUI-Manager HTTP API (works against remote instances) or official comfy-cli locally, and re-reads the installed-pack list afterwards so a Manager no-op is reported as NOT disabled rather than as success. A ComfyUI restart is required for the change to take effect. REFUSES the sidebar panel pack.
* action:"enable" — Re-enable a pack previously disabled with action:"disable". Same Manager/comfy-cli mechanics and the same post-op re-read, so a Manager no-op is reported as NOT enabled rather than as success. A ComfyUI restart is required for the change to take effect. REFUSES the sidebar panel pack.
* action:"list" — List installed packs with their version and enabled/disabled state. Uses the ComfyUI-Manager HTTP API (works against remote instances); the cm-cli fallback returns names only. Read-only.
* action:"sync\_deps" — Reconcile the Python dependencies of ALL installed packs through official `comfy node restore-dependencies`. Requires a local ComfyUI install and comfy-cli; takes no other parameters.

<Tip>**In plain terms:** Everything you do to someone ELSE'S node pack once you have found it: install it, keep it working, turn it off, remove it. One tool for the whole pack lifecycle, chosen with `action`. FINDING a pack is `search_custom_nodes`; writing your own is `node_pack`.</Tip>

### Parameters

<ParamField path="action" type="enum" required>
  Which custom-node operation to perform. "install", "update", "reinstall", "fix", "uninstall", "enable" and "disable" require `id`; "list" and "sync\_deps" take no required parameters. Options: `action:"install"`, `action:"update"`, `action:"reinstall"`, `action:"fix"`, `action:"uninstall"`, `action:"enable"`, `action:"disable"`, `action:"list"`, `action:"sync_deps"`.
</ParamField>

<ParamField path="id" type="string">
  The pack to act on. REQUIRED for actions "install", "update", "reinstall", "fix", "uninstall", "enable" and "disable". For "install" this is a registry id, git URL, or node-pack name (find one with search\_custom\_nodes); for "update"/"fix" it may also be 'all' (every installed pack); for "update"/"reinstall"/"fix"/"uninstall"/"enable"/"disable" it is a registry id / module name of an INSTALLED pack.
</ParamField>

<ParamField path="source" type="enum">
  action:"install" — how to interpret `id` (default 'auto', which detects git URLs vs registry ids). Options: `registry`, `git`, `auto`.
</ParamField>

<ParamField path="version" type="string">
  Version to install. action:"install" — e.g. 'latest', 'nightly', or a semver; for git installs this is treated as a git ref unless `ref` is also provided, and registry installs default to 'latest'. action:"reinstall" — version to reinstall (default 'latest').
</ParamField>

<ParamField path="ref" type="string">
  action:"install" — git ref (commit SHA, branch, or tag) to pin when installing a git URL. Overrides any ref parsed from the URL and any `version` value. Ignored for registry-id installs.
</ParamField>

<ParamField path="mode" type="enum">
  Two distinct meanings, one per action group. For actions "install"/"update"/"reinstall"/"fix": the ComfyUI-Manager data source (default 'remote'); 'remote' fetches the live node list, 'local'/'cache' use bundled/cached data. For action:"list": 'default' lists all installed packs, 'imported' lists only those successfully imported this session. Passing a value from the wrong group is refused, naming the ones the action accepts. Options: `remote`, `local`, `cache`, `default`, `imported`.
</ParamField>

<ParamField path="channel" type="string">
  ComfyUI-Manager channel name (default 'default').
</ParamField>

<ParamField path="useCmCli" type="boolean">
  Prefer the official comfy-cli subprocess instead of the ComfyUI-Manager HTTP API. Local operations use comfy-cli by default; set false to force Manager HTTP. Requires a local ComfyUI install — for actions "install"/"disable"/"enable"/"uninstall", an unavailable CLI falls back to Manager HTTP automatically (disclosed in the result); "update"/"reinstall"/"fix"/"list" do not fall back.
</ParamField>

### Examples

**You say:** Install the Impact Pack.

```json theme={null}
{
  "tool": "install_custom_node",
  "arguments": {
    "action": "install",
    "id": "comfyui-impact-pack"
  }
}
```

**You get back:** Progress, then confirmation. New nodes do not appear until ComfyUI restarts — the agent will normally offer to do that for you.

<Warning>A custom node pack is third-party code that runs inside your ComfyUI. Install packs you have reason to trust, the same as any other plugin.</Warning>

**You say:** What node packs have I got installed?

```json theme={null}
{
  "tool": "install_custom_node",
  "arguments": {
    "action": "list"
  }
}
```

**You get back:** Every installed pack with its version and whether it is enabled or disabled. Read-only.

**You say:** That pack has been broken since the last update — repair it.

```json theme={null}
{
  "tool": "install_custom_node",
  "arguments": {
    "action": "fix",
    "id": "comfyui-impact-pack"
  }
}
```

**You get back:** What the repair did to the pack's install and Python dependencies. A restart is usually needed before the result is visible.

<Warning>Repairing re-runs the pack's own install step, which is third-party code. Pass id "all" only if you mean every installed pack.</Warning>

**You say:** Turn that pack off but don't delete it — I want to see if it's the culprit.

```json theme={null}
{
  "tool": "install_custom_node",
  "arguments": {
    "action": "disable",
    "id": "comfyui-impact-pack"
  }
}
```

**You get back:** Confirmation, re-read from the installed-pack list so a Manager no-op is reported as NOT disabled rather than as success. Restart ComfyUI for it to take effect; action "enable" puts it back.

**You say:** Get rid of that pack for good.

```json theme={null}
{
  "tool": "install_custom_node",
  "arguments": {
    "action": "uninstall",
    "id": "comfyui-impact-pack"
  }
}
```

**You get back:** Confirmation, but only after the installed-pack list is re-read and the pack is provably GONE. An id that resolves nowhere is refused before anything is queued.

<Warning>This REMOVES the pack and is irreversible through this tool — any workflow using its nodes will go red. For a cleanup audit use action "disable" first, which is reversible.</Warning>

***

## node\_pack

Author, edit, test and publish YOUR OWN ComfyUI custom-node pack under \<COMFYUI\_PATH>/custom\_nodes/. LOCAL-ONLY: it acts on the local filesystem and is meaningless for a remote --comfyui-url target. Every file-touching action (list\_files, read, search, write, patch, git) is jailed to custom\_nodes/ and needs COMFYUI\_PATH; the one exception is action:"publish", which also accepts an explicit `path` to a pack directory ANYWHERE on this machine and therefore works without COMFYUI\_PATH. To INSTALL or update someone else's pack use install\_custom\_node instead. Driven by the `action` parameter:

* action:"scaffold" — Generate a new pack from a template into \<COMFYUI\_PATH>/custom\_nodes/\<name>/. Writes pyproject.toml (with the \[tool.comfy] PublisherId/DisplayName/Icon table the Comfy Registry requires), **init**.py exporting NODE\_CLASS\_MAPPINGS / NODE\_DISPLAY\_NAME\_MAPPINGS, and src/nodes.py containing a runnable sample node (INPUT\_TYPES/RETURN\_TYPES/FUNCTION/CATEGORY), plus .comfyignore and .gitignore. Optionally emits a web/js frontend stub (wiring WEB\_DIRECTORY) and a GitHub Actions publish workflow (with\_ci). This is the FIRST step of the author loop: scaffold here, then restart\_comfyui to load it, test it, and finally action:"publish". Names must be a safe lowercase slug and cannot escape custom\_nodes/; an existing non-empty directory is left untouched unless overwrite is true. Requires `name` and `display_name`.
* action:"verify" — Test that a pack actually LOADS in ComfyUI — the middle step of the author loop. Restarts the local ComfyUI and waits for it to become ready, then checks that the pack's node class\_types appear in /object\_info. A node that fails to import (a missing dependency or a syntax error) simply never registers, so any missing class\_types pinpoint a broken pack. Provide `class_types` explicitly, or a pack `name` whose **init**.py declares NODE\_CLASS\_MAPPINGS (the keys are inferred). Needs a managed local ComfyUI. Set restart:false to check the already-running server without restarting it.
* action:"publish" — Publish a local pack to the public Comfy Registry (registry.comfy.org) by running `comfy node publish` inside the pack directory. First validates the pack's pyproject.toml has the required \[project].name, \[project].version and \[tool.comfy].PublisherId (refusing the scaffold placeholder), then publishes using the API key from the REGISTRY\_ACCESS\_TOKEN environment variable (passed to comfy-cli via the environment, never via logged arguments). This is the LAST step of the author loop and an IRREVERSIBLE, EXTERNAL action: it creates/updates a PUBLIC registry version that this tool cannot undo. Requires comfy-cli installed and REGISTRY\_ACCESS\_TOKEN set. Give `name` (a folder under custom\_nodes/) or `path` (an explicit pack directory).
* action:"list\_files" — List the files in one installed pack under custom\_nodes/\<pack>/ (read-only). Skips .git/, **pycache**/ and node\_modules/. Use this to orient before action:"read" / action:"search" when diagnosing or editing a pack you found via bisect or install\_custom\_node (action:"fix"). Requires `pack`.
* action:"read" — Read a slice of ONE file inside a pack (read-only), with bounded output so a huge file can't flood the context. Returns the requested line range with a truncation notice when clipped; long lines are chunked. Pair with action:"search" to locate the line, then action:"patch" or action:"write" to change it. Requires `path`.
* action:"search" — Regex-search custom-node source under custom\_nodes/ (read-only). Uses ripgrep when it's on PATH, otherwise a bounded built-in scanner (skips dot-dirs, **pycache**/node\_modules, binary and >1 MiB files). Returns file/line/text matches with per-line and result caps. Use this to find where a node class, import, or error string lives before reading or patching. Requires `query`.
* action:"write" — Create or overwrite ONE file inside a pack. Refuses to clobber an existing file unless overwrite is true, and creates parent directories by default. Use for whole-file edits or new files; for surgical edits prefer action:"patch". After writing, run action:"verify" and restart\_comfyui to load the change. Requires `path` and `content`.
* action:"patch" — Apply a unified diff to custom-node source under custom\_nodes/. Every touched path is jail-checked BEFORE any git call, then the patch is validated with `git apply --check` and only applied if the check passes (two-phase; never uses --unsafe-paths). Paths are relative to custom\_nodes/ and may carry a/ b/ prefixes; works on non-repo packs too. Ideal for surgical edits located via action:"search". Requires `patch`.
* action:"git" — Run a git operation inside one pack, selected by `git_action` (status/diff/log/commit/push). Reads (status/diff/log) are always allowed. Writes (commit/push) require the environment flag COMFYUI\_MCP\_ALLOW\_GIT\_WRITES=1 (default OFF) and otherwise return a structured DISABLED\_BY\_CONFIG refusal so you can self-correct. commit requires a `message` and stages either the given `paths` or all pack changes. This is the final step of the author loop after scaffold → write/patch → verify → restart\_comfyui, before action:"publish". Requires `pack` and `git_action`.

<Tip>**In plain terms:** The loop for authoring YOUR OWN node pack: scaffold it, read and edit its source, check it actually loads, commit it, publish it. Everything is local and jailed to `custom_nodes/`. Installing someone else's pack is `install_custom_node`.</Tip>

### Parameters

<ParamField path="action" type="enum" required>
  Which node-pack operation to perform. "scaffold" requires `name` + `display_name`; "list\_files" requires `pack`; "read" requires `path`; "search" requires `query`; "write" requires `path` + `content`; "patch" requires `patch`; "git" requires `pack` + `git_action`. "verify" and "publish" have no required parameters — "verify" resolves the pack from `name` (or checks `class_types` directly), "publish" from `name` or `path`. Options: `action:"scaffold"`, `action:"verify"`, `action:"publish"`, `action:"list_files"`, `action:"read"`, `action:"search"`, `action:"write"`, `action:"patch"`, `action:"git"`.
</ParamField>

<ParamField path="name" type="string">
  Pack folder name under \<COMFYUI\_PATH>/custom\_nodes/. REQUIRED for action:"scaffold" — a safe lowercase slug (letters, digits, hyphens, underscores), e.g. 'my-cool-nodes', which becomes the directory under custom\_nodes/ and the pyproject \[project].name. For action:"verify", the pack whose **init**.py NODE\_CLASS\_MAPPINGS keys are inferred and checked when `class_types` is omitted. For action:"publish", the pack folder to publish (give this or `path`).
</ParamField>

<ParamField path="display_name" type="string">
  action:"scaffold" — REQUIRED. Human-readable name shown in the ComfyUI node menu and the registry listing.
</ParamField>

<ParamField path="category" type="string">
  action:"scaffold" — node menu category for the sample node (default 'custom').
</ParamField>

<ParamField path="description" type="string">
  action:"scaffold" — short description written to pyproject \[project].description.
</ParamField>

<ParamField path="publisher_id" type="string">
  action:"scaffold" — your Comfy Registry publisher id, stamped into \[tool.comfy].PublisherId. If omitted a placeholder is written that you must replace before publishing.
</ParamField>

<ParamField path="with_frontend" type="boolean">
  action:"scaffold" — if true, also generate a web/js/\<name>.js extension stub and set WEB\_DIRECTORY (default false).
</ParamField>

<ParamField path="with_ci" type="boolean">
  action:"scaffold" — if true, also generate .github/workflows/publish\_action.yml (Comfy-Org/publish-node-action; needs the REGISTRY\_ACCESS\_TOKEN repo secret) so pushing a pyproject.toml version bump auto-publishes (default false).
</ParamField>

<ParamField path="overwrite" type="boolean">
  action:"scaffold" — overwrite template files in an existing pack directory instead of refusing (default false). action:"write" — overwrite an existing file instead of refusing (default false).
</ParamField>

<ParamField path="class_types" type="string[]">
  action:"verify" — explicit NODE\_CLASS\_MAPPINGS keys to confirm are registered in /object\_info. Takes precedence over inferring from `name`.
</ParamField>

<ParamField path="restart" type="boolean">
  action:"verify" — restart ComfyUI before checking so newly-added packs load (default true). Set false to check the live server as-is.
</ParamField>

<ParamField path="pack" type="string">
  Pack folder name under custom\_nodes/ (e.g. 'ComfyUI-Manager'). REQUIRED for action:"list\_files" and action:"git".
</ParamField>

<ParamField path="path" type="string">
  REQUIRED for action:"read" and action:"write": a pack-relative path under custom\_nodes/, e.g. 'MyPack/nodes.py'. For action:"search", the pack-relative directory to search, or '.' for all packs (default '.'). For action:"publish" ONLY, this is instead an explicit absolute path to the pack directory to publish, and it overrides `name` when both are given.
</ParamField>

<ParamField path="content" type="string">
  action:"write" — REQUIRED. Full file contents to write.
</ParamField>

<ParamField path="create_dirs" type="boolean">
  action:"write" — create missing parent directories (default true).
</ParamField>

<ParamField path="patch" type="string">
  action:"patch" — REQUIRED. A unified diff. File headers (---/+++) are read to determine touched paths, which must resolve inside custom\_nodes/ (e.g. 'a/MyPack/nodes.py').
</ParamField>

<ParamField path="query" type="string">
  action:"search" — REQUIRED. Regular expression to search for.
</ParamField>

<ParamField path="glob" type="string">
  action:"list\_files" — optional glob to filter entries (supports *, **, ?), matched against pack-relative paths. action:"search" — optional glob to restrict which files are searched (e.g. '**/*.py').
</ParamField>

<ParamField path="max_entries" type="integer">
  action:"list\_files" — maximum entries to return (default 500, max 2000 — a hard clamp). The walk STOPS at this many, so a capped result is not the pack's full file list.
</ParamField>

<ParamField path="start_line" type="integer">
  action:"read" — 1-based line to start at (default 1).
</ParamField>

<ParamField path="line_count" type="integer">
  action:"read" — number of lines to return (default 240, max 800).
</ParamField>

<ParamField path="max_chars" type="integer">
  action:"read" — maximum characters to return (default 12000, min 500, max 24000 — hard clamps; values outside are silently pulled into range). action:"git" — maximum characters of git output to return (default 12000, min 500, max 24000 — hard clamps the runtime applies; values outside are silently pulled into range).
</ParamField>

<ParamField path="max_results" type="integer">
  action:"search" — maximum matches to return (default 50, max 100). The scan STOPS at this many, so a capped result is not a complete match set.
</ParamField>

<ParamField path="case_sensitive" type="boolean">
  action:"search" — match case-sensitively (default false).
</ParamField>

<ParamField path="git_action" type="enum">
  action:"git" — REQUIRED. Which git operation to run: status/diff/log are read-only; commit/push require COMFYUI\_MCP\_ALLOW\_GIT\_WRITES=1. Named `git_action` rather than `action` only because `action` is this tool's dispatch field; the git operation itself is unchanged. Options: `status`, `diff`, `log`, `commit`, `push`.
</ParamField>

<ParamField path="message" type="string">
  action:"git" — commit message (required for git\_action 'commit').
</ParamField>

<ParamField path="paths" type="string[]">
  action:"git" — pack-relative paths to stage/scope (jail-checked). Defaults to all pack changes.
</ParamField>

### Examples

**You say:** Start me a new node pack called my-cool-nodes.

```json theme={null}
{
  "tool": "node_pack",
  "arguments": {
    "action": "scaffold",
    "name": "my-cool-nodes",
    "display_name": "My Cool Nodes"
  }
}
```

**You get back:** The files it wrote under custom\_nodes/my-cool-nodes/ — pyproject.toml, **init**.py and a runnable sample node. Restart ComfyUI to load it.

**You say:** Where is that pack's sampler class defined?

```json theme={null}
{
  "tool": "node_pack",
  "arguments": {
    "action": "search",
    "query": "class .*Sampler",
    "glob": "**/*.py"
  }
}
```

**You get back:** File/line/text matches under custom\_nodes/, capped so a broad regex cannot flood the reply. Read-only.

**You say:** Show me the top of that file.

```json theme={null}
{
  "tool": "node_pack",
  "arguments": {
    "action": "read",
    "path": "my-cool-nodes/src/nodes.py",
    "line_count": 60
  }
}
```

**You get back:** The requested line range, with a truncation notice if it was clipped.

**You say:** Replace that file with the fixed version.

```json theme={null}
{
  "tool": "node_pack",
  "arguments": {
    "action": "write",
    "path": "my-cool-nodes/src/nodes.py",
    "content": "# ...the full new contents of the file...",
    "overwrite": true
  }
}
```

<Note>`content` is the COMPLETE new file, shortened to one line here. For a small edit prefer action "patch", which applies a unified diff.</Note>

**You get back:** The path written and its byte count.

<Warning>With overwrite true this replaces the whole file. There is no undo here — commit first with action "git" if the pack is a repo.</Warning>

**You say:** Does my pack actually load?

```json theme={null}
{
  "tool": "node_pack",
  "arguments": {
    "action": "verify",
    "name": "my-cool-nodes"
  }
}
```

**You get back:** Whether each of the pack's node classes appeared in /object\_info. A class that is missing failed to import — that is your bug.

<Warning>By default this RESTARTS your local ComfyUI, which aborts anything rendering. Pass restart false to check the live server as-is.</Warning>

**You say:** Commit what I changed in that pack.

```json theme={null}
{
  "tool": "node_pack",
  "arguments": {
    "action": "git",
    "pack": "my-cool-nodes",
    "git_action": "commit",
    "message": "fix: sampler returns the right latent"
  }
}
```

**You get back:** The git output, capped. status/diff/log always work; commit and push need COMFYUI\_MCP\_ALLOW\_GIT\_WRITES=1 and otherwise return a structured refusal naming that flag.

**You say:** Publish it to the registry.

```json theme={null}
{
  "tool": "node_pack",
  "arguments": {
    "action": "publish",
    "name": "my-cool-nodes"
  }
}
```

**You get back:** What comfy-cli published, after pyproject.toml is validated for a real name, version and PublisherId.

<Warning>IRREVERSIBLE and PUBLIC: this creates or updates a version on registry.comfy.org that this tool cannot take back. Needs comfy-cli and REGISTRY\_ACCESS\_TOKEN.</Warning>

***

## node\_snapshot

Custom-node snapshots via ComfyUI-Manager (mirrors `comfy node save-snapshot` / `restore-snapshot`). Driven by the `action` parameter:

* action:"list" — List the snapshots ComfyUI-Manager knows about. No other parameters. Read-only.
* action:"save" — Save the current custom-node and version state. With no `name`, Manager assigns a timestamped snapshot (works against remote instances). Providing `name` writes a custom-named snapshot file, which requires a local ComfyUI install root (COMFYUI\_PATH or a saved default workspace — see the workspace tool) and is unavailable against a genuinely remote ComfyUI.
* action:"restore" — Restore a previously saved snapshot by `name` (required). ComfyUI-Manager applies the custom-node changes on the next ComfyUI restart; use action:"list" to find available names.

<Tip>**In plain terms:** A restore point for your installed node packs. Take one before a round of installing, and you have a way back if something breaks.</Tip>

### Parameters

<ParamField path="action" type="enum" required>
  Which snapshot operation to perform. "list" takes no other parameters; "save" takes an optional `name`; "restore" requires `name`. Options: `action:"list"`, `action:"save"`, `action:"restore"`.
</ParamField>

<ParamField path="name" type="string">
  Snapshot name (no extension, no path separators). REQUIRED for action:"restore" (as shown by action:"list"). OPTIONAL for action:"save" — omit to let ComfyUI-Manager assign a timestamped name. Ignored by action:"list".
</ParamField>

### Examples

**You say:** Save where my nodes are at before I start installing things.

```json theme={null}
{
  "tool": "node_snapshot",
  "arguments": {
    "action": "save",
    "name": "before-impact-pack"
  }
}
```

**You get back:** Confirmation that the snapshot was recorded.

**You say:** That broke everything. Put it back how it was.

```json theme={null}
{
  "tool": "node_snapshot",
  "arguments": {
    "action": "restore",
    "name": "before-impact-pack"
  }
}
```

**You get back:** What it added, removed or re-pinned to match the snapshot.

<Warning>Restoring rewrites your installed node packs to match the snapshot — anything installed since is uninstalled or downgraded.</Warning>

***

## bisect

Binary-search (git-bisect style) over installed ComfyUI custom nodes to find which one causes a problem. A state machine driven by the `action` parameter:

* action:"start" — Begin a session over all currently-enabled custom nodes. Enables half and disables the rest for the first test round, then guide the search with good/bad. Prefers the ComfyUI-Manager HTTP API; falls back to toggling .disabled directory suffixes for local installs. A ComfyUI restart may be needed for changes to take effect.
* action:"good" — Mark the currently enabled set as GOOD (the problem is absent with this set). Narrows the search to the disabled candidates and enables the next subset. Resolves and reports the culprit when one node remains.
* action:"bad" — Mark the currently enabled set as BAD (the problem is present with this set). Narrows the search to the enabled subset and enables the next subset. Resolves and reports the culprit when one node remains.
* action:"reset" — Re-enable all custom nodes and clear the session. Use to abort a bisection or restore the installation after the search completes.
* action:"status" — Report the current session state: status (idle/running/resolved), the remaining candidate node set, which nodes are enabled this round, and the identified culprit if resolved.
  All actions are argument-free; `action` is the only parameter. `good`/`bad` require a session already started with action:"start".

### Parameters

<ParamField path="action" type="enum" required>
  Which bisect operation to perform. "start" begins a session; "good"/"bad" narrow it (require a running session); "reset" clears it and re-enables everything; "status" reports state. No other arguments are needed for any action. Options: `action:"start"`, `action:"good"`, `action:"bad"`, `action:"reset"`, `action:"status"`.
</ParamField>

### Example

<Note>No worked example yet — the call below is a skeleton generated from the required parameters. Real examples live in `scripts/tool-doc-examples.ts`; contributions welcome.</Note>

```json theme={null}
{
  "tool": "bisect",
  "arguments": {
    "action": "start"
  }
}
```

***
