comfyui-mcp

Local-first, agent-native control plane for ComfyUI — MCP server + autonomous sidebar agent that drives your live graph in natural language on ANY LLM: Claude/ChatGPT/Gemini on your subscription (no API key), free local models via Ollama (fully offline),

npm · fingerprint 44bf4e73d9b474416b00d0de · repository · RSS

41tools
3recorded versions
1dtracked
changed today

What changed

Changed 2026-09-03 10:25 UTC

1 schema or annotation change with the description left byte-identical.

--- pinned/save_workflow
+++ observed/save_workflow
  {
    "description": "WRITE to the ComfyUI user library: persist a workflow, or capture/verify its provenance lock. This is the only tool here that writes — reading is get_workflow. Driven by the `action` parameter:\n- action:\"save\" — Save a workflow JSON to the connected ComfyUI server's user library so it appears in the ComfyUI web UI. Requires a running ComfyUI server; this writes to that server's userdata and OVERWRITES any existing file with the same filename without confirmation. Web-UI-format JSON ({ nodes: [], links: [] }) is saved as-is and is the preferred input — when re-saving an existing workflow, load it with get_workflow (action:\"get\", format='ui') and modify THAT. API-format graphs ({ '1': { class_type, inputs } }) are AUTO-CONVERTED to Web UI format with a generated layout so the saved file always opens in the ComfyUI canvas (the canvas cannot open raw API format). Returns a confirmation message (noting the conversion and any warnings), or the HTTP status and error text on failure.\n- action:\"lock\" — Capture a provenance lock for a saved workflow so it can be exactly reproduced later. Walks the workflow's model loaders (CheckpointLoaderSimple, UNETLoader, VAELoader, LoraLoader, ControlNetLoader, etc.), SHA-256s every referenced model file, records the git commit currently checked out for every custom node pack the workflow's class_types come from, and captures ComfyUI's reported version. WRITES `<filename>.lock.json` next to the workflow in ComfyUI's user library. Requires local filesystem access: models resolve from the data/model roots, and pack commits inspect custom_nodes on the live --base-directory / COMFYUI_PATH data root (not COMFYUI_CODE_PATH). Pair with action:\"verify_lock\" later to detect drift.\n- action:\"verify_lock\" — Compare a saved workflow's lock file against the current state of the local install and report drift. Loads `<filename>.lock.json`, re-computes a current lock from the same workflow, and diffs: which models have a different SHA-256, which custom node packs are on a different commit, whether ComfyUI's version changed. Use before re-running an important workflow days or weeks later to confirm it'll behave the same. Supports split local installs (models and packs stay on the data/base root). Read-only; returns a structured drift report (empty arrays everywhere mean perfect parity).",
    "execution": {
-         "description": "Workflow filename in the ComfyUI user library (e.g. 'my_workflow.json'). REQUIRED for every action. For action:\"save\" this OVERWRITES an existing file of the same name; for \"lock\"/\"verify_lock\" the lock is read/written as '<filename>.lock.json' alongside it.",
+         "description": "Workflow filename in the ComfyUI user library (e.g. 'my_workflow.json'). REQUIRED for every action. A missing `.json` suffix is appended before use; extension case and forward-slash subfolders are preserved. The name must be a safe relative path. For action:\"save\" this OVERWRITES an existing file of the same canonical name; for \"lock\"/\"verify_lock\" the lock is read/written as '<filename>.lock.json' alongside it.",
          "type": "string"
        },
        "workflow": {

Changed 2026-09-02 19:12 UTC

2 descriptions rewritten, 1 schema or annotation change with the description left byte-identical.

--- pinned/get_image
+++ observed/get_image
  {
-   "description": "Fetch, browse and inspect ComfyUI images and registered assets. Driven by the `action` parameter:\n- action:\"get\" — Fetch a generated image from ComfyUI by FILENAME and return it as an inline image. Video/audio outputs (e.g. a VHS_VideoCombine .mp4) and allowlisted mesh/material attachments (.obj, .glb, .gltf, .fbx, .ply, .stl, .mtl) are saved to save_dir with their original extension instead of being rendered inline. Works with remote ComfyUI instances — does not require COMFYUI_PATH. Use get_history (action:\"list\") first to obtain the filename.\n- action:\"view\" — Fetch a registered asset's bytes by ASSET ID and return them as an inline image so the agent can see the result. Use this after a render completes (asset_id is included in the completion notification) to inspect, critique, or compare generated images. Only supports image mime types (PNG/JPEG/WebP); audio/video assets must be saved to disk via action:\"get\".\n- action:\"list_outputs\" — List recently generated image AND video files from ComfyUI's output/ directory, newest-first, with each file's kind ('image' | 'video'), subfolder, size, and modification time. Covers stills (.png/.jpg/.jpeg/.bmp) and video/animation outputs (.mp4/.webm/.mov/.mkv/.m4v/.avi/.gif/.webp). LOCAL ComfyUI (COMFYUI_PATH set): a RECURSIVE filesystem scan of output/ (stills + video, including subfolders like video/ that VHS/SaveVideo write to) AND of temp/ for video files — VHS_VideoCombine with `save_output` unchecked writes the completed .mp4 (including the \"-audio.mp4\" a run completion names) there; those entries are tagged type:\"temp\" so action:\"get\" / upload_image (action:\"stage\") can fetch them. Reports size + modification time. Preview stills in temp/ (PreviewImage) are omitted. REMOTE ComfyUI: derives the list from /history over HTTP instead (size/modified are unavailable and omitted) and includes type:\"temp\" videos from history the same way. It does NOT return the media bytes themselves — fetch those with action:\"get\". USE THIS TO CONFIRM A VIDEO RENDER (e.g. VHS_VideoCombine / LTX / WAN output) when get_history (action:\"list\") shows the prompt done but lists no output: VHS-style video nodes write the file but often do NOT register in ComfyUI's /history, so the local filesystem scan is the reliable way to verify the .mp4 exists — then chain it with upload_image (action:\"stage\"). THAT GUARANTEE IS LOCAL-ONLY AND INVERTS ON A REMOTE TARGET: with no disk to scan, this falls back to /history, so a REMOTE listing can neither confirm nor deny a VHS video that never registered, and absence from it is NOT evidence the file is missing. Check a specific filename with action:\"get\" or upload_image (action:\"stage\") instead — both read /view. Every remote result says so in its own text. Read-only.\n- action:\"convert\" — Re-encode a generated image to PNG, JPEG, or WebP and return it inline as an image content block. Source can be a registered asset_id or a path under the local ComfyUI output directory. Optionally writes the converted image back under the output directory and reports source/output size plus bytes saved.\n- action:\"analyze_color\" — Measure the color of a rendered image (not by eye): returns black/white points, contrast (luma std), saturation, per-channel means + cast, and clipping — plus heuristic flags (washedOut, lowContrast, liftedBlacks, dimHighlights, lowSaturation, colorCast) and a one-line verdict. Source = asset_id, a ComfyUI output ref (filename/subfolder/type), or an image path. Pass reference_path to shot-match against a known-good frame (target−reference deltas). Set histogram:true to also get an overlaid R/G/B/luma histogram PNG. Use this to diagnose 'washed out' objectively and decide a color fix; for a video, extract a frame to PNG first.\n- action:\"list_assets\" — List recently generated assets, newest-first. Each call first reconciles ComfyUI's /history, so outputs are listed even when this session did not watch the render complete (e.g. queued via panel_run, by an earlier session, or before a server restart) — those are tagged source:'history-reconcile', versus source:'watched' for renders this server saw finish. Newly reconciled image refs are checked through /view before registration; stale or unavailable refs are omitted and disclosed in the response note. Returns count + assets (asset_id, prompt_id, filename, url, source, created_at). The registry is ephemeral and clears on server restart; records expire after COMFYUI_ASSET_TTL_HOURS (default 24h), and only the most recent completed runs are reconciled — use get_history (action:\"list\") / action:\"get\" by filename for anything older.\n- action:\"asset_metadata\" — Get full provenance for a registered asset including the workflow snapshot that produced it. Use this to inspect the parameters that generated an image before calling generate_image (action:\"regenerate\") with overrides.",
+   "description": "Fetch, browse and inspect ComfyUI images and registered assets. Driven by the `action` parameter:\n- action:\"get\" — Fetch a generated image from ComfyUI by FILENAME and return it as an inline image. Video/audio outputs (e.g. a VHS_VideoCombine .mp4) and allowlisted mesh/material attachments (.obj, .glb, .gltf, .fbx, .ply, .stl, .mtl) are saved to save_dir with their original extension instead of being rendered inline. Works with remote ComfyUI instances — does not require COMFYUI_PATH. Use get_history (action:\"list\") first to obtain the filename; if it returns `subfolder/filename`, pass that relative path as-is and get_image will split it automatically.\n- action:\"view\" — Fetch a registered asset's bytes by ASSET ID and return them as an inline image so the agent can see the result. Use this after a render completes (asset_id is included in the completion notification) to inspect, critique, or compare generated images. Only supports image mime types (PNG/JPEG/WebP); audio/video assets must be saved to disk via action:\"get\".\n- action:\"list_outputs\" — List recently generated image AND video files from ComfyUI's output/ directory, newest-first, with each file's kind ('image' | 'video'), subfolder, size, and modification time. Covers stills (.png/.jpg/.jpeg/.bmp) and video/animation outputs (.mp4/.webm/.mov/.mkv/.m4v/.avi/.gif/.webp). LOCAL ComfyUI (COMFYUI_PATH set): a RECURSIVE filesystem scan of output/ (stills + video, including subfolders like video/ that VHS/SaveVideo write to) AND of temp/ for video files — VHS_VideoCombine with `save_output` unchecked writes the completed .mp4 (including the \"-audio.mp4\" a run completion names) there; those entries are tagged type:\"temp\" so action:\"get\" / upload_image (action:\"stage\") can fetch them. Reports size + modification time. Preview stills in temp/ (PreviewImage) are omitted. REMOTE ComfyUI: derives the list from /history over HTTP instead (size/modified are unavailable and omitted) and includes type:\"temp\" videos from history the same way. It does NOT return the media bytes themselves — fetch those with action:\"get\". USE THIS TO CONFIRM A VIDEO RENDER (e.g. VHS_VideoCombine / LTX / WAN output) when get_history (action:\"list\") shows the prompt done but lists no output: VHS-style video nodes write the file but often do NOT register in ComfyUI's /history, so the local filesystem scan is the reliable way to verify the .mp4 exists — then chain it with upload_image (action:\"stage\"). THAT GUARANTEE IS LOCAL-ONLY AND INVERTS ON A REMOTE TARGET: with no disk to scan, this falls back to /history, so a REMOTE listing can neither confirm nor deny a VHS video that never registered, and absence from it is NOT evidence the file is missing. Check a specific filename with action:\"get\" or upload_image (action:\"stage\") instead — both read /view. Every remote result says so in its own text. Read-only.\n- action:\"convert\" — Re-encode a generated image to PNG, JPEG, or WebP and return it inline as an image content block. Source can be a registered asset_id or a path under the local ComfyUI output directory. Optionally writes the converted image back under the output directory and reports source/output size plus bytes saved.\n- action:\"analyze_color\" — Measure the color of a rendered image (not by eye): returns black/white points, contrast (luma std), saturation, per-channel means + cast, and clipping — plus heuristic flags (washedOut, lowContrast, liftedBlacks, dimHighlights, lowSaturation, colorCast) and a one-line verdict. Source = asset_id, a ComfyUI output ref (filename/subfolder/type), or an image path. Pass reference_path to shot-match against a known-good frame (target−reference deltas). Set histogram:true to also get an overlaid R/G/B/luma histogram PNG. Use this to diagnose 'washed out' objectively and decide a color fix; for a video, extract a frame to PNG first.\n- action:\"list_assets\" — List recently generated assets, newest-first. Each call first reconciles ComfyUI's /history, so outputs are listed even when this session did not watch the render complete (e.g. queued via panel_run, by an earlier session, or before a server restart) — those are tagged source:'history-reconcile', versus source:'watched' for renders this server saw finish. Newly reconciled image refs are checked through /view before registration; stale or unavailable refs are omitted and disclosed in the response note. Returns count + assets (asset_id, prompt_id, filename, url, source, created_at). The registry is ephemeral and clears on server restart; records expire after COMFYUI_ASSET_TTL_HOURS (default 24h), and only the most recent completed runs are reconciled — use get_history (action:\"list\") / action:\"get\" by filename for anything older.\n- action:\"asset_metadata\" — Get full provenance for a registered asset including the workflow snapshot that produced it. Use this to inspect the parameters that generated an image before calling generate_image (action:\"regenerate\") with overrides.",
    "execution": {
      "taskSupport": "forbidden"
    },
-         "description": "Output image filename, e.g. PulID_Klein_00001_.png. REQUIRED for action:\"get\". OPTIONAL for action:\"analyze_color\", where it is one of the three ways to name a source (pair it with subfolder/type).",
+         "description": "Output image filename or a relative `subfolder/filename` reference from get_history, e.g. PulID_Klein_00001_.png or out_F/PulID_Klein_00001_.png. REQUIRED for action:\"get\". Relative prefixes are split automatically; absolute paths, drive prefixes, and `..` segments are refused. OPTIONAL for action:\"analyze_color\", where it is one of the three ways to name a source (pair it with subfolder/type).",
          "type": "string"
        },
        "format": {
-         "description": "Subfolder within the directory, if any (default empty). Used by action:\"get\" and by action:\"analyze_color\" when the source is a `filename`.",
+         "description": "Subfolder within the directory, if any (default empty). Used by action:\"get\" and by action:\"analyze_color\" when the source is a `filename`. If filename already includes a relative prefix, it is combined with this subfolder.",
          "type": "string"
        },
        "type": {
--- pinned/get_workflow
+++ observed/get_workflow
  {
    "description": "Return, list, summarize or query a SAVED workflow FILE — files on disk, named from the library or given as a path/JSON — NOT the graph open on the user's canvas (that is panel_graph_outline). Every action here is READ-ONLY; saving and locking are save_workflow. Driven by the `action` parameter:\n- action:\"get\" — the full JSON of one saved workflow FILE named from the library. Defaults to converted API format; pass format:'ui' for the raw on-disk UI JSON. Use action:\"analyze\" instead if you just need to UNDERSTAND the workflow — it returns a structured summary without flooding context with JSON. Use action:\"get\" only when you need the actual JSON for enqueue_workflow, create_workflow (action:\"modify\"), or save_workflow.\n- action:\"list\" — the workflows saved in the connected ComfyUI server's user library (the same ones visible in the ComfyUI web UI), INCLUDING the ones filed in subfolders. Requires a running ComfyUI server. Takes no other parameters. Returns a numbered list of library names, each relative to the library root — a workflow in a folder appears as 'VIDEO/MiniMaxH3/clip.json', and that whole string is what `filename` takes. It never reports an absence it did not establish: a listing it could not read says so, and an EMPTY listing says the library could not be CONFIRMED empty (an answer with no names in it cannot show whether it covered subfolders) and tells you to check the ComfyUI sidebar rather than recreate anything.\n- action:\"strip\" — strip a workflow to a clean, flat API graph, resolving Get/Set buses, Reroutes, subgraph definitions, and bypassed/muted nodes into real connections (the 'de-getter-setter' pass). Unlike action:\"get\" this reads from ANY server-side file path on disk (not just the workflow library), so it loads ad-hoc / expert workflow files that action:\"list\" and panel_open_workflow can't resolve. Provide exactly one of: path, filename, or graph. Returns conversion warnings, a node-type summary, and the stripped graph (much smaller than the raw UI JSON).\n- action:\"slice\" — slice ONE pipeline out of a toggle-template workflow, the kind built with rgthree 'Fast Groups Bypasser/Muter' where one graph holds many pipelines and only one is active at a time. Seeds from the output/SaveImage nodes in the named `groups`, takes their backward dependency closure (through real links AND virtual Set/Get buses), un-bypasses the kept nodes (and the internals of any subgraph defs they use), and returns a STANDALONE, activated UI graph carrying only the subgraph defs it uses. Pair with action:\"strip\" afterward to flatten the Set/Get buses into real connections.\n- action:\"from_image\" — extract embedded ComfyUI workflow metadata from a PNG file. ComfyUI stores the full workflow (API format) and prompt data in PNG tEXt chunks. Use this to reverse-engineer how any ComfyUI image was generated.\n- action:\"analyze\" — SUMMARIZE a saved workflow file named from the library: sections, node settings, connections, and data flow. Returns a concise text summary (not raw JSON) optimized for AI reasoning. Prefer this over action:\"get\" unless you need the raw JSON for enqueue_workflow or create_workflow (action:\"modify\").\n- action:\"query\" — filter, traverse, project, and aggregate over a saved workflow's nodes WITHOUT dumping the whole JSON (the missing middle between action:\"analyze\"'s fixed summary and action:\"get\"'s full dump; on 100+-node graphs this is the ONLY context-safe way to answer questions like 'which KSamplers run cfg>7', 'what feeds node 42', 'count nodes by type'). Provide exactly one of path/filename/graph, then combine: `types`, `title`, `where` widget predicates ANDed ('cfg>7', 'steps<=20', 'sampler_name=euler', 'text~sunset' — ops = != >= <= > < ~contains), `ids`, `upstream_of`/`downstream_of` + `depth`, `fields`, `group_by`, `limit`, `max_chars`. Output is TOKEN-BOUNDED and, when it truncates, the tail names WHICH of the two caps fired and the exact parameter to raise — read it and retry rather than concluding the graph can't be read. For the LIVE canvas this is panel_query_graph instead.\n- action:\"prompt_director\" — read Prompt Director's latest sanitized RUNTIME state after its nodes execute: each node id, node kind, resolved Model Explorer model/LoRA context, structured edit plan, source analysis, exact final prompt, warnings, or Result Critic verdict. Secrets and image tensors are redacted. Pair it with a live panel graph audit: graph inspection explains wiring and widget state, while this explains what the nodes actually resolved and compiled. Pass `node_id` to inspect one executed Prompt Director node.",
    "execution": {
-         "description": "action:\"strip\" / \"slice\" / \"query\" — Absolute server-side path to a workflow .json on disk (e.g. C:\\\\Users\\\\you\\\\ComfyUI\\\\user\\\\default\\\\workflows\\\\pusa_extend.json). Read directly from disk — no library lookup.",
+         "description": "action:\"strip\" / \"slice\" / \"query\" — Absolute server-side path to a workflow .json on disk (e.g. C:\\\\Users\\\\you\\\\ComfyUI\\\\user\\\\default\\\\workflows\\\\pusa_extend.json). Read directly from disk — no library lookup. If a userdata path is missing the `workflows` segment after `user/default`, it is retried with that segment restored, preserving the filename exactly.",
          "type": "string"
        },
        "section": {
--- pinned/node_pack
+++ observed/node_pack
  {
-   "description": "Author, edit, test and publish YOUR OWN ComfyUI custom-node pack under the custom_nodes/ directory the running ComfyUI actually scans. 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/ under the directory the running ComfyUI actually scans — the server's own --base-directory when it reports one (on ComfyUI Desktop that is NOT the code install root), else the live main.py checkout on a split install that has no --base-directory (the data workspace is not scanned unless the flag said so), else COMFYUI_PATH, else the saved default workspace, else the running LOCAL server's own install root; 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:\n- action:\"scaffold\" — Generate a new pack from a template into the local ComfyUI's scanned custom_nodes/<name>/ (the install base resolves from the running server's --base-directory when it reports one, else the live main.py checkout on a split install, else COMFYUI_PATH, else the saved default workspace, else the running LOCAL server this session is connected to). 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`.\n- 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.\n- 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).\n- 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`.\n- 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`.\n- 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`.\n- 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`.\n- action:\"patch\" — Apply a unified diff (---/+++ headers) or an apply-patch / simplified diff (`*** Begin Patch` / `*** Update File`) 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`.\n- 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`.",
+   "description": "Author, edit, test and publish YOUR OWN ComfyUI custom-node pack under the custom_nodes/ directory the running ComfyUI actually scans. 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/ under the directory the running ComfyUI actually scans — the server's own --base-directory when it reports one (on ComfyUI Desktop that is NOT the code install root), else the live main.py checkout on a split install that has no --base-directory (the data workspace is not scanned unless the flag said so), else COMFYUI_PATH, else the saved default workspace, else the running LOCAL server's own install root; 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:\n- action:\"scaffold\" — Generate a new pack from a template into the local ComfyUI's scanned custom_nodes/<name>/ (the install base resolves from the running server's --base-directory when it reports one, else the live main.py checkout on a split install, else COMFYUI_PATH, else the saved default workspace, else the running LOCAL server this session is connected to). 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`.\n- 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.\n- 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).\n- 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`.\n- 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`.\n- 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`.\n- 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`.\n- action:\"patch\" — Apply a unified diff (---/+++ headers) or an apply-patch / simplified diff (`*** Begin Patch` / `*** Update File`) 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`.\n- 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. `paths` entries are pack-relative paths to stage/scope, resolved against the selected pack root (not custom_nodes/); an absolute path is accepted only when it remains inside both the selected pack and custom_nodes/ jails. 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`.",
    "execution": {
      "taskSupport": "forbidden"
    },

Current tools

Show all 41 tool fingerprints
apply_manifest
30883c8250aee3af
apps
aed640a51689dfa6
batch
f6bf93ce434faf90
bisect
2d7588947071a98e
calculate
adc8ae7a4867e921
call_tool
fcb8b3107bd40649
clear_vram
3a9f1731fbcfff0d
comfy_cli
d085084b7e1d3f3f
create_workflow
4b5ba602d738f1ef
describe_tool
dc92d894996b5b91
download_model
aeff22079210b20a
enqueue_workflow
024ca07aa808ad0f
generate_image
7bc99a6505e0bab2
get_defaults
58bc5ff571c3c704
get_history
ecca52ca87f55410
get_image
8d381b90b8f139f0
get_system_stats
35aa016c7bf3f499
get_workflow
9be48076d19c45e5
install_comfyui
d908f02d4563819a
install_custom_node
a1981c08a7c4902f
kitchen
ba8f1008c459839e
list_api_nodes
5642cfd50a53bfd9
list_local_models
ae2102a8ec8a0a57
list_packs
6154a5f28e79690b
list_tools
7f60c20e8f033cbd
model_metadata
2806897cb32cb3ac
node_pack
153c489922972926
node_snapshot
904eac33bcd1ed40
queue
4103079910f79870
report_issue
a721bdfc4e4d293d
restart_comfyui
827d9d58b498de85
runpod
41019f356692a58d
runpod_watch
3f543ae39b1ad2e1
save_workflow
e156c8fb7fa7a99f
search_custom_nodes
e666874b48b477e6
train_doctor
ba61a88ff8ede545
train_prepare_dataset
2dd772c2957ee51a
train_start
34fd689dc3bafbae
upload_image
e2f63571a45ff447
visualize_workflow
dc6fb2d4b3d27d67
workspace
b242ca52472061fc

Watch this server yourself

If you run this server, put the proxy in front of it. It pins these exact fingerprints on first connect and stops the session if they move.

npx --yes mcp-pin@0.1.0 -- <your comfyui-mcp command>

Or subscribe to this page's RSS feed to be told when it changes.

Badge

mcp-pin status badge for comfyui-mcp

The badge states one fact about time and nothing else. It never claims a server is safe.

[![mcp-pin](https://mcp-pin.gautamkhosla.com/badge/2397a197fd77d140.svg)](https://mcp-pin.gautamkhosla.com/servers/2397a197fd77d140.html)