@fre4x/comfyui 1.0.57 → 1.0.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/index.js +197 -75
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,12 @@ An MCP server to interact with ComfyUI remotely. Probe server state, inspect nod
|
|
|
11
11
|
- `comfyui_get_queue`: Check current server queue.
|
|
12
12
|
- `comfyui_get_view_url`: Get a direct URL for a generated file.
|
|
13
13
|
|
|
14
|
+
**Do not assume `structuredContent` is what the model actually sees.** Many MCP clients
|
|
15
|
+
surface `content.text` to the model first and treat `structuredContent` as secondary
|
|
16
|
+
machine-readable metadata. Every actionable detail that an agent needs for its next step
|
|
17
|
+
must therefore appear in `content.text` as well: node options, filenames, URLs, prompt
|
|
18
|
+
IDs, and recovery hints.
|
|
19
|
+
|
|
14
20
|
## Setup
|
|
15
21
|
|
|
16
22
|
### Environment Variables
|
package/dist/index.js
CHANGED
|
@@ -30585,6 +30585,7 @@ var zNamespace = zod_exports;
|
|
|
30585
30585
|
var z3 = zNamespace.z ?? zNamespace.default ?? zNamespace;
|
|
30586
30586
|
var COMFYUI_SERVER_URL = process.env.COMFYUI_SERVER_URL || "http://localhost:8188";
|
|
30587
30587
|
var PACKAGE_VERSION = getPackageVersion(import.meta.url);
|
|
30588
|
+
var BYTES_PER_GIB = 1024 ** 3;
|
|
30588
30589
|
var PaginationMetadataSchema = z3.object({
|
|
30589
30590
|
total_count: z3.number(),
|
|
30590
30591
|
limit: z3.number(),
|
|
@@ -30716,6 +30717,161 @@ function normalizeSystemInfo(source) {
|
|
|
30716
30717
|
})
|
|
30717
30718
|
};
|
|
30718
30719
|
}
|
|
30720
|
+
function formatGiB(bytes) {
|
|
30721
|
+
return `${(bytes / BYTES_PER_GIB).toFixed(1)} GiB`;
|
|
30722
|
+
}
|
|
30723
|
+
function getEnumLikeOptions(value) {
|
|
30724
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
30725
|
+
return [];
|
|
30726
|
+
}
|
|
30727
|
+
const firstEntry = value[0];
|
|
30728
|
+
if (!Array.isArray(firstEntry)) {
|
|
30729
|
+
return [];
|
|
30730
|
+
}
|
|
30731
|
+
return firstEntry.filter(
|
|
30732
|
+
(entry) => typeof entry === "string" || typeof entry === "number" || typeof entry === "boolean"
|
|
30733
|
+
);
|
|
30734
|
+
}
|
|
30735
|
+
function summarizeOptionLines(inputSection) {
|
|
30736
|
+
if (!inputSection) {
|
|
30737
|
+
return [];
|
|
30738
|
+
}
|
|
30739
|
+
return Object.entries(inputSection).flatMap(([inputName, definition]) => {
|
|
30740
|
+
const options = getEnumLikeOptions(definition);
|
|
30741
|
+
if (options.length === 0) {
|
|
30742
|
+
return [];
|
|
30743
|
+
}
|
|
30744
|
+
const preview = options.slice(0, 5).map(String).join(", ");
|
|
30745
|
+
const suffix = options.length > 5 ? `, ... (${options.length} total)` : "";
|
|
30746
|
+
return [`${inputName}: ${preview}${suffix}`];
|
|
30747
|
+
});
|
|
30748
|
+
}
|
|
30749
|
+
function summarizeSystemInfoText(structuredContent) {
|
|
30750
|
+
const deviceSummary = structuredContent.devices.length === 0 ? "_No devices reported._" : structuredContent.devices.map(
|
|
30751
|
+
(device) => `- [${device.index}] ${device.name} (${device.type}, ${formatGiB(device.vram_free)} free / ${formatGiB(device.vram_total)} total VRAM)`
|
|
30752
|
+
).join("\n");
|
|
30753
|
+
return [
|
|
30754
|
+
`ComfyUI server is running on ${structuredContent.system.os} with ${structuredContent.devices.length} device(s).`,
|
|
30755
|
+
formatFields([
|
|
30756
|
+
["Python", structuredContent.system.python_version],
|
|
30757
|
+
[
|
|
30758
|
+
"Embedded Python",
|
|
30759
|
+
structuredContent.system.embedded_python ? "yes" : "no"
|
|
30760
|
+
]
|
|
30761
|
+
]),
|
|
30762
|
+
`**Devices:**
|
|
30763
|
+
${deviceSummary}`
|
|
30764
|
+
].join("\n");
|
|
30765
|
+
}
|
|
30766
|
+
function summarizeNodeDefinitionText(nodeClass, nodeDefinition) {
|
|
30767
|
+
if (!isRecord(nodeDefinition)) {
|
|
30768
|
+
return `Schema retrieved for node: ${nodeClass}`;
|
|
30769
|
+
}
|
|
30770
|
+
const inputRecord = isRecord(nodeDefinition.input) ? nodeDefinition.input : void 0;
|
|
30771
|
+
const requiredInputs = isRecord(inputRecord?.required) ? inputRecord.required : void 0;
|
|
30772
|
+
const optionalInputs = isRecord(inputRecord?.optional) ? inputRecord.optional : void 0;
|
|
30773
|
+
const outputNames = Array.isArray(nodeDefinition.output) ? nodeDefinition.output.filter(
|
|
30774
|
+
(output) => typeof output === "string"
|
|
30775
|
+
) : [];
|
|
30776
|
+
const optionLines = [
|
|
30777
|
+
...summarizeOptionLines(requiredInputs),
|
|
30778
|
+
...summarizeOptionLines(optionalInputs)
|
|
30779
|
+
];
|
|
30780
|
+
return [
|
|
30781
|
+
`Schema retrieved for node: ${nodeClass}`,
|
|
30782
|
+
formatFields([
|
|
30783
|
+
[
|
|
30784
|
+
"Display Name",
|
|
30785
|
+
typeof nodeDefinition.display_name === "string" ? nodeDefinition.display_name : typeof nodeDefinition.name === "string" ? nodeDefinition.name : nodeClass
|
|
30786
|
+
],
|
|
30787
|
+
[
|
|
30788
|
+
"Category",
|
|
30789
|
+
typeof nodeDefinition.category === "string" ? nodeDefinition.category : null
|
|
30790
|
+
],
|
|
30791
|
+
[
|
|
30792
|
+
"Required Inputs",
|
|
30793
|
+
requiredInputs ? Object.keys(requiredInputs).join(", ") || "none" : "none"
|
|
30794
|
+
],
|
|
30795
|
+
[
|
|
30796
|
+
"Optional Inputs",
|
|
30797
|
+
optionalInputs ? Object.keys(optionalInputs).join(", ") || null : null
|
|
30798
|
+
],
|
|
30799
|
+
["Outputs", outputNames.join(", ") || null]
|
|
30800
|
+
]),
|
|
30801
|
+
optionLines.length > 0 ? `**Enumerated options:**
|
|
30802
|
+
${optionLines.map((line) => `- ${line}`).join("\n")}` : ""
|
|
30803
|
+
].filter(Boolean).join("\n");
|
|
30804
|
+
}
|
|
30805
|
+
function summarizeHistoryEntryText(promptId, entry) {
|
|
30806
|
+
if (!isRecord(entry)) {
|
|
30807
|
+
return `History retrieved for job: ${promptId}`;
|
|
30808
|
+
}
|
|
30809
|
+
const statusRecord = isRecord(entry.status) ? entry.status : void 0;
|
|
30810
|
+
const outputsRecord = isRecord(entry.outputs) ? entry.outputs : void 0;
|
|
30811
|
+
const imageRefs = [];
|
|
30812
|
+
if (outputsRecord) {
|
|
30813
|
+
for (const output of Object.values(outputsRecord)) {
|
|
30814
|
+
if (!isRecord(output) || !Array.isArray(output.images)) {
|
|
30815
|
+
continue;
|
|
30816
|
+
}
|
|
30817
|
+
for (const image of output.images) {
|
|
30818
|
+
if (!isRecord(image) || typeof image.filename !== "string") {
|
|
30819
|
+
continue;
|
|
30820
|
+
}
|
|
30821
|
+
const subfolder = typeof image.subfolder === "string" && image.subfolder ? `${image.subfolder}/` : "";
|
|
30822
|
+
imageRefs.push(`${subfolder}${image.filename}`);
|
|
30823
|
+
}
|
|
30824
|
+
}
|
|
30825
|
+
}
|
|
30826
|
+
return [
|
|
30827
|
+
`History retrieved for job: ${promptId}`,
|
|
30828
|
+
formatFields([
|
|
30829
|
+
[
|
|
30830
|
+
"Status",
|
|
30831
|
+
typeof statusRecord?.status_str === "string" ? statusRecord.status_str : "unknown"
|
|
30832
|
+
],
|
|
30833
|
+
[
|
|
30834
|
+
"Completed",
|
|
30835
|
+
statusRecord?.completed === true ? "yes" : statusRecord?.completed === false ? "no" : "unknown"
|
|
30836
|
+
],
|
|
30837
|
+
[
|
|
30838
|
+
"Images",
|
|
30839
|
+
imageRefs.length > 0 ? imageRefs.slice(0, 5).join(", ") : "none reported"
|
|
30840
|
+
]
|
|
30841
|
+
])
|
|
30842
|
+
].join("\n");
|
|
30843
|
+
}
|
|
30844
|
+
function summarizeWorkflowResponseText(result) {
|
|
30845
|
+
const hasErrors = Object.keys(result.node_errors).length > 0;
|
|
30846
|
+
return [
|
|
30847
|
+
formatFields([
|
|
30848
|
+
[
|
|
30849
|
+
"Status",
|
|
30850
|
+
hasErrors ? "Submitted with node errors" : "Successfully queued"
|
|
30851
|
+
],
|
|
30852
|
+
["Prompt ID", result.prompt_id],
|
|
30853
|
+
["Queue Number", result.number]
|
|
30854
|
+
]),
|
|
30855
|
+
hasErrors ? "Fix the reported node_errors before retrying this workflow." : `Next step: call comfyui_get_history with prompt_id="${result.prompt_id}" to monitor outputs.`
|
|
30856
|
+
].join("\n");
|
|
30857
|
+
}
|
|
30858
|
+
function createAgentFacingResult(text, structuredContent) {
|
|
30859
|
+
const normalizedText = text.trim();
|
|
30860
|
+
if (normalizedText.length === 0) {
|
|
30861
|
+
throw new Error(
|
|
30862
|
+
"Tool response text must be non-empty. Do not rely on structuredContent alone because many MCP clients expose content.text to the model first."
|
|
30863
|
+
);
|
|
30864
|
+
}
|
|
30865
|
+
return {
|
|
30866
|
+
content: [
|
|
30867
|
+
{
|
|
30868
|
+
type: "text",
|
|
30869
|
+
text: normalizedText
|
|
30870
|
+
}
|
|
30871
|
+
],
|
|
30872
|
+
structuredContent
|
|
30873
|
+
};
|
|
30874
|
+
}
|
|
30719
30875
|
async function fetchComfyJson(apiPath, options) {
|
|
30720
30876
|
const url2 = new URL(apiPath, COMFYUI_SERVER_URL).toString();
|
|
30721
30877
|
let response;
|
|
@@ -30775,15 +30931,10 @@ async function handleGetSystemInfo() {
|
|
|
30775
30931
|
try {
|
|
30776
30932
|
const rawStats = IS_MOCK ? MOCK_FIXTURES.system_stats : await fetchComfyJson("/system_stats");
|
|
30777
30933
|
const structuredContent = normalizeSystemInfo(rawStats);
|
|
30778
|
-
return
|
|
30779
|
-
|
|
30780
|
-
{
|
|
30781
|
-
type: "text",
|
|
30782
|
-
text: `ComfyUI server is running on ${structuredContent.system.os} with ${structuredContent.devices.length} device(s).`
|
|
30783
|
-
}
|
|
30784
|
-
],
|
|
30934
|
+
return createAgentFacingResult(
|
|
30935
|
+
summarizeSystemInfoText(structuredContent),
|
|
30785
30936
|
structuredContent
|
|
30786
|
-
|
|
30937
|
+
);
|
|
30787
30938
|
} catch (error48) {
|
|
30788
30939
|
return handleComfyError(error48);
|
|
30789
30940
|
}
|
|
@@ -30798,17 +30949,12 @@ async function handleListObjectInfo(args) {
|
|
|
30798
30949
|
return createNotFoundError(`Node class ${args.node_class}`);
|
|
30799
30950
|
}
|
|
30800
30951
|
const nodeDefinition = unwrapNamedNode(args.node_class, rawInfo);
|
|
30801
|
-
return
|
|
30802
|
-
|
|
30803
|
-
|
|
30804
|
-
type: "text",
|
|
30805
|
-
text: `Schema retrieved for node: ${args.node_class}`
|
|
30806
|
-
}
|
|
30807
|
-
],
|
|
30808
|
-
structuredContent: {
|
|
30952
|
+
return createAgentFacingResult(
|
|
30953
|
+
summarizeNodeDefinitionText(args.node_class, nodeDefinition),
|
|
30954
|
+
{
|
|
30809
30955
|
nodes: { [args.node_class]: nodeDefinition }
|
|
30810
30956
|
}
|
|
30811
|
-
|
|
30957
|
+
);
|
|
30812
30958
|
}
|
|
30813
30959
|
const nodes = ensureRecord(rawInfo, "object_info");
|
|
30814
30960
|
const entries = Object.entries(nodes);
|
|
@@ -30822,15 +30968,17 @@ async function handleListObjectInfo(args) {
|
|
|
30822
30968
|
has_more: paginated.hasMore
|
|
30823
30969
|
}
|
|
30824
30970
|
};
|
|
30825
|
-
return
|
|
30826
|
-
|
|
30827
|
-
{
|
|
30828
|
-
|
|
30829
|
-
|
|
30830
|
-
}
|
|
30831
|
-
|
|
30971
|
+
return createAgentFacingResult(
|
|
30972
|
+
[
|
|
30973
|
+
`Retrieved ${paginated.items.length} of ${paginated.total} node definitions.`,
|
|
30974
|
+
paginated.items.length > 0 ? `Node classes: ${paginated.items.slice(0, 10).map(([nodeClass]) => nodeClass).join(
|
|
30975
|
+
", "
|
|
30976
|
+
)}${paginated.items.length > 10 ? ", ..." : ""}` : "",
|
|
30977
|
+
paginated.hasMore ? `Use offset=${paginated.offset + paginated.limit} for more.` : "",
|
|
30978
|
+
"If you need a usable workflow field list, call comfyui_list_object_info again with node_class set to the exact node name."
|
|
30979
|
+
].filter(Boolean).join("\n"),
|
|
30832
30980
|
structuredContent
|
|
30833
|
-
|
|
30981
|
+
);
|
|
30834
30982
|
} catch (error48) {
|
|
30835
30983
|
return handleComfyError(error48);
|
|
30836
30984
|
}
|
|
@@ -30852,23 +31000,10 @@ async function handleExecuteWorkflow(args) {
|
|
|
30852
31000
|
number: result.number,
|
|
30853
31001
|
node_errors: result.node_errors
|
|
30854
31002
|
};
|
|
30855
|
-
|
|
30856
|
-
|
|
30857
|
-
content: [
|
|
30858
|
-
{
|
|
30859
|
-
type: "text",
|
|
30860
|
-
text: formatFields([
|
|
30861
|
-
[
|
|
30862
|
-
"Status",
|
|
30863
|
-
hasErrors ? "Submitted with node errors" : "Successfully queued"
|
|
30864
|
-
],
|
|
30865
|
-
["Prompt ID", result.prompt_id],
|
|
30866
|
-
["Queue Number", result.number]
|
|
30867
|
-
])
|
|
30868
|
-
}
|
|
30869
|
-
],
|
|
31003
|
+
return createAgentFacingResult(
|
|
31004
|
+
summarizeWorkflowResponseText(result),
|
|
30870
31005
|
structuredContent
|
|
30871
|
-
|
|
31006
|
+
);
|
|
30872
31007
|
} catch (error48) {
|
|
30873
31008
|
return handleComfyError(error48);
|
|
30874
31009
|
}
|
|
@@ -30882,17 +31017,12 @@ async function handleGetHistory(args) {
|
|
|
30882
31017
|
if (rawHistory === void 0) {
|
|
30883
31018
|
return createNotFoundError(`Prompt history ${args.prompt_id}`);
|
|
30884
31019
|
}
|
|
30885
|
-
return
|
|
30886
|
-
|
|
30887
|
-
|
|
30888
|
-
type: "text",
|
|
30889
|
-
text: `History retrieved for job: ${args.prompt_id}`
|
|
30890
|
-
}
|
|
30891
|
-
],
|
|
30892
|
-
structuredContent: {
|
|
31020
|
+
return createAgentFacingResult(
|
|
31021
|
+
summarizeHistoryEntryText(args.prompt_id, rawHistory),
|
|
31022
|
+
{
|
|
30893
31023
|
history: { [args.prompt_id]: rawHistory }
|
|
30894
31024
|
}
|
|
30895
|
-
|
|
31025
|
+
);
|
|
30896
31026
|
}
|
|
30897
31027
|
const history = ensureRecord(rawHistory, "history");
|
|
30898
31028
|
const entries = Object.entries(history);
|
|
@@ -30906,15 +31036,16 @@ async function handleGetHistory(args) {
|
|
|
30906
31036
|
has_more: paginated.hasMore
|
|
30907
31037
|
}
|
|
30908
31038
|
};
|
|
30909
|
-
return
|
|
30910
|
-
|
|
30911
|
-
{
|
|
30912
|
-
|
|
30913
|
-
|
|
30914
|
-
}
|
|
30915
|
-
|
|
31039
|
+
return createAgentFacingResult(
|
|
31040
|
+
[
|
|
31041
|
+
`Retrieved ${paginated.items.length} of ${paginated.total} job(s) from history.`,
|
|
31042
|
+
paginated.items.length > 0 ? `Prompt IDs: ${paginated.items.slice(0, 10).map(([promptId]) => promptId).join(
|
|
31043
|
+
", "
|
|
31044
|
+
)}${paginated.items.length > 10 ? ", ..." : ""}` : "",
|
|
31045
|
+
paginated.hasMore ? `Use offset=${paginated.offset + paginated.limit} for more.` : ""
|
|
31046
|
+
].filter(Boolean).join("\n"),
|
|
30916
31047
|
structuredContent
|
|
30917
|
-
|
|
31048
|
+
);
|
|
30918
31049
|
} catch (error48) {
|
|
30919
31050
|
return handleComfyError(error48);
|
|
30920
31051
|
}
|
|
@@ -30927,15 +31058,10 @@ async function handleGetQueue() {
|
|
|
30927
31058
|
queue_running: Array.isArray(queueRecord.queue_running) ? queueRecord.queue_running : [],
|
|
30928
31059
|
queue_pending: Array.isArray(queueRecord.queue_pending) ? queueRecord.queue_pending : []
|
|
30929
31060
|
};
|
|
30930
|
-
return
|
|
30931
|
-
|
|
30932
|
-
{
|
|
30933
|
-
type: "text",
|
|
30934
|
-
text: `Queue status: ${structuredContent.queue_running.length} running, ${structuredContent.queue_pending.length} pending.`
|
|
30935
|
-
}
|
|
30936
|
-
],
|
|
31061
|
+
return createAgentFacingResult(
|
|
31062
|
+
`Queue status: ${structuredContent.queue_running.length} running, ${structuredContent.queue_pending.length} pending.`,
|
|
30937
31063
|
structuredContent
|
|
30938
|
-
|
|
31064
|
+
);
|
|
30939
31065
|
} catch (error48) {
|
|
30940
31066
|
return handleComfyError(error48);
|
|
30941
31067
|
}
|
|
@@ -30952,15 +31078,11 @@ async function handleGetViewUrl(args) {
|
|
|
30952
31078
|
`/view?${params.toString()}`,
|
|
30953
31079
|
COMFYUI_SERVER_URL
|
|
30954
31080
|
).toString();
|
|
30955
|
-
return
|
|
30956
|
-
|
|
30957
|
-
|
|
30958
|
-
|
|
30959
|
-
|
|
30960
|
-
}
|
|
30961
|
-
],
|
|
30962
|
-
structuredContent: { url: url2 }
|
|
30963
|
-
};
|
|
31081
|
+
return createAgentFacingResult(
|
|
31082
|
+
`View URL generated for ${args.filename}
|
|
31083
|
+
${url2}`,
|
|
31084
|
+
{ url: url2 }
|
|
31085
|
+
);
|
|
30964
31086
|
}
|
|
30965
31087
|
function createServer() {
|
|
30966
31088
|
const server = new McpServer({
|