Multi-backend pool, async-completion hardening, LLM-friendly responses, 42-workflow library - #15
Open
svilendotorg wants to merge 26 commits into
Open
Multi-backend pool, async-completion hardening, LLM-friendly responses, 42-workflow library#15svilendotorg wants to merge 26 commits into
svilendotorg wants to merge 26 commits into
Conversation
- Add 42 production workflows (image/video/audio/3D) replacing default stubs - Add .meta.json sidecar per workflow with timing, resolution, and best-for hints (loaded by _load_workflows to populate MCP tool descriptions) - Add ui2api_full.py: advanced UI→API converter (UUID subgraphs, Reroutes, bypassed nodes, nested subgraphs, inject_params for PARAM_ placeholders) - Patch workflow_manager.py: read sidecar metadata description in _load_workflows, skip .meta.json files in glob, date substitution in filename_prefix fields - Patch comfyui_client.py: (upstream patches applied) - Remove default stub workflows (basic_api_test, generate_image, generate_song)
…-{wid} convention
- comfyui_pool.py: ComfyUIPool drop-in for ComfyUIClient - parse_backends(): COMFYUI_URLS=name=url,name=url env format - least-queue routing via /queue endpoint (depth 999 = unreachable) - explicit routing via backend= kwarg (name or URL substring) - server.py: use ComfyUIPool when COMFYUI_URLS env set; startup check uses first pool backend - tools/generation.py: inject backend: Optional[str] = None into all workflow tool signatures - tools/workflow.py: add backend param to run_workflow
Adds tools/upload.py with upload_file(filename, base64_content, backend, content_type, target). Routes through pool _pick_client; supports explicit backend param. 100MB max decoded. MIME inferred from extension; falls back to application/octet-stream. Tested: PNG → rtx3090, WAV → rtx4090. Both fetchable via view_url after upload.
Single-shot base64 doesn't scale — large payloads explode tool-call response tokens and truncate the model output. Adds chunk_index/total_chunks/upload_id params so clients can split a file into sequential ≤140 KB base64 calls. Server buffers in memory keyed by upload_id (uuid), assembles on final chunk, posts to ComfyUI. Also: overwrite=False default with auto-suffix (foo.jpg → foo_1.jpg) so re-tests don't silently clobber. Stale upload sessions GC after 30 min. Tested: 256 KB binary in 4 chunks → byte-exact roundtrip. Auto-suffix verified.
Pool now queries each backend's /foreign_vram endpoint (custom_node, served by ComfyUI itself via pynvml) and excludes backends where another process is holding >= POOL_MAX_FOREIGN_VRAM_GB (default 4 GB) on the pinned GPU. Distinguishes ComfyUI's own warm-loaded checkpoint (own_vram, allowed) from external squatters like a llama.cpp server (foreign_vram, disqualifies). Falls back to all backends if every one is VRAM-tight (better to try than to hard-fail). Explicit backend= override always honored. Tested: simulated 8 GB hog on GPU 0 → pool log emits "Skipping rtx3090-0: foreign VRAM 8.9GB >= 4.0GB" and routes elsewhere. After hog dies, foreign returns to 0.
…/mesh) Centralized output dir surfaced an inconsistency — some workflows wrote to output/ root, others to subfolders. Now uniform: - images/ for all *_t2i and *_i2i workflows - audio/ for all *_t2a workflows - mesh/ for hunyuan3d_* - video/ already correct (unchanged) 31 changes across 23 workflows. qwen25_i2i_angles has 8 SaveImage nodes (one per camera angle) — all updated.
ComfyUIPool was a partial drop-in replacement — run_custom_workflow worked but get_queue_status / get_job / cancel_job MCP tools all errored with "ComfyUIPool object has no attribute get_queue" because those methods existed only on ComfyUIClient, not the pool wrapper. - get_queue: aggregates queue_running + queue_pending across all backends, tags each entry with the backend name so callers can see pool-wide queue depth. - get_history(prompt_id): fans out, returns the first match. Each prompt_id lives on exactly one backend (the one it ran on) so the result is unambiguous. - cancel_prompt: fans out; only the backend running the prompt will succeed.
When a workflow exceeds the 30s sync timeout, run_custom_workflow returns a "running" handle and the asset never gets registered. Subsequent get_job calls returned the outputs but list_assets / view_image saw nothing because the registry was empty. Now: - get_job walks completed-prompt outputs and registers each via the AssetRegistry. Idempotent (registry dedupes on filename+subfolder+type). - ComfyUIPool.get_history stamps _pool_backend_url onto the returned dict. - get_job uses that to set the asset record base_url so generated URLs point at the correct backend hostname (not localhost:8188 fallback). - ComfyUIPool.get_queue now returns the same flat list[list] shape as ComfyUIClient.get_queue (no per-backend wrapping) so existing prompt_id matching code works unchanged. Tested: ltx2_t2v-style async submit → get_job → list_assets shows the video with correct comfyui<N>.svilen.org asset_url.
Adds a ready-to-paste Markdown snippet to every tool result so the assistant can render the asset inline in chat by simply echoing the field. Image MIMEs get "" (rendered as image by Claude Desktop); audio/video/3D get "[emoji label](url)" (clickable link — Claude Desktop does not inline-render non-image content blocks). Background: Anthropic moved MCP image content into the collapsed tool-output expander, so FastMCP Image responses are no longer visible inline. The fix relies on the LLM emitting markdown in the conversation text, which Claude Desktop renders natively. Tool descriptions now include a one-liner instructing the LLM to use the field.
Workflows like hunyuan3d_i2mesh, hunyuan3d_t2mv, sonic_a2v take only an image (and audio) — no prompt/tags to auto-derive a topic from. Now the resolver parses the source filename for an embedded topic slug and reuses it. Mechanism: regex out _NNNNN_, _comfyui<N>, and the trailing workflow_id (matched against the loaded _KNOWN_WORKFLOW_IDS list, longest-first); strip the leading date prefix; whatever remains is the prior topic. Empty when the source had no topic baked in either. Result: 3D mesh / sonic video filenames inherit the topic of the input image — chained workflows keep a coherent slug across the chain.
ComfyUIClient._extract_first_asset_info raised an exception for hunyuan3d_*
because their output nodes use the key 3d/mesh which was not in the
preferred_output_keys set (only images/audio/video). The mesh file landed
on disk fine but the MCP tool error prevented asset registration.
Adds MESH_OUTPUT_KEYS = ("3d", "mesh", "glb", "gltf", "obj") and
extends _guess_output_preferences to detect mesh-class workflows
(hy3d / hunyuan3d / mesh / 3d in class_type).
…decars The 42 sidecar files have been merged into their workflow JSONs under a top-level "_meta" key. Loader now reads from there directly; submit pipeline strips _meta from the in-memory copy so ComfyUIs /prompt never sees it. Result: half as many files in workflows/, single-source-of-truth metadata, no behaviour change for callers.
Author
… off) Every *.json in workflows/ was registered both as a typed MCP tool and via the run_workflow/list_workflows dispatcher that already covers it. With ~40 workflows this is ~20k tokens of duplicate schema in client context per session. New env var COMFY_MCP_REGISTER_PER_WORKFLOW_TOOLS (default: false) gates the per-workflow registration. Default-off saves the context; set to true to restore the legacy per-workflow tool layout. Dispatcher and regenerate tools are unaffected.
The dispatcher (run_workflow) loads workflows via load_workflow which did not strip the _meta metadata key, causing ComfyUI to reject the workflow with 'missing_node_type' on the _meta entry. The per-workflow tool path went through render_workflow which was already _meta-aware, so the bug only surfaces when run_workflow is the primary entrypoint.
Clones of flux_klein_9b_t2i and flux_klein_9b_i2i_distilled with the UNet swapped to fluxtraitFLUX2KleinFLUXZ_klein9bV3.safetensors (Flux 2 Klein 9B BF16, FluxTrait portrait/skin tune from CivitAI 2086049). Trigger word: Fluxtrait. Verified e2e on comfyui1, comfyui2, comfyui3.
Pony Diffusion V6 XL + serene-yoga-studio Pony LoRA (0.8) + YogaTreePony pose LoRA (0.8). DPM++ 2M Karras, CFG 7, 30 steps, 832x1216. Trigger words baked into the recommended prompt prefix; documented in _meta. Verified e2e on comfyui1, comfyui2, comfyui3.
pony_monika_t2i: Pony V6 XL + monika-pony-v1 LoRA (0.85). Standalone likeness for any prompt. pony_yoga_monika_t2i: 3-LoRA stack \(serene-yoga-studio 0.8 + YogaTreePony 0.8 + monika-pony-v1 0.85\). One-shot "Monika doing tree pose in a yoga studio". Both verified e2e on comfyui1 - generation succeeds, image saved.
ralphgrewe
added a commit
to ralphgrewe/comfyui-mcp-server
that referenced
this pull request
Jul 18, 2026
…oenorton#15) publish_asset previously could only write into the server's own fixed publish_root (project_root/public/gen at startup), with no way to target a caller-specified directory. Add an optional target_subdir param, backed by a persisted publish_base_dir (configured once via the new set_publish_base_dir tool, mirroring set_comfyui_output_root), so a caller can publish straight into e.g. a game's assets/ folder without going through the demo publish_root. target_subdir is validated (relative, no traversal) and containment-checked against the base dir on every call.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is an umbrella PR for ~20 patches accumulated in
svilendotorg/ComfyUI-MCP-Serverover a homelab deployment. Full per-patch write-up + rationale lives in IMPROVEMENTS.md. I'm happy to split into smaller PRs for any subset you want to land — just say which ones and I'll cherry-pick onto fresh branches.Headline features
Multi-backend pool with VRAM-aware routing —
comfyui_pool.py(new)Drop-in replacement for
ComfyUIClientthat fronts multiple ComfyUI instances overCOMFYUI_URLS=name=url,.... Routing modes:foreign_vram > POOL_MAX_FOREIGN_VRAM_GB(default 4 GB) are excluded — distinguishes "ComfyUI's own warm checkpoint" (allowed, fast) from "another process is squatting" (skip).backend="..."parameter that overrides the filter.VRAM signal sourced from a small companion custom_node (ComfyUI-ForeignVRAM) that exposes
/foreign_vram[?max_foreign_gb=N](returns 503 when over threshold so an upstream Traefik / NGINX healthcheck can also kick busy backends out of LB rotation).Async-completion asset registration —
tools/job.pyWorkflows over the 30s sync timeout return a
runninghandle. Pollingget_job(prompt_id)previously returned outputs but never wrote toAssetRegistry, solist_assets/view_imagecouldn't find them. Nowget_jobwalks completed-prompt outputs and registers each viaAssetRegistry.register_asset. Pool stamps_pool_backend_urlonto the returned history dict so the registration uses the correct hostname.LLM-friendly response shape
Every workflow +
get_jobresponse now includesmarkdown_preview— a ready-to-paste markdown snippet. Image MIMEs →(Claude Desktop renders inline images in chat). Audio →[🔊 filename](url). Video →[🎬 ...]. 3D →[🎲 ...]. Tool descriptions are augmented with a hint to paste the field.Optional
topicslug + auto-deriveEvery workflow tool accepts an optional
topicarg (1–2 word slug). Server slugifies and injects between the date and workflow id infilename_prefix:When the LLM skips
topic, the server auto-derives one — first fromprompt/tags(drop English stopwords + generic words likerealistic,image); failing that, parse the sourceimagefilename and carry the slug forward (sosdxl_t2i → hunyuan3d_i2meshchains keep one coherent topic).Friendlier image / audio inputs
image,image_last, andaudioparameters now accept any of:input/)asset_idreturned by a previous tool call (resolved via the registry)get_asset_by_identityscan over known output subfolders)Lets the LLM chain workflows naturally without manually constructing URLs.
Mesh / 3D output keys
Output-key matching previously knew only
images / image / gifs / gif / audio / audios / files. Hunyuan3D workflows emit a3dkey — files landed on disk but the MCP raised an exception and the LLM never saw the asset. AddsMESH_OUTPUT_KEYS = ("3d", "mesh", "glb", "gltf", "obj")and extends_guess_output_preferencesto detect mesh-class workflows.upload_fileMCP tool —tools/upload.py(new)A new tool for pushing arbitrary binary files (images / audio / video) into ComfyUI's input folder via base64. Single-shot for ≤ 100 KB; chunked mode (
chunk_index+total_chunks+upload_id) for larger files (≤ 100 MB total) — MCP tool calls don't handle multi-MB base64 payloads gracefully otherwise.Workflow library — 42 workflows + embedded
_meta42 workflow JSONs covering image / audio / video / 3D mesh tasks. Each has a top-level
_metakey with timing / resolution / best-for / param hints;WorkflowManager._load_workflow_metadatareads from there and uses it as the MCP tool description. The_metakey is stripped before submission to ComfyUI's/promptendpoint.Naming:
{model}_{task}[_{variant}]— tasks includet2ii2it2vi2vd2vfl2vt2at2mvi2mesha2v. Namespace (image / video / audio / mesh) auto-detected from the task suffix.Output subfolder convention
Every workflow's
filename_prefixwrites into a typed subfolder ofoutput/—images/for*_t2i*/*_i2i*,audio/for*_t2a*,video/for*_t2v*/*_i2v*/*_fl2v*/*_d2v*/sonic_a2v,mesh/forhunyuan3d_*.Misc fixes
%date:yyyy-MM-dd%patterns infilename_prefixat submit time (ComfyUI'sSaveImagedoesn't do it natively).result["backend_url"] = client.base_urlafter submit so asset URLs returned to the caller point at the actual backend hostname, not the registry's localhost default.EmptyFlux2LatentImage/EmptyLTXVLatentVideobatch_sizecopy-paste bugs (set to1),ResizeImageMaskNodeDynamicCombo dot-prefix sub-fields,ComfyMathExpressionvalues.a/values.bre-binding, removedClownSamplerpaths from LTX-2.3 workflows,PARAM_AUDIOinjection forLoadAudionodes, secondLoadImagenode inwan22_fl2vcorrectly bound toPARAM_IMAGE_LAST.Splitting
If any subset is welcome and others aren't, I can rebase individual commits onto separate branches and open them as standalone PRs. Just say which ones.
Tested
Two RTX 3090s on one host (LXC, dual-instance ComfyUI) + an RTX 4090 on a separate Windows host, fronted by Traefik LB and the multi-backend pool. All 42 workflows run end-to-end across all three backends.