Initial commit: ComfyUI MCP Server with Node Integration
This is a complete rewrite of the MCP server configuration system: Features: - ComfyUI custom node for visual MCP configuration - Bilingual documentation (English/Chinese) - Auto-installation and auto-configuration for AI assistants - Support for Claude Code, Claude Desktop, and Gemini CLI - Simplified setup without manual bat files Architecture: - src/comfy_mcp_server/ - MCP server core (auto-started by AI assistants) - nodes/ - ComfyUI custom node for configuration - workflow/ - Example workflow files - Merged pyproject.toml for both MCP server and ComfyUI node Based on the original work by lalanikarim/comfy-mcp-server Refactored and enhanced by dseditor for easier ComfyUI integration
This commit is contained in:
+71
@@ -0,0 +1,71 @@
|
||||
# Python generated files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
*.egg
|
||||
*.egg-info/
|
||||
dist/
|
||||
build/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
|
||||
# Virtual environments
|
||||
.venv/
|
||||
venv/
|
||||
env/
|
||||
ENV/
|
||||
|
||||
# IDE and Editor files
|
||||
.vscode/
|
||||
.idea/
|
||||
.aider*
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.envrc
|
||||
|
||||
# User-specific Claude Code configuration
|
||||
.claude/
|
||||
|
||||
# Generated configuration files (created by the node at runtime)
|
||||
mcp_config.ini
|
||||
claude_desktop_mcp_config*.json
|
||||
gemini_settings*.json
|
||||
temp_mcp_config.json
|
||||
|
||||
# Generated images
|
||||
generated_images/
|
||||
|
||||
# Log files
|
||||
*.log
|
||||
mcp_server.log
|
||||
|
||||
# Backup files
|
||||
backup/
|
||||
*.backup
|
||||
*.bak
|
||||
*.old
|
||||
|
||||
# OS specific files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# Development lock files (optional - uncomment if you want to track it)
|
||||
# uv.lock
|
||||
|
||||
# Test and temporary files
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.tox/
|
||||
*.zip
|
||||
GIT_COMMIT_GUIDE.md
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Karim Lalani
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,221 @@
|
||||
# ComfyUI MCP Server
|
||||
|
||||
[中文说明](#中文说明) | [English](#english)
|
||||
|
||||
---
|
||||
|
||||
## English
|
||||
|
||||
MCP Server for ComfyUI - Seamlessly integrate AI assistants (Claude Code, Claude Desktop, Gemini CLI) with ComfyUI through the Model Context Protocol.
|
||||
|
||||
### Features
|
||||
|
||||
- 🎨 **ComfyUI Integration**: Connect AI assistants to ComfyUI via MCP protocol
|
||||
- 🔧 **Custom Node**: Configure MCP parameters visually within ComfyUI
|
||||
- 📦 **Portable Setup**: Use relative paths for easy environment migration
|
||||
- 🚀 **Auto-Start**: MCP Server launches automatically when AI assistants need it
|
||||
- ⚙️ **Auto-Configuration**: Automatically install and configure MCP for AI assistants
|
||||
|
||||
### Quick Start
|
||||
|
||||
#### 1. Start ComfyUI
|
||||
|
||||
Simply start ComfyUI with your normal startup script (no special MCP launcher needed):
|
||||
|
||||
\`\`\`batch
|
||||
# Windows
|
||||
run_nvidia_gpu.bat
|
||||
|
||||
# Or any other ComfyUI startup script you normally use
|
||||
\`\`\`
|
||||
|
||||
#### 2. Configure MCP in ComfyUI
|
||||
|
||||
Add the **MCP Config Generator** node to your workflow:
|
||||
|
||||
1. Add \`MCP Config Generator\` node in ComfyUI
|
||||
2. Configure the node parameters:
|
||||
- **workflow_file**: Select your workflow JSON file
|
||||
- **prompt_node_id**: Node ID for text input (e.g., "45")
|
||||
- **output_node_id**: Node ID for image output (e.g., "9")
|
||||
- **comfy_url**: ComfyUI server URL (default: \`http://127.0.0.1:8188\`)
|
||||
- **auto_install**: ✅ Enable (for first-time setup)
|
||||
- **auto_update_claude_code**: ✅ Enable (if using Claude Code)
|
||||
- **auto_update_claude_desktop**: ✅ Enable (if using Claude Desktop)
|
||||
- **auto_update_gemini_cli**: ✅ Enable (if using Gemini CLI)
|
||||
3. Execute the workflow
|
||||
|
||||
The node will automatically:
|
||||
- Install the MCP server module
|
||||
- Generate configuration files
|
||||
- Update AI assistant configuration files
|
||||
|
||||
#### 3. Use with AI Assistants
|
||||
|
||||
That's it! The MCP server will automatically start when you use the AI assistant.
|
||||
|
||||
**In Claude Code or Claude Desktop:**
|
||||
\`\`\`
|
||||
Generate an image of a cat in a sunny garden
|
||||
\`\`\`
|
||||
|
||||
The AI assistant will automatically:
|
||||
1. Start the MCP server (if not running)
|
||||
2. Call ComfyUI to generate the image
|
||||
3. Return the generated image
|
||||
|
||||
### Manual Installation (Optional)
|
||||
|
||||
If auto-installation doesn't work, you can install manually:
|
||||
|
||||
\`\`\`batch
|
||||
cd path/to/ComfyUI/custom_nodes/ComfyUI-MCP
|
||||
python -m pip install -r requirements.txt
|
||||
python -m pip install -e .
|
||||
\`\`\`
|
||||
|
||||
### System Requirements
|
||||
|
||||
- Python >= 3.10
|
||||
- ComfyUI
|
||||
- Claude Code, Claude Desktop, or Gemini CLI
|
||||
|
||||
### Architecture
|
||||
|
||||
This project uses a dual-layer architecture:
|
||||
|
||||
\`\`\`
|
||||
ComfyUI-MCP/
|
||||
├── src/comfy_mcp_server/ ← MCP Server core (auto-started by AI assistants)
|
||||
├── nodes/mcp_config_node.py ← ComfyUI node (for configuration)
|
||||
├── workflow/ ← Workflow JSON files
|
||||
├── pyproject.toml ← Package configuration
|
||||
└── requirements.txt ← Dependencies
|
||||
\`\`\`
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
**MCP server not working?**
|
||||
1. Check that \`auto_install\` is enabled in the node
|
||||
2. Restart ComfyUI after first installation
|
||||
3. Verify the module is installed: \`python -m pip list | grep comfy-mcp-server\`
|
||||
|
||||
**AI assistant can't find MCP server?**
|
||||
1. Check that \`auto_update_*\` is enabled for your AI assistant
|
||||
2. Restart the AI assistant after configuration
|
||||
3. Manually check the configuration file:
|
||||
- Claude Code: \`~/.config/claude-code/mcp.json\`
|
||||
- Claude Desktop: \`%APPDATA%/Claude/claude_desktop_config.json\`
|
||||
|
||||
### License
|
||||
|
||||
MIT
|
||||
|
||||
---
|
||||
|
||||
## 中文说明
|
||||
|
||||
ComfyUI MCP 服务器 - 通过模型上下文协议(MCP)无缝整合 AI 助手(Claude Code、Claude Desktop、Gemini CLI)与 ComfyUI。
|
||||
|
||||
### 功能特点
|
||||
|
||||
- 🎨 **ComfyUI 整合**: 通过 MCP 协议连接 AI 助手与 ComfyUI
|
||||
- 🔧 **自定义节点**: 在 ComfyUI 中可视化配置 MCP 参数
|
||||
- 📦 **便携式设置**: 使用相对路径,便于环境迁移
|
||||
- 🚀 **自动启动**: AI 助手需要时自动启动 MCP 服务器
|
||||
- ⚙️ **自动配置**: 自动安装并配置 MCP 到 AI 助手
|
||||
|
||||
### 快速开始
|
||||
|
||||
#### 1. 启动 ComfyUI
|
||||
|
||||
使用普通的启动脚本即可(不需要特殊的 MCP 启动器):
|
||||
|
||||
\`\`\`batch
|
||||
# Windows
|
||||
run_nvidia_gpu.bat
|
||||
|
||||
# 或者使用你平时使用的任何 ComfyUI 启动脚本
|
||||
\`\`\`
|
||||
|
||||
#### 2. 在 ComfyUI 中配置 MCP
|
||||
|
||||
在工作流中添加 **MCP Config Generator** 节点:
|
||||
|
||||
1. 在 ComfyUI 中添加 \`MCP Config Generator\` 节点
|
||||
2. 配置节点参数:
|
||||
- **workflow_file**: 选择工作流 JSON 文件
|
||||
- **prompt_node_id**: 文本输入节点 ID(例如 "45")
|
||||
- **output_node_id**: 图像输出节点 ID(例如 "9")
|
||||
- **comfy_url**: ComfyUI 服务器 URL(默认:\`http://127.0.0.1:8188\`)
|
||||
- **auto_install**: ✅ 启用(首次设置时)
|
||||
- **auto_update_claude_code**: ✅ 启用(如果使用 Claude Code)
|
||||
- **auto_update_claude_desktop**: ✅ 启用(如果使用 Claude Desktop)
|
||||
- **auto_update_gemini_cli**: ✅ 启用(如果使用 Gemini CLI)
|
||||
3. 执行工作流
|
||||
|
||||
节点会自动:
|
||||
- 安装 MCP 服务器模块
|
||||
- 生成配置文件
|
||||
- 更新 AI 助手配置文件
|
||||
|
||||
#### 3. 在 AI 助手中使用
|
||||
|
||||
完成!MCP 服务器会在你使用 AI 助手时自动启动。
|
||||
|
||||
**在 Claude Code 或 Claude Desktop 中:**
|
||||
\`\`\`
|
||||
生成一张阳光花园中的猫的图片
|
||||
\`\`\`
|
||||
|
||||
AI 助手会自动:
|
||||
1. 启动 MCP 服务器(如果未运行)
|
||||
2. 调用 ComfyUI 生成图片
|
||||
3. 返回生成的图片
|
||||
|
||||
### 手动安装(可选)
|
||||
|
||||
如果自动安装不起作用,可以手动安装:
|
||||
|
||||
\`\`\`batch
|
||||
cd ComfyUI所在路径/custom_nodes/ComfyUI-MCP
|
||||
python -m pip install -r requirements.txt
|
||||
python -m pip install -e .
|
||||
\`\`\`
|
||||
|
||||
### 系统要求
|
||||
|
||||
- Python >= 3.10
|
||||
- ComfyUI
|
||||
- Claude Code、Claude Desktop 或 Gemini CLI
|
||||
|
||||
### 架构说明
|
||||
|
||||
本项目采用双层架构:
|
||||
|
||||
\`\`\`
|
||||
ComfyUI-MCP/
|
||||
├── src/comfy_mcp_server/ ← MCP 服务器核心(由 AI 助手自动启动)
|
||||
├── nodes/mcp_config_node.py ← ComfyUI 节点(用于配置)
|
||||
├── workflow/ ← 工作流 JSON 文件
|
||||
├── pyproject.toml ← 包配置
|
||||
└── requirements.txt ← 依赖项
|
||||
\`\`\`
|
||||
|
||||
### 故障排除
|
||||
|
||||
**MCP 服务器无法工作?**
|
||||
1. 检查节点中是否启用了 \`auto_install\`
|
||||
2. 首次安装后重启 ComfyUI
|
||||
3. 验证模块已安装:\`python -m pip list | grep comfy-mcp-server\`
|
||||
|
||||
**AI 助手找不到 MCP 服务器?**
|
||||
1. 检查是否为你的 AI 助手启用了 \`auto_update_*\`
|
||||
2. 配置后重启 AI 助手
|
||||
3. 手动检查配置文件:
|
||||
- Claude Code: \`~/.config/claude-code/mcp.json\`
|
||||
- Claude Desktop: \`%APPDATA%/Claude/claude_desktop_config.json\`
|
||||
|
||||
### 许可证
|
||||
|
||||
MIT
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
"""
|
||||
ComfyUI-MCP Custom Nodes
|
||||
為 ComfyUI 提供 MCP (Model Context Protocol) 整合節點
|
||||
"""
|
||||
|
||||
from .nodes.mcp_config_node import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
|
||||
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
|
||||
|
||||
# 版本資訊
|
||||
WEB_DIRECTORY = "./web"
|
||||
__version__ = "1.0.0"
|
||||
@@ -0,0 +1 @@
|
||||
# ComfyUI-MCP Nodes Package
|
||||
@@ -0,0 +1,529 @@
|
||||
import os
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
class MCPConfigGenerator:
|
||||
"""
|
||||
ComfyUI Node: Generate MCP Server Configuration
|
||||
Select workflow files from workflow folder and configure MCP parameters
|
||||
Includes auto-install, Claude Code config, multi-format output, etc.
|
||||
"""
|
||||
|
||||
RETURN_TYPES = ("STRING", "STRING")
|
||||
RETURN_NAMES = ("status_text", "json_config")
|
||||
FUNCTION = "generate_config"
|
||||
CATEGORY = "MCP"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
# Get all JSON files from workflow folder
|
||||
workflow_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "workflow")
|
||||
|
||||
workflow_files = ["None"]
|
||||
if os.path.exists(workflow_dir):
|
||||
workflow_files = ["None"] + [
|
||||
f for f in os.listdir(workflow_dir)
|
||||
if f.endswith('.json')
|
||||
]
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"workflow_file": (workflow_files,),
|
||||
"prompt_node_id": ("STRING", {
|
||||
"default": "45",
|
||||
"multiline": False
|
||||
}),
|
||||
"output_node_id": ("STRING", {
|
||||
"default": "9",
|
||||
"multiline": False
|
||||
}),
|
||||
"comfy_url": ("STRING", {
|
||||
"default": "http://127.0.0.1:8188",
|
||||
"multiline": False
|
||||
}),
|
||||
"max_poll_attempts": ("INT", {
|
||||
"default": 60,
|
||||
"min": 10,
|
||||
"max": 300,
|
||||
"step": 1
|
||||
}),
|
||||
"poll_interval": ("INT", {
|
||||
"default": 2,
|
||||
"min": 1,
|
||||
"max": 10,
|
||||
"step": 1
|
||||
}),
|
||||
"output_mode": (["url", "file"],),
|
||||
"config_format": (["ClaudeDesktop", "Gemini"],),
|
||||
"auto_install": ("BOOLEAN", {"default": False}),
|
||||
"auto_update_claude_code": ("BOOLEAN", {"default": False}),
|
||||
"auto_update_claude_desktop": ("BOOLEAN", {"default": False}),
|
||||
"auto_update_gemini_cli": ("BOOLEAN", {"default": False}),
|
||||
}
|
||||
}
|
||||
|
||||
def generate_config(self, workflow_file, prompt_node_id, output_node_id,
|
||||
comfy_url, max_poll_attempts, poll_interval, output_mode,
|
||||
config_format, auto_install, auto_update_claude_code,
|
||||
auto_update_claude_desktop, auto_update_gemini_cli):
|
||||
"""
|
||||
Generate MCP config, update config files, auto-install module, auto-update AI configs
|
||||
"""
|
||||
|
||||
status_messages = []
|
||||
warnings = []
|
||||
|
||||
# Get project root directory
|
||||
node_dir = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
# Determine full path of workflow file
|
||||
if workflow_file == "None":
|
||||
# Default workflow should also be in workflow folder
|
||||
workflow_path = os.path.join(node_dir, "workflow", "image_z_image_turbo.json")
|
||||
workflow_file = "image_z_image_turbo.json"
|
||||
else:
|
||||
workflow_path = os.path.join(node_dir, "workflow", workflow_file)
|
||||
|
||||
# Verify workflow file exists
|
||||
if not os.path.exists(workflow_path):
|
||||
error_msg = f"Error: Workflow file not found: {workflow_path}"
|
||||
return (error_msg, "{}")
|
||||
|
||||
# Get absolute path of python_embeded
|
||||
python_exe = os.path.abspath(
|
||||
os.path.join(node_dir, "..", "..", "..", "python_embeded", "python.exe")
|
||||
)
|
||||
|
||||
# Check if Python exists
|
||||
if not os.path.exists(python_exe):
|
||||
error_msg = f"Error: Python not found: {python_exe}"
|
||||
return (error_msg, "{}")
|
||||
|
||||
# 1. Auto-install module (if enabled)
|
||||
if auto_install:
|
||||
status_messages.append("=" * 50)
|
||||
status_messages.append("Auto-Install MCP Module")
|
||||
status_messages.append("=" * 50)
|
||||
warnings.append("⚠ Restart ComfyUI after installation to take effect!")
|
||||
|
||||
install_result = self._install_module(python_exe, node_dir)
|
||||
status_messages.append(install_result)
|
||||
status_messages.append("")
|
||||
|
||||
# Build MCP base configuration
|
||||
mcp_env_config = {
|
||||
"COMFY_URL": comfy_url,
|
||||
"COMFY_URL_EXTERNAL": comfy_url,
|
||||
"COMFY_WORKFLOW_JSON_FILE": workflow_path,
|
||||
"PROMPT_NODE_ID": str(prompt_node_id),
|
||||
"OUTPUT_NODE_ID": str(output_node_id),
|
||||
"OUTPUT_MODE": output_mode,
|
||||
"COMFY_MAX_POLL_ATTEMPTS": str(max_poll_attempts),
|
||||
"COMFY_POLL_INTERVAL": str(poll_interval)
|
||||
}
|
||||
|
||||
# Generate configuration based on format
|
||||
if config_format == "ClaudeDesktop":
|
||||
mcp_config = {
|
||||
"mcpServers": {
|
||||
"comfyui-image-generator": {
|
||||
"command": python_exe,
|
||||
"args": ["-m", "comfy_mcp_server"],
|
||||
"cwd": node_dir,
|
||||
"env": mcp_env_config
|
||||
}
|
||||
}
|
||||
}
|
||||
else: # Gemini
|
||||
mcp_config = {
|
||||
"mcp_servers": {
|
||||
"comfyui-image-generator": {
|
||||
"command": python_exe,
|
||||
"args": ["-m", "comfy_mcp_server"],
|
||||
"cwd": node_dir,
|
||||
"env": mcp_env_config
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 2. Auto-update AI platform configs (if enabled)
|
||||
if auto_update_claude_code:
|
||||
status_messages.append("=" * 50)
|
||||
status_messages.append("Auto-Update: Claude Code (User Scope)")
|
||||
status_messages.append("=" * 50)
|
||||
|
||||
claude_code_config = {
|
||||
"command": python_exe,
|
||||
"args": ["-m", "comfy_mcp_server"],
|
||||
"cwd": node_dir,
|
||||
"env": mcp_env_config
|
||||
}
|
||||
|
||||
result = self._update_claude_code(claude_code_config)
|
||||
status_messages.append(result)
|
||||
status_messages.append("")
|
||||
|
||||
if auto_update_claude_desktop:
|
||||
status_messages.append("=" * 50)
|
||||
status_messages.append("Auto-Update: Claude Desktop")
|
||||
status_messages.append("=" * 50)
|
||||
|
||||
claude_desktop_config = {
|
||||
"command": python_exe,
|
||||
"args": ["-m", "comfy_mcp_server"],
|
||||
"cwd": node_dir,
|
||||
"env": mcp_env_config
|
||||
}
|
||||
|
||||
result = self._update_claude_desktop(claude_desktop_config)
|
||||
status_messages.append(result)
|
||||
status_messages.append("")
|
||||
|
||||
if auto_update_gemini_cli:
|
||||
status_messages.append("=" * 50)
|
||||
status_messages.append("Auto-Update: Gemini CLI")
|
||||
status_messages.append("=" * 50)
|
||||
|
||||
gemini_config = {
|
||||
"command": python_exe,
|
||||
"args": ["-m", "comfy_mcp_server"],
|
||||
"env": mcp_env_config,
|
||||
"timeout": 120000,
|
||||
"trust": False
|
||||
}
|
||||
|
||||
result = self._update_gemini_cli(gemini_config)
|
||||
status_messages.append(result)
|
||||
status_messages.append("")
|
||||
|
||||
# 3. Save JSON config file
|
||||
if config_format == "ClaudeDesktop":
|
||||
json_filename = f"claude_desktop_mcp_config_{workflow_file.replace('.json', '')}.json"
|
||||
else:
|
||||
json_filename = f"gemini_settings_{workflow_file.replace('.json', '')}.json"
|
||||
|
||||
json_path = os.path.join(node_dir, json_filename)
|
||||
|
||||
try:
|
||||
|
||||
with open(json_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(mcp_config, f, indent=2, ensure_ascii=False)
|
||||
|
||||
status_messages.append("=" * 50)
|
||||
status_messages.append("Configuration File Generated")
|
||||
status_messages.append("=" * 50)
|
||||
status_messages.append(f"✓ {json_filename} generated")
|
||||
status_messages.append(f" Location: {json_path}")
|
||||
status_messages.append("")
|
||||
|
||||
# 4. Update mcp_config.ini config file
|
||||
config_ini_path = os.path.join(node_dir, "mcp_config.ini")
|
||||
try:
|
||||
with open(config_ini_path, 'w', encoding='utf-8') as f:
|
||||
f.write(f"WORKFLOW_FILE={workflow_path}\n")
|
||||
f.write(f"PROMPT_NODE_ID={prompt_node_id}\n")
|
||||
f.write(f"OUTPUT_NODE_ID={output_node_id}\n")
|
||||
f.write(f"COMFY_URL={comfy_url}\n")
|
||||
f.write(f"OUTPUT_MODE={output_mode}\n")
|
||||
f.write(f"MAX_POLL_ATTEMPTS={max_poll_attempts}\n")
|
||||
f.write(f"POLL_INTERVAL={poll_interval}\n")
|
||||
status_messages.append("✓ mcp_config.ini updated")
|
||||
except Exception as e:
|
||||
status_messages.append(f"⚠ Failed to update mcp_config.ini: {e}")
|
||||
|
||||
# 5. Check MCP status
|
||||
status_messages.append("")
|
||||
status_messages.append("=" * 50)
|
||||
status_messages.append("MCP Status Check")
|
||||
status_messages.append("=" * 50)
|
||||
|
||||
mcp_status = self._check_mcp_status(python_exe, node_dir)
|
||||
status_messages.append(mcp_status)
|
||||
|
||||
# 6. Generate configuration summary
|
||||
status_messages.append("")
|
||||
status_messages.append("=" * 50)
|
||||
status_messages.append("Configuration Summary")
|
||||
status_messages.append("=" * 50)
|
||||
status_messages.append(f"Workflow: {workflow_file}")
|
||||
status_messages.append(f"Prompt Node ID: {prompt_node_id}")
|
||||
status_messages.append(f"Output Node ID: {output_node_id}")
|
||||
status_messages.append(f"ComfyUI URL: {comfy_url}")
|
||||
status_messages.append(f"Output Mode: {output_mode}")
|
||||
status_messages.append(f"Config Format: {config_format}")
|
||||
status_messages.append(f"Auto Install: {'Yes' if auto_install else 'No'}")
|
||||
status_messages.append(f"Auto Update Claude Code: {'Yes' if auto_update_claude_code else 'No'}")
|
||||
status_messages.append(f"Auto Update Claude Desktop: {'Yes' if auto_update_claude_desktop else 'No'}")
|
||||
status_messages.append(f"Auto Update Gemini CLI: {'Yes' if auto_update_gemini_cli else 'No'}")
|
||||
|
||||
# 7. Add usage instructions
|
||||
if config_format == "ClaudeDesktop":
|
||||
status_messages.append("")
|
||||
status_messages.append("=" * 50)
|
||||
status_messages.append("Claude Desktop Configuration Guide")
|
||||
status_messages.append("=" * 50)
|
||||
status_messages.append("Merge generated JSON content to:")
|
||||
status_messages.append("Windows: %APPDATA%\\Claude\\claude_desktop_config.json")
|
||||
status_messages.append("macOS: ~/Library/Application Support/Claude/claude_desktop_config.json")
|
||||
else: # Gemini
|
||||
status_messages.append("")
|
||||
status_messages.append("=" * 50)
|
||||
status_messages.append("Gemini Configuration Guide")
|
||||
status_messages.append("=" * 50)
|
||||
status_messages.append("Add generated JSON content to Gemini's settings.json")
|
||||
|
||||
# 8. Add warnings
|
||||
if warnings:
|
||||
status_messages.append("")
|
||||
status_messages.append("=" * 50)
|
||||
status_messages.append("Important Notice")
|
||||
status_messages.append("=" * 50)
|
||||
for warning in warnings:
|
||||
status_messages.append(warning)
|
||||
|
||||
# Combine final output
|
||||
final_status = "\n".join(status_messages)
|
||||
json_output = json.dumps(mcp_config, indent=2, ensure_ascii=False)
|
||||
|
||||
return (final_status, json_output)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error: Cannot generate config file - {str(e)}"
|
||||
return (error_msg, "{}")
|
||||
|
||||
def _install_module(self, python_exe, node_dir):
|
||||
"""Install MCP module"""
|
||||
try:
|
||||
# Install hatchling
|
||||
result = subprocess.run(
|
||||
[python_exe, "-m", "pip", "install", "hatchling", "--quiet"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=node_dir
|
||||
)
|
||||
|
||||
# Install comfy-mcp-server
|
||||
result = subprocess.run(
|
||||
[python_exe, "-m", "pip", "install", "-e", "."],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=node_dir
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
# Verify installation
|
||||
verify_result = subprocess.run(
|
||||
[python_exe, "-m", "comfy_mcp_server", "--help"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=node_dir
|
||||
)
|
||||
|
||||
if verify_result.returncode == 0:
|
||||
return "✓ MCP module installed successfully and can run properly"
|
||||
else:
|
||||
return "⚠ Module installed but cannot execute, may need to check environment"
|
||||
else:
|
||||
return f"✗ Installation failed: {result.stderr}"
|
||||
|
||||
except Exception as e:
|
||||
return f"✗ Error during installation: {str(e)}"
|
||||
|
||||
def _update_claude_code(self, config):
|
||||
"""Auto-update Claude Code MCP configuration (User Scope)"""
|
||||
try:
|
||||
json_str = json.dumps(config)
|
||||
# Escape double quotes in JSON string
|
||||
json_str_escaped = json_str.replace('"', '\\"')
|
||||
|
||||
# Windows requires shell=True to execute .cmd files
|
||||
cmd = f'claude mcp add-json --scope user comfyui-image-generator "{json_str_escaped}"'
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
shell=True,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
return "✓ Successfully added/updated MCP configuration to Claude Code (User Scope)"
|
||||
else:
|
||||
return f"✗ Failed to update Claude Code: {result.stderr}\n Hint: Please ensure Claude Code CLI is installed"
|
||||
|
||||
except FileNotFoundError:
|
||||
return "✗ Claude Code CLI not found. Please install Claude Code first"
|
||||
except subprocess.TimeoutExpired:
|
||||
return "✗ Claude Code update timeout (network may be slow)"
|
||||
except Exception as e:
|
||||
return f"✗ Error updating Claude Code: {str(e)}"
|
||||
|
||||
def _update_claude_desktop(self, config):
|
||||
"""Auto-update Claude Desktop config file"""
|
||||
try:
|
||||
# Get username
|
||||
username = os.getenv('USERNAME') or os.getenv('USER')
|
||||
if not username:
|
||||
return "✗ Cannot determine username for Claude Desktop config path"
|
||||
|
||||
# Claude Desktop config path
|
||||
config_path = os.path.join(
|
||||
"C:", "Users", username, "AppData", "Roaming", "Claude", "claude_desktop_config.json"
|
||||
)
|
||||
|
||||
# Check if config file exists
|
||||
if not os.path.exists(config_path):
|
||||
# Create directory and initial config
|
||||
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
||||
existing_config = {"mcpServers": {}}
|
||||
else:
|
||||
# Read existing config
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
existing_config = json.load(f)
|
||||
|
||||
# Ensure mcpServers key exists
|
||||
if "mcpServers" not in existing_config:
|
||||
existing_config["mcpServers"] = {}
|
||||
|
||||
# Update or add comfyui-image-generator config
|
||||
existing_config["mcpServers"]["comfyui-image-generator"] = config
|
||||
|
||||
# Write back to file
|
||||
with open(config_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(existing_config, f, indent=2, ensure_ascii=False)
|
||||
|
||||
return f"✓ Successfully updated Claude Desktop config\n Path: {config_path}"
|
||||
|
||||
except PermissionError:
|
||||
return f"✗ Permission denied: Cannot write to {config_path}"
|
||||
except Exception as e:
|
||||
return f"✗ Error updating Claude Desktop config: {str(e)}"
|
||||
|
||||
def _update_gemini_cli(self, config):
|
||||
"""Auto-update Gemini CLI config file"""
|
||||
try:
|
||||
# Get username
|
||||
username = os.getenv('USERNAME') or os.getenv('USER')
|
||||
if not username:
|
||||
return "✗ Cannot determine username for Gemini CLI config path"
|
||||
|
||||
# Gemini CLI config path
|
||||
config_path = os.path.join(
|
||||
"C:", "Users", username, ".gemini", "settings.json"
|
||||
)
|
||||
|
||||
# Check if config file exists
|
||||
if not os.path.exists(config_path):
|
||||
# Create directory and initial config
|
||||
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
||||
existing_config = {"mcpServers": {}}
|
||||
else:
|
||||
# Read existing config
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
existing_config = json.load(f)
|
||||
|
||||
# Ensure mcpServers key exists
|
||||
if "mcpServers" not in existing_config:
|
||||
existing_config["mcpServers"] = {}
|
||||
|
||||
# Update or add comfyui-image-generator config
|
||||
existing_config["mcpServers"]["comfyui-image-generator"] = config
|
||||
|
||||
# Write back to file
|
||||
with open(config_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(existing_config, f, indent=2, ensure_ascii=False)
|
||||
|
||||
return f"✓ Successfully updated Gemini CLI config\n Path: {config_path}"
|
||||
|
||||
except PermissionError:
|
||||
return f"✗ Permission denied: Cannot write to {config_path}"
|
||||
except Exception as e:
|
||||
return f"✗ Error updating Gemini CLI config: {str(e)}"
|
||||
|
||||
def _check_mcp_status(self, python_exe, node_dir):
|
||||
"""Check MCP module status"""
|
||||
status_lines = []
|
||||
|
||||
# 1. Check if module is installed
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[python_exe, "-m", "pip", "show", "comfy-mcp-server"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=node_dir
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
status_lines.append("✓ MCP module is installed")
|
||||
|
||||
# Get version info
|
||||
for line in result.stdout.split('\n'):
|
||||
if line.startswith('Version:'):
|
||||
status_lines.append(f" Version: {line.split(':', 1)[1].strip()}")
|
||||
break
|
||||
else:
|
||||
status_lines.append("✗ MCP module is not installed")
|
||||
except Exception as e:
|
||||
status_lines.append(f"✗ Error checking module installation: {str(e)}")
|
||||
|
||||
# 2. Check if module can be executed
|
||||
try:
|
||||
# Test if module can be loaded
|
||||
src_path = os.path.join(node_dir, "src")
|
||||
verify_result = subprocess.run(
|
||||
[python_exe, "-c",
|
||||
f"import sys; sys.path.insert(0, r'{src_path}'); from comfy_mcp_server import mcp; print('OK')"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=node_dir
|
||||
)
|
||||
|
||||
if verify_result.returncode == 0 and "OK" in verify_result.stdout:
|
||||
status_lines.append("✓ MCP module can be loaded successfully")
|
||||
else:
|
||||
status_lines.append("✗ MCP module cannot be loaded")
|
||||
if verify_result.stderr:
|
||||
status_lines.append(f" Error: {verify_result.stderr.strip()[:100]}")
|
||||
except Exception as e:
|
||||
status_lines.append(f"✗ Error testing module: {str(e)}")
|
||||
|
||||
# 3. Check Claude Code MCP config (handle safely)
|
||||
try:
|
||||
# Windows requires shell=True to execute .cmd files
|
||||
claude_result = subprocess.run(
|
||||
"claude mcp list",
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=30,
|
||||
shell=True
|
||||
)
|
||||
|
||||
if claude_result.returncode == 0:
|
||||
if "comfyui-image-generator" in claude_result.stdout:
|
||||
status_lines.append("✓ Claude Code MCP is configured")
|
||||
else:
|
||||
status_lines.append("ℹ Claude Code MCP is not configured")
|
||||
else:
|
||||
status_lines.append("ℹ Claude Code CLI error")
|
||||
except FileNotFoundError:
|
||||
status_lines.append("ℹ Claude Code CLI not found (optional)")
|
||||
except subprocess.TimeoutExpired:
|
||||
status_lines.append("ℹ Claude Code CLI timeout (network may be slow, but MCP might still work)")
|
||||
except Exception as e:
|
||||
status_lines.append(f"ℹ Cannot check Claude Code status (but MCP might still work)")
|
||||
|
||||
return "\n".join(status_lines)
|
||||
|
||||
|
||||
# Node mappings
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"MCPConfigGenerator": MCPConfigGenerator
|
||||
}
|
||||
|
||||
# Node display names
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"MCPConfigGenerator": "MCP Config Generator"
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
[project]
|
||||
name = "comfy-mcp-server"
|
||||
version = "0.0.1"
|
||||
description = "Simple MCP Server for ComfyUI text to image Workflow - ComfyUI Node Integration , Based on lalanikarim/comfy-mcp-server work"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
license = "MIT"
|
||||
license-files = ["LICENSE"]
|
||||
|
||||
dependencies = [
|
||||
"langchain>=0.3.19",
|
||||
"langchain-ollama>=0.2.3",
|
||||
"mcp[cli]>=1.2.1",
|
||||
"Pillow>=10.0.0",
|
||||
]
|
||||
|
||||
authors = [
|
||||
{name = "dseditor", email = "comfyuimixer1231@gmail.com"},
|
||||
]
|
||||
|
||||
maintainers = [
|
||||
{name = "dseditor", email = "comfyuimixer1231@gmail.com"},
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/dseditor/Comfy-MCP/"
|
||||
Repository = "https://github.com/dseditor/Comfy-MCP/"
|
||||
Documentation = "https://github.com/dseditor/Comfy-MCP/wiki"
|
||||
"Bug Tracker" = "https://github.com/dseditor/Comfy-MCP/issues"
|
||||
"Original Project" = "https://github.com/lalanikarim/comfy-mcp-server"
|
||||
|
||||
# MCP Server entry point - required for `python -m comfy_mcp_server`
|
||||
[project.scripts]
|
||||
comfy-mcp-server = "comfy_mcp_server:run_server"
|
||||
|
||||
# Build system configuration - required for pip install
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
# Package source location - tells pip where to find the code
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src/comfy_mcp_server"]
|
||||
|
||||
# ComfyUI Registry configuration
|
||||
[tool.comfy]
|
||||
PublisherId = "dseditor"
|
||||
DisplayName = "ComfyUI MCP Server"
|
||||
Icon = ""
|
||||
includes = []
|
||||
# requires-comfyui = ">=1.0.0"
|
||||
@@ -0,0 +1,5 @@
|
||||
langchain>=0.3.19
|
||||
langchain-ollama>=0.2.3
|
||||
mcp[cli]>=1.2.1
|
||||
Pillow>=10.0.0
|
||||
hatchling
|
||||
@@ -0,0 +1,203 @@
|
||||
from mcp.server.fastmcp import FastMCP, Context
|
||||
from mcp.types import ImageContent, TextContent
|
||||
import json
|
||||
import urllib
|
||||
from urllib import request
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
|
||||
# Suppress warnings to avoid polluting MCP stdout
|
||||
warnings.filterwarnings("ignore")
|
||||
|
||||
# Ensure all logging goes to stderr, not stdout
|
||||
import logging
|
||||
logging.basicConfig(stream=sys.stderr, level=logging.WARNING)
|
||||
|
||||
from langchain_ollama.chat_models import ChatOllama
|
||||
from langchain_core.prompts import PromptTemplate
|
||||
from langchain_core.output_parsers import StrOutputParser
|
||||
|
||||
mcp = FastMCP("Comfy MCP Server")
|
||||
|
||||
host = os.environ.get("COMFY_URL")
|
||||
override_host = os.environ.get("COMFY_URL_EXTERNAL")
|
||||
if override_host is None:
|
||||
override_host = host
|
||||
workflow = os.environ.get("COMFY_WORKFLOW_JSON_FILE")
|
||||
|
||||
prompt_template = json.load(
|
||||
open(workflow, "r")
|
||||
) if workflow is not None else None
|
||||
|
||||
prompt_node_id = os.environ.get("PROMPT_NODE_ID")
|
||||
output_node_id = os.environ.get("OUTPUT_NODE_ID")
|
||||
output_mode = os.environ.get("OUTPUT_MODE")
|
||||
|
||||
# Configurable timeout settings
|
||||
max_poll_attempts = int(os.environ.get("COMFY_MAX_POLL_ATTEMPTS", "60")) # Default 60 attempts
|
||||
poll_interval = int(os.environ.get("COMFY_POLL_INTERVAL", "2")) # Default 2 seconds
|
||||
|
||||
ollama_api_base = os.environ.get("OLLAMA_API_BASE")
|
||||
prompt_llm = os.environ.get("PROMPT_LLM")
|
||||
|
||||
|
||||
def get_file_url(server: str, url_values: str) -> str:
|
||||
return f"{server}/view?{url_values}"
|
||||
|
||||
|
||||
if ollama_api_base is not None and prompt_llm is not None:
|
||||
@mcp.tool()
|
||||
def generate_prompt(topic: str, ctx: Context) -> str:
|
||||
"""Write an image generation prompt for a provided topic"""
|
||||
|
||||
model = ChatOllama(base_url=ollama_api_base, model=prompt_llm)
|
||||
prompt = PromptTemplate.from_template("""You are an AI Image Generation Prompt Assistant.
|
||||
Your job is to review the topic provided by the user for an image generation task and create
|
||||
an appropriate prompt from it. Repond with a single prompt. Don't ask for feedback about the prompt.
|
||||
|
||||
Topic: {topic}
|
||||
Prompt: """)
|
||||
chain = prompt | model | StrOutputParser()
|
||||
response = chain.invoke({"topic": topic})
|
||||
return response
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def generate_image(prompt: str, ctx: Context) -> list[TextContent | ImageContent]:
|
||||
"""Generate an image using ComfyUI workflow"""
|
||||
|
||||
prompt_template[prompt_node_id]['inputs']['text'] = prompt
|
||||
p = {"prompt": prompt_template}
|
||||
data = json.dumps(p).encode('utf-8')
|
||||
req = request.Request(f"{host}/prompt", data)
|
||||
resp = request.urlopen(req)
|
||||
response_ready = False
|
||||
override_file_url = None
|
||||
image_data = None
|
||||
|
||||
if resp.status == 200:
|
||||
ctx.info("Submitted prompt")
|
||||
resp_data = json.loads(resp.read())
|
||||
prompt_id = resp_data["prompt_id"]
|
||||
|
||||
for t in range(0, max_poll_attempts):
|
||||
history_req = request.Request(
|
||||
f"{host}/history/{prompt_id}")
|
||||
history_resp = request.urlopen(history_req)
|
||||
if history_resp.status == 200:
|
||||
ctx.info(f"Checking status... (attempt {t+1}/{max_poll_attempts})")
|
||||
history_resp_data = json.loads(history_resp.read())
|
||||
if prompt_id in history_resp_data:
|
||||
status = (
|
||||
history_resp_data[prompt_id]['status']['completed']
|
||||
)
|
||||
if status:
|
||||
output_data = (
|
||||
history_resp_data[prompt_id]
|
||||
['outputs'][output_node_id]['images'][0]
|
||||
)
|
||||
url_values = urllib.parse.urlencode(output_data)
|
||||
file_url = get_file_url(host, url_values)
|
||||
override_file_url = get_file_url(
|
||||
override_host, url_values)
|
||||
file_req = request.Request(file_url)
|
||||
file_resp = request.urlopen(file_req)
|
||||
if file_resp.status == 200:
|
||||
ctx.info("Image generated")
|
||||
image_data = file_resp.read()
|
||||
response_ready = True
|
||||
break
|
||||
else:
|
||||
time.sleep(poll_interval)
|
||||
else:
|
||||
time.sleep(poll_interval)
|
||||
|
||||
if response_ready and override_file_url and image_data:
|
||||
import base64
|
||||
|
||||
# Save image locally for Claude Code to access
|
||||
local_save_dir = os.environ.get("COMFY_LOCAL_SAVE_DIR", "./generated_images")
|
||||
os.makedirs(local_save_dir, exist_ok=True)
|
||||
|
||||
# Generate filename with timestamp
|
||||
from datetime import datetime
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
|
||||
# Convert PNG to WebP for smaller file size
|
||||
try:
|
||||
# Open the PNG image
|
||||
png_image = Image.open(BytesIO(image_data))
|
||||
|
||||
# Save as WebP locally
|
||||
local_filename = f"image_{timestamp}.webp"
|
||||
local_path = os.path.abspath(os.path.join(local_save_dir, local_filename))
|
||||
png_image.save(local_path, format='WebP', quality=85, method=6)
|
||||
|
||||
# Calculate size reduction
|
||||
original_size_kb = len(image_data) / 1024
|
||||
webp_size_kb = os.path.getsize(local_path) / 1024
|
||||
reduction_percent = ((original_size_kb - webp_size_kb) / original_size_kb) * 100
|
||||
|
||||
ctx.info(f"Saved to {local_path}: {original_size_kb:.1f}KB -> {webp_size_kb:.1f}KB ({reduction_percent:.1f}% reduction)")
|
||||
|
||||
return [
|
||||
TextContent(
|
||||
type="text",
|
||||
text=f"Image generated successfully!\n\n"
|
||||
f"Remote URL: {override_file_url}\n"
|
||||
f"Local Path: {local_path}\n"
|
||||
f"Size: {original_size_kb:.1f}KB (PNG) -> {webp_size_kb:.1f}KB (WebP)\n"
|
||||
f"Compression: {reduction_percent:.1f}% smaller\n\n"
|
||||
f"You can read the image from the local path using the Read tool."
|
||||
)
|
||||
]
|
||||
except Exception as e:
|
||||
ctx.error(f"WebP conversion failed: {e}, falling back to PNG")
|
||||
# Fallback to PNG if WebP conversion fails
|
||||
local_filename = f"image_{timestamp}.png"
|
||||
local_path = os.path.abspath(os.path.join(local_save_dir, local_filename))
|
||||
with open(local_path, 'wb') as f:
|
||||
f.write(image_data)
|
||||
|
||||
image_size_kb = len(image_data) / 1024
|
||||
return [
|
||||
TextContent(
|
||||
type="text",
|
||||
text=f"Image generated successfully!\n\n"
|
||||
f"Remote URL: {override_file_url}\n"
|
||||
f"Local Path: {local_path}\n"
|
||||
f"Size: {image_size_kb:.1f}KB (PNG)\n\n"
|
||||
f"You can read the image from the local path using the Read tool."
|
||||
)
|
||||
]
|
||||
else:
|
||||
return [TextContent(type="text", text="Failed to generate image. Please check server logs.")]
|
||||
|
||||
|
||||
def run_server():
|
||||
errors = []
|
||||
if host is None:
|
||||
errors.append("- COMFY_URL environment variable not set")
|
||||
if workflow is None:
|
||||
errors.append(
|
||||
"- COMFY_WORKFLOW_JSON_FILE environment variable not set")
|
||||
if prompt_node_id is None:
|
||||
errors.append("- PROMPT_NODE_ID environment variable not set")
|
||||
if output_node_id is None:
|
||||
errors.append("- OUTPUT_NODE_ID environment variable not set")
|
||||
|
||||
if len(errors) > 0:
|
||||
import sys
|
||||
errors = ["Failed to start Comfy MCP Server:"] + errors
|
||||
sys.stderr.write("\n".join(errors) + "\n")
|
||||
sys.exit(1)
|
||||
else:
|
||||
mcp.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_server()
|
||||
@@ -0,0 +1,4 @@
|
||||
from comfy_mcp_server import run_server
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_server()
|
||||
@@ -0,0 +1,140 @@
|
||||
{
|
||||
"9": {
|
||||
"inputs": {
|
||||
"filename_prefix": "z-image",
|
||||
"images": [
|
||||
"43",
|
||||
0
|
||||
]
|
||||
},
|
||||
"class_type": "SaveImage",
|
||||
"_meta": {
|
||||
"title": "Save Image"
|
||||
}
|
||||
},
|
||||
"39": {
|
||||
"inputs": {
|
||||
"clip_name": "qwen_3_4b.safetensors",
|
||||
"type": "lumina2",
|
||||
"device": "default"
|
||||
},
|
||||
"class_type": "CLIPLoader",
|
||||
"_meta": {
|
||||
"title": "Load CLIP"
|
||||
}
|
||||
},
|
||||
"40": {
|
||||
"inputs": {
|
||||
"vae_name": "ae.safetensors"
|
||||
},
|
||||
"class_type": "VAELoader",
|
||||
"_meta": {
|
||||
"title": "Load VAE"
|
||||
}
|
||||
},
|
||||
"41": {
|
||||
"inputs": {
|
||||
"width": 1024,
|
||||
"height": 1024,
|
||||
"batch_size": 1
|
||||
},
|
||||
"class_type": "EmptySD3LatentImage",
|
||||
"_meta": {
|
||||
"title": "EmptySD3LatentImage"
|
||||
}
|
||||
},
|
||||
"42": {
|
||||
"inputs": {
|
||||
"conditioning": [
|
||||
"45",
|
||||
0
|
||||
]
|
||||
},
|
||||
"class_type": "ConditioningZeroOut",
|
||||
"_meta": {
|
||||
"title": "ConditioningZeroOut"
|
||||
}
|
||||
},
|
||||
"43": {
|
||||
"inputs": {
|
||||
"samples": [
|
||||
"44",
|
||||
0
|
||||
],
|
||||
"vae": [
|
||||
"40",
|
||||
0
|
||||
]
|
||||
},
|
||||
"class_type": "VAEDecode",
|
||||
"_meta": {
|
||||
"title": "VAE Decode"
|
||||
}
|
||||
},
|
||||
"44": {
|
||||
"inputs": {
|
||||
"seed": 410513707389275,
|
||||
"steps": 9,
|
||||
"cfg": 1,
|
||||
"sampler_name": "res_multistep",
|
||||
"scheduler": "simple",
|
||||
"denoise": 1,
|
||||
"model": [
|
||||
"47",
|
||||
0
|
||||
],
|
||||
"positive": [
|
||||
"45",
|
||||
0
|
||||
],
|
||||
"negative": [
|
||||
"42",
|
||||
0
|
||||
],
|
||||
"latent_image": [
|
||||
"41",
|
||||
0
|
||||
]
|
||||
},
|
||||
"class_type": "KSampler",
|
||||
"_meta": {
|
||||
"title": "KSampler"
|
||||
}
|
||||
},
|
||||
"45": {
|
||||
"inputs": {
|
||||
"text": "Latina female with thick wavy hair, harbor boats and pastel houses behind. Breezy seaside light, warm tones, cinematic close-up.",
|
||||
"clip": [
|
||||
"39",
|
||||
0
|
||||
]
|
||||
},
|
||||
"class_type": "CLIPTextEncode",
|
||||
"_meta": {
|
||||
"title": "CLIP Text Encode (Prompt)"
|
||||
}
|
||||
},
|
||||
"46": {
|
||||
"inputs": {
|
||||
"unet_name": "z-image-turbo_fp8_scaled_e4m3fn_KJ.safetensors",
|
||||
"weight_dtype": "default"
|
||||
},
|
||||
"class_type": "UNETLoader",
|
||||
"_meta": {
|
||||
"title": "Load Diffusion Model"
|
||||
}
|
||||
},
|
||||
"47": {
|
||||
"inputs": {
|
||||
"shift": 3,
|
||||
"model": [
|
||||
"46",
|
||||
0
|
||||
]
|
||||
},
|
||||
"class_type": "ModelSamplingAuraFlow",
|
||||
"_meta": {
|
||||
"title": "ModelSamplingAuraFlow"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user