@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.
- package/.env +179 -0
- package/.github/workflows/ci.yml +63 -0
- package/.husky/pre-commit +11 -0
- package/LICENSE +21 -0
- package/README-en.md +635 -0
- package/README.md +656 -0
- package/__tests__/services/task/execution.test.ts +272 -0
- package/__tests__/services/task/wait.test.ts +220 -0
- package/__tests__/types/result.test.ts +227 -0
- package/__tests__/utils/mcp-helpers.test.ts +137 -0
- package/__tests__/utils/special-node-handler.test.ts +186 -0
- package/comfyui-mcp-server-skill/SKILL.md +43 -0
- package/comfyui-mcp-server-skill/references/api-json.md +323 -0
- package/comfyui-mcp-server-skill/references/catalog.md +251 -0
- package/comfyui-mcp-server-skill/rules/api-json.md +94 -0
- package/comfyui-mcp-server-skill/rules/catalog.md +93 -0
- package/comfyui-mcp-server-skill/rules/comfyui.md +158 -0
- package/configs/inspector/dev.json +11 -0
- package/configs/inspector/prod.json +9 -0
- package/docs/en/content/public/inspector-example.png +0 -0
- package/docs/en/content/public/workflow_name_example.png +0 -0
- package/docs/en/content/public/workflow_parameter_example.png +0 -0
- package/docs/en/md/Project-Advantages.md +75 -0
- package/docs/zh-CN/content/public/inspector-example.png +0 -0
- package/docs/zh-CN/content/public/workflow_name_example.png +0 -0
- package/docs/zh-CN/content/public/workflow_parameter_example.png +0 -0
- package/docs/zh-CN/md/why-us.md +51 -0
- package/example.json +26 -0
- package/jest.config.js +45 -0
- package/locales/en.json +150 -0
- package/locales/zh.json +150 -0
- package/package.json +68 -0
- package/src/api/api.ts +123 -0
- package/src/api/http.ts +70 -0
- package/src/constants/common.ts +38 -0
- package/src/constants/index.ts +1 -0
- package/src/hooks/websocket.ts +93 -0
- package/src/i18n.ts +40 -0
- package/src/index.ts +268 -0
- package/src/scripts/comfy-ui/list-history.ts +31 -0
- package/src/scripts/comfy-ui/run-ws.ts +23 -0
- package/src/scripts/ws/send-feature-flags.ts +23 -0
- package/src/server-stdio.ts +23 -0
- package/src/services/business.ts +194 -0
- package/src/services/dynamic-tool.ts +303 -0
- package/src/services/index.ts +19 -0
- package/src/services/storage/asset-storage.ts +48 -0
- package/src/services/storage/index.ts +2 -0
- package/src/services/storage/workflow-storage.ts +192 -0
- package/src/services/task/execution.ts +69 -0
- package/src/services/task/fetch.ts +131 -0
- package/src/services/task/index.ts +3 -0
- package/src/services/task/wait.ts +294 -0
- package/src/services/workflow/executor.ts +134 -0
- package/src/services/workflow/index.ts +1 -0
- package/src/tools/handlers/core-manual.ts +28 -0
- package/src/tools/handlers/index.ts +22 -0
- package/src/tools/handlers/interrupt-prompt.ts +35 -0
- package/src/tools/handlers/list-models.ts +51 -0
- package/src/tools/handlers/mount-workflow.ts +88 -0
- package/src/tools/handlers/prompt-result.ts +35 -0
- package/src/tools/handlers/prompts.ts +61 -0
- package/src/tools/handlers/queue-custom-prompt.ts +164 -0
- package/src/tools/handlers/queue-prompt.ts +170 -0
- package/src/tools/handlers/save-custom-workflow.ts +67 -0
- package/src/tools/handlers/save-task-assets.ts +82 -0
- package/src/tools/handlers/system-status.ts +30 -0
- package/src/tools/handlers/task-detail.ts +35 -0
- package/src/tools/handlers/tool-status-map.ts +33 -0
- package/src/tools/handlers/upload-assets.ts +82 -0
- package/src/tools/handlers/workflow-api.ts +46 -0
- package/src/tools/handlers/workflows-catalog.ts +79 -0
- package/src/tools/index.ts +101 -0
- package/src/types/common.ts +74 -0
- package/src/types/dynamic-tool.ts +85 -0
- package/src/types/enums/result.ts +29 -0
- package/src/types/execute.ts +24 -0
- package/src/types/object-info.ts +43 -0
- package/src/types/result.ts +126 -0
- package/src/types/task.ts +118 -0
- package/src/types/workflow.ts +111 -0
- package/src/types/ws.ts +80 -0
- package/src/utils/format.ts +134 -0
- package/src/utils/mcp-helpers.ts +110 -0
- package/src/utils/special-node-handler.ts +36 -0
- package/src/utils/workflow-converter.ts +140 -0
- package/src/utils/ws.ts +219 -0
- 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
|
+
```
|