comfyui-mcp 0.50.94 → 0.50.95

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.
@@ -149,6 +149,55 @@ function fail(err) {
149
149
  const msg = err instanceof Error ? err.message : String(err);
150
150
  return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
151
151
  }
152
+ /**
153
+ * #971 — the AMBIGUOUS-rebind refusal, worded so it can be acted on.
154
+ *
155
+ * Refusing here is right (#474: with 2+ live tabs the rebind is ambiguous, so the
156
+ * user picks rather than us guessing). What was wrong is what it refused WITH:
157
+ * rebindToActiveTab propagates the bridge's raw resolve error, which ends
158
+ * "— pass tab_id". This tool has no tab_id parameter (schema: mode/path/filename)
159
+ * and #754 made these schemas strict, so an unknown key is a hard validation error
160
+ * rather than a no-op. Since panel_open_workflow's own refusal names THIS tool as
161
+ * the way out, an instruction that cannot be followed closes the loop and the
162
+ * session stays wedged with no exit — the reporter's wedge.
163
+ *
164
+ * The action named here is the one that actually works, which is NOT the obvious one.
165
+ * UiBridge.setLastActiveTab is documented as the ONLY writer of lastActiveTabId and
166
+ * fires on `msg.type === "user_message"` (ui-bridge.ts) — a tab becomes last-active
167
+ * when a MESSAGE IS SENT from its Agent panel, not when the browser tab is focused or
168
+ * clicked. "Switch to the tab you want" (which panel_reload's sibling message says)
169
+ * would leave the state unchanged and send the user round the loop again.
170
+ * Name the connected tabs too, so the user knows what they are choosing among.
171
+ */
172
+ function ambiguousRebindGuidance(ctx, err) {
173
+ const raw = err instanceof Error ? err.message : String(err ?? "");
174
+ // Only reword the ambiguity refusal — every other failure keeps its own words.
175
+ if (!/multiple|last active|pass tab_id/i.test(raw))
176
+ return raw;
177
+ let listed = "";
178
+ try {
179
+ const live = typeof ctx.bridge.tabs === "function" ? ctx.bridge.tabs() : undefined;
180
+ if (Array.isArray(live)) {
181
+ // isHeadless THROUGH the bridge — a detached reference loses `this` and throws
182
+ // on `this.conns` (#478). A headless viewer is not a pickable canvas tab.
183
+ const isHeadlessTab = (id) => typeof ctx.bridge.isHeadless === "function" && ctx.bridge.isHeadless(id);
184
+ const names = live
185
+ .filter((t) => !isHeadlessTab(t.tab_id))
186
+ .map((t) => (t.title ? `${shortTabId(t.tab_id)} ("${t.title}")` : shortTabId(t.tab_id)));
187
+ if (names.length)
188
+ listed = ` Connected ComfyUI tabs: ${names.join(", ")}.`;
189
+ }
190
+ }
191
+ catch {
192
+ // Enumeration is best-effort; the instruction below stands without the list.
193
+ }
194
+ return ("Several ComfyUI tabs are connected and none is marked active, so this session cannot " +
195
+ "tell which one to follow — and this tool takes no tab_id to disambiguate with." +
196
+ `${listed} A tab becomes the active one when a message is SENT from its Agent panel ` +
197
+ "(focusing or clicking the browser tab does not do it), so ask the user to send any " +
198
+ "message from the Agent panel in the ComfyUI tab they want you working in, then call " +
199
+ 'panel_set_workflow_target({mode:"current"}) again. Nothing was rebound.');
200
+ }
152
201
  // ── Honest secret-save reporting (#826) ─────────────────────────────────────
153
202
  // `panel_request_secret` used to answer "the comfyui tools respawn with it as
154
203
  // soon as this turn ends" unconditionally — a claim about a FUTURE event nothing
@@ -5524,8 +5573,9 @@ function desktopCanvasRedirect(ctx, label) {
5524
5573
  return {
5525
5574
  error: `${label} needs an open ComfyUI desktop panel canvas, but this session is bound to ` +
5526
5575
  `a canvas-less (mobile/remote) client and multiple desktop tabs are open — it can't ` +
5527
- `pick one automatically. Switch to the ComfyUI tab you want, rebind with ` +
5528
- `panel_set_workflow_target({mode:"current"}), then retry.`,
5576
+ `pick one automatically. Ask the user to send a message from the Agent panel in the ` +
5577
+ `ComfyUI tab they want (that — not focusing the tab — is what marks it active), ` +
5578
+ `rebind with panel_set_workflow_target({mode:"current"}), then retry.`,
5529
5579
  };
5530
5580
  }
5531
5581
  /** Dispatch a command to a SPECIFIC tab id (the #624 desktop-canvas redirect target)
@@ -7209,9 +7259,10 @@ export function buildPanelToolDefs() {
7209
7259
  if (!reachable) {
7210
7260
  return fail("This conversation's current turn is pinned to a tab that is no longer " +
7211
7261
  "reachable (it disconnected or its origin is ambiguous), so the reload " +
7212
- "frame has nowhere safe to go. Switch to the ComfyUI tab you want, call " +
7213
- 'panel_set_workflow_target({mode:"current"}) to re-bind this session onto ' +
7214
- "it, then retry panel_reload.");
7262
+ "frame has nowhere safe to go. Ask the user to send a message from the Agent " +
7263
+ "panel in the ComfyUI tab they want (that — not focusing the tab — is what " +
7264
+ 'marks it active), call panel_set_workflow_target({mode:"current"}) to ' +
7265
+ "re-bind this session onto it, then retry panel_reload.");
7215
7266
  }
7216
7267
  }
7217
7268
  else if (ctx.rebindToActiveTab) {
@@ -7235,8 +7286,10 @@ export function buildPanelToolDefs() {
7235
7286
  : live;
7236
7287
  if (orphaned && Array.isArray(interactive) && interactive.length > 1) {
7237
7288
  return fail("This session's ComfyUI tab was replaced and multiple tabs are now open — " +
7238
- "can't safely pick one. Switch to the tab you want, then call " +
7239
- 'panel_set_workflow_target({mode:"current"}) before panel_reload.');
7289
+ "can't safely pick one. Ask the user to send a message from the Agent panel " +
7290
+ "in the tab they want (that — not focusing the tab — is what marks it " +
7291
+ 'active), then call panel_set_workflow_target({mode:"current"}) before ' +
7292
+ "panel_reload.");
7240
7293
  }
7241
7294
  try {
7242
7295
  ctx.rebindToActiveTab();
@@ -7991,7 +8044,7 @@ export function buildPanelToolDefs() {
7991
8044
  !/multiple|last active|pass tab_id/i.test(msg);
7992
8045
  }
7993
8046
  if (!noTabsConnected)
7994
- return fail(err);
8047
+ return fail(ambiguousRebindGuidance(ctx, err));
7995
8048
  deferredBind = true;
7996
8049
  rebindNote =
7997
8050
  " No panel tab is connected yet — cleared the stale binding; this session will " +