@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.
Files changed (3) hide show
  1. package/README.md +6 -0
  2. package/dist/index.js +197 -75
  3. 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
- content: [
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
- content: [
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
- content: [
30827
- {
30828
- type: "text",
30829
- text: `Retrieved ${paginated.items.length} of ${paginated.total} node definitions.${paginated.hasMore ? " Use offset for more." : ""}`
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
- const hasErrors = Object.keys(result.node_errors).length > 0;
30856
- return {
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
- content: [
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
- content: [
30911
- {
30912
- type: "text",
30913
- text: `Retrieved ${paginated.items.length} of ${paginated.total} job(s) from history.${paginated.hasMore ? " Use offset for more." : ""}`
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
- content: [
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
- content: [
30957
- {
30958
- type: "text",
30959
- text: `View URL generated for ${args.filename}`
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({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fre4x/comfyui",
3
- "version": "1.0.57",
3
+ "version": "1.0.58",
4
4
  "description": "MCP server for ComfyUI. Execute workflows and probe server state remotely.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",