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.
- package/dist/comfyui/client.js +52 -4
- package/dist/comfyui/client.js.map +1 -1
- package/dist/orchestrator/index.js +30 -5
- package/dist/orchestrator/index.js.map +1 -1
- package/dist/orchestrator/panel-tools.js +58 -3
- package/dist/orchestrator/panel-tools.js.map +1 -1
- package/dist/services/comfy-view-ref.js +46 -0
- package/dist/services/comfy-view-ref.js.map +1 -1
- package/dist/services/session-scope.js +31 -0
- package/dist/services/session-scope.js.map +1 -1
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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.", {
|