local-comfyui-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| COMFYUI_LANG | No | Language for windows and installer. Leave empty to auto-detect. | |
| COMFYUI_ROOT | Yes | Root folder of the ComfyUI installation. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| comfy_statusA | Check whether ComfyUI is running and report GPU/VRAM and queue state. |
| comfy_startA | Launch the portable ComfyUI instance. Args: wait: block until the HTTP API answers (up to COMFYUI_STARTUP_TIMEOUT). |
| comfy_stopA | Stop the ComfyUI process that this server started, and everything under it. A ComfyUI you launched yourself is not touched - ownership is the test, not whether something is answering the port. This reaches a ComfyUI started before the server was last restarted as well: ownership is recorded on disk and taken back at startup, so an instance orphaned by a crash is still stoppable here. |
| restart_comfyA | Restart ComfyUI and wait for it to answer again. What a newly installed or edited node needs: Two mechanisms, picked by who owns the process. One this server started is stopped and relaunched through COMFYUI_LAUNCH_SCRIPT. One you started yourself is asked to re-exec itself, through the bridge node - which is the only way to reach it at all, and has the advantage of coming back on the exact command line and environment it already had, rather than on this server's guess at them. That path therefore needs the node installed. Schemas and model directories are dropped, since a restart is precisely what makes them stale. A tab that was connected reconnects on its own and is waited for; the workflow on its canvas survives, being the browser's, not ComfyUI's. Args: wait: seconds to wait for ComfyUI to come back. force: restart even with jobs running or queued, throwing that work away. |
| get_comfy_logA | Read ComfyUI's console - the Python side, where import and dependency failures land. This is the terminal ComfyUI is running in, not the browser. It is where a custom node says it could not import, where a missing package is named, and where a traceback from inside a node ends up. Nothing else in this server can see any of it: a node that failed to load simply does not appear in /object_info, which looks identical to a node that was never installed. Args:
lines: how many of the most recent matching lines to return; 0 for all of them.
level: keep only this severity and above (DEBUG/INFO/WARNING/ERROR/CRITICAL).
Untagged output - third-party packs printing directly - is dropped by this.
search: keep only lines containing this text, case-insensitive.
regex: treat |
| open_workspaceA | Open ComfyUI in a browser, so the workspace tools have a tab to talk to. The bridge needs a page open - with none, every workspace_* tool fails with
It waits for the tab to register itself rather than firing and returning, because the caller's next move is a workspace tool: coming back before the page has loaded its JavaScript would just fail again, for a reason that no longer has anything to do with what went wrong. A tab that is already connected is left alone and reported - a second one would work, but it becomes the preferred client and the user's own tab silently stops being the one that gets edited. Pass force to open anyway. Args: wait: seconds to wait for the tab to connect. 0 returns as soon as the browser has been handed the URL. force: open a tab even when one is already connected. |
| workspace_statusA | Check whether the live ComfyUI workspace can be reached. The workspace is the workflow open in the browser. Reaching it needs two things
beyond a running ComfyUI - the bridge node installed and a tab connected - and
this reports which one is missing. Call it before the other workspace_* tools
when they fail, and to find |
| get_console_logA | Read the browser console - the frontend half, where extension failures land. The companion to get_comfy_log, and it answers a different question. A node has two halves: a Python class ComfyUI imports, and often a JavaScript extension that gives it its widgets and menus. When the JavaScript half fails, the Python half still registers, so the node appears in /object_info and on the canvas and merely behaves wrongly - which is the one failure nothing else here can see.
Needs a connected tab, and only sees what was logged since that tab loaded - a reload starts the record over. Args:
lines: how many of the most recent matching entries to return; 0 for all.
level: keep only this severity and above (DEBUG/INFO/WARNING/ERROR/CRITICAL).
search: keep only entries containing this text, case-insensitive.
regex: treat |
| get_workspace_graphA | Read the workflow currently open in the browser, unsaved edits included. This is the one view of a workflow that no file and no HTTP endpoint can give: what the user is actually looking at. Use it to answer questions about the graph on screen; use describe_workflow for the files in the workflows directory. The summary reports A large workflow does not fit in one answer at any detail that includes its
wiring, so the report steps down a level at a time until it does and says so in
Args:
format: "summary" for a structured report - nodes, links, groups and a list
of issues (missing node types, muted or bypassed nodes, unconnected
required inputs). "ui" for the raw graph as a Save would write it.
"api" for the API-format prompt, the same JSON that Export (API) and
run_workflow use - only the frontend can produce it, which is why it is
available here and nowhere else in this server.
scope: "root" for the top level of the workflow, "active" for the subgraph
on screen, "all" to descend into every subgraph. On a workflow built
from subgraphs "root" is a handful of boxes and everything inside them
is invisible, so reach for "all" when the question is about the whole
thing. Nested nodes come back with path ids - |
| save_workspaceA | Write the workflow open in the browser to a file. This is how to get at a graph too large to fit in one answer: the reply is a path and a few numbers, and the file can then be read in slices or searched like any other. It is also how an unsaved canvas becomes something that survives the tab being closed. Args: name: file name, with or without .json. format: "ui" writes exactly what ComfyUI's own Save writes - positions, groups, titles, collapsed state - into the export directory, and it can be opened in ComfyUI again. "api" writes the API-format prompt into the workflows directory, where run_workflow and describe_workflow find it. The two are not interchangeable: UI format keeps the layout and cannot be run, API format is runnable and has no layout at all. scope: "root" for the whole workflow, "active" for the subgraph on screen. overwrite: replace the file if it already exists. client_id: which tab to ask; defaults to the most recently focused one. |
| ask_workspaceA | Ask the person at the ComfyUI screen a question, and wait for their answer. This is the one tool whose answer comes from a human rather than from the graph. Reach for it when the next step turns on something only they know - which of two results they preferred, what the subject of a prompt should be, whether a value looks right - instead of guessing and building on the guess. The question goes in a panel below the canvas, not over it. That matters
for the question most worth asking - "look at this and tell me" - because a
modal dialog hides the canvas and closes on a click beside it, so looking is
what cancels it. In the panel the graph stays visible and usable, and they can
pan around before answering. It costs them an interruption either way, so it is worth one question rather than three: only one can be waiting at a time, on either surface, and a second call while one is open is refused rather than queued.
Three outcomes, and they mean different things. Args:
question: what to ask. Shown as the body of the dialog.
title: heading above it. Defaults to "Question".
default: text the input starts with, for when there is an obvious answer.
placeholder: grey hint inside an empty input, or in the |
| confirm_workspaceA | Ask the person at the ComfyUI screen a yes/no question, and wait. The same interruption as ask_workspace, for when the answer is a decision rather than a value - before something slow, or something with no undo. Asked in the panel below the canvas by default, so the graph stays visible;
Args:
question: what to ask.
title: heading above it. Defaults to "Confirm".
hint: smaller helper text under the question.
deny_label: caption for the deny button. Only |
| undo_workspaceA | Undo the last edit in the browser, exactly as Ctrl+Z would. Every workspace edit is bracketed so that one call is one undo step - a batch of three values set together comes back in one press. This is that press, which is what makes a wrong edit cheap to take back rather than something to reconstruct by hand. The history is the user's, not this server's. Their own edits sit on the same stack, interleaved with ours in the order they happened, and nothing distinguishes them. One step back is almost always ours; several steps back is taking back whatever was there, theirs included. Undo one, look, undo again. Args: steps: how many to take back, 1 to 50. Pass 0 to read the depth without moving - the only way to tell an undo that will do something from one that will silently do nothing, since an empty history is not an error. redo: step forward instead. A redo history exists only until the next edit, which clears it. client_id: which tab; defaults to the most recently focused one. |
| reload_workspaceA | Reload the ComfyUI browser tab, and wait for it to come back. This is the fix for exactly one thing: changed extension JavaScript. The bridge's own JS, and every node pack's, is served from disk on each page load and never re-read otherwise, so editing it takes a reload and nothing else - restarting ComfyUI would cost minutes and achieve the same thing by accident. Installing or upgrading a node is the opposite case and needs restart_comfy, because Python packs are imported once at startup. It waits for the tab to register again rather than firing and returning, for the reason open_workspace does: a page part-way through loading fails exactly like no page at all. The browser can refuse. ComfyUI asks "Leave site?" when a workflow has
unsaved edits, and that dialog waits for a human - nothing here can dismiss
it. So a tab with edits is refused by default; Args: wait: seconds to wait for the tab to reconnect. 0 returns immediately. force: reload even though the browser may put a confirmation on screen. client_id: which tab, when several are open. Defaults to the focused one. |
| load_workspaceA | Open a saved workflow in the browser, as a tab in the ComfyUI window. The other half of save_workspace, and the way to put a workflow file under the workspace tools: load it, edit it with them, save it back. Without this they can only reach whatever the user happened to have open. Both formats work and neither has to be named - a file in It opens a tab rather than replacing the one on screen. ComfyUI reuses a tab only when its own workflow store already knows the name, and that store does not sync from this server's workflows directory - so a file usually arrives in a new tab marked unsaved, with the workflow that was on screen still open in its own. Loading that same file again reuses the tab it made the first time, and that one is replaced. The reply reports the tabs and whether one was added; switch_workspace_tab moves between them afterwards. A name ComfyUI already knows is refused. It resolves the name against its
own saved workflows rather than against the directory the file came from, so
a file whose name matches one of the user's workflows would fill that
workflow's tab instead of opening one - leaving it looking edited, one Ctrl+S
from overwriting their work. Rename the file, or pass This is not undoable. ComfyUI resets the undo history when a workflow is loaded, so Ctrl+Z will not bring the previous canvas back. That is what the backup is for, and it is the reused-tab case it covers: the canvas is written to the export directory first and the reply names the file. Args: name: the file, with or without .json. Looked for in the workflows directory first, then the export directory; an absolute path also works. backup: write the current canvas to the export directory before replacing it. Leave it on unless the canvas is known to be worth nothing. force: load even when ComfyUI already has a saved workflow of this name, taking over its tab. Only when replacing that workflow is meant. client_id: which tab to load into; defaults to the most recently focused one. |
| screenshot_workspaceA | Photograph the ComfyUI canvas: what the workflow looks like, not what it says. This answers the questions a graph dump cannot - whether the layout reads as a mess, which boxes overlap, where a link crosses the whole screen, what the user means by "that one over there". After a layout change it is the only way to check the result rather than assume it. It is a picture of the canvas, and that has a hard edge: prompts, image previews, markdown notes and audio players are HTML drawn over the canvas and are not in it. An empty-looking prompt box in the picture says nothing about the prompt - get_workspace_graph is what reads values. The report says how many such widgets the graph has. Always the graph on screen. To photograph a subgraph, navigate_workspace into it first; the report names which graph it is. Args: fit: what to frame. "graph" for the whole workflow, "view" for the viewport exactly as the user has it, "selected" for what they have clicked on, or a list of node ids to frame those. A whole large graph is legible only as a shape - for reading titles, frame a handful of nodes. max_edge: longest edge in pixels, 256 to 4096. Bigger reads better and costs more; it does not make a wide graph legible, only a small subset. format: "png" keeps the text crisp and is the right answer for a diagram. "jpeg" and "webp" are smaller and blur it. client_id: which tab to ask; defaults to the most recently focused one. |
| set_workspace_valuesA | Set widget values, node properties and on-screen labels in the open workflow. This edits the live canvas, exactly as if the values had been typed in. The change lands on the frontend's undo stack as a single step, so one Ctrl+Z in the browser takes back the whole call - which is the reason to pass several values at once rather than one per call, and why properties live here rather than in a tool of their own. Nothing is written unless every key validates, so a rejected call leaves the graph as it was. Numeric ranges are enforced; a combo value outside the listed options is reported as a note and written anyway, because a node's option list is not a whitelist - ComfyUI decides. Args:
values: |
| set_workspace_node_modesA | Mute or bypass nodes in the workflow open in the browser. Muted ("never") stops a node producing anything; bypassed passes its inputs straight through to whatever it feeds, which is how a branch is taken out without unwiring it. Both are how a workflow gets narrowed to the part being worked on. Like set_workspace_values, the whole call is one undo step and nothing changes unless every entry is valid. Args:
modes: |
| navigate_workspaceA | Move the ComfyUI canvas into a subgraph, back out one level, or to the top. The other workspace tools take This changes what the user is looking at and nothing about the workflow, so it is not on the undo stack - Ctrl+Z would otherwise mean two different things. Args: to: "root" for the top level, "up" for one level out, or the id of a subgraph node in the graph currently on screen to go into it. client_id: which tab to move; defaults to the most recently focused one. |
| switch_workspace_tabA | List the workflow tabs open in the ComfyUI window, and switch between them. These are the tabs along the top of ComfyUI: open workflows, exactly one of
which is on screen. They are not browser tabs - one browser tab holds all of
them, and With no Switching does what clicking the tab does, and no more: the canvas is
reloaded from that workflow's own stored state. Unsaved edits in the tab
being left behind are not lost - they belong to that workflow, which is why
a tab can report "new" opens a blank workflow and switches to it, which is the same command the + button on the tab bar runs. That is the way to start something from nothing without disturbing what the user already has open. Args: to: which tab - an index from a previous call, its path or its filename, or "next", "previous", "recent" for the one active before this, or "new" for a fresh blank workflow. Empty reports without moving. A name matching two open tabs is refused rather than guessed. force: reload the tab already on screen instead of reporting that it is already there. client_id: which browser tab to ask; defaults to the most recently focused. |
| close_workspace_tabA | Close one of the workflow tabs open in the ComfyUI window. The counterpart to switch_workspace_tab, and the way to tidy up after a run of loads - each one opens its own tab, and they stay until something closes them. Nothing here is written to disk and nothing can be undone. A closed
workflow is out of the tab bar; if it had unsaved changes, they are gone. So
a tab with unsaved changes is refused unless Closing the tab on screen moves to a neighbour first, so the canvas never ends up showing a workflow that is no longer open. The last remaining tab is refused: closing it would leave the window with nothing. Args: tab: which one - an index from switch_workspace_tab, its path, or its filename. Empty closes the tab currently on screen. A name matching two open tabs is refused rather than guessed. force: close even when the tab has unsaved changes, losing them. client_id: which browser tab to ask; defaults to the most recently focused. |
| promote_workspace_inputsA | Expose an inner node's inputs on the face of the subgraph that holds it. A subgraph with nothing promoted is a sealed box: the values that drive it can only be reached by going inside, and nothing outside can be wired to it. This is what ComfyUI offers as "Promote widget" on a widget's context menu, and it is the difference between a subgraph that is usable from the outside and one that merely hides its contents. Both kinds of input take the same path. A widget row ( The id names both ends. One call is one Ctrl+Z. Nothing is written unless every named input resolves, so a typo refuses the batch rather than half-applying it. Args:
promote: which inputs to expose, as {":": [names]}.
An input already exposed is reported in |
| pack_workspace_subgraphA | Wrap nodes and groups into a new subgraph, or dissolve one back out. ComfyUI's "Convert to Subgraph" and its Unpack counterpart. Folding a stage into a subgraph is how a workflow stops being a wall of nodes: the box keeps its own wiring, and promote_workspace_inputs decides which of its values show on the outside. It acts on the graph on screen, so packing while inside a subgraph nests
one. Ids are the plain local ones a read of that graph reports, not the
A group can be named instead of listing its nodes: it goes in along with everything inside it, which is what selecting one on the canvas amounts to. One call is one Ctrl+Z, and unpacking happens before packing - so a single call can dissolve a subgraph and re-wrap its parts differently. Ids do not survive either direction. Packing replaces the nodes with one new node; unpacking hands the contents fresh ids rather than the ones they went in with - measured, 144 and 145 came back out as 158 and 159. Read the graph again afterwards rather than reusing ids from before. This is not the same as moving a node into a subgraph that already exists. ComfyUI has no operation for that; unpack the subgraph, then pack the parts together with whatever else belongs there. Args: pack: node and group ids to fold into one new subgraph. Anything that is not one convertible block is refused by litegraph, which says nothing else, so the refusal names that. unpack: subgraph node ids to dissolve, putting their contents back into the graph on screen. A node that is not a subgraph is refused. client_id: which browser tab to act in; defaults to the most recently focused one. |
| set_workspace_selectionA | Highlight nodes and groups on the canvas, so the user can see which ones you mean. The write half of what get_workspace_graph reports as Selecting nothing clears the selection. A selection is not part of the workflow, so this does not go on the undo stack and Ctrl+Z will not take it back. Args:
nodes: node ids to select, as they appear in get_workspace_graph. They are
local to the graph on screen - navigate_workspace first for a subgraph,
and drop any |
| diagnose_workspaceA | Report what is wrong with the workflow open in the browser, worst first. Reads the live graph and checks it against ComfyUI's own node schemas: node types that are not installed, required inputs with nothing plugged in, links whose types do not match, widget values outside the declared range, and inputs a node no longer has. Muted and bypassed nodes are reported too - they are the commonest reason a workflow "does nothing" while looking fine. Defaults to Each finding names the node and, where there is one, a A clean report is not a promise the run will succeed: ComfyUI validates more at queue time, and a value can be legal and still wrong. Args: scope: "all", "root" or "active" - see get_workspace_graph. refresh_schemas: re-fetch /object_info first. Schemas are cached for the life of the process, so pass this after installing nodes or models. client_id: which tab to inspect; defaults to the most recently focused one. |
| add_workspace_nodeA | Add a node to the workflow open in the browser, wired up and configured. Inserting a node is one edit, so it is one call and one Ctrl+Z: the widgets and links go in with it. Doing it in three calls would take three presses to undo one intention, and a failure partway would leave a stray node behind. Nothing is added unless every value and link validates. Link types are checked against the slots before anything is written, because litegraph refuses a mismatched connection by doing nothing and reporting nothing. Args:
type: the registered node type, e.g. "ImageScale" or "VAEEncodeTiled".
find_node_types looks one up, by slot type when the name is not
known; an unknown one comes back with near matches rather than a
bare refusal.
title: the label on the node. Defaults to the type's own.
pos: [x, y] on the canvas. Defaults to the middle of the current view,
so the user can see what arrived.
values: widget values for the new node, |
| remove_workspace_nodesA | Delete nodes from the workflow open in the browser. The whole batch is one undo step and nothing is removed unless every id
exists. The response reports To take a node out of the picture without losing its wiring, prefer set_workspace_node_modes - bypassing passes inputs straight through. Args: nodes: node ids, from get_workspace_graph. scope: "root" for the whole workflow, "active" for the subgraph on screen. client_id: which tab to edit; defaults to the most recently focused one. |
| set_workspace_linksA | Wire and unwire nodes in the workflow open in the browser. Both lists are applied in one undo step, disconnects first, so moving a link from one input to another is a single Ctrl+Z rather than a half-wired graph between two calls. An input holds one link, so connecting to a taken input replaces what was
there - the response says what under Args:
connect: |
| set_workspace_layoutA | Move, resize, fold and unfold nodes on the canvas open in the browser. None of this changes what a workflow does - it runs the same however it is laid out - so this is purely about making a graph readable. The whole batch is one undo step and nothing is written unless every id exists. Groups follow the nodes they were holding. A group is only a rectangle, and what is "inside" it is whatever falls within it, so moving nodes out from under one would silently empty it; membership is read before anything moves and each affected group is refitted around those same nodes afterwards. arrange_workspace computes positions rather than taking them, and its answer can be passed straight in here. Args:
positions: |
| set_workspace_groupsA | Create, edit and delete the group boxes on the canvas open in the browser. A group is a labelled rectangle drawn behind the nodes that fall inside it. It holds no membership of its own - which nodes are "in" it is decided by where the box is - so creating one around a list of nodes means fitting the box to them, and that is what this does. Deleting a group takes only the box: the nodes it framed stay exactly where they are. Get the current groups, with their ids and members, from get_workspace_graph. Args:
create: |
| arrange_workspaceA | Lay the workflow out left to right, in the order the data flows through it. Each node goes as far right as its consumers allow, so a loader sits beside the sampler that reads it rather than in a column of loaders at the far edge, and the nodes in a column are ordered and placed to face what they are wired to. The result keeps the top-left corner it already had, so it lands where the author left it rather than at the origin. Groups are laid out as groups. A group is a rectangle with no membership - what is in it is whatever falls inside - so arranging a grouped canvas flat scatters each group across the columns and its box stretches to follow, which on a real workflow turned eight tidy groups into overlapping sheets covering everything. So the layout runs inside each group first and then over the blocks they form, and ungrouped nodes travel together as one more block. It still moves every node it is given, which is the blunt instrument. Args: only: node ids to arrange, leaving every other node where it is. Links to nodes outside the list are ignored, since they cannot place anything. spacing_x: gap between columns, past the widest node in the left one. spacing_y: gap between nodes stacked in one column. origin: [x, y] for the top-left of the result. Defaults to the top-left of what is being arranged, so nothing wanders off. apply: write the positions. False computes and reports them without touching the canvas - the same dict can then be passed to set_workspace_layout. scope: "root" for the top level, "active" for the subgraph on screen. Not "all": a subgraph is a canvas of its own with its own coordinates, so there is no one layout that covers several. Use navigate_workspace to go in and arrange with "active". client_id: which tab to arrange; defaults to the most recently focused one. |
| align_workspaceA | Line nodes up on a common edge, space them evenly, or both. Unlike arrange_workspace this never reads a link: it moves the nodes it is given along one axis and changes nothing else about the layout. That is the point - straightening a row of loaders should not rearrange the workflow around them. Reach for this when the graph is already laid out the way the author wants and only looks untidy. Edges account for how big each node draws, so aligning Args:
nodes: the node ids to align - at least two. From get_workspace_graph.
edge: "left", "right", "top", "bottom", "centre_x" or "centre_y". The
centre forms use the middle of the whole selection.
distribute: "x" or "y" - even out the gaps between the nodes along that
axis. The outermost two stay where they are and the rest are shared
out between them; gaps rather than centres, since nodes differ in
size enough that even centres look uneven.
spacing: an exact gap for |
| list_workflowsA | List API-format workflow files available in the workflows directory. |
| describe_workflowA | Report the parameters a workflow accepts, plus its outputs and model files. Each parameter lists where it is actually written in the graph. Values reached
through primitives and switches are resolved automatically, so When ComfyUI is running, each parameter also carries its real type, allowed options and numeric range, taken from the node's own schema. Args: name: workflow file name, without the .json extension. refresh_schemas: re-read node schemas from ComfyUI. Use after installing models or custom nodes, since schemas are cached. |
| get_workflow_guideA | Read the instruction file that ships with a workflow. A graph says which inputs exist, never what belongs in them. Some workflows only work with input in a particular shape - Ideogram 4 wants a JSON caption carrying bounding boxes, not a prose prompt - and that convention lives in a Markdown file named after the workflow. Read it and follow it before calling run_workflow. Workflows that have one are flagged as Args: name: workflow file name, without the .json extension. |
| describe_nodeA | Look up one node type: its inputs, their valid values, and what it outputs. Use before wiring a node in with add_workspace_node, or to check allowed combo values (sampler names, schedulers, model files) before passing them to run_workflow. find_node_types is how you get the class_type in the first place. Args: class_type: the node's class name, e.g. 'KSampler'. full: return the raw /object_info entry instead of the summary. Combo option lists are complete there and can be very large - one node on this install measures 199k characters - so only ask when a truncated option list is actually the problem. |
| find_node_typesA | Find a node type to add to a workflow, by name or by what it connects to. Filters are ANDed and all are optional; with none of them this lists what is installed. Each result carries the node's slots, so it is usually enough on its own - reach for describe_node when you need a widget's allowed values. The type filters answer the question a graph editor actually asks. "What turns a LATENT into an IMAGE" is input_type='LATENT', output_type='IMAGE'; searching for the word 'latent' would never find VAEDecode, whose name and category contain neither word. Args: search: case-insensitive substring, matched against the node's name, title, category, description, and the search aliases ComfyUI ships - which is why 'latent to image' finds VAEDecode. input_type: only nodes accepting this slot type, e.g. 'IMAGE', 'MODEL'. output_type: only nodes producing it. Wildcard ('*') slots match anything, as they do when the link is drawn, but rank below nodes that name the type. category: substring of the node's category path, e.g. 'upscal', 'loaders'. pack: substring of the pack it came from, e.g. 'kjnodes', 'comfy_extras'. include_deprecated: include nodes ComfyUI marks as superseded. Off by default because something replaced them. Experimental nodes are always included - that flag means new, not unreliable. include_api: include paid cloud API nodes, which need an account. refresh: re-fetch /object_info first. Needed after installing nodes. limit: maximum results. What matched beyond it is still counted. |
| list_modelsA | List the models ComfyUI can actually load. Asks ComfyUI itself, so the result honours extra_model_paths.yaml and matches the
values a loader node will accept. A plain disk scan does not: model folders are
routinely mapped in from elsewhere and Args: folder: which folder to list, e.g. 'loras', 'checkpoints', 'diffusion_models'. Empty lists the available folder names instead. search: case-insensitive substring filter on the file name. limit: maximum number of files to return. |
| download_modelA | Download a model file into the folder ComfyUI will actually load it from. Meant for the models a workflow declares on its own loaders: at detail="full"
get_workspace_graph reports each node's Where the file lands is decided by ComfyUI, not by this server: the directory list honours extra_model_paths.yaml, so it is routinely on another drive entirely, and a model written anywhere else is invisible however right the bytes are. The reply always names the directory it chose. Interrupted transfers resume: the bytes go to a Args:
url: direct link to the file, as written in the note.
folder: ComfyUI model folder, e.g. 'vae' or 'diffusion_models'.
list_models() with no arguments lists the valid names.
filename: name to save as. Defaults to the last segment of the URL.
A 'subdir/name.safetensors' is allowed; ComfyUI loads those.
directory: which of the folder's registered directories to use. Defaults
to the first one that exists, which is what ComfyUI's own
|
| get_download_progressA | Report how far a download has got: bytes, percent, speed and an ETA.
Args: job_id: '/', or just the file name, or empty for the most recent download. |
| cancel_downloadA | Stop a download that is still running. What has arrived stays in the Args: job_id: '/', or just the file name, or empty for the most recent download. |
| run_workflowA | Run a workflow and return the paths of the files it produced. Call describe_workflow first to see which parameter names a workflow accepts. Pass seed=-1 to randomise the seed. Args: name: workflow file name, without the .json extension. params: parameter overrides, e.g. {"prompt": "a red fox", "seed": -1, "steps": 8}. Raw '.' keys are accepted for anything not discovered. wait: wait for the run to finish. When False, returns the prompt_id immediately and the run keeps being watched in the background, so get_progress(prompt_id) reports its steps as they happen. Prefer this for anything slow: waiting blind is what makes a caller mistake a working generation for a hung one. timeout: seconds to wait before giving up on a running job. save_outputs: convert PreviewImage nodes to SaveImage so results are written to output/ instead of the temp folder that ComfyUI clears on restart. free_on_switch: unload models from VRAM before this run when it needs a different set than the previous one AND free VRAM is already below COMFYUI_FREE_VRAM_MIN_FRACTION. Defaults to COMFYUI_FREE_ON_SWITCH. Low free VRAM is normal on its own - ComfyUI keeps models cached - so this deliberately does nothing when there is headroom. |
| run_workspaceA | Press Queue Prompt in the browser and watch the run from here. The tab queues its own canvas, unsaved edits and all, so everything reacts the way it does when the button is clicked by hand: nodes light up, progress bars fill, sampler previews appear, and the result lands in the node that produced it. Nothing is written to the workflows directory. Use this when the user is working on a graph in front of them; use run_workflow for a file. It has to be the tab that queues. ComfyUI addresses execution events to whoever
submitted the job, and the frontend only tracks jobs it queued itself, so a
graph submitted from here leaves the canvas reporting someone else's run. The
events are copied back to this server so progress is still reported; if that
copy cannot be set up the run still happens and Two consequences of it being the real button. Widget callbacks run, so a seed
set to There is no Muted and bypassed nodes are dropped when the graph is converted, so a muted
SaveImage produces no output at all. get_workspace_graph lists both under
Args: wait: wait for the run to finish. When False, returns the prompt_id immediately and keeps watching in the background, so get_progress(prompt_id) reports steps as they happen. Prefer this for anything slow - waiting blind is what makes a caller mistake a working generation for a hung one. timeout: seconds to wait before giving up on a running job. client_id: which tab to run; defaults to the most recently focused one. |
| get_resultC | Fetch the outputs of a previously submitted prompt. |
| get_progressA | Report how far a run has got: step, percent, elapsed time and an ETA. A generation takes minutes, and nothing about a long silence distinguishes real
work from a hang - so check here instead of guessing. Interrupting and re-running costs more than waiting: the models are already resident and a re-run pays for them again. Args: prompt_id: which run to report on. Defaults to the most recent one. |
| get_queueA | Show what ComfyUI is currently running and what is queued behind it. Running items carry their progress when this server started them; get_progress gives the same detail for one run. |
| interruptA | Interrupt the job ComfyUI is currently executing. For a run that is merely slow this is counter-productive - the steps already computed are lost and the models get reloaded. Check get_progress first. |
| free_memoryA | Ask ComfyUI to unload models and free VRAM. |
| show_imageA | Return a generated image so it can be viewed, downscaled to keep it small. Args: path: a path from run_workflow's outputs. max_edge: longest edge in pixels after downscaling. |
| upload_input_imageA | Upload a local image into ComfyUI's input folder so workflows can load it. Returns the name to pass to a LoadImage node. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/pytraveler/local-comfyui-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server