openbio-comfy-mcp 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -7,11 +7,11 @@
7
7
  [![Node.js 20+](https://img.shields.io/badge/Node.js-20%2B-339933.svg)](https://nodejs.org/)
8
8
  [![ComfyUI 0.33.0+](https://img.shields.io/badge/ComfyUI-0.33.0%2B-blue.svg)](https://github.com/Comfy-Org/ComfyUI)
9
9
 
10
- Let Cursor, Codex, Claude, or another local AI **inspect and edit the workflow currently open** on your ComfyUI canvas.
10
+ Let Cursor, Codex, Claude, or another local AI **inspect, edit, and queue** the workflow currently open on your ComfyUI canvas.
11
11
 
12
- It uses ComfyUI's own graph, selection, groups, and undo. It never queues, runs, or saves a workflow for you.
12
+ It uses ComfyUI's own graph, selection, groups, undo, and Queue button. It never saves a workflow for you.
13
13
 
14
- This is **not** [Comfy's official MCP](https://docs.comfy.org/agent-tools/mcp). Official Comfy MCP generates images on Comfy Cloud or runs workflow files through `comfy-mcp`. This project edits the live graph you already have open.
14
+ This is **not** [Comfy's official MCP](https://docs.comfy.org/agent-tools/mcp). Official Comfy MCP generates images on Comfy Cloud or runs workflow files through `comfy-mcp`. This project edits and queues the live graph you already have open.
15
15
 
16
16
  ## What you can ask
17
17
 
@@ -21,8 +21,9 @@ Once both pieces below are installed and a ComfyUI window is open:
21
21
  - Add a node, connect these two, or move this group.
22
22
  - Search the node types installed in *this* ComfyUI.
23
23
  - Pack or unpack a native subgraph.
24
+ - Queue the open canvas and get output file paths when it finishes.
24
25
 
25
- Changes appear on the canvas immediately. One ComfyUI undo reverts a whole patch.
26
+ Changes appear on the canvas immediately. One ComfyUI undo reverts a whole patch. Queue uses the same path as the Queue button.
26
27
 
27
28
  ## Install
28
29
 
@@ -88,10 +89,10 @@ Local ComfyUI instances are discovered automatically. You only need one MCP regi
88
89
 
89
90
  | Variable | Default | When to set it |
90
91
  | --- | --- | --- |
91
- | `OPENBIO_COMFY_URL` | Unset (discover locally) | Pin this MCP connection to one instance, for example `http://127.0.0.1:8189`. |
92
+ | `OPENBIO_COMFY_URL` | Unset (discover locally) | Pin this MCP connection to one instance, for example `http://127.0.0.1:8189` or `http://192.168.1.13:8188`. |
92
93
  | `OPENBIO_COMFY_REGISTRY_DIR` | `~/.openbio-comfy-mcp/instances` | Override the shared registration directory. ComfyUI and the MCP host must use the same path. |
93
94
 
94
- Install the extension in every ComfyUI instance you want to edit. Each running backend writes its address under the shared directory. Discovery uses loopback only; remote ComfyUI is not supported.
95
+ Install the extension in every ComfyUI instance you want to edit. Each running backend writes its address under the shared directory. Discovery accepts loopback and private LAN addresses (`192.168.x.x`, `10.x`, `172.16–31.x`). Public internet hosts are not supported. ComfyUI listening on `0.0.0.0` still registers as `127.0.0.1`; from another machine set `OPENBIO_COMFY_URL` to that host's LAN URL, for example `http://192.168.1.13:8188`.
95
96
 
96
97
  With several online instances, tools pick the one whose page was focused last. Say “edit the workflow on port 8189” if you need a specific one. `list_instances` shows `instance_id`, status, and connected canvases.
97
98
 
@@ -105,16 +106,19 @@ With several online instances, tools pick the one whose page was focused last. S
105
106
  | `inspect_node_type` | Read-only | Read the native schema of one `class_type`. |
106
107
  | `present_canvas` | UI only | Navigate graphs, select items, optionally fit the view. |
107
108
  | `apply_canvas_patch` | Writes the canvas | Apply one atomic, undoable batch of graph edits. |
109
+ | `queue_canvas` | Queues the live canvas | Same as the Queue button, including seed widgets. Returns `prompt_id` and `prompt_ids`; does not wait or save. |
110
+ | `inspect_prompt` | Read-only | Status of a queued prompt, plus output filenames, local paths, and view URLs. Failed runs are `error`, not `completed`. |
111
+ | `wait_for_prompt` | Read-only | Poll until that prompt finishes, fails, or the timeout elapses. |
108
112
 
109
- Typical flow: inspect → search a node type if needed → apply one patch → undo in ComfyUI if the result is wrong.
113
+ Typical flow: inspect → search a node type if needed → apply one patch → `queue_canvas` → `wait_for_prompt` → undo in ComfyUI if the graph edit was wrong.
110
114
 
111
115
  Exact operations live in [docs/spec.md](docs/spec.md).
112
116
 
113
117
  ## Security
114
118
 
115
119
  - The MCP process runs with the same OS permissions as the host that launched it.
116
- - `apply_canvas_patch` can change the open workflow. Review it in your host if you gate write tools.
117
- - The server opens no listening port. Canvas commands are accepted from loopback only. That is not a general ComfyUI login; do not expose an unauthenticated ComfyUI to untrusted networks.
120
+ - `apply_canvas_patch` can change the open workflow. `queue_canvas` runs that workflow on your GPU. Review both in your host if you gate write tools.
121
+ - The server opens no listening port. Canvas commands are accepted from loopback and private LAN addresses only. That is not a general ComfyUI login; do not expose an unauthenticated ComfyUI to the public internet.
118
122
  - Inspections can include prompts, filenames, and widget values. What happens next follows your MCP host and model provider.
119
123
 
120
124
  ## Troubleshooting
@@ -123,6 +127,7 @@ Exact operations live in [docs/spec.md](docs/spec.md).
123
127
  - Host shows no tools: confirm Node 20+ is on `PATH`, that the host command is `npx -y openbio-comfy-mcp@latest`, then restart the host.
124
128
  - Health route missing: the extension is not loaded. Reinstall under `custom_nodes`, restart ComfyUI, check its console.
125
129
  - `STALE_CANVAS`: inspect again and send a new patch.
130
+ - `PROMPT_TIMEOUT`: the queued prompt was still running when `wait_for_prompt` stopped. Call `inspect_prompt` or wait again.
126
131
  - `AMBIGUOUS_INSTANCE`: several instances, no clear focus. Use `list_instances` and pass an `instance_id` or `canvas_id`.
127
132
  - `INSTANCE_UNAVAILABLE` / `INSTANCE_NOT_FOUND`: that ComfyUI is down or was restarted. List and inspect again.
128
133
  - Port `5173` Vite frontend: custom-node JavaScript does not load there. Use the frontend served by ComfyUI.
package/README.zh-CN.md CHANGED
@@ -7,11 +7,11 @@
7
7
  [![Node.js 20+](https://img.shields.io/badge/Node.js-20%2B-339933.svg)](https://nodejs.org/)
8
8
  [![ComfyUI 0.33.0+](https://img.shields.io/badge/ComfyUI-0.33.0%2B-blue.svg)](https://github.com/Comfy-Org/ComfyUI)
9
9
 
10
- 让 Cursor、Codex、Claude 或其他本地 AI **查看并修改你 ComfyUI 里当前打开的工作流画布**。
10
+ 让 Cursor、Codex、Claude 或其他本地 AI **查看、修改并排队执行**你 ComfyUI 里当前打开的工作流画布。
11
11
 
12
- 它走 ComfyUI 自己的图、选择、分组和撤销,不会帮你排队、执行或保存。
12
+ 它走 ComfyUI 自己的图、选择、分组、撤销和 Queue 按钮,不会帮你保存工作流。
13
13
 
14
- 这不是 [Comfy 官方 MCP](https://docs.comfy.org/agent-tools/mcp)。官方 Cloud MCP 在云端出图,`comfy-mcp` 跑的是工作流文件。本项目改的是你屏幕上正在打开的那张图。
14
+ 这不是 [Comfy 官方 MCP](https://docs.comfy.org/agent-tools/mcp)。官方 Cloud MCP 在云端出图,`comfy-mcp` 跑的是工作流文件。本项目改的是你屏幕上正在打开的那张图,并可以按 Queue 把它跑起来。
15
15
 
16
16
  ## 可以让 AI 做什么
17
17
 
@@ -21,8 +21,9 @@
21
21
  - 加一个节点、把这两个连上、挪一下这个组。
22
22
  - 搜一下**这台** ComfyUI 里装了哪些节点。
23
23
  - 把节点打成原生子图,或再拆开。
24
+ - 把当前画布排队,完成后拿到输出文件路径。
24
25
 
25
- 修改会立刻出现在画布上。一次 ComfyUI 撤销会整批还原。
26
+ 修改会立刻出现在画布上。一次 ComfyUI 撤销会整批还原。排队走的是和 Queue 按钮相同的路径。
26
27
 
27
28
  ## 安装
28
29
 
@@ -88,10 +89,10 @@ codex mcp get openbio-comfy-mcp --json
88
89
 
89
90
  | 变量 | 默认值 | 什么时候设 |
90
91
  | --- | --- | --- |
91
- | `OPENBIO_COMFY_URL` | 不设置(自动发现) | 把这次 MCP 连接钉在某一个实例上,例如 `http://127.0.0.1:8189`。 |
92
+ | `OPENBIO_COMFY_URL` | 不设置(自动发现) | 把这次 MCP 连接钉在某一个实例上,例如 `http://127.0.0.1:8189` 或 `http://192.168.1.13:8188`。 |
92
93
  | `OPENBIO_COMFY_REGISTRY_DIR` | `~/.openbio-comfy-mcp/instances` | 覆盖共享注册目录。ComfyUI 和 MCP Host 必须用同一个路径。 |
93
94
 
94
- 每个要编辑的 ComfyUI 实例都要装这个扩展。各后端会把本机地址写进共享目录。只发现回环地址,不支持远程 ComfyUI。
95
+ 每个要编辑的 ComfyUI 实例都要装这个扩展。各后端会把地址写进共享目录。发现范围是回环和私有网段(`192.168.x.x`、`10.x`、`172.16–31.x`),不支持公网地址。ComfyUI 监听 `0.0.0.0` 时仍会登记成 `127.0.0.1`;从另一台机器控制时,把 `OPENBIO_COMFY_URL` 设成那台机器的局域网地址,例如 `http://192.168.1.13:8188`。
95
96
 
96
97
  多个实例同时在线时,默认选最近获得焦点的页面。需要指定时可以说「改 8189 端口上的工作流」。`list_instances` 会列出 `instance_id`、状态和已连接画布。
97
98
 
@@ -105,16 +106,19 @@ codex mcp get openbio-comfy-mcp --json
105
106
  | `inspect_node_type` | 只读 | 读取某个 `class_type` 的原生结构。 |
106
107
  | `present_canvas` | 仅界面 | 切换图、选中对象、按需适配视口。 |
107
108
  | `apply_canvas_patch` | 写入画布 | 原子化执行一批可撤销的图编辑。 |
109
+ | `queue_canvas` | 排队当前画布 | 等同于 Queue 按钮,包括 seed 控件。返回 `prompt_id` 和 `prompt_ids`,不等待、不保存。 |
110
+ | `inspect_prompt` | 只读 | 查看排队任务状态,以及输出文件名、本地路径和查看 URL。失败是 `error`,不是 `completed`。 |
111
+ | `wait_for_prompt` | 只读 | 轮询直到该任务完成、失败或超时。 |
108
112
 
109
- 常见流程:先检查 → 需要时搜节点类型 → 发一次补丁 → 不满意就在 ComfyUI 里撤销。
113
+ 常见流程:先检查 → 需要时搜节点类型 → 发一次补丁 → `queue_canvas` → `wait_for_prompt` → 图改错了就在 ComfyUI 里撤销。
110
114
 
111
115
  完整操作集合见 [docs/spec.md](docs/spec.md)。
112
116
 
113
117
  ## 安全
114
118
 
115
119
  - MCP 进程的操作系统权限与拉起它的 Host 相同。
116
- - `apply_canvas_patch` 会改当前打开的工作流。若 Host 会审核写入工具,请打开审核。
117
- - 服务器不监听端口。画布命令只接受回环请求。这不是 ComfyUI 的登录层;不要把未认证的 ComfyUI 暴露到不可信网络。
120
+ - `apply_canvas_patch` 会改当前打开的工作流。`queue_canvas` 会在你的 GPU 上跑这张图。若 Host 会审核写入工具,请把这两个都打开审核。
121
+ - 服务器不监听端口。画布命令只接受回环和私有网段请求。这不是 ComfyUI 的登录层;不要把未认证的 ComfyUI 暴露到公网。
118
122
  - 检查结果可能包含提示词、文件名和控件值。后续如何处理取决于你的 MCP Host 和模型提供方。
119
123
 
120
124
  ## 故障排查
@@ -123,6 +127,7 @@ codex mcp get openbio-comfy-mcp --json
123
127
  - Host 里没有工具:确认 PATH 上有 Node 20+,命令是 `npx -y openbio-comfy-mcp@latest`,然后重启 Host。
124
128
  - 健康检查路由不存在:扩展没加载。确认装在 `custom_nodes` 下,重启 ComfyUI,看控制台。
125
129
  - `STALE_CANVAS`:重新检查,再发新补丁。
130
+ - `PROMPT_TIMEOUT`:`wait_for_prompt` 结束时任务还在跑。再调 `inspect_prompt`,或再等一次。
126
131
  - `AMBIGUOUS_INSTANCE`:多个实例且焦点不明确。先 `list_instances`,再传入 `instance_id` 或 `canvas_id`。
127
132
  - `INSTANCE_UNAVAILABLE` / `INSTANCE_NOT_FOUND`:该 ComfyUI 已关掉或刚重启。重新列出并检查。
128
133
  - 端口 `5173` 的 Vite 前端:不会加载自定义节点的 JavaScript。请用 ComfyUI 自己提供的前端。
@@ -15794,10 +15794,27 @@ function canvasRoute(canvasId) {
15794
15794
  throw new McpHostError("INVALID_CANVAS_ID", "Use the canvas_id returned by list_instances or inspect_canvas.");
15795
15795
  }
15796
15796
  }
15797
+ function isLocalHostname(hostname) {
15798
+ const host = hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
15799
+ if (host === "localhost") return true;
15800
+ const version2 = isIP(host);
15801
+ if (version2 === 4) {
15802
+ const [a, b] = host.split(".").map(Number);
15803
+ return a === 127 || a === 10 || a === 192 && b === 168 || a === 172 && b >= 16 && b <= 31 || a === 169 && b === 254;
15804
+ }
15805
+ if (version2 === 6) {
15806
+ const ip = host.toLowerCase();
15807
+ if (ip === "::1") return true;
15808
+ const mapped = ip.startsWith("::ffff:") ? ip.slice(7) : "";
15809
+ if (mapped && isIP(mapped) === 4) return isLocalHostname(mapped);
15810
+ return ip.startsWith("fc") || ip.startsWith("fd") || ip.startsWith("fe80:");
15811
+ }
15812
+ return false;
15813
+ }
15797
15814
  function isLocalUrl(value) {
15798
15815
  try {
15799
15816
  const url = new URL(value);
15800
- return ["http:", "https:"].includes(url.protocol) && !url.username && !url.password && !url.search && !url.hash && (url.hostname === "localhost" || url.hostname === "[::1]" || isIP(url.hostname) === 4 && url.hostname.startsWith("127."));
15817
+ return ["http:", "https:"].includes(url.protocol) && !url.username && !url.password && !url.search && !url.hash && isLocalHostname(url.hostname);
15801
15818
  } catch {
15802
15819
  return false;
15803
15820
  }
@@ -15990,6 +16007,113 @@ function createInstanceRouter({
15990
16007
  };
15991
16008
  }
15992
16009
 
16010
+ // mcp_host/prompt.mjs
16011
+ var DEFAULT_WAIT_SECONDS = 120;
16012
+ var MAX_WAIT_SECONDS = 600;
16013
+ var POLL_MS = 500;
16014
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "error"]);
16015
+ function sleepMs(ms) {
16016
+ return new Promise((resolve) => {
16017
+ setTimeout(resolve, ms);
16018
+ });
16019
+ }
16020
+ function viewUrl(baseUrl, output) {
16021
+ const origin = baseUrl.replace(/\/$/, "");
16022
+ const params = new URLSearchParams({
16023
+ filename: output.filename,
16024
+ subfolder: output.subfolder ?? "",
16025
+ type: output.type ?? "output"
16026
+ });
16027
+ return `${origin}/view?${params}`;
16028
+ }
16029
+ function withViewUrls(baseUrl, value) {
16030
+ return {
16031
+ ...value,
16032
+ outputs: (value.outputs ?? []).map((output) => ({
16033
+ ...output,
16034
+ view_url: viewUrl(baseUrl, output)
16035
+ }))
16036
+ };
16037
+ }
16038
+ async function inspectPrompt({ prompt_id: promptId }, {
16039
+ baseUrl,
16040
+ fetchImpl = globalThis.fetch
16041
+ } = {}) {
16042
+ if (typeof promptId !== "string" || !promptId) {
16043
+ throw new McpHostError("INVALID_REQUEST", "inspect_prompt requires prompt_id.");
16044
+ }
16045
+ const origin = baseUrl.replace(/\/$/, "");
16046
+ const url = `${origin}/openbio-comfy-mcp/prompt/${encodeURIComponent(promptId)}`;
16047
+ let response;
16048
+ try {
16049
+ response = await fetchImpl(url);
16050
+ } catch (error2) {
16051
+ throw unavailableError(origin, error2);
16052
+ }
16053
+ let body;
16054
+ try {
16055
+ body = await response.json();
16056
+ } catch (error2) {
16057
+ throw new McpHostError(
16058
+ "COMFYUI_INVALID_RESPONSE",
16059
+ "ComfyUI returned invalid JSON for prompt status.",
16060
+ { cause: error2 instanceof Error ? error2.message : String(error2) }
16061
+ );
16062
+ }
16063
+ if (!response.ok || body?.error) {
16064
+ throw new McpHostError(
16065
+ body?.error?.code ?? "COMFYUI_REQUEST_FAILED",
16066
+ body?.error?.message ?? `ComfyUI returned HTTP ${response.status} for prompt status.`,
16067
+ body?.error?.details
16068
+ );
16069
+ }
16070
+ const value = body.result ?? body;
16071
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
16072
+ throw new McpHostError(
16073
+ "COMFYUI_INVALID_RESPONSE",
16074
+ "ComfyUI returned an invalid prompt status."
16075
+ );
16076
+ }
16077
+ return withViewUrls(origin, {
16078
+ prompt_id: value.prompt_id ?? promptId,
16079
+ status: value.status ?? "not_found",
16080
+ outputs: Array.isArray(value.outputs) ? value.outputs : [],
16081
+ ...value.error && typeof value.error === "object" && !Array.isArray(value.error) ? { error: value.error } : {}
16082
+ });
16083
+ }
16084
+ async function waitForPrompt({
16085
+ prompt_id: promptId,
16086
+ timeout_seconds: timeoutSeconds = DEFAULT_WAIT_SECONDS
16087
+ } = {}, {
16088
+ baseUrl,
16089
+ fetchImpl = globalThis.fetch,
16090
+ sleep = sleepMs
16091
+ } = {}) {
16092
+ if (typeof promptId !== "string" || !promptId) {
16093
+ throw new McpHostError("INVALID_REQUEST", "wait_for_prompt requires prompt_id.");
16094
+ }
16095
+ if (!Number.isFinite(timeoutSeconds) || timeoutSeconds < 1) {
16096
+ throw new McpHostError("INVALID_REQUEST", "timeout_seconds must be at least 1.");
16097
+ }
16098
+ const timeoutMs = Math.min(timeoutSeconds, MAX_WAIT_SECONDS) * 1e3;
16099
+ let elapsed = 0;
16100
+ let value;
16101
+ while (true) {
16102
+ value = await inspectPrompt({ prompt_id: promptId }, { baseUrl, fetchImpl });
16103
+ if (TERMINAL_STATUSES.has(value.status)) return value;
16104
+ if (elapsed >= timeoutMs) {
16105
+ if (value.status === "not_found") return value;
16106
+ throw new McpHostError(
16107
+ "PROMPT_TIMEOUT",
16108
+ "Timed out waiting for the queued prompt to finish.",
16109
+ { prompt_id: promptId, status: value.status }
16110
+ );
16111
+ }
16112
+ await sleep(POLL_MS);
16113
+ elapsed += POLL_MS;
16114
+ }
16115
+ }
16116
+
15993
16117
  // mcp_host/search_nodes.mjs
15994
16118
  function inputRows(group, required2) {
15995
16119
  return Object.entries(group ?? {}).map(([name, definition]) => ({
@@ -16457,6 +16581,60 @@ var TOOL_DEFINITIONS = [
16457
16581
  idempotentHint: false,
16458
16582
  openWorldHint: false
16459
16583
  }
16584
+ },
16585
+ {
16586
+ name: "queue_canvas",
16587
+ description: "Queue the live ComfyUI canvas using the same path as the Queue button, including beforeQueued seed widgets. Returns prompt_id and prompt_ids immediately; it does not wait for execution.",
16588
+ inputSchema: {
16589
+ type: "object",
16590
+ properties: {
16591
+ canvas_id: { type: "string" },
16592
+ base_revision: {
16593
+ type: "string",
16594
+ description: "Revision from inspect_canvas. If set and stale, the queue is rejected."
16595
+ },
16596
+ batch_count: { type: "integer", minimum: 1, default: 1 }
16597
+ },
16598
+ required: ["canvas_id"],
16599
+ additionalProperties: false
16600
+ },
16601
+ annotations: {
16602
+ readOnlyHint: false,
16603
+ destructiveHint: true,
16604
+ idempotentHint: false,
16605
+ openWorldHint: false
16606
+ }
16607
+ },
16608
+ {
16609
+ name: "inspect_prompt",
16610
+ description: "Read the status of a queued prompt and, when finished, its output filenames, local paths, and view URLs. Failed runs are status error, not completed.",
16611
+ inputSchema: {
16612
+ type: "object",
16613
+ properties: {
16614
+ instance_id: INSTANCE_ID_SCHEMA,
16615
+ canvas_id: { type: "string", description: "Use the instance that owns this inspected canvas." },
16616
+ prompt_id: { type: "string", minLength: 1 }
16617
+ },
16618
+ required: ["prompt_id"],
16619
+ additionalProperties: false
16620
+ },
16621
+ annotations: { readOnlyHint: true, openWorldHint: false }
16622
+ },
16623
+ {
16624
+ name: "wait_for_prompt",
16625
+ description: "Poll inspect_prompt until the prompt completes or fails, or the timeout elapses. Does not hold the live-canvas relay.",
16626
+ inputSchema: {
16627
+ type: "object",
16628
+ properties: {
16629
+ instance_id: INSTANCE_ID_SCHEMA,
16630
+ canvas_id: { type: "string", description: "Use the instance that owns this inspected canvas." },
16631
+ prompt_id: { type: "string", minLength: 1 },
16632
+ timeout_seconds: { type: "number", minimum: 1, maximum: 600, default: 120 }
16633
+ },
16634
+ required: ["prompt_id"],
16635
+ additionalProperties: false
16636
+ },
16637
+ annotations: { readOnlyHint: true, openWorldHint: false }
16460
16638
  }
16461
16639
  ];
16462
16640
  function toolResult(value) {
@@ -16521,10 +16699,10 @@ function createMcpServer({
16521
16699
  } = {}) {
16522
16700
  const router = createInstanceRouter({ baseUrl, registryDir, fetchImpl });
16523
16701
  const server2 = new Server(
16524
- { name: "openbio-comfy-mcp", version: "0.1.0" },
16702
+ { name: "openbio-comfy-mcp", version: "0.1.2" },
16525
16703
  {
16526
16704
  capabilities: { tools: {} },
16527
- instructions: "Call inspect_canvas without refs for a compact live topology, then pass its native node or group refs back only when details are needed. Use search_nodes to find an installed class_type, then call inspect_node_type when its complete native schema is needed. Before applying a patch, reuse the inspection's canvas_id and revision as base_revision. Call present_canvas without a revision to navigate between live graphs or change selection and viewport; its optional graph_id accepts a node's subgraph_id or root_graph_id from inspection. Pass the current canvas_id and refs belonging to the destination graph, then use the returned canvas_id and inspect again after navigation. Native graph IDs are not installed class_type values. Read subgraph boundary node IDs and slot names from inspection and reuse connect/disconnect for boundary links. Subgraph edits affect the shared definition and all its instances. convert_to_subgraph and unpack_subgraph must be the last operation in a patch; inspect again afterward for remapped IDs. Each successful patch is one native ComfyUI undo transaction. The bridge never queues or executes a workflow and never saves it automatically. If the canvas is stale, inspect again instead of retrying the old patch. Use list_instances to discover local ComfyUI instances and their canvases. Without an explicit target, tools use the online instance with the most recent reported last_focused_at, even after focus returns to chat. If focus times are unavailable or tied, choose instance_id using list_instances. An explicit instance_id or canvas_id takes priority. Reuse the returned opaque canvas_id unchanged for all subsequent presentation and editing, including after navigation. Use that canvas_id with search_nodes and inspect_node_type to query the same instance. Canvas IDs include process identity: if an instance restarts, explicitly inspect the new instance; never retry a write on a different instance."
16705
+ instructions: "Call inspect_canvas without refs for a compact live topology, then pass its native node or group refs back only when details are needed. Use search_nodes to find an installed class_type, then call inspect_node_type when its complete native schema is needed. Before applying a patch, reuse the inspection's canvas_id and revision as base_revision. Call present_canvas without a revision to navigate between live graphs or change selection and viewport; its optional graph_id accepts a node's subgraph_id or root_graph_id from inspection. Pass the current canvas_id and refs belonging to the destination graph, then use the returned canvas_id and inspect again after navigation. Native graph IDs are not installed class_type values. Read subgraph boundary node IDs and slot names from inspection and reuse connect/disconnect for boundary links. Subgraph edits affect the shared definition and all its instances. convert_to_subgraph and unpack_subgraph must be the last operation in a patch; inspect again afterward for remapped IDs. Each successful patch is one native ComfyUI undo transaction. To run the live canvas, call queue_canvas with the current canvas_id; optionally pass base_revision. It returns prompt_id and prompt_ids immediately and never saves the workflow. Then call wait_for_prompt, or poll inspect_prompt, for status, output paths, or an error. If the canvas is stale, inspect again instead of retrying the old patch. Use list_instances to discover local ComfyUI instances and their canvases. Without an explicit target, tools use the online instance with the most recent reported last_focused_at, even after focus returns to chat. If focus times are unavailable or tied, choose instance_id using list_instances. An explicit instance_id or canvas_id takes priority. Reuse the returned opaque canvas_id unchanged for all subsequent presentation and editing, including after navigation. Use that canvas_id with search_nodes and inspect_node_type to query the same instance. Canvas IDs include process identity: if an instance restarts, explicitly inspect the new instance; never retry a write on a different instance."
16528
16706
  }
16529
16707
  );
16530
16708
  server2.setRequestHandler(ListToolsRequestSchema, async () => ({
@@ -16536,7 +16714,7 @@ function createMcpServer({
16536
16714
  if (name === "list_instances") {
16537
16715
  return toolResult(await router.listInstances());
16538
16716
  }
16539
- if (name === "inspect_canvas" || name === "present_canvas" || name === "apply_canvas_patch") {
16717
+ if (name === "inspect_canvas" || name === "present_canvas" || name === "apply_canvas_patch" || name === "queue_canvas") {
16540
16718
  const instance = await router.resolve(arguments_);
16541
16719
  return await relayCommand(name, arguments_, { instance, router, fetchImpl });
16542
16720
  }
@@ -16550,6 +16728,16 @@ function createMcpServer({
16550
16728
  const value = await inspectNodeType(arguments_, { baseUrl: instance.base_url, fetchImpl });
16551
16729
  return toolResult(router.bindResult(instance, value));
16552
16730
  }
16731
+ if (name === "inspect_prompt") {
16732
+ const instance = await router.resolve(arguments_);
16733
+ const value = await inspectPrompt(arguments_, { baseUrl: instance.base_url, fetchImpl });
16734
+ return toolResult(router.bindResult(instance, value));
16735
+ }
16736
+ if (name === "wait_for_prompt") {
16737
+ const instance = await router.resolve(arguments_);
16738
+ const value = await waitForPrompt(arguments_, { baseUrl: instance.base_url, fetchImpl });
16739
+ return toolResult(router.bindResult(instance, value));
16740
+ }
16553
16741
  } catch (error2) {
16554
16742
  if (error2 instanceof McpHostError) {
16555
16743
  return toolError({
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "openbio-comfy-mcp",
3
- "version": "0.1.0",
4
- "description": "Let an AI inspect and edit the workflow currently open on your ComfyUI canvas.",
3
+ "version": "0.1.2",
4
+ "description": "Let an AI inspect, edit, and queue the workflow currently open on your ComfyUI canvas.",
5
5
  "license": "MIT",
6
6
  "author": "Open-Bio",
7
7
  "repository": {
@@ -23,11 +23,10 @@
23
23
  ],
24
24
  "type": "module",
25
25
  "bin": {
26
- "openbio-comfy-mcp": "./dist/openbio-comfy-mcp.mjs"
26
+ "openbio-comfy-mcp": "dist/openbio-comfy-mcp.mjs"
27
27
  },
28
28
  "files": [
29
- "dist/openbio-comfy-mcp.mjs",
30
- "README.zh-CN.md"
29
+ "dist/openbio-comfy-mcp.mjs"
31
30
  ],
32
31
  "publishConfig": {
33
32
  "access": "public"