comfyui-mcp
Version:
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),
78 lines • 3.42 kB
JavaScript
import { liveCategoryListing, } from "./model-resolver.js";
/** Only widget names whose model directory is unambiguous in current ComfyUI APIs. */
const MODEL_CATEGORY_BY_WIDGET = Object.freeze({
ckpt_name: "checkpoints",
lora_name: "loras",
vae_name: "vae",
unet_name: "diffusion_models",
diffusion_model: "diffusion_models",
control_net_name: "controlnet",
controlnet_name: "controlnet",
clip_name: "text_encoders",
clip_name1: "text_encoders",
clip_name2: "text_encoders",
clip_name3: "text_encoders",
clip_name4: "text_encoders",
style_model_name: "style_models",
embedding_name: "embeddings",
gligen_name: "gligen",
hypernetwork_name: "hypernetworks",
});
const MODEL_INVENTORY_DISCLOSURE_TIMEOUT_MS = 2000;
function normalizeInventoryEntry(value) {
return value.replace(/\\/g, "/").replace(/^\.?\//, "");
}
/**
* Current panel_set_widget refusals distinguish a real, readable combo list
* from an empty/unreadable list. Only the former can establish this mismatch.
*/
export function isReadableComboValueRefusal(refusalText) {
if (typeof refusalText !== "string")
return false;
return (/not a valid option for combo widget/i.test(refusalText) &&
/option list was read successfully/i.test(refusalText) &&
/none of them this value/i.test(refusalText));
}
/**
* Compare the panel's strict combo refusal with the connected ComfyUI's live
* `/models/<category>` listing. `undefined` is deliberately inconclusive: an
* unavailable or non-enumerable endpoint must never become a mismatch claim.
*/
export async function getModelInventoryDisclosure(widget, value, refusalText, listCategory = (category, options) => liveCategoryListing(category, options)) {
if (!isReadableComboValueRefusal(refusalText))
return null;
if (typeof widget !== "string")
return null;
if (typeof value !== "string" || value.length === 0)
return null;
const category = Object.prototype.hasOwnProperty.call(MODEL_CATEGORY_BY_WIDGET, widget)
? MODEL_CATEGORY_BY_WIDGET[widget]
: undefined;
if (!category)
return null;
let listing;
try {
listing = await listCategory(category, {
timeoutMs: MODEL_INVENTORY_DISCLOSURE_TIMEOUT_MS,
});
}
catch {
return null;
}
if (!listing)
return null;
const normalizedValue = normalizeInventoryEntry(value);
if (!listing.some((entry) => typeof entry === "string" && normalizeInventoryEntry(entry) === normalizedValue)) {
return null;
}
return (`\n\nModel inventory disclosure: the connected ComfyUI's "/models/${category}" ` +
`listing includes "${value}", but the panel's refreshed "/object_info" combo ` +
`did not. These are different server surfaces: "/models" visibility does not ` +
`establish "/object_info" availability, and the panel cannot make the server ` +
`rescan its model filename cache. Nothing was written. This comparison assumes ` +
`the MCP and panel target the same ComfyUI instance; verify that if they may differ. ` +
`Restart ComfyUI, or use a server-side cache-refresh mechanism provided by your ` +
`ComfyUI build. After "/object_info" includes the value, call panel_refresh_nodes ` +
`and retry.`);
}
//# sourceMappingURL=model-inventory-disclosure.js.map