@metabrain-labs/comfyui-mcp-server 1.0.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.
Files changed (88) hide show
  1. package/.env +179 -0
  2. package/.github/workflows/ci.yml +63 -0
  3. package/.husky/pre-commit +11 -0
  4. package/LICENSE +21 -0
  5. package/README-en.md +635 -0
  6. package/README.md +656 -0
  7. package/__tests__/services/task/execution.test.ts +272 -0
  8. package/__tests__/services/task/wait.test.ts +220 -0
  9. package/__tests__/types/result.test.ts +227 -0
  10. package/__tests__/utils/mcp-helpers.test.ts +137 -0
  11. package/__tests__/utils/special-node-handler.test.ts +186 -0
  12. package/comfyui-mcp-server-skill/SKILL.md +43 -0
  13. package/comfyui-mcp-server-skill/references/api-json.md +323 -0
  14. package/comfyui-mcp-server-skill/references/catalog.md +251 -0
  15. package/comfyui-mcp-server-skill/rules/api-json.md +94 -0
  16. package/comfyui-mcp-server-skill/rules/catalog.md +93 -0
  17. package/comfyui-mcp-server-skill/rules/comfyui.md +158 -0
  18. package/configs/inspector/dev.json +11 -0
  19. package/configs/inspector/prod.json +9 -0
  20. package/docs/en/content/public/inspector-example.png +0 -0
  21. package/docs/en/content/public/workflow_name_example.png +0 -0
  22. package/docs/en/content/public/workflow_parameter_example.png +0 -0
  23. package/docs/en/md/Project-Advantages.md +75 -0
  24. package/docs/zh-CN/content/public/inspector-example.png +0 -0
  25. package/docs/zh-CN/content/public/workflow_name_example.png +0 -0
  26. package/docs/zh-CN/content/public/workflow_parameter_example.png +0 -0
  27. package/docs/zh-CN/md/why-us.md +51 -0
  28. package/example.json +26 -0
  29. package/jest.config.js +45 -0
  30. package/locales/en.json +150 -0
  31. package/locales/zh.json +150 -0
  32. package/package.json +68 -0
  33. package/src/api/api.ts +123 -0
  34. package/src/api/http.ts +70 -0
  35. package/src/constants/common.ts +38 -0
  36. package/src/constants/index.ts +1 -0
  37. package/src/hooks/websocket.ts +93 -0
  38. package/src/i18n.ts +40 -0
  39. package/src/index.ts +268 -0
  40. package/src/scripts/comfy-ui/list-history.ts +31 -0
  41. package/src/scripts/comfy-ui/run-ws.ts +23 -0
  42. package/src/scripts/ws/send-feature-flags.ts +23 -0
  43. package/src/server-stdio.ts +23 -0
  44. package/src/services/business.ts +194 -0
  45. package/src/services/dynamic-tool.ts +303 -0
  46. package/src/services/index.ts +19 -0
  47. package/src/services/storage/asset-storage.ts +48 -0
  48. package/src/services/storage/index.ts +2 -0
  49. package/src/services/storage/workflow-storage.ts +192 -0
  50. package/src/services/task/execution.ts +69 -0
  51. package/src/services/task/fetch.ts +131 -0
  52. package/src/services/task/index.ts +3 -0
  53. package/src/services/task/wait.ts +294 -0
  54. package/src/services/workflow/executor.ts +134 -0
  55. package/src/services/workflow/index.ts +1 -0
  56. package/src/tools/handlers/core-manual.ts +28 -0
  57. package/src/tools/handlers/index.ts +22 -0
  58. package/src/tools/handlers/interrupt-prompt.ts +35 -0
  59. package/src/tools/handlers/list-models.ts +51 -0
  60. package/src/tools/handlers/mount-workflow.ts +88 -0
  61. package/src/tools/handlers/prompt-result.ts +35 -0
  62. package/src/tools/handlers/prompts.ts +61 -0
  63. package/src/tools/handlers/queue-custom-prompt.ts +164 -0
  64. package/src/tools/handlers/queue-prompt.ts +170 -0
  65. package/src/tools/handlers/save-custom-workflow.ts +67 -0
  66. package/src/tools/handlers/save-task-assets.ts +82 -0
  67. package/src/tools/handlers/system-status.ts +30 -0
  68. package/src/tools/handlers/task-detail.ts +35 -0
  69. package/src/tools/handlers/tool-status-map.ts +33 -0
  70. package/src/tools/handlers/upload-assets.ts +82 -0
  71. package/src/tools/handlers/workflow-api.ts +46 -0
  72. package/src/tools/handlers/workflows-catalog.ts +79 -0
  73. package/src/tools/index.ts +101 -0
  74. package/src/types/common.ts +74 -0
  75. package/src/types/dynamic-tool.ts +85 -0
  76. package/src/types/enums/result.ts +29 -0
  77. package/src/types/execute.ts +24 -0
  78. package/src/types/object-info.ts +43 -0
  79. package/src/types/result.ts +126 -0
  80. package/src/types/task.ts +118 -0
  81. package/src/types/workflow.ts +111 -0
  82. package/src/types/ws.ts +80 -0
  83. package/src/utils/format.ts +134 -0
  84. package/src/utils/mcp-helpers.ts +110 -0
  85. package/src/utils/special-node-handler.ts +36 -0
  86. package/src/utils/workflow-converter.ts +140 -0
  87. package/src/utils/ws.ts +219 -0
  88. package/tsconfig.json +18 -0
@@ -0,0 +1,137 @@
1
+ /**
2
+ * @file mcp-helpers.test.ts
3
+ * @description 测试 MCP 辅助函数
4
+ */
5
+
6
+ import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
7
+ import type { CallToolResult, TextContent } from '@modelcontextprotocol/sdk/types.js';
8
+ import {
9
+ withMcpErrorHandling,
10
+ ResultToMcpResponse,
11
+ ResultToMcpStringResponse,
12
+ buildComfyViewUrls,
13
+ } from '../../src/utils/mcp-helpers.js';
14
+ import { ok, error } from '../../src/types/result.js';
15
+ import { ExecutionResult } from '../../src/types/execute.js';
16
+
17
+ const originalEnv = process.env;
18
+
19
+ describe('mcp-helpers', () => {
20
+ beforeEach(() => {
21
+ // 使用实际环境变量或默认值
22
+ if (!process.env.COMFY_UI_SERVER_IP) {
23
+ process.env.COMFY_UI_SERVER_IP = 'http://localhost:8188';
24
+ }
25
+ });
26
+
27
+ afterEach(() => {
28
+ process.env = originalEnv;
29
+ });
30
+
31
+ describe('withMcpErrorHandling()', () => {
32
+ it('should return result when handler succeeds with content property', async () => {
33
+ const mockHandler = jest.fn().mockResolvedValue({
34
+ content: [{ type: 'text', text: 'success' }],
35
+ } as CallToolResult);
36
+
37
+ const wrapped = withMcpErrorHandling(mockHandler);
38
+ const result = await wrapped('arg1', 'arg2');
39
+
40
+ expect(mockHandler).toHaveBeenCalledWith('arg1', 'arg2');
41
+ expect(result).toEqual({ content: [{ type: 'text', text: 'success' }] });
42
+ });
43
+
44
+ it('should wrap result when handler succeeds without content property', async () => {
45
+ const mockHandler = jest.fn().mockResolvedValue({ data: 'test' });
46
+
47
+ const wrapped = withMcpErrorHandling(mockHandler);
48
+ const result = await wrapped();
49
+
50
+ expect(result).toEqual({
51
+ content: [{ type: 'text', text: JSON.stringify({ data: 'test' }, null, 2) }],
52
+ });
53
+ });
54
+
55
+ it('should re-throw McpError without wrapping', async () => {
56
+ const mcpError = new McpError(ErrorCode.InternalError, 'MCP Error');
57
+ const mockHandler = jest.fn().mockRejectedValue(mcpError);
58
+
59
+ const wrapped = withMcpErrorHandling(mockHandler);
60
+
61
+ await expect(wrapped()).rejects.toThrow(McpError);
62
+ await expect(wrapped()).rejects.toThrow('MCP Error');
63
+ });
64
+
65
+ it('should wrap plain Error in McpError', async () => {
66
+ const plainError = new Error('Plain error message');
67
+ const mockHandler = jest.fn().mockRejectedValue(plainError);
68
+
69
+ const wrapped = withMcpErrorHandling(mockHandler);
70
+
71
+ await expect(wrapped()).rejects.toThrow(McpError);
72
+ await expect(wrapped()).rejects.toThrow('Plain error message');
73
+ });
74
+ });
75
+
76
+ describe('ResultToMcpResponse()', () => {
77
+ it('should convert success result to MCP response without isError', () => {
78
+ const result = ok('Success', { data: 'test' });
79
+ const response = ResultToMcpResponse(result);
80
+
81
+ expect(response.isError).toBeUndefined();
82
+ });
83
+
84
+ it('should convert error result to MCP response with isError flag', () => {
85
+ const result = error('Error occurred');
86
+ const response = ResultToMcpResponse(result);
87
+
88
+ expect(response.isError).toBe(true);
89
+ });
90
+ });
91
+
92
+ describe('buildComfyViewUrls()', () => {
93
+ it('should return empty array when outputs is undefined', () => {
94
+ const result: ExecutionResult = {
95
+ success: true,
96
+ promptId: '123',
97
+ outputs: undefined,
98
+ executionTime: 100
99
+ };
100
+ const urls = buildComfyViewUrls(result);
101
+
102
+ expect(urls).toEqual([]);
103
+ });
104
+
105
+ it('should return empty array when outputs is empty object', () => {
106
+ const result: ExecutionResult = {
107
+ success: true,
108
+ promptId: '123',
109
+ outputs: {},
110
+ executionTime: 100
111
+ };
112
+ const urls = buildComfyViewUrls(result);
113
+
114
+ expect(urls).toEqual([]);
115
+ });
116
+
117
+ it('should build URLs for images with all fields', () => {
118
+ const result: ExecutionResult = {
119
+ success: true,
120
+ promptId: '123',
121
+ outputs: {
122
+ node1: {
123
+ images: [{ filename: 'image1.png', type: 'output', subfolder: 'sub1' }],
124
+ },
125
+ },
126
+ executionTime: 100,
127
+ };
128
+
129
+ const urls = buildComfyViewUrls(result);
130
+
131
+ const baseUrl = process.env.COMFY_UI_SERVER_IP || 'http://127.0.0.1:8188';
132
+ expect(urls).toEqual([
133
+ `${baseUrl}/view?filename=image1.png&type=output&subfolder=sub1`,
134
+ ]);
135
+ });
136
+ });
137
+ });
@@ -0,0 +1,186 @@
1
+ /**
2
+ * @file special-node-handler.test.ts
3
+ * @description 测试特殊节点处理函数
4
+ *
5
+ * 【分支覆盖分析】
6
+ * 1. isSupportedKey() 函数:
7
+ * - 正常路径: key 在 supportedKeys 中 (如 "seed")
8
+ * - 边界: key 不在 supportedKeys 中
9
+ * - 边界: 大小写不敏感匹配 ("SEED", "Seed", "SeEd")
10
+ *
11
+ * 2. handleKey() 函数:
12
+ * - 分支: key === "seed" (小写)
13
+ * - 分支: key !== "seed" (走 default 分支)
14
+ * - 边界: 大小写混合 ("Seed", "SEED")
15
+ *
16
+ * 3. generateSeed32() 函数:
17
+ * - 正常路径: 返回 32 位随机数
18
+ * - 验证: 返回值在有效范围内 (0 到 2^32-1)
19
+ *
20
+ * 【Mock 策略】
21
+ * - crypto.getRandomValues: Mock 为返回固定值以确保可预测性
22
+ */
23
+
24
+ import {
25
+ isSupportedKey,
26
+ handleKey,
27
+ generateSeed32,
28
+ } from '../../src/utils/special-node-handler';
29
+
30
+ describe('special-node-handler', () => {
31
+ describe('isSupportedKey()', () => {
32
+ it('should return true for "seed" (lowercase)', () => {
33
+ expect(isSupportedKey('seed')).toBe(true);
34
+ });
35
+
36
+ it('should return true for "SEED" (uppercase)', () => {
37
+ expect(isSupportedKey('SEED')).toBe(true);
38
+ });
39
+
40
+ it('should return true for "Seed" (mixed case)', () => {
41
+ expect(isSupportedKey('Seed')).toBe(true);
42
+ });
43
+
44
+ it('should return true for "SeEd" (random case)', () => {
45
+ expect(isSupportedKey('SeEd')).toBe(true);
46
+ });
47
+
48
+ it('should return false for unsupported key "random"', () => {
49
+ expect(isSupportedKey('random')).toBe(false);
50
+ });
51
+
52
+ it('should return false for empty string', () => {
53
+ expect(isSupportedKey('')).toBe(false);
54
+ });
55
+
56
+ it('should return false for similar but different key "seeds"', () => {
57
+ expect(isSupportedKey('seeds')).toBe(false);
58
+ });
59
+
60
+ it('should return false for key containing "seed" substring', () => {
61
+ expect(isSupportedKey('my_seed')).toBe(false);
62
+ expect(isSupportedKey('seed_value')).toBe(false);
63
+ });
64
+ });
65
+
66
+ describe('handleKey()', () => {
67
+ // 保存原始 crypto 对象
68
+ const originalCrypto = global.crypto;
69
+
70
+ beforeEach(() => {
71
+ // Mock crypto.getRandomValues
72
+ global.crypto = {
73
+ getRandomValues: jest.fn((array: Uint32Array) => {
74
+ array[0] = 12345;
75
+ return array;
76
+ }),
77
+ } as unknown as Crypto;
78
+ });
79
+
80
+ afterEach(() => {
81
+ global.crypto = originalCrypto;
82
+ });
83
+
84
+ it('should handle "seed" key and return 32-bit random number', () => {
85
+ const result = handleKey('seed');
86
+
87
+ expect(global.crypto.getRandomValues).toHaveBeenCalledWith(expect.any(Uint32Array));
88
+ expect(result).toBe(12345);
89
+ });
90
+
91
+ it('should handle "SEED" key (uppercase) and return random number', () => {
92
+ const result = handleKey('SEED');
93
+
94
+ expect(global.crypto.getRandomValues).toHaveBeenCalledWith(expect.any(Uint32Array));
95
+ expect(result).toBe(12345);
96
+ });
97
+
98
+ it('should handle "Seed" key (mixed case) and return random number', () => {
99
+ const result = handleKey('Seed');
100
+
101
+ expect(global.crypto.getRandomValues).toHaveBeenCalledWith(expect.any(Uint32Array));
102
+ expect(result).toBe(12345);
103
+ });
104
+
105
+ it('should handle unsupported key and return random number (default case)', () => {
106
+ const result = handleKey('unsupported');
107
+
108
+ expect(global.crypto.getRandomValues).toHaveBeenCalledWith(expect.any(Uint32Array));
109
+ expect(result).toBe(12345);
110
+ });
111
+
112
+ it('should handle empty string key and return random number (default case)', () => {
113
+ const result = handleKey('');
114
+
115
+ expect(global.crypto.getRandomValues).toHaveBeenCalledWith(expect.any(Uint32Array));
116
+ expect(result).toBe(12345);
117
+ });
118
+ });
119
+
120
+ describe('generateSeed32()', () => {
121
+ const originalCrypto = global.crypto;
122
+
123
+ beforeEach(() => {
124
+ global.crypto = {
125
+ getRandomValues: jest.fn((array: Uint32Array) => {
126
+ array[0] = 987654321;
127
+ return array;
128
+ }),
129
+ } as unknown as Crypto;
130
+ });
131
+
132
+ afterEach(() => {
133
+ global.crypto = originalCrypto;
134
+ });
135
+
136
+ it('should return a 32-bit random number', () => {
137
+ const result = generateSeed32();
138
+
139
+ expect(global.crypto.getRandomValues).toHaveBeenCalledWith(expect.any(Uint32Array));
140
+ expect(typeof result).toBe('number');
141
+ expect(result).toBe(987654321);
142
+ });
143
+
144
+ it('should return value within 32-bit unsigned integer range', () => {
145
+ global.crypto = {
146
+ getRandomValues: jest.fn((array: Uint32Array) => {
147
+ array[0] = 4294967295; // Max 32-bit unsigned int
148
+ return array;
149
+ }),
150
+ } as unknown as Crypto;
151
+
152
+ const result = generateSeed32();
153
+ expect(result).toBe(4294967295);
154
+ });
155
+
156
+ it('should return 0 when crypto returns 0', () => {
157
+ global.crypto = {
158
+ getRandomValues: jest.fn((array: Uint32Array) => {
159
+ array[0] = 0;
160
+ return array;
161
+ }),
162
+ } as unknown as Crypto;
163
+
164
+ const result = generateSeed32();
165
+ expect(result).toBe(0);
166
+ });
167
+
168
+ it('should modify the provided array in place', () => {
169
+ const mockArray = new Uint32Array(1);
170
+
171
+ global.crypto = {
172
+ getRandomValues: jest.fn((array: Uint32Array) => {
173
+ array[0] = 55555;
174
+ return array;
175
+ }),
176
+ } as unknown as Crypto;
177
+
178
+ generateSeed32();
179
+
180
+ expect(global.crypto.getRandomValues).toHaveBeenCalled();
181
+ const calledArray = (global.crypto.getRandomValues as jest.Mock).mock.calls[0][0] as Uint32Array;
182
+ expect(calledArray).toBeInstanceOf(Uint32Array);
183
+ expect(calledArray.length).toBe(1);
184
+ });
185
+ });
186
+ });
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: comfyui-mcp-server
3
+
4
+ description: Enables AI agents to discover, inspect, and execute ComfyUI workflows for image and video generation through a structured MCP interface. Supports the full task lifecycle—from context loading and workflow discovery, to parameter mounting and task submission, and finally result retrieval and generated asset management.
5
+
6
+ tags: ComfyUI, Image Generation, Video Generation, MCP, Workflow Automation
7
+ ---
8
+
9
+ ## Use Cases
10
+
11
+ Activate this skill when users request generate image, video, or multimedia generation. Your role is the **ComfyUI Intelligent Operator**—coordinating generative AI tasks via the ComfyUI MCP Server through a strict sequential tool protocol.
12
+
13
+ ## Usage Method
14
+
15
+ ### Mandatory Initialization
16
+
17
+ Before executing any workflow task:
18
+
19
+ 1. Read `rules/**.md`
20
+ 2. Follow the tool calling sequence described there.
21
+ 3. Do not skip this step.
22
+
23
+ Refer to the respective rule files for detailed instructions and code examples:
24
+
25
+ ---
26
+
27
+ **If the user does not specify, this rule is applied.**
28
+
29
+ - [rules/catalog.md](rules/catalog.md) - Generate images and videos by constructing workflow directories
30
+ - [references/catalog.md](references/catalog.md) - Reference examples for generating images and videos via the build workflow directory
31
+
32
+ ---
33
+
34
+ - [rules/api-json.md](rules/api-json.md) - Generate images and videos using user-provided API JSON
35
+ - [references/Catalog.md](references/api-json.md) - Reference examples for generating images and videos using user-provided API JSON
36
+
37
+ ---
38
+
39
+ ## Important Notes
40
+
41
+ 1. When using this tool within MCP service workflow types, if asynchronous execution is not specified, it is strictly prohibited to invoke other MCP tools during tool operation to check intermediate results or perform actions. Wait for the tool to complete execution.
42
+
43
+ 2. Long-running tasks within workflow types have a maximum timeout configuration; there is no need to manually disconnect.
@@ -0,0 +1,323 @@
1
+ # ComfyUI MCP Server – Agent Reference (Custom Workflow)
2
+
3
+ ## Example User Request
4
+
5
+ User instruction:
6
+
7
+ > Use the provided API JSON workflow to generate an image of a **cat and a dog**.
8
+
9
+ Requirements:
10
+
11
+ - The **cat and dog are lying down**
12
+ - The **cat is grooming (licking) the dog**
13
+ - The background must contain a **grassy meadow**
14
+ - The weather must be **sunny**
15
+ - **No humans** should appear in the image
16
+
17
+ ---
18
+
19
+ # Execution Flow
20
+
21
+ The Agent must execute the following tool calls **in order**:
22
+
23
+ ```
24
+ 1. get_core_manual
25
+ 2. save_custom_workflow
26
+ 3. mount_workflow
27
+ 4. queue_prompt
28
+ ```
29
+
30
+ Optional:
31
+
32
+ ```
33
+ 5. save_task_assets
34
+ ```
35
+
36
+ ---
37
+
38
+ # Step 1 — Retrieve Core Manual
39
+
40
+ ### Tool
41
+
42
+ ```
43
+ comfy-ui-advanced/get_core_manual
44
+ ```
45
+
46
+ ### Purpose
47
+
48
+ Retrieve the **ComfyUI MCP core manual** when the Host does not support Skills or when no Skill matches the request.
49
+
50
+ ### Request
51
+
52
+ ```json
53
+ {}
54
+ ```
55
+
56
+ ### Example Response
57
+
58
+ ```json
59
+ {
60
+ "success": true,
61
+ "message": "Core manual loaded. Please strictly follow its protocols and parameter strategies for subsequent operations.",
62
+ "detail": {
63
+ "data": "...",
64
+ "code": 200,
65
+ "timestamp": 1773194304101
66
+ }
67
+ }
68
+ ```
69
+
70
+ ---
71
+
72
+ # Step 2 — Save Custom Workflow
73
+
74
+ ### Tool
75
+
76
+ ```
77
+ comfy-ui-advanced/save_custom_workflow
78
+ ```
79
+
80
+ ### Purpose
81
+
82
+ Convert the provided **ComfyUI API JSON** into a workflow file and validate its feasibility.
83
+
84
+ If validation succeeds:
85
+
86
+ - The workflow is saved in the **workflow directory**
87
+ - A **promptId** is returned
88
+
89
+ ### Request
90
+
91
+ ```json
92
+ {
93
+ "filename": "cat_dog_workflow.json",
94
+ "apiJson": { ... }
95
+ }
96
+ ```
97
+
98
+ (The `apiJson` is the ComfyUI workflow JSON provided by the user.)
99
+
100
+ ### Example Response
101
+
102
+ ```json
103
+ {
104
+ "success": true,
105
+ "message": "Custom workflow saved",
106
+ "detail": {
107
+ "data": {
108
+ "filePath": "E:\\comfyui-mcp\\workflow\\cat_dog_workflow.json",
109
+ "promptId": "a068f812-db05-4255-b755-596799e3dc22"
110
+ }
111
+ }
112
+ }
113
+ ```
114
+
115
+ ### Important Output
116
+
117
+ The Agent must store:
118
+
119
+ ```
120
+ promptId
121
+ ```
122
+
123
+ Example:
124
+
125
+ ```
126
+ a068f812-db05-4255-b755-596799e3dc22
127
+ ```
128
+
129
+ This value will be used in the next step.
130
+
131
+ ---
132
+
133
+ # Step 3 — Mount Workflow as a Dynamic Tool
134
+
135
+ ### Tool
136
+
137
+ ```
138
+ comfy-ui-advanced/mount_workflow
139
+ ```
140
+
141
+ ### Purpose
142
+
143
+ Mount the saved workflow as a **dynamic executable tool**.
144
+
145
+ ### Request
146
+
147
+ ```json
148
+ {
149
+ "promptId": "a068f812-db05-4255-b755-596799e3dc22",
150
+ "toolName": "cat_dog_workflow"
151
+ }
152
+ ```
153
+
154
+ ### Response (Key Data)
155
+
156
+ The response returns the **configurable parameters**.
157
+
158
+ Example:
159
+
160
+ ```json
161
+ {
162
+ "name": "cat_dog_workflow",
163
+ "configurableParams": [
164
+ {
165
+ "key": "3_text",
166
+ "description": "Positive prompt"
167
+ },
168
+ {
169
+ "key": "4_text",
170
+ "description": "Negative prompt"
171
+ },
172
+ {
173
+ "key": "12_value",
174
+ "description": "Random seed"
175
+ }
176
+ ]
177
+ }
178
+ ```
179
+
180
+ ### Configurable Parameters
181
+
182
+ | Parameter | Description |
183
+ | ---------- | --------------- |
184
+ | `3_text` | Positive prompt |
185
+ | `4_text` | Negative prompt |
186
+ | `12_value` | Random seed |
187
+
188
+ ### Agent Constraints
189
+
190
+ Allowed:
191
+
192
+ ```
193
+ Modify parameter values
194
+ ```
195
+
196
+ Not allowed:
197
+
198
+ ```
199
+ Modify workflow structure
200
+ Add or remove nodes
201
+ ```
202
+
203
+ ---
204
+
205
+ # Step 4 — Execute Workflow
206
+
207
+ ### Tool
208
+
209
+ ```
210
+ comfy-ui-advanced/queue_prompt
211
+ ```
212
+
213
+ ### Purpose
214
+
215
+ Execute the mounted workflow with customized parameters.
216
+
217
+ ### Request
218
+
219
+ ```json
220
+ {
221
+ "workflowName": "cat_dog_workflow",
222
+ "isAsync": false,
223
+ "params": {
224
+ "3_text": "A cat and a dog lying down on a grassy meadow, the cat is grooming the dog, sunny weather, blue sky, no people, realistic style, high quality, detailed",
225
+ "4_text": "people, human, buildings, urban, rainy, cloudy, low quality, blurry, distorted",
226
+ "12_value": 42
227
+ }
228
+ }
229
+ ```
230
+
231
+ ### Prompt Strategy
232
+
233
+ Positive prompt should include:
234
+
235
+ ```
236
+ cat and dog interaction
237
+ lying posture
238
+ grooming behavior
239
+ grassy meadow background
240
+ sunny weather
241
+ realistic style
242
+ ```
243
+
244
+ Negative prompt should exclude:
245
+
246
+ ```
247
+ humans
248
+ urban environments
249
+ bad quality
250
+ visual artifacts
251
+ ```
252
+
253
+ ---
254
+
255
+ # Execution Result
256
+
257
+ Example response:
258
+
259
+ ```json
260
+ {
261
+ "success": true,
262
+ "detail": {
263
+ "data": {
264
+ "promptId": "bd47c07f-6dc6-4b7a-8ed1-38535314de79",
265
+ "img": [
266
+ "http://192.168.0.171:8188/view?filename=ComfyUI_temp_bldgb_00003_.png"
267
+ ]
268
+ }
269
+ }
270
+ }
271
+ ```
272
+
273
+ ### Important Output
274
+
275
+ ```
276
+ Generated image URL
277
+ promptId
278
+ ```
279
+
280
+ ---
281
+
282
+ # Optional Step — Save Generated Assets
283
+
284
+ ### Tool
285
+
286
+ ```
287
+ comfy-ui-advanced/save_task_assets
288
+ ```
289
+
290
+ ### Purpose
291
+
292
+ Save generated images to the local **assets directory**.
293
+
294
+ ### Request
295
+
296
+ ```json
297
+ {
298
+ "promptId": "bd47c07f-6dc6-4b7a-8ed1-38535314de79"
299
+ }
300
+ ```
301
+
302
+ ---
303
+
304
+ # Agent Execution Summary
305
+
306
+ Complete workflow:
307
+
308
+ ```
309
+ 1 Retrieve MCP manual
310
+ get_core_manual
311
+
312
+ 2 Save user-provided workflow
313
+ save_custom_workflow
314
+
315
+ 3 Mount workflow as dynamic tool
316
+ mount_workflow
317
+
318
+ 4 Execute workflow
319
+ queue_prompt
320
+
321
+ 5 (Optional) Save generated assets
322
+ save_task_assets
323
+ ```