comfyui-mcp 0.50.2 → 0.50.3

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.
@@ -25,7 +25,7 @@
25
25
  import { z } from "zod";
26
26
  import { randomUUID } from "node:crypto";
27
27
  import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
28
- import { forwardedByReferenceNote, oversizedInlineRefusal, resolveServableViewRef, } from "../services/comfy-view-ref.js";
28
+ import { forwardedByReferenceNote, oversizedInlineRefusal, resolveServableViewRef, unverifiedViewRefNote, } from "../services/comfy-view-ref.js";
29
29
  import { extname, isAbsolute, join, resolve, sep } from "node:path";
30
30
  import { fileURLToPath } from "node:url";
31
31
  import { comfyuiFetch } from "../comfyui/fetch.js";
@@ -33,7 +33,7 @@ import { assertPanelNotTargetedUnverifiable } from "../services/panel-pin-guard.
33
33
  import { nodesInstallCommandArgs } from "../services/node-management.js";
34
34
  import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
35
35
  import { parse as parseYaml } from "yaml";
36
- import { conversationOfScopeAddress, isScopeAddress } from "../services/session-scope.js";
36
+ import { conversationOfScopeAddress, isScopeAddress, shortTabId } from "../services/session-scope.js";
37
37
  /** #884 — journal TICKETS (run completions #468, ask answers #486) must be
38
38
  * keyed by the REAL tab a run/card was routed to: the panel reports back under
39
39
  * that tab id, and a ticket keyed by the shared scope address a tool ctx is
@@ -6606,7 +6606,12 @@ export function buildPanelToolDefs() {
6606
6606
  // Detect the rebind regardless of whether awaitReachable or rebindToActiveTab
6607
6607
  // performed it (either mutates ctx.tabId), so the note is never swallowed.
6608
6608
  if (!deferredBind && ctx.tabId !== before) {
6609
- rebindNote = ` Rebound this session from tab ${before.slice(0, 8)} onto the active tab ${ctx.tabId.slice(0, 8)}.`;
6609
+ // #934 — `.slice(0, 8)` renders EVERY `wf:workflows/…` tab as the
6610
+ // literal "wf:workf", so this note read "Rebound this session from
6611
+ // tab wf:workf onto the active tab wf:workf" for a rebind between two
6612
+ // DIFFERENT workflows. In the middle of a wedge whose entire question
6613
+ // was whether the retarget did anything, it read as a no-op.
6614
+ rebindNote = ` Rebound this session from tab ${shortTabId(before)} onto the active tab ${shortTabId(ctx.tabId)}.`;
6610
6615
  }
6611
6616
  }
6612
6617
  // PIN: bind to the EXACT open-workflow identity from the authoritative
@@ -7795,6 +7800,10 @@ export function buildPanelToolDefs() {
7795
7800
  const resolved = [];
7796
7801
  /** Oversized items that took the /view reference route instead (#648). */
7797
7802
  const forwarded = [];
7803
+ // #941 — /view references handed to a BROWSER panel, whose rendering this
7804
+ // process never observes. Collected so the reply can say what "painted"
7805
+ // does and does not establish.
7806
+ const unverifiedRefs = [];
7798
7807
  for (const item of items) {
7799
7808
  const src = item.source;
7800
7809
  if ("path" in src) {
@@ -7942,6 +7951,15 @@ export function buildPanelToolDefs() {
7942
7951
  filename: src.filename,
7943
7952
  caption: item.caption,
7944
7953
  });
7954
+ // #941 — the panel will report this as painted, meaning it made a
7955
+ // card. Whether the IMAGE loads is decided later, by the browser's
7956
+ // own /view fetch, and a proxied ComfyUI answering HTML breaks it
7957
+ // with no error anywhere. Remember the ref so the reply can say so.
7958
+ unverifiedRefs.push({
7959
+ filename: src.filename,
7960
+ subfolder: src.subfolder,
7961
+ type: src.type,
7962
+ });
7945
7963
  }
7946
7964
  }
7947
7965
  }
@@ -7956,6 +7974,43 @@ export function buildPanelToolDefs() {
7956
7974
  text: forwardedByReferenceNote(forwarded, MAX_BYTES),
7957
7975
  });
7958
7976
  }
7977
+ // #941 — a forwarded /view reference is DISPATCHED, not displayed. Probe a
7978
+ // bounded sample from here so the note can carry evidence instead of only
7979
+ // a caveat; the probe is best-effort and never affects the result.
7980
+ if (unverifiedRefs.length > 0 && !res.isError) {
7981
+ const probe = { checked: 0, nonMedia: [] };
7982
+ const base = (process.env.COMFYUI_URL ?? "http://127.0.0.1:8188").replace(/\/+$/, "");
7983
+ for (const ref of unverifiedRefs.slice(0, 3)) {
7984
+ try {
7985
+ const qs = new URLSearchParams({ filename: ref.filename, type: ref.type ?? "output" });
7986
+ if (ref.subfolder)
7987
+ qs.set("subfolder", ref.subfolder);
7988
+ const resp = await comfyuiFetch(`${base}/view?${qs.toString()}`, {
7989
+ method: "HEAD",
7990
+ signal: AbortSignal.timeout(4000),
7991
+ });
7992
+ probe.checked++;
7993
+ const mime = resp.headers.get("content-type") ?? "";
7994
+ if (!resp.ok) {
7995
+ probe.nonMedia.push({ filename: ref.filename, detail: `HTTP ${resp.status}` });
7996
+ }
7997
+ else if (!mime.startsWith("image/") && !mime.startsWith("video/")) {
7998
+ probe.nonMedia.push({
7999
+ filename: ref.filename,
8000
+ detail: `content-type "${mime || "unset"}"`,
8001
+ });
8002
+ }
8003
+ }
8004
+ catch {
8005
+ // An unreachable /view from HERE says nothing reliable about the
8006
+ // browser's origin, so it is simply not counted as checked.
8007
+ }
8008
+ }
8009
+ res.content.push({
8010
+ type: "text",
8011
+ text: unverifiedViewRefNote(unverifiedRefs, probe),
8012
+ });
8013
+ }
7959
8014
  return res;
7960
8015
  }),
7961
8016
  def("panel_ui_render", "Render an INTERACTIVE UI CARD in the panel chat from an A2UI-subset JSON spec — choice buttons, forms (TextField/Select/Checkbox + a submit Button), node-wiring diagrams (comfy:graph), and bar/line charts (comfy:chart). Use a card whenever the user must pick between options, confirm a plan, fill in parameters, or would understand a wiring explanation better as a diagram. The card is non-blocking: this returns { card_id } immediately; when the user clicks a button (or submits a form) their choice arrives as a NORMAL chat message (the button's `reply` text; submit buttons append 'name: value' lines) — so after rendering a card that asks a question, END YOUR TURN and wait. Set surface:'wide' for diagram-heavy cards (the panel widens and restores automatically). Spec shape: { surface?, title?, root: '<id>', components: [ {id, type, ...} ] } with children referenced by id. Types: Text{text}, Heading{text,level?}, Button{label,reply?,submit?,style?:'primary'|'secondary'}, Row/Column/Card{children:[ids]}, Divider, Image{src:/view-URL,caption?}, TextField{label,name,value?,placeholder?}, Select{label,name,options:[{label,value?}],value?}, Checkbox{label,name,checked?}, 'comfy:graph'{nodes:[{id,label,color?}],edges:[{from,to,label?}],direction?:'lr'|'tb'}, 'comfy:chart'{kind:'bar'|'line',series:[{label,values:[num]}],x?:[labels]}. Caps: ≤64 components, ≤30 graph nodes, ≤8×256 chart points. On a validation error, FIX the spec and retry.", {