Bundles live under ComfyUI’s user dir at
<user>/comfyui-mcp-panel/apps/<app-id>/ — deliberately not the workflows
dir, so a hidden app never surfaces in the workflow browser.
There is one storage and run implementation — the panel pack’s HTTP routes
(
/comfyui_mcp_panel/apps/*). The desktop panel, the mobile Apps tab, and the
apps_* MCP tools are all clients of it, so an app behaves identically
wherever you launch it from.Requirements
Apps are served by the panel pack (comfyui-mcp-panel), not by the MCP
server alone. If the pack on your ComfyUI predates the feature, apps with
action:"list" fails with an explicit “the panel pack on this ComfyUI predates the Apps
feature” message — update the pack and restart ComfyUI.
Converting a workflow into an app
In the panel, the Apps toolbar button (next to Civitai) opens the app grid. Converting the open workflow does three things:- Imports ComfyUI APP-mode config if the workflow already carries one, and
otherwise picks inputs and outputs heuristically (prompt widgets, seeds,
sampler settings;
SaveImage-class nodes as outputs). Imported APP-mode inputs are honored on any node type, so custom-node endpoints survive the conversion. - Scans dependencies — the models and custom-node packs the graph needs —
into
manifest.deps. - Snapshots the prompt in API format. Widget values at conversion time
become each input’s form
default.
appMode.inputs carries nodeId, widget, label, and a kind
of text, number, combo, toggle, image, or model; combos also carry
choices. That is what the run form renders from — on desktop and on mobile.
Hiding the workflow
hideWorkflow drops workflow.json from the bundle entirely, so the graph
isn’t handed to whoever runs or installs the app.
Running an app
A run patches your form values into the stored snapshot and queues the result. Patch keys are"<nodeId>.<widget>" — for example {"6.text": "a cat", "3.seed": 42}. The key splits on the first dot only, so widget names that
themselves contain dots (LoRA stacks, lora_1.model) stay intact.
Patching is strict: a key addressing a node or an input that doesn’t exist
in the snapshot is a hard error, not a silent skip. A miss means the manifest
has drifted from the snapshot, and failing loudly beats running with stale
values. Inputs you omit keep their conversion-time defaults.
The run returns a prompt_id; poll it for status (pending → running →
done, or unknown if ComfyUI has never heard of it) and for the outputs
grouped under each output node.
Running on a RunPod pod
The panel’s Run on RunPod path reuses the same patch engine in dry mode: the panel asks for the patched prompt without queueing it locally, pushes any pinned dependencies to the pod, and enqueues the prompt there instead.Publish and Explore
The panel’s Explore tab is a public registry (a Cloudflare Worker backed by D1 + R2) with trending / new / most-starred listings and search. Trending is 7-daystars * 3 + runs. Publishing uploads the bundle — manifest, prompt,
workflow unless hidden, thumbnail — under a sha256-keyed creator identity.
Installing from Explore shows a dependency-consent dialog first: an app’s
deps are reported, never silently installed. Nothing installs a model or a
custom-node pack on your rig because you tapped a card.
pricing_json and hosted_only exist in the manifest schema and are passed
through unchanged, but nothing reads them. They reserve space for a
design-only monetization phase — there is no paid-app behavior today.The apps MCP tool
One tool with five actions, all thin proxies over the panel’s Apps API. It is
the canvas-less surface: what the mobile app and a directly-driven agent use.
It is on the orchestrator’s call_tool whitelist — list/get/run_status
are read-only, and run carries the same risk posture as enqueue_workflow
(it queues a job the user explicitly tapped).
Parameters
action is the only schema-required parameter — each action needs a different
subset, so the rest are optional in the schema and their presence is enforced by
the handler, which names the field it is missing.
The
prompt_id shape constraint is enforced twice — at the schema boundary
and again inside the handler — because the id is interpolated into a URL path. A
traversal-shaped “prompt id” must never reach the URL builder even if a caller
bypasses the schema.
For the generated per-tool schema reference, see
Apps tools.
Importing from the registry
action:"import" fetches the registry bundle server-side and creates it as a
local app. The registry id becomes the local id, so re-importing an app you
already have reports an id conflict rather than duplicating it. The thumbnail
lives at a separate registry endpoint and is fetched and forwarded separately,
so an installed app keeps its card art.
Dependencies are not installed. The tool returns the manifest’s deps so
the caller can report them and let the user install them deliberately.
Limits and validation
Things you can actually hit:
Validation you’ll notice:
- App ids must be uuids. Anything else is rejected before a path is built, and the resolved bundle path is re-checked for containment under the apps root.
- A prompt must be API format — numeric node-id keys, each node a
{class_type, inputs}object. UI-format graphs are rejected. - A UI workflow is required unless
hideWorkflowis set. - Creating an app that already exists is a conflict, not an overwrite.
- Partial manifest updates are genuinely partial. Publishing or hiding an
app sends only its own fields and will not wipe your name, description, or
appMode. - Unknown manifest keys are dropped, except the reserved pass-through fields, so an older rig ignores fields it doesn’t understand instead of failing.
COMFYUI_MCP_APPS_DIR (primarily for tests);
by default it’s derived from ComfyUI’s own user directory, so it survives
portable installs.
On the phone
The mobile app ships a real Apps tab — not a preview. It has two halves:- My Apps — the apps installed on your rig, listed over the bridge via
action:"list". Tapping one opens a generated run form, queues it withaction:"run", and pollsaction:"run_status"every 2s (bounded at 30 minutes) until the outputs render. - Explore — the public registry, hit directly over HTTPS from the phone
(no bridge hop, so browsing works before you’ve paired). Installing goes the
other direction: the rig fetches the bundle itself via
action:"import".
See also
- Apps tools — the generated per-tool schema reference
- Sidebar Panel — where apps are converted, published, and explored
- Mobile app — the Apps tab in context
- RunPod pods — the pod the “Run on RunPod” path targets
- Roadmap