comfyui-mcp 0.50.104 → 0.50.106

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.
@@ -1099,6 +1099,60 @@ export function restartTimeoutFallbackAdvice({ headlessBase, panelBase, observed
1099
1099
  "card — check that this is the same ComfyUI you have been working on, since I " +
1100
1100
  "could not confirm which one this panel is running inside.");
1101
1101
  }
1102
+ /**
1103
+ * #1359 — an /object_info failure that names WHICH host it asked, and why that host.
1104
+ *
1105
+ * `panel_strip_workflow` reads the graph from the connected panel and then fetches node
1106
+ * definitions through the global headless client, which resolves COMFYUI_URL. For a
1107
+ * local session those are one machine. For a connected REMOTE panel they are two, and
1108
+ * the tool fails with a bare
1109
+ *
1110
+ * fetch failed: connect ECONNREFUSED 127.0.0.1:8188 — while requesting
1111
+ * http://127.0.0.1:8188/object_info
1112
+ *
1113
+ * which says nothing about the canvas being on a different host. The reporter of #1359
1114
+ * had to read the compiled orchestrator to find that out.
1115
+ *
1116
+ * This does NOT fix the split authority — that needs the panel to serve its own
1117
+ * /object_info, a protocol change tracked separately. It makes the existing failure
1118
+ * diagnosable, and names the workaround that works today.
1119
+ *
1120
+ * The panel's origin is the SERVER-OBSERVED handshake Origin where available: the
1121
+ * browser sets it on the WS upgrade and page JS cannot forge it. It is only being
1122
+ * PRINTED here, never fetched from, so an unreadable one costs a detail rather than a
1123
+ * wrong conclusion.
1124
+ */
1125
+ function objectInfoHostMismatchMessage(ctx, err, liveCanvasSource) {
1126
+ const raw = err instanceof Error ? err.message : String(err);
1127
+ let panelOrigin = null;
1128
+ try {
1129
+ panelOrigin = ctx.bridge?.tabServerOrigin?.(ctx.tabId) ?? null;
1130
+ }
1131
+ catch {
1132
+ /* best effort — the message stands without it */
1133
+ }
1134
+ const configured = getComfyUIBaseUrl();
1135
+ if (!liveCanvasSource) {
1136
+ // pack / path / inline: COMFYUI_URL is the right authority, so the bare failure is
1137
+ // already about the host the caller asked for. Say only what is true.
1138
+ return `${raw}\n\nNode definitions are read from COMFYUI_URL (${configured}) for a pack/path/inline source. That host did not answer /object_info.`;
1139
+ }
1140
+ const differs = panelOrigin != null && configured != null && !sameHttpBase(panelOrigin, configured);
1141
+ return (`${raw}\n\nTHE GRAPH AND ITS NODE DEFINITIONS CAME FROM DIFFERENT PLACES. The workflow was ` +
1142
+ `captured from the connected panel` +
1143
+ (panelOrigin ? ` (ComfyUI at ${panelOrigin})` : "") +
1144
+ `, but node definitions are fetched over COMFYUI_URL (${configured}) — and that is the ` +
1145
+ `request that failed` +
1146
+ (differs
1147
+ ? `. Those are two different hosts, which is why this could not work: the orchestrator ` +
1148
+ `has no route to the panel's ComfyUI for /object_info.`
1149
+ : `.`) +
1150
+ `\n\nWORKAROUND: point COMFYUI_URL at the same ComfyUI the panel is connected to` +
1151
+ (panelOrigin ? ` (${panelOrigin})` : "") +
1152
+ ` and retry, or pass an explicit \`graph\`/\`pack\`/\`path\` source instead of the live ` +
1153
+ `canvas. This is a known split of authority (#1359): stripping the LIVE canvas needs ` +
1154
+ `the definitions to come from the panel's own ComfyUI, which needs a panel-side change.`);
1155
+ }
1102
1156
  function captureRebootHealthBase(ctx) {
1103
1157
  if (isCloudMode() || isRemoteMode())
1104
1158
  return null;
@@ -6848,7 +6902,26 @@ export function buildPanelToolDefs() {
6848
6902
  const captureNotes = [];
6849
6903
  const raw = await resolveWorkflowInput(args, ctx, true, captureNotes);
6850
6904
  const ui = raw;
6851
- const bulk = await getObjectInfo();
6905
+ // #1359 — the graph came from the PANEL; the node definitions come from
6906
+ // COMFYUI_URL. Those are the same machine for a local session and different
6907
+ // ones for a connected REMOTE panel, and when they differ this fails with a
6908
+ // bare `ECONNREFUSED 127.0.0.1:8188` that never mentions the canvas host. The
6909
+ // reporter had to read dist/ to discover that the two halves have different
6910
+ // authorities.
6911
+ //
6912
+ // Only the live-canvas source is affected: a pack/path/inline graph is
6913
+ // deliberately tied to COMFYUI_URL, so its definitions belong there.
6914
+ const liveCanvasSource = args.pack == null && args.path == null && args.graph == null;
6915
+ let bulk;
6916
+ try {
6917
+ bulk = await getObjectInfo();
6918
+ }
6919
+ catch (err) {
6920
+ // Returned as a tool ERROR rather than thrown: a throw here escapes the
6921
+ // handler and reaches the caller as a transport-shaped failure, which is how
6922
+ // the bare ECONNREFUSED got to the reporter in the first place.
6923
+ return fail(objectInfoHostMismatchMessage(ctx, err, liveCanvasSource));
6924
+ }
6852
6925
  const objectInfo = await backfillObjectInfo(bulk, collectNodeTypes(ui));
6853
6926
  const converted = convertUiToApi(ui, objectInfo);
6854
6927
  const workflow = converted.workflow;
@@ -7763,6 +7836,34 @@ export function buildPanelToolDefs() {
7763
7836
  return ok(`🔒 Token saved to MCP server "${server}" (${args.target_kind} "${args.key}"). Call panel_reload to load it.`);
7764
7837
  }
7765
7838
  catch (err) {
7839
+ // #1352 — A TIMED-OUT SECRET CARD IS NOT A FROZEN TAB.
7840
+ //
7841
+ // The bridge's generic no-reply message ("the ComfyUI tab may be backgrounded
7842
+ // or frozen") is right for every other command and wrong here. This card
7843
+ // blocks up to 300s on a MASKED INPUT, and the overwhelmingly likely reason it
7844
+ // went unanswered is that a human is still typing — off finding the key in a
7845
+ // password manager or a billing console. Nothing is wrong with the tab, and
7846
+ // sending someone to fix one is the #1332 defect in a new place.
7847
+ //
7848
+ // It also has to say what happens to a value entered LATE, because that is the
7849
+ // question the caller will actually have. MEASURED: this card is sent as a raw
7850
+ // `request_secret` command with NO ask_id, and the late-reply buffer
7851
+ // (takeLateAskReply) is keyed BY ask id — so a value typed after the timeout
7852
+ // has no path back to this call. It is discarded, and the tool must be called
7853
+ // again. "It may still arrive" would be a guess, and an expensive one: the
7854
+ // user would wait for a save that cannot happen.
7855
+ if (isReplyTimeoutError(err)) {
7856
+ return fail(`The secret card was not answered within 300s, so NOTHING was saved and no ` +
7857
+ `credential was read. That is very likely just time — the card asks for a ` +
7858
+ `token, and finding one in a password manager or a billing console can take ` +
7859
+ `longer than that. It does NOT mean the tab is frozen, and it does NOT mean ` +
7860
+ `the user declined.\n\n` +
7861
+ `A value typed into that card NOW cannot reach this call: it carries no id ` +
7862
+ `this reply could be matched to, so a late answer is discarded rather than ` +
7863
+ `saved. Ask whether they still want to set it and call panel_request_secret ` +
7864
+ `again when they are ready to paste — the value goes straight from the masked ` +
7865
+ `field into storage, and must never be typed into the conversation.`);
7866
+ }
7766
7867
  return fail(err);
7767
7868
  }
7768
7869
  }),