comfyui-mcp 0.10.1 → 0.11.0
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/CHANGELOG.md +25 -0
- package/design/embedded-agent-panel.md +28 -3
- package/dist/experimental/chat-handler.js +61 -1
- package/dist/experimental/chat-handler.js.map +1 -1
- package/dist/index.js +45 -3
- package/dist/index.js.map +1 -1
- package/dist/services/ui-bridge.js +174 -0
- package/dist/services/ui-bridge.js.map +1 -0
- package/dist/tools/panel.js +145 -0
- package/dist/tools/panel.js.map +1 -0
- package/dist/transport/cli.js +8 -1
- package/dist/transport/cli.js.map +1 -1
- package/package.json +3 -1
- package/web/extensions/comfyui-mcp-agent-panel/README.md +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,31 @@ All notable changes to this project are documented here. This project adheres to
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Channels mode (`--channels`) — your own agent session drives the ComfyUI
|
|
12
|
+
sidebar panel. No LLM API keys.** The server hosts a loopback WebSocket
|
|
13
|
+
bridge (`COMFYUI_MCP_BRIDGE_PORT`, default 9101) that the
|
|
14
|
+
[comfyui-mcp-panel](https://github.com/artokun/comfyui-mcp-panel) pack
|
|
15
|
+
connects to, and registers nine `panel_*` MCP tools (`status`, `get_graph`,
|
|
16
|
+
`add_node`, `remove_node`, `connect`, `disconnect`, `set_widget`, `say`,
|
|
17
|
+
`inbox`). The agent — your existing Claude Code (or any MCP client) session,
|
|
18
|
+
subscription-billed — edits the user's live graph through its MCP
|
|
19
|
+
connection; every mutation is Ctrl+Z-undoable. Messages typed into the panel
|
|
20
|
+
queue for `panel_inbox` and are pushed as `notifications/claude/channel`
|
|
21
|
+
events on hosts that surface them. Bridge design (rid-correlated
|
|
22
|
+
request/reply, loopback-only, last-writer-wins) ported from the author's
|
|
23
|
+
node-lab project. New dependency: `ws`.
|
|
24
|
+
|
|
25
|
+
- **Live graph edits for the agent panel** (superseded same-day by channels
|
|
26
|
+
mode above, retained as the legacy API-key path). The experimental
|
|
27
|
+
`/api/chat` backend declares six client-side `graph_*` tools that the
|
|
28
|
+
sidebar panel executes against the user's open LiteGraph graph. The panel
|
|
29
|
+
ships as the **comfyui-mcp-panel** pack (the manual drop-in under
|
|
30
|
+
`web/extensions/` is deprecated and will be removed next minor). Epic B
|
|
31
|
+
step 4, built on v1 LiteGraph shims instead of waiting for
|
|
32
|
+
`@comfyorg/extension-api` v2.
|
|
33
|
+
|
|
9
34
|
### Fixed
|
|
10
35
|
|
|
11
36
|
- **Long jobs no longer killed at 10 minutes.** The job watcher's completion
|
|
@@ -106,9 +106,9 @@ A Node HTTP server on `localhost:PORT`:
|
|
|
106
106
|
1. **Tunnel helper** — port `tunnel-manager` into our server (`startQuickTunnel(port) → url`), behind a flag. ✅ done (`src/services/tunnel.ts`).
|
|
107
107
|
2. **AI SDK chat endpoint** — `/api/chat` with one server-side tool (`generate_image`) end-to-end. ✅ done (`src/experimental/{agent-poc,chat-handler}.ts`).
|
|
108
108
|
3. **Sidebar skeleton** — sidebar tab + chat UI hitting the tunnel; render stream. ✅ done **as a v1 extension** (see §7).
|
|
109
|
-
4. **Live edit** —
|
|
109
|
+
4. **Live edit** — client-side graph tools applied to the open graph. ✅ done **via v1 LiteGraph shims** (2026-06-12): six `graph_*` tools (get_state / add_node / remove_node / connect / disconnect / set_widget) declared executorless in `src/experimental/chat-handler.ts`, executed by the panel against `window.app.graph` with beforeChange/afterChange for native Ctrl+Z. Shipped in the **[`comfyui-mcp-panel`](https://github.com/artokun/comfyui-mcp-panel) custom-node pack** (registry-installable; supersedes the manual drop-in in `web/extensions/`).
|
|
110
110
|
5. **Wire comfyui-mcp** as the server-side tool surface (MCP client); expand client-side graph tools.
|
|
111
|
-
6. **Provider switch** (Claude/Codex/Gemini) + connection/key UX + polish
|
|
111
|
+
6. **Provider switch** (Claude/Codex/Gemini) + connection/key UX + polish. (Pack packaging ✅ done — see step 4.)
|
|
112
112
|
|
|
113
113
|
## 7. Panel implementation status — v1 now, v2 later
|
|
114
114
|
|
|
@@ -137,7 +137,32 @@ The panel currently implements:
|
|
|
137
137
|
|
|
138
138
|
Cross-reference for the full pattern map: `plugin/skills/comfyui-frontend-extensions/references/migrate-v1-to-v2.md`.
|
|
139
139
|
|
|
140
|
-
Step 4 (live graph edits)
|
|
140
|
+
Step 4 (live graph edits) shipped 2026-06-12 via **v1 LiteGraph shims** in the
|
|
141
|
+
[`comfyui-mcp-panel`](https://github.com/artokun/comfyui-mcp-panel) pack —
|
|
142
|
+
competition wasn't waiting for v2 and neither did we. The v2 migration
|
|
143
|
+
(`WidgetHandle.setValue` etc.) remains tracked at every `// TODO(v2):` call site.
|
|
144
|
+
|
|
145
|
+
## 8. Architecture pivot (2026-06-12): MCP-driven, no API keys
|
|
146
|
+
|
|
147
|
+
The AI-SDK backend above is now the **legacy POC** (kept flag-gated behind
|
|
148
|
+
`COMFYUI_MCP_AGENT_POC`, API-key billed, deprioritized). The shipping
|
|
149
|
+
architecture is **channels mode** (`--channels`), modeled on node-lab's
|
|
150
|
+
MCP↔browser bridge:
|
|
151
|
+
|
|
152
|
+
- `src/services/ui-bridge.ts` — loopback WS server (default :9101),
|
|
153
|
+
rid-correlated request/reply, exactly node-lab's `bridge.ts` design.
|
|
154
|
+
- `src/tools/panel.ts` — eight `panel_*` MCP tools wrapping `bridge.send()`.
|
|
155
|
+
**The user's own Claude Code session is the agent** — it already holds the
|
|
156
|
+
full 88-tool comfyui-mcp surface (satisfying old step 5 by construction)
|
|
157
|
+
and now gets hands on the live canvas. Subscription-billed; zero API keys.
|
|
158
|
+
- Panel→agent: `user_message` frames queue for the `panel_inbox` tool and are
|
|
159
|
+
emitted as `notifications/claude/channel` events (the 0000_dev_mcp /
|
|
160
|
+
slack-bus channels pattern) for hosts that surface them.
|
|
161
|
+
- The pack's panel (v0.2) is a thin WS client: executes the fixed
|
|
162
|
+
`graph_*` allowlist, renders `say` bubbles, sends user messages up.
|
|
163
|
+
|
|
164
|
+
Remote pairing (PartyKit-style relay rooms, node-lab `party-bridge.ts`) is the
|
|
165
|
+
documented follow-up for ComfyUI-on-a-server setups.
|
|
141
166
|
|
|
142
167
|
## References
|
|
143
168
|
- Ungate (MIT): https://github.com/orchidfiles/ungate — clone at `~/code/ungate`.
|
|
@@ -8,6 +8,8 @@ async function loadAi() {
|
|
|
8
8
|
});
|
|
9
9
|
}
|
|
10
10
|
export function buildTools(toolFn) {
|
|
11
|
+
// A slot is addressable by name ("MODEL", "samples") or numeric index.
|
|
12
|
+
const slotRef = z.union([z.string(), z.number().int().min(0)]);
|
|
11
13
|
return {
|
|
12
14
|
generate_image: toolFn({
|
|
13
15
|
description: "Generate an image from a text prompt using ComfyUI. (POC stub — returns a placeholder result instead of running a real workflow.)",
|
|
@@ -29,9 +31,67 @@ export function buildTools(toolFn) {
|
|
|
29
31
|
};
|
|
30
32
|
},
|
|
31
33
|
}),
|
|
34
|
+
// ── Client-side graph tools ──────────────────────────────────────────
|
|
35
|
+
// No `execute` on any of these: the AI SDK forwards the call to the
|
|
36
|
+
// client (the sidebar panel running inside ComfyUI), the stream pauses,
|
|
37
|
+
// and the panel executes against the live LiteGraph graph and re-POSTs
|
|
38
|
+
// the conversation with the tool result appended. See
|
|
39
|
+
// comfyui-mcp-panel's web/js/comfyui-mcp-panel.js for the executors.
|
|
40
|
+
graph_get_state: toolFn({
|
|
41
|
+
description: "Read the user's currently-open ComfyUI graph: node ids, types, titles, widget values, and connections. ALWAYS call this before editing so node ids and slot names are accurate. Read-only.",
|
|
42
|
+
inputSchema: z.object({}),
|
|
43
|
+
}),
|
|
44
|
+
graph_add_node: toolFn({
|
|
45
|
+
description: "Add a node to the open ComfyUI graph by its class_type (e.g. 'KSampler', 'CheckpointLoaderSimple'). Returns the created node's id, slots, and default widget values. Undoable with Ctrl+Z.",
|
|
46
|
+
inputSchema: z.object({
|
|
47
|
+
class_type: z.string().describe("Exact ComfyUI node class_type to create."),
|
|
48
|
+
pos: z
|
|
49
|
+
.tuple([z.number(), z.number()])
|
|
50
|
+
.optional()
|
|
51
|
+
.describe("Canvas [x, y] position. Auto-placed beside existing nodes when omitted."),
|
|
52
|
+
title: z.string().optional().describe("Optional custom node title."),
|
|
53
|
+
}),
|
|
54
|
+
}),
|
|
55
|
+
graph_remove_node: toolFn({
|
|
56
|
+
description: "Remove a node (and its connections) from the open graph by id. Undoable with Ctrl+Z.",
|
|
57
|
+
inputSchema: z.object({
|
|
58
|
+
node_id: z.number().int().describe("Node id from graph_get_state."),
|
|
59
|
+
}),
|
|
60
|
+
}),
|
|
61
|
+
graph_connect: toolFn({
|
|
62
|
+
description: "Connect an output slot of one node to an input slot of another in the open graph. Slots accept a name ('MODEL', 'samples') or numeric index. Fails with the list of available slots when a name doesn't match — re-check with graph_get_state.",
|
|
63
|
+
inputSchema: z.object({
|
|
64
|
+
from_node_id: z.number().int().describe("Source node id."),
|
|
65
|
+
from_output: slotRef.optional().describe("Source output slot name or index (default 0)."),
|
|
66
|
+
to_node_id: z.number().int().describe("Target node id."),
|
|
67
|
+
to_input: slotRef.optional().describe("Target input slot name or index (default 0)."),
|
|
68
|
+
}),
|
|
69
|
+
}),
|
|
70
|
+
graph_disconnect: toolFn({
|
|
71
|
+
description: "Disconnect an input slot of a node in the open graph. Undoable with Ctrl+Z.",
|
|
72
|
+
inputSchema: z.object({
|
|
73
|
+
node_id: z.number().int().describe("Node id whose input to disconnect."),
|
|
74
|
+
input: slotRef.optional().describe("Input slot name or index (default 0)."),
|
|
75
|
+
}),
|
|
76
|
+
}),
|
|
77
|
+
graph_set_widget: toolFn({
|
|
78
|
+
description: "Set a widget value on a node in the open graph (e.g. steps, cfg, seed, ckpt_name, text prompts). Returns the previous and new value. Undoable with Ctrl+Z.",
|
|
79
|
+
inputSchema: z.object({
|
|
80
|
+
node_id: z.number().int().describe("Node id from graph_get_state."),
|
|
81
|
+
widget: z.string().describe("Widget name (e.g. 'steps', 'cfg', 'text')."),
|
|
82
|
+
value: z
|
|
83
|
+
.union([z.string(), z.number(), z.boolean()])
|
|
84
|
+
.describe("New value. Must match the widget's expected type."),
|
|
85
|
+
}),
|
|
86
|
+
}),
|
|
32
87
|
};
|
|
33
88
|
}
|
|
34
|
-
const DEFAULT_SYSTEM = "You are an assistant embedded in ComfyUI. You can
|
|
89
|
+
const DEFAULT_SYSTEM = "You are an assistant embedded in ComfyUI's sidebar. You can edit the user's " +
|
|
90
|
+
"open graph live with the graph_* tools: ALWAYS call graph_get_state before " +
|
|
91
|
+
"your first edit so node ids, widget names, and slot names are accurate. " +
|
|
92
|
+
"Prefer small incremental edits over rebuilding the graph, and tell the user " +
|
|
93
|
+
"what you changed (every edit is undoable with Ctrl+Z). You can also generate " +
|
|
94
|
+
"images with the generate_image tool.";
|
|
35
95
|
/**
|
|
36
96
|
* Handle a chat request. Accepts a Fetch-style `Request` whose JSON body is
|
|
37
97
|
* `{ messages: UIMessage[], model?: string }` and returns a streaming
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-handler.js","sourceRoot":"","sources":["../../src/experimental/chat-handler.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAmB9D,KAAK,UAAU,MAAM;IACnB,OAAO,kBAAkB,CAAW,IAAI,EAAE;QACxC,OAAO,EAAE,0BAA0B;QACnC,WAAW,EAAE,gEAAgE;KAC9E,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAwB;IACjD,OAAO;QACL,cAAc,EAAE,MAAM,CAAC;YACrB,WAAW,EACT,mIAAmI;YACrI,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;gBACpB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4CAA4C,CAAC;gBACzE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;gBAChF,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC;aACnF,CAAC;YACF,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE;gBAC3C,oEAAoE;gBACpE,6CAA6C;gBAC7C,OAAO;oBACL,MAAM,EAAE,SAAS;oBACjB,MAAM;oBACN,KAAK,EAAE,KAAK,IAAI,IAAI;oBACpB,MAAM,EAAE,MAAM,IAAI,IAAI;oBACtB,QAAQ,EAAE,6CAA6C;oBACvD,IAAI,EAAE,iEAAiE;iBACxE,CAAC;YACJ,CAAC;SACF,CAAC;KACe,CAAC;AACtB,CAAC;AAaD,MAAM,cAAc,GAClB,
|
|
1
|
+
{"version":3,"file":"chat-handler.js","sourceRoot":"","sources":["../../src/experimental/chat-handler.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -3,11 +3,51 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
5
|
import { registerAllTools } from "./tools/index.js";
|
|
6
|
+
import { enqueuePanelMessage, registerPanelTools } from "./tools/panel.js";
|
|
6
7
|
import { logger } from "./utils/logger.js";
|
|
7
8
|
import { JobWatcher } from "./services/job-watcher.js";
|
|
9
|
+
import { startUiBridge } from "./services/ui-bridge.js";
|
|
8
10
|
import { parseCliArgs } from "./transport/cli.js";
|
|
9
11
|
import { startHttpServer } from "./transport/http.js";
|
|
10
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Channels mode (--channels / COMFYUI_MCP_CHANNELS=1): start the loopback
|
|
14
|
+
* WS bridge the comfyui-mcp-panel pack connects to, register the panel_*
|
|
15
|
+
* tools, and forward panel user messages into the agent session — queued for
|
|
16
|
+
* panel_inbox, and pushed as a `notifications/claude/channel` event for hosts
|
|
17
|
+
* that surface those (Claude Code). The user's own subscription session is
|
|
18
|
+
* the agent; no LLM API keys are involved.
|
|
19
|
+
*/
|
|
20
|
+
function enableChannels(server) {
|
|
21
|
+
const bridge = startUiBridge();
|
|
22
|
+
registerPanelTools(server);
|
|
23
|
+
bridge.onPanelMessage = (event) => {
|
|
24
|
+
if (event.type !== "user_message" || typeof event.text !== "string")
|
|
25
|
+
return;
|
|
26
|
+
enqueuePanelMessage(event.text);
|
|
27
|
+
// Echo into the panel feed so the user sees their message land.
|
|
28
|
+
bridge.push({ type: "echo", text: event.text });
|
|
29
|
+
// Best-effort push into the agent session. Unknown notification methods
|
|
30
|
+
// are ignored by hosts that don't support them; panel_inbox remains the
|
|
31
|
+
// pull path either way.
|
|
32
|
+
void server.server
|
|
33
|
+
.notification({
|
|
34
|
+
method: "notifications/claude/channel",
|
|
35
|
+
params: {
|
|
36
|
+
source: "comfyui-panel",
|
|
37
|
+
kind: "user_message",
|
|
38
|
+
text: event.text,
|
|
39
|
+
ts: new Date().toISOString(),
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
.catch((err) => {
|
|
43
|
+
logger.debug("channel notification not delivered", {
|
|
44
|
+
error: err instanceof Error ? err.message : String(err),
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
logger.info("[channels] panel bridge active — panel_* tools registered");
|
|
49
|
+
}
|
|
50
|
+
async function createConfiguredServer(channels = false) {
|
|
11
51
|
const server = new McpServer({
|
|
12
52
|
name: "comfyui-mcp",
|
|
13
53
|
version: "0.1.0",
|
|
@@ -29,6 +69,8 @@ async function createConfiguredServer() {
|
|
|
29
69
|
server.server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
30
70
|
prompts: [],
|
|
31
71
|
}));
|
|
72
|
+
if (channels)
|
|
73
|
+
enableChannels(server);
|
|
32
74
|
return server;
|
|
33
75
|
}
|
|
34
76
|
async function main() {
|
|
@@ -38,12 +80,12 @@ async function main() {
|
|
|
38
80
|
await startHttpServer({
|
|
39
81
|
host: cli.host,
|
|
40
82
|
port: cli.port,
|
|
41
|
-
createServer: createConfiguredServer,
|
|
83
|
+
createServer: () => createConfiguredServer(cli.channels),
|
|
42
84
|
});
|
|
43
85
|
logger.info(`ComfyUI MCP server running on http://${cli.host}:${cli.port}/mcp`);
|
|
44
86
|
}
|
|
45
87
|
else {
|
|
46
|
-
const server = await createConfiguredServer();
|
|
88
|
+
const server = await createConfiguredServer(cli.channels);
|
|
47
89
|
const transport = new StdioServerTransport();
|
|
48
90
|
await server.connect(transport);
|
|
49
91
|
logger.info("ComfyUI MCP server running on stdio");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,wBAAwB,EACxB,0BAA0B,EAC1B,kCAAkC,GACnC,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,KAAK,UAAU,sBAAsB;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// UI bridge: a loopback WebSocket server the comfyui-mcp-panel pack connects
|
|
2
|
+
// to. MCP tool handlers (src/tools/panel.ts) call `send(cmd)` and await the
|
|
3
|
+
// panel's rid-correlated reply — the user's own Claude Code session drives the
|
|
4
|
+
// live ComfyUI graph through its MCP connection, with zero LLM API keys.
|
|
5
|
+
//
|
|
6
|
+
// Design ported from node-lab's mcp/bridge.ts (same author): every request is
|
|
7
|
+
// `{ rid, cmd, ...args }`; the panel replies `{ rid, ok, result }` or
|
|
8
|
+
// `{ rid, ok: false, error }`. One panel connection at a time (last writer
|
|
9
|
+
// wins). If the port is taken, another comfyui-mcp session owns the panel and
|
|
10
|
+
// tools surface a clear, actionable error.
|
|
11
|
+
//
|
|
12
|
+
// Inbound frames WITHOUT a rid are panel-initiated events (e.g.
|
|
13
|
+
// `{ type: "user_message", text }`) and are forwarded to `onPanelMessage` —
|
|
14
|
+
// the channels half of the design (see registerChannels in index.ts).
|
|
15
|
+
import { randomUUID } from "node:crypto";
|
|
16
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
17
|
+
import { logger } from "../utils/logger.js";
|
|
18
|
+
export const DEFAULT_BRIDGE_PORT = 9101;
|
|
19
|
+
export class UiBridge {
|
|
20
|
+
wss = null;
|
|
21
|
+
sock = null;
|
|
22
|
+
pending = new Map();
|
|
23
|
+
portInUse = false;
|
|
24
|
+
port;
|
|
25
|
+
/** Called for panel-initiated frames (no rid): user messages, hellos. */
|
|
26
|
+
onPanelMessage = null;
|
|
27
|
+
constructor(port = DEFAULT_BRIDGE_PORT) {
|
|
28
|
+
this.port = port;
|
|
29
|
+
}
|
|
30
|
+
start() {
|
|
31
|
+
// Loopback only — this drives the user's live editor and must never be
|
|
32
|
+
// reachable from the LAN.
|
|
33
|
+
const wss = new WebSocketServer({ port: this.port, host: "127.0.0.1" });
|
|
34
|
+
wss.on("error", (err) => {
|
|
35
|
+
if (err.code === "EADDRINUSE") {
|
|
36
|
+
this.portInUse = true;
|
|
37
|
+
logger.warn(`[ui-bridge] port ${this.port} in use — another comfyui-mcp session likely owns the panel`);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
logger.error(`[ui-bridge] server error: ${err.message}`);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
wss.on("connection", (sock) => {
|
|
44
|
+
// Last writer wins: a reconnecting panel replaces the stale socket.
|
|
45
|
+
if (this.sock && this.sock !== sock) {
|
|
46
|
+
try {
|
|
47
|
+
this.sock.close();
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Already gone.
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
this.sock = sock;
|
|
54
|
+
logger.info("[ui-bridge] panel connected");
|
|
55
|
+
sock.on("message", (buf) => this.onMessage(buf.toString()));
|
|
56
|
+
sock.on("close", () => {
|
|
57
|
+
if (this.sock === sock)
|
|
58
|
+
this.sock = null;
|
|
59
|
+
// Fail in-flight commands immediately rather than letting them hang
|
|
60
|
+
// to timeout.
|
|
61
|
+
for (const [rid, p] of this.pending) {
|
|
62
|
+
clearTimeout(p.timer);
|
|
63
|
+
p.reject(new Error("panel disconnected mid-command"));
|
|
64
|
+
this.pending.delete(rid);
|
|
65
|
+
}
|
|
66
|
+
logger.info("[ui-bridge] panel disconnected");
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
this.wss = wss;
|
|
70
|
+
logger.info(`[ui-bridge] listening on ws://127.0.0.1:${this.port}`);
|
|
71
|
+
}
|
|
72
|
+
connected() {
|
|
73
|
+
return !!this.sock && this.sock.readyState === WebSocket.OPEN;
|
|
74
|
+
}
|
|
75
|
+
status() {
|
|
76
|
+
if (this.portInUse) {
|
|
77
|
+
return `port ${this.port} is held by another comfyui-mcp session — close it or free the port (lsof -ti:${this.port} | xargs kill)`;
|
|
78
|
+
}
|
|
79
|
+
return this.connected()
|
|
80
|
+
? "panel connected"
|
|
81
|
+
: "no panel connected — open ComfyUI with the comfyui-mcp-panel pack installed and check the Agent sidebar tab";
|
|
82
|
+
}
|
|
83
|
+
send(cmd, timeoutMs = 6000) {
|
|
84
|
+
if (!this.connected()) {
|
|
85
|
+
return Promise.reject(new Error(`Panel not reachable: ${this.status()}`));
|
|
86
|
+
}
|
|
87
|
+
const rid = randomUUID();
|
|
88
|
+
const sock = this.sock;
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
const timer = setTimeout(() => {
|
|
91
|
+
this.pending.delete(rid);
|
|
92
|
+
reject(new Error(`Panel did not reply to "${cmd.cmd}" within ${timeoutMs} ms — the ComfyUI tab may be backgrounded or frozen`));
|
|
93
|
+
}, timeoutMs);
|
|
94
|
+
this.pending.set(rid, { resolve, reject, timer });
|
|
95
|
+
try {
|
|
96
|
+
sock.send(JSON.stringify({ rid, ...cmd }));
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
clearTimeout(timer);
|
|
100
|
+
this.pending.delete(rid);
|
|
101
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
onMessage(raw) {
|
|
106
|
+
let msg;
|
|
107
|
+
try {
|
|
108
|
+
msg = JSON.parse(raw);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
logger.warn("[ui-bridge] dropping malformed frame from panel");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const rid = typeof msg.rid === "string" ? msg.rid : undefined;
|
|
115
|
+
if (rid) {
|
|
116
|
+
const p = this.pending.get(rid);
|
|
117
|
+
if (!p)
|
|
118
|
+
return; // late reply for a timed-out command
|
|
119
|
+
clearTimeout(p.timer);
|
|
120
|
+
this.pending.delete(rid);
|
|
121
|
+
if (msg.ok) {
|
|
122
|
+
p.resolve(msg.result);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
p.reject(new Error(String(msg.error ?? "panel reported an error")));
|
|
126
|
+
}
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
// Panel-initiated event (user message, hello, etc.).
|
|
130
|
+
if (typeof msg.type === "string") {
|
|
131
|
+
this.onPanelMessage?.(msg);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/** Push a fire-and-forget frame to the panel (no reply expected). */
|
|
135
|
+
push(frame) {
|
|
136
|
+
if (!this.connected())
|
|
137
|
+
return;
|
|
138
|
+
try {
|
|
139
|
+
this.sock?.send(JSON.stringify(frame));
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// Panel mid-disconnect — drop.
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async stop() {
|
|
146
|
+
for (const [rid, p] of this.pending) {
|
|
147
|
+
clearTimeout(p.timer);
|
|
148
|
+
p.reject(new Error("bridge stopped"));
|
|
149
|
+
this.pending.delete(rid);
|
|
150
|
+
}
|
|
151
|
+
this.sock?.close();
|
|
152
|
+
this.sock = null;
|
|
153
|
+
await new Promise((resolve) => {
|
|
154
|
+
if (!this.wss)
|
|
155
|
+
return resolve();
|
|
156
|
+
this.wss.close(() => resolve());
|
|
157
|
+
});
|
|
158
|
+
this.wss = null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Module-level singleton, started by --channels in src/index.ts.
|
|
162
|
+
let bridgeInstance = null;
|
|
163
|
+
export function startUiBridge(port) {
|
|
164
|
+
if (!bridgeInstance) {
|
|
165
|
+
bridgeInstance = new UiBridge(port ??
|
|
166
|
+
(Number(process.env.COMFYUI_MCP_BRIDGE_PORT) || DEFAULT_BRIDGE_PORT));
|
|
167
|
+
bridgeInstance.start();
|
|
168
|
+
}
|
|
169
|
+
return bridgeInstance;
|
|
170
|
+
}
|
|
171
|
+
export function getUiBridge() {
|
|
172
|
+
return bridgeInstance;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=ui-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-bridge.js","sourceRoot":"","sources":["../../src/services/ui-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// MCP tools that drive the user's live ComfyUI graph through the
|
|
2
|
+
// comfyui-mcp-panel sidebar pack, over the loopback WebSocket bridge
|
|
3
|
+
// (src/services/ui-bridge.ts). Registered only in --channels mode.
|
|
4
|
+
//
|
|
5
|
+
// You — the agent reading these tool descriptions — are the brain here: the
|
|
6
|
+
// user's chat messages from the panel arrive in your session, and these tools
|
|
7
|
+
// are your hands on their canvas. Every mutation is undoable with Ctrl+Z.
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { getUiBridge } from "../services/ui-bridge.js";
|
|
10
|
+
import { errorToToolResult, ComfyUIError } from "../utils/errors.js";
|
|
11
|
+
const slotRef = z.union([z.string(), z.number().int().min(0)]);
|
|
12
|
+
function bridge() {
|
|
13
|
+
const b = getUiBridge();
|
|
14
|
+
if (!b) {
|
|
15
|
+
throw new ComfyUIError("The panel bridge is not running. Start the server with --channels (or COMFYUI_MCP_CHANNELS=1).", "BRIDGE_NOT_RUNNING");
|
|
16
|
+
}
|
|
17
|
+
return b;
|
|
18
|
+
}
|
|
19
|
+
function textResult(value) {
|
|
20
|
+
return {
|
|
21
|
+
content: [
|
|
22
|
+
{
|
|
23
|
+
type: "text",
|
|
24
|
+
text: typeof value === "string" ? value : JSON.stringify(value, null, 2),
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function registerPanelTools(server) {
|
|
30
|
+
server.tool("panel_status", "Check whether the ComfyUI MCP Panel (the sidebar pack in the user's browser) is connected to this server's bridge. Call this before any other panel_* tool when a command fails — it distinguishes 'panel tab not open' from 'another comfyui-mcp session owns the port'. Read-only.", {}, async () => {
|
|
31
|
+
try {
|
|
32
|
+
return textResult(bridge().status());
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
return errorToToolResult(err);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
server.tool("panel_get_graph", "Read the user's currently-open ComfyUI graph through the sidebar panel: node ids, types, titles, widget values, and connections. ALWAYS call this before your first edit so node ids, widget names, and slot names are accurate. Requires the comfyui-mcp-panel pack connected (check panel_status). Read-only.", {}, async () => {
|
|
39
|
+
try {
|
|
40
|
+
return textResult(await bridge().send({ cmd: "graph_get_state" }));
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
return errorToToolResult(err);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
server.tool("panel_add_node", "Add a node to the user's open ComfyUI graph by class_type (e.g. 'KSampler', 'CheckpointLoaderSimple'). Returns the created node's id, slots, and default widget values. The user sees it appear live; Ctrl+Z undoes it. Requires the panel connected.", {
|
|
47
|
+
class_type: z.string().describe("Exact ComfyUI node class_type to create."),
|
|
48
|
+
pos: z
|
|
49
|
+
.tuple([z.number(), z.number()])
|
|
50
|
+
.optional()
|
|
51
|
+
.describe("Canvas [x, y]. Auto-placed beside existing nodes when omitted."),
|
|
52
|
+
title: z.string().optional().describe("Optional custom node title."),
|
|
53
|
+
}, async (args) => {
|
|
54
|
+
try {
|
|
55
|
+
return textResult(await bridge().send({ cmd: "graph_add_node", ...args }));
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
return errorToToolResult(err);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
server.tool("panel_remove_node", "Remove a node (and its connections) from the user's open graph by id. Undoable with Ctrl+Z. Requires the panel connected.", {
|
|
62
|
+
node_id: z.number().int().describe("Node id from panel_get_graph."),
|
|
63
|
+
}, async (args) => {
|
|
64
|
+
try {
|
|
65
|
+
return textResult(await bridge().send({ cmd: "graph_remove_node", ...args }));
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
return errorToToolResult(err);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
server.tool("panel_connect", "Connect an output slot of one node to an input slot of another in the user's open graph. Slots accept a name ('MODEL', 'samples') or numeric index. On a name mismatch the error lists the available slots — re-check with panel_get_graph. Undoable with Ctrl+Z.", {
|
|
72
|
+
from_node_id: z.number().int().describe("Source node id."),
|
|
73
|
+
from_output: slotRef.optional().describe("Source output slot name or index (default 0)."),
|
|
74
|
+
to_node_id: z.number().int().describe("Target node id."),
|
|
75
|
+
to_input: slotRef.optional().describe("Target input slot name or index (default 0)."),
|
|
76
|
+
}, async (args) => {
|
|
77
|
+
try {
|
|
78
|
+
return textResult(await bridge().send({ cmd: "graph_connect", ...args }));
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
return errorToToolResult(err);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
server.tool("panel_disconnect", "Disconnect an input slot of a node in the user's open graph. Undoable with Ctrl+Z. Requires the panel connected.", {
|
|
85
|
+
node_id: z.number().int().describe("Node id whose input to disconnect."),
|
|
86
|
+
input: slotRef.optional().describe("Input slot name or index (default 0)."),
|
|
87
|
+
}, async (args) => {
|
|
88
|
+
try {
|
|
89
|
+
return textResult(await bridge().send({ cmd: "graph_disconnect", ...args }));
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
return errorToToolResult(err);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
server.tool("panel_set_widget", "Set a widget value on a node in the user's open graph (steps, cfg, seed, ckpt_name, text prompts, …). Returns the previous and new value. Undoable with Ctrl+Z. Requires the panel connected.", {
|
|
96
|
+
node_id: z.number().int().describe("Node id from panel_get_graph."),
|
|
97
|
+
widget: z.string().describe("Widget name (e.g. 'steps', 'cfg', 'text')."),
|
|
98
|
+
value: z
|
|
99
|
+
.union([z.string(), z.number(), z.boolean()])
|
|
100
|
+
.describe("New value. Must match the widget's expected type."),
|
|
101
|
+
}, async (args) => {
|
|
102
|
+
try {
|
|
103
|
+
return textResult(await bridge().send({ cmd: "graph_set_widget", ...args }));
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
return errorToToolResult(err);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
server.tool("panel_say", "Post a message into the panel's chat feed in the user's ComfyUI sidebar. Use this to narrate what you changed, confirm completion, or ask the user a question — it's the ONLY way your words reach the panel UI. Supports plain text with simple markdown (bold, code).", {
|
|
110
|
+
text: z.string().min(1).describe("The message to show in the panel chat feed."),
|
|
111
|
+
}, async (args) => {
|
|
112
|
+
try {
|
|
113
|
+
bridge().push({ type: "say", text: args.text });
|
|
114
|
+
return textResult("delivered");
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
return errorToToolResult(err);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
server.tool("panel_inbox", "Drain user messages typed into the panel chat since the last call. Returns an array of { text, ts }. Use when the user says they'll talk to you through the ComfyUI panel — poll this after each action, and reply with panel_say. (When channel notifications are enabled, new messages also arrive as session events and polling is unnecessary.)", {}, async () => {
|
|
121
|
+
try {
|
|
122
|
+
bridge(); // throw early if no bridge
|
|
123
|
+
return textResult(drainInbox());
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
return errorToToolResult(err);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
// Panel → agent inbox. user_message frames land here (wired in index.ts);
|
|
132
|
+
// the agent drains them via panel_inbox, and — when the host supports it —
|
|
133
|
+
// also receives them pushed as `notifications/claude/channel` events.
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
const MAX_INBOX = 200;
|
|
136
|
+
const inbox = [];
|
|
137
|
+
export function enqueuePanelMessage(text) {
|
|
138
|
+
inbox.push({ text, ts: new Date().toISOString() });
|
|
139
|
+
if (inbox.length > MAX_INBOX)
|
|
140
|
+
inbox.splice(0, inbox.length - MAX_INBOX);
|
|
141
|
+
}
|
|
142
|
+
export function drainInbox() {
|
|
143
|
+
return inbox.splice(0, inbox.length);
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=panel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panel.js","sourceRoot":"","sources":["../../src/tools/panel.ts"],"names":[],"mappings":"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"}
|
package/dist/transport/cli.js
CHANGED
|
@@ -12,6 +12,7 @@ export function parseCliArgs(argv, env = process.env) {
|
|
|
12
12
|
let transport = env.MCP_TRANSPORT === "http" ? "http" : "stdio";
|
|
13
13
|
let host = env.MCP_HOST ?? DEFAULT_HOST;
|
|
14
14
|
let port = env.MCP_PORT ? Number(env.MCP_PORT) : DEFAULT_PORT;
|
|
15
|
+
let channels = env.COMFYUI_MCP_CHANNELS === "1" || env.COMFYUI_MCP_CHANNELS === "true";
|
|
15
16
|
const valueOf = (current, inline, i) => {
|
|
16
17
|
if (current.includes("="))
|
|
17
18
|
return [current.slice(current.indexOf("=") + 1), i];
|
|
@@ -42,7 +43,13 @@ export function parseCliArgs(argv, env = process.env) {
|
|
|
42
43
|
port = Number(v);
|
|
43
44
|
i = ni;
|
|
44
45
|
}
|
|
46
|
+
else if (a === "--channels") {
|
|
47
|
+
channels = true;
|
|
48
|
+
}
|
|
49
|
+
else if (a === "--no-channels") {
|
|
50
|
+
channels = false;
|
|
51
|
+
}
|
|
45
52
|
}
|
|
46
|
-
return { transport, host, port };
|
|
53
|
+
return { transport, host, port, channels };
|
|
47
54
|
}
|
|
48
55
|
//# sourceMappingURL=cli.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/transport/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/transport/cli.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "comfyui-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"mcpName": "io.github.artokun/comfyui-mcp",
|
|
5
5
|
"description": "MCP server for ComfyUI — workflow execution, visualization, composition, registry, and skill generation",
|
|
6
6
|
"homepage": "https://comfyui-mcp.artokun.io/docs",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"better-sqlite3": "^12.6.2",
|
|
32
32
|
"dotenv": "^16.4.7",
|
|
33
33
|
"sharp": "^0.34.5",
|
|
34
|
+
"ws": "^8.21.0",
|
|
34
35
|
"yaml": "^2.9.0",
|
|
35
36
|
"zod": "^3.24.2"
|
|
36
37
|
},
|
|
@@ -46,6 +47,7 @@
|
|
|
46
47
|
"devDependencies": {
|
|
47
48
|
"@types/better-sqlite3": "^7.6.13",
|
|
48
49
|
"@types/node": "^22.13.4",
|
|
50
|
+
"@types/ws": "^8.18.1",
|
|
49
51
|
"cross-env": "^10.1.0",
|
|
50
52
|
"tsx": "^4.19.2",
|
|
51
53
|
"typescript": "^5.7.3",
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# comfyui-mcp Agent Panel — v1 ComfyUI extension
|
|
2
2
|
|
|
3
|
+
> **⚠️ DEPRECATED — install [`comfyui-mcp-panel`](https://github.com/artokun/comfyui-mcp-panel) instead.**
|
|
4
|
+
> The panel now ships as a proper custom-node pack (installable via
|
|
5
|
+
> ComfyUI-Manager / the Comfy Registry) and gained **live graph edits** —
|
|
6
|
+
> the agent can add/remove/connect nodes and set widget values on your open
|
|
7
|
+
> graph, all Ctrl+Z-undoable. This manual drop-in file will be removed in the
|
|
8
|
+
> next minor release. Settings carry over automatically (same localStorage
|
|
9
|
+
> keys, same extension id — having both installed won't double-register).
|
|
10
|
+
|
|
3
11
|
A drop-in sidebar tab for ComfyUI that hosts a chat UI talking to the
|
|
4
12
|
[experimental agent backend](../../../src/experimental/agent-poc.ts) shipped
|
|
5
13
|
with `comfyui-mcp`. This is the **v1 implementation** — built against today's
|