Skip to main content
Glama

comfyui-mcp-secure

A secure MCP (Model Context Protocol) server for ComfyUI. Enables AI assistants like Claude to generate images, run workflows, and manage jobs through ComfyUI — with built-in security controls that existing ComfyUI MCP servers lack.

Using Claude? This repo also ships as a Claude Code plugin — 8 /comfy:* slash commands (/comfy:gen, /comfy:workflow, /comfy:troubleshooting, …) and a PostToolUse security hook, all pre-wired to the MCP server. One command to install: claude plugin install . See Install as a Claude plugin for the full reference + worked end-to-end example.

Why this exists

Every existing ComfyUI MCP server is a thin passthrough to ComfyUI's API with no security guardrails. They allow arbitrary workflow execution (including malicious custom nodes that run eval/exec), have no input validation, no file path sanitization, no rate limiting, and no audit trail.

This server adds five security layers between the AI assistant and ComfyUI:

Layer

What it does

Workflow Inspector

Parses every workflow before execution, extracts node types, flags dangerous patterns (eval, exec, __import__, subprocess). Configurable audit-only or enforcement mode.

Path Sanitizer

Validates all filenames, subfolders, and URL path segments — blocks path traversal (../), null bytes, percent-encoded attacks, absolute paths, and disallowed file extensions.

Rate Limiter

Token-bucket rate limiting per tool category to prevent runaway loops.

Audit Logger

Structured JSON logging of every operation with automatic redaction of sensitive fields (tokens, passwords).

Selective API Surface

Only exposes safe ComfyUI endpoints. Dangerous endpoints (/userdata, /free, /users) are never proxied. /system_stats is called internally by comfyui_get_system_info but only a strict whitelist (GPU VRAM, queue counts, version) is returned.

Real-time progress tracking

When wait=True is passed to comfyui_generate_image or comfyui_run_workflow, the server connects to ComfyUI's WebSocket to track execution in real time — reporting step progress, current node, and output files when complete. If the WebSocket connection fails, it automatically falls back to HTTP polling. Use comfyui_get_progress to check status of any job at any time.

For workflow streaming, use the mode that matches your use case:

  • comfyui_run_workflow(..., wait=True) returns a summarized, tool-friendly completion response.

  • comfyui_run_workflow_stream(...) returns raw WebSocket event flow (progress, executing, executed, etc.) plus final status and outputs.

Structured output & rich schemas

Tools expose Pydantic Field constraints on input parameters (ranges, lengths, descriptions) and outputSchema for structured responses. MCP clients get:

  • Input validation: Parameter constraints like steps: 1-100, cfg: 1.0-30.0, width: 64-4096 appear in the tool's JSON schema

  • Output schemas: 26 tools return structured data with auto-generated outputSchema, enabling clients to parse responses without guessing the shape

  • Streamable HTTP transport: Optional remote transport via transport.remote.enabled using the MCP spec's recommended Streamable HTTP protocol

Related MCP server: ComfyUI MCP Server

Recent Breaking Changes (2026-05)

2.1.0 (2026-05-12) is additive — no breaking changes since 2.0.0. Adds comfyui_analyze_workflow, replaces the bespoke Ollama eval runner with an Inspect AI Task module, and introduces a Phase 5 live-execution eval. See the CHANGELOG for the full per-PR breakdown. The breaking changes below all shipped in 2.0.0.

Parameter renames — update keyword arguments (positional calls are unaffected):

  • comfyui_install_custom_node, comfyui_uninstall_custom_node, comfyui_update_custom_node: idnode_id.

  • comfyui_summarize_workflow: formatoutput_format, restricted to text or mermaid via a Pydantic Literal.

Response-shape changes — these tools now return the standard pagination envelope {items, total, offset, limit, has_more} instead of bare lists or raw dicts:

  • comfyui_list_extensions (was: list[str])

  • comfyui_list_model_folders (was: list[str])

  • comfyui_list_workflows (was: dict[package_name, list[template]]; now flattened to items: [{package, templates}])

Callers must update to read result["items"] instead of indexing the response directly. The new envelope also exposes limit and offset parameters for pagination.

Unified return envelope for workflow-submitting toolscomfyui_run_workflow, comfyui_run_workflow_stream, comfyui_generate_image, comfyui_transform_image, comfyui_inpaint_image, comfyui_upscale_image now all return a uniform dict[str, Any] regardless of wait/stream mode:

{
  "status": "submitted" | "completed" | "interrupted" | "error" | "timeout",
  "prompt_id": "<uuid>",
  "warnings": [...]             # only when the workflow inspector produced warnings
  # When wait=True or stream:
  "outputs": [...],
  "elapsed_seconds": float,
  "step" / "total_steps" / "current_node" / "queue_position": ...,
  # When stream:
  "events": [...]
}

Previously these tools returned either a free-form sentence (wait=False) or a JSON-serialized string (wait=True/stream), forcing callers to try both shapes. Callers that previously parsed the response as text — or via json.loads() for wait=True — must update to read fields directly off the dict.

Quick start

Prerequisites

  • Python 3.12+

  • uv package manager

  • A running ComfyUI instance (local or remote)

Install

Claude Code users: the fastest path is the plugin — see Install as a Claude plugin below. It wires the MCP server + slash commands + security hook in one step. The options below are for everyone else (raw MCP wiring, Docker, source installs).

Option A: From PyPI

pip install comfyui-mcp-secure

For an isolated CLI install, use one of:

uv tool install comfyui-mcp-secure
pipx install comfyui-mcp-secure

For a one-shot run without installing first:

uvx comfyui-mcp-secure --help
git clone https://github.com/hybridindie/comfyui_mcp.git
cd comfyui_mcp
uv sync

Option C: Docker (no clone required)

docker pull ghcr.io/hybridindie/comfyui_mcp:latest

Or build locally from the repo:

docker build -t comfyui-mcp-secure .

Configure

Create a minimal config for your ComfyUI instance:

mkdir -p ~/.comfyui-mcp
cat > ~/.comfyui-mcp/config.yaml << 'EOF'
comfyui:
  url: "http://127.0.0.1:8188"
EOF

For a remote server:

cat > ~/.comfyui-mcp/config.yaml << 'EOF'
comfyui:
  url: "https://your-gpu-server:8188"
EOF

Add to Claude Code / Claude Desktop

The MCP server communicates over stdio. Add one of the following configurations depending on how you installed.

From source (uv):

{
  "mcpServers": {
    "comfyui": {
      "command": "uv",
      "args": ["--directory", "/path/to/comfyui_mcp", "run", "comfyui-mcp-secure"]
    }
  }
}

From PyPI / pipx / uv tool install:

{
  "mcpServers": {
    "comfyui": {
      "command": "comfyui-mcp-secure"
    }
  }
}

From PyPI without a persistent install (uvx):

{
  "mcpServers": {
    "comfyui": {
      "command": "uvx",
      "args": ["comfyui-mcp-secure"]
    }
  }
}

Docker (GitHub Container Registry):

{
  "mcpServers": {
    "comfyui": {
      "command": "docker",
      "args": [
        "run", "--rm", "-i",
        "-e", "COMFYUI_URL=http://host.docker.internal:8188",
        "-v", "~/.comfyui-mcp:/home/app/.comfyui-mcp:ro",
        "ghcr.io/hybridindie/comfyui_mcp:latest"
      ]
    }
  }
}

Note: host.docker.internal routes to your host machine from inside Docker. If ComfyUI runs on a remote server, replace with that server's URL. On Linux, you may need to add --add-host=host.docker.internal:host-gateway.

Install as a Claude plugin (from this repo)

This repository ships as a complete Claude Code plugin — manifest at .claude-plugin/plugin.json. Two install paths:

# Direct from GitHub (recommended — pulls the published tag):
claude plugin install https://github.com/hybridindie/comfyui_mcp

# Or from a local clone (for development or unreleased changes):
claude plugin install .

Plugin-related files in this repo:

  • .claude-plugin/plugin.json (plugin manifest — name, version, license, homepage)

  • .mcp.json (MCP server bootstrap config)

  • hooks/ (security warning hook)

  • skills/ (slash-command skills)

If you use the included .mcp.json, set both internal and optional external ComfyUI URLs as needed:

{
  "mcpServers": {
    "comfyui": {
      "command": "uvx",
      "args": ["comfyui-mcp-secure"],
      "env": {
        "COMFYUI_URL": "http://comfyui:8188",
        "COMFYUI_EXTERNAL_URL": "https://comfyui.example.com"
      }
    }
  }
}

What the plugin gives you

Three cooperating layers, used together:

  1. MCP tool surface — 47 tools exposing ComfyUI's workflow / generation / discovery / security API. The full table is in the Tools section below — these are the lowest-level primitives, available to any model connected to the server.

  2. Slash-command skills under /comfy:* — pre-authored recipes that wrap common multi-tool flows so a user doesn't have to choreograph the calls themselves. Skills load lazily; the two "knowledge" skills (workflows, troubleshooting) get auto-applied by Claude when the conversation matches their topic.

  3. PostToolUse security hook — fires after the MCP tools that touch dangerous surface and surfaces a one-line warning if the workflow inspector or node auditor flagged anything.

Slash-command reference

Command

What it does

/comfy:gen <prompt>

Generate an image. Picks a model via comfyui_list_models, calls comfyui_generate_image(wait=True), fetches the result via comfyui_get_image.

/comfy:workflow <description>

Build a workflow from a built-in template, validate it, then offer to run or modify.

/comfy:workflows

Knowledge skill — auto-applied when the conversation involves building/modifying workflows. Covers workflow JSON format, common node chains (txt2img/img2img/ControlNet/LoRA), and the key node reference.

/comfy:status

Show queue state (running + pending jobs).

/comfy:progress <prompt_id>

Per-job execution progress (current node, step X of Y, status).

/comfy:history

Recent completions with prompt IDs and output filenames.

/comfy:models [folder]

List models in a folder type (defaults to checkpoints).

/comfy:troubleshooting

Knowledge skill — auto-applied when users report connection, model, workflow, or security errors. Covers connection failures, model-not-found, workflow execution failures, queue-stuck, security warnings, and the two upstream-plugin (ComfyUI-Manager, ComfyUI-Model-Manager) setup issues.

Security hook

A single PostToolUse hook (hooks/security-warning.sh, wired via hooks/hooks.json) fires after these MCP tools:

  • comfyui_audit_dangerous_nodes

  • comfyui_install_custom_node

  • comfyui_update_custom_node

  • comfyui_run_workflow

  • comfyui_generate_image

It scans the tool output for WorkflowInspector markers ("Dangerous node type", "Suspicious input") and NodeAuditor results with dangerous.count > 0. If anything matches, it prints a one-line warning so Claude sees it and asks the user to confirm before proceeding. The hook always exits 0 — it never blocks; it just adds a heads-up.

End-to-end example

A user types /comfy:gen a yellow apple, photorealistic, 4k. The layers cooperate:

  1. The gen skill parses the prompt and applies defaults (512×512, 20 steps, cfg 7.0).

  2. It calls comfyui_list_models(folder="checkpoints"), picks an available model from the paginated items list, and confirms with the user if ambiguous.

  3. It calls comfyui_generate_image(prompt=..., model=..., wait=True).

  4. Server-side, the MCP tool runs WorkflowInspector.inspect() on the workflow before submitting it to ComfyUI. With a clean built-in workflow there are no warnings.

  5. ComfyUI executes; the tool blocks until the unified envelope comes back with status="completed".

  6. The PostToolUse hook fires, checks the tool output for the threat patterns, finds none, and exits silently.

  7. The skill reads result["outputs"][0] (a {node_id, filename, subfolder} dict), calls comfyui_get_image(filename=..., subfolder="output", preview_format="webp", preview_quality=80) for a cheap thumbnail, and presents the image inline.

Contrast that with running a user-supplied custom workflow that contains an Exec-class node: step 4's inspector emits warnings: ["Dangerous node type: Exec..."], the hook detects the pattern in step 6, and surfaces:

SECURITY: Dangerous node patterns detected. Review the audit results above before proceeding.

Claude sees this in its context and asks the user to confirm before continuing — exactly the audit-mode-default behavior the project ships with.

Verify

# From source
uv run python -c "from comfyui_mcp.server import mcp; print(f'Server {mcp.name!r} ready')"

# Docker
docker run --rm ghcr.io/hybridindie/comfyui_mcp:latest --help

Tools

Generation & Workflows

Tool

Description

comfyui_generate_image

Text-to-image using a built-in workflow. Params: prompt, negative_prompt, width, height, steps, cfg, model. Set wait=True to block until complete and return outputs.

comfyui_transform_image

Image-to-image transformation. Params: image (filename), prompt, negative_prompt, strength (0.0-1.0), steps, cfg, model. Input must be uploaded via comfyui_upload_image first.

comfyui_inpaint_image

Inpaint masked regions of an image. Params: image, mask (filenames), prompt, negative_prompt, strength, steps, cfg, model. Both files must be uploaded first.

comfyui_upscale_image

Upscale an image using a model-based upscaler. Params: image (filename), upscale_model (default: RealESRGAN_x4plus.pth).

comfyui_run_workflow

Submit arbitrary ComfyUI workflow JSON. Inspected for dangerous nodes before execution. Set wait=True to block until complete and return outputs.

comfyui_run_workflow_stream

Submit workflow JSON and capture ComfyUI websocket stream events (progress, executing, executed, etc.) until terminal status, returning events plus final outputs/status.

comfyui_summarize_workflow

Summarize a workflow's structure, data flow, models, and parameters. Supports output_format="text" (default) or output_format="mermaid" for diagram markup.

comfyui_create_workflow

Create a workflow from templates including txt2img/img2img/upscale/inpaint, txt2vid_animatediff/txt2vid_wan, controlnet_canny/controlnet_depth/controlnet_openpose, ip_adapter, lora_stack, face_restore, flux_txt2img, and sdxl_txt2img.

comfyui_modify_workflow

Apply batch operations (add_node, remove_node, set_input, connect, disconnect) to a workflow.

comfyui_analyze_workflow

Return a structured analysis of a workflow as a dict (node_count, class_types, flow, models, parameters, pipeline, prompt_nodes, negative_nodes). Use this when you want to read fields like pipeline programmatically; use comfyui_summarize_workflow for a human-readable text or Mermaid rendering.

comfyui_validate_workflow

Validate workflow structure, server compatibility, and security.

Job Management

Tool

Description

comfyui_get_queue

Get current execution queue state.

comfyui_list_jobs

List jobs across queue + history with status filter, sorting, and pagination.

comfyui_get_job

Look up a single job (queued/running/finished) by prompt_id.

comfyui_cancel_job

Cancel a running or queued job.

comfyui_interrupt

Interrupt the running workflow (global, or targeted via optional prompt_id).

comfyui_get_queue_status

Get detailed queue status including running and pending prompts.

comfyui_clear_queue

Clear pending and/or running items from the queue.

comfyui_get_progress

Get execution progress for a workflow by prompt_id. Returns status, queue position, and outputs.

Discovery

Tool

Description

comfyui_list_models

List available models by folder (checkpoints, loras, vae, etc.).

comfyui_list_nodes

List all available node types.

comfyui_get_node_info

Get detailed info about a specific node type.

comfyui_list_workflows

List saved workflow templates.

comfyui_list_extensions

List available ComfyUI extensions.

comfyui_get_server_features

Get ComfyUI server features and capabilities.

comfyui_list_model_folders

List available model folder types.

comfyui_get_model_metadata

Get metadata for a specific model file.

comfyui_audit_dangerous_nodes

Scan all installed nodes to identify potentially dangerous ones.

comfyui_get_system_info

Sanitized GPU VRAM, queue depth, and ComfyUI version (whitelist-filtered from /system_stats).

Custom Node Management

Tool

Description

comfyui_search_custom_nodes

Search ComfyUI Manager registry custom node packs by name/description/author.

comfyui_install_custom_node

Queue install for a custom node pack by node_id; optional restart and post-install security audit.

comfyui_uninstall_custom_node

Queue uninstall for a custom node pack by node_id; optional restart.

comfyui_update_custom_node

Queue update for a custom node pack by node_id; optional restart and post-update security audit.

comfyui_get_custom_node_status

Get custom node queue status (pending/running/completed).

Requires: ComfyUI-Manager available on the target ComfyUI server. If unavailable, node-management tools return a helpful error.

History

Tool

Description

comfyui_get_history

Browse execution history (read-only). Server-side paging via limit (1-100, default 25) and offset (no upper bound). Returns {items, count, offset, limit, has_more, total}; total is only set on the last page (the upstream endpoint does not expose a count).

Model Search & Download

Tool

Description

comfyui_search_models

Search HuggingFace or CivitAI for models. Returns name, download URL, size, and stats.

comfyui_download_model

Download a model via ComfyUI-Model-Manager. URL and extension validated.

comfyui_get_download_tasks

Check status of active model downloads (progress, speed, status).

comfyui_cancel_download

Cancel or clean up a model download task.

comfyui_get_model_presets

Return recommended sampler/scheduler/steps/CFG defaults for a model family.

comfyui_get_prompting_guide

Return model-family prompt engineering tips and negative prompt guidance.

Requires: ComfyUI-Model-Manager installed in your ComfyUI instance. Download tools are gated behind lazy detection — if Model Manager is not installed, these tools return a helpful error message. comfyui_search_models works without it.

Model Manager download lifecycle

Model Manager tracks downloads as tasks. After a download completes, the task remains in the list with status: "pause" and progress: 100 — this is upstream Model Manager behavior. Call comfyui_cancel_download to remove it:

comfyui_download_model(url="...", folder="checkpoints", filename="model.safetensors")
→ { "taskId": "abc123", ... }

comfyui_get_download_tasks()
→ { "tasks": [{ "taskId": "abc123", "status": "pause", "progress": 100, ... }] }

comfyui_cancel_download(task_id="abc123")
→ { "success": true, ... }

The comfyui_download_model tool always sends a previewFile field (required by Model Manager even when empty). Omitting it causes the server to silently fail and delete the task.

File Operations

Tool

Description

comfyui_upload_image

Upload a base64-encoded image to ComfyUI. Path-sanitized. Params: filename, image_data, subfolder, destination="input"|"output"|"temp" (default input), overwrite (default False — ComfyUI auto-renames duplicates).

comfyui_get_image

Download a generated image. response_format="data_uri" (default) returns inline base64; response_format="url" returns a direct /view URL. With data_uri, optional preview_format="webp"|"jpeg" + preview_quality=1-100 request a server-rendered thumbnail (smaller payload, lossy). Optional base_url_override can override URL host per call. Path-sanitized.

comfyui_list_outputs

List generated output filenames from history.

comfyui_upload_mask

Upload a mask image to ComfyUI. Path-sanitized. Params: filename, mask_data, original_image, subfolder, original_subfolder, destination="input"|"output"|"temp" (default input), overwrite (default False — ComfyUI auto-renames duplicates).

comfyui_get_workflow_from_image

Extract embedded workflow and prompt metadata from a ComfyUI-generated PNG.

Deliberately not exposed

These ComfyUI endpoints are never proxied due to security risks:

  • /userdata — arbitrary file read/write

  • /free — unload models (DoS vector)

  • /users — user management

  • /history POST — delete history

/system_stats is called internally only by comfyui_get_system_info, which applies a strict whitelist and never forwards the raw response.

Configuration

Config file: ~/.comfyui-mcp/config.yaml

comfyui:
  url: "http://127.0.0.1:8188"   # ComfyUI server URL
  external_url: null               # Optional public URL for get_image URL responses
                                   # If unset, URL responses use comfyui.url
  tls_verify: true                 # TLS certificate verification
  timeout_connect: 30              # Connection timeout (seconds)
  timeout_read: 300                # Read timeout (seconds)

security:
  mode: "audit"                    # "audit" (log only) or "enforce" (block unapproved)
  allowed_nodes: []                # Enforce mode: only these nodes can run
  dangerous_nodes:                 # Always flagged in audit log (showing subset)
    - "Terminal"                   # comfyui-colab: shell via subprocess
    - "interpreter_tool"           # comfyui_LLM_party: exec/eval
    - "KY_Eval_Python"             # ComfyUI-KYNode: exec Python
    - "Image Send HTTP"            # was-node-suite: arbitrary HTTP
    - "Load Text File"             # was-node-suite: reads arbitrary files
    - "Save Text File"             # was-node-suite: writes arbitrary files
    # ... see config.py _DEFAULT_DANGEROUS_NODES for the full list
  max_upload_size_mb: 50
  allowed_extensions:
    - ".png"
    - ".jpg"
    - ".jpeg"
    - ".webp"
    - ".gif"
    - ".json"

rate_limits:                       # Requests per minute
  workflow: 10
  generation: 10
  file_ops: 30
  read_only: 60

model_search:
  huggingface_token: ""            # Optional; needed for gated/private HF models
  civitai_api_key: ""              # Optional; needed for auth-only CivitAI access
  max_search_results: 10

logging:
  audit_file: "~/.comfyui-mcp/audit.log"

transport:
  remote:
    enabled: false
    host: "127.0.0.1"
    port: 8080

When transport.remote.enabled is true, the server starts in Streamable HTTP mode and binds to transport.remote.host and transport.remote.port. Keep this bound to localhost unless you are running behind authenticated TLS reverse proxy infrastructure.

Environment variables

Environment variables override config file values:

Variable

Overrides

COMFYUI_URL

comfyui.url

COMFYUI_EXTERNAL_URL

comfyui.external_url

COMFYUI_TLS_VERIFY

comfyui.tls_verify

COMFYUI_TIMEOUT_CONNECT

comfyui.timeout_connect

COMFYUI_TIMEOUT_READ

comfyui.timeout_read

COMFYUI_SECURITY_MODE

security.mode

COMFYUI_AUDIT_FILE

logging.audit_file

COMFYUI_HUGGINGFACE_TOKEN

model_search.huggingface_token

COMFYUI_CIVITAI_API_KEY

model_search.civitai_api_key

COMFYUI_MAX_SEARCH_RESULTS

model_search.max_search_results

COMFYUI_ALLOWED_DOWNLOAD_DOMAINS

security.allowed_download_domains

HuggingFace and CivitAI API keys

comfyui_search_models and comfyui_download_model work without API keys for many public models. Add keys when you need access to gated/private resources or higher provider limits.

Set them in config:

model_search:
  huggingface_token: "hf_xxx"
  civitai_api_key: "xxx"

Or via environment variables:

export COMFYUI_HUGGINGFACE_TOKEN="hf_xxx"
export COMFYUI_CIVITAI_API_KEY="xxx"

Security notes:

  • Prefer environment variables in production so secrets do not live in files committed to git.

  • Audit logs redact sensitive fields (token, api_key, etc.), but avoid printing secrets in shell history when possible.

Security modes

Audit mode (default)

Every workflow is inspected and logged, but nothing is blocked. Use this during development to understand what nodes your workflows use.

security:
  mode: "audit"

Audit log entries look like:

{
  "timestamp": "2026-02-25T14:30:00+00:00",
  "tool": "run_workflow",
  "action": "inspected",
  "nodes_used": ["KSampler", "CLIPTextEncode", "VAEDecode", "SaveImage"],
  "warnings": []
}

When a dangerous node is detected, warnings are included in the tool response:

Workflow submitted. prompt_id: abc123

⚠️ Warnings detected:
  - Dangerous node type: ExecutePython
  - Suspicious input in node 5 (ExecutePython), field 'code'

The MCP instructions tell the LLM to inform users and ask for confirmation before proceeding when warnings are present.

Building your dangerous node list

Use the comfyui_audit_dangerous_nodes tool to scan your ComfyUI installation for potentially dangerous nodes:

Tool

Description

comfyui_audit_dangerous_nodes

Scans all installed nodes and returns dangerous/suspicious ones with reasons

Run this once to see what dangerous nodes are installed:

comfyui_audit_dangerous_nodes() → {
  "total_nodes": 456,
  "dangerous": {
    "count": 12,
    "nodes": [
      {"class": "ExecutePython", "reason": "Name matches pattern: \\bexec\\b"},
      {"class": "RunPython", "reason": "Name matches pattern: \\brunpython\\b"},
      {"class": "ShellCommand", "reason": "Name matches pattern: \\bshell\\b"}
    ]
  },
  "suspicious": {...}
}

Add these to your config:

security:
  mode: "audit"
  dangerous_nodes:
    - "ExecutePython"      # from audit_dangerous_nodes
    - "RunPython"
    - "ShellCommand"
    # ... other nodes found by audit

Enforce mode

Only explicitly approved nodes can run. Any workflow containing an unapproved node is rejected.

security:
  mode: "enforce"
  allowed_nodes:
    - "KSampler"
    - "CheckpointLoaderSimple"
    - "CLIPTextEncode"
    - "VAEDecode"
    - "EmptyLatentImage"
    - "SaveImage"
    - "LoadImage"
    - "LoraLoader"

Tip: Use comfyui_audit_dangerous_nodes to identify dangerous nodes, run workflows in audit mode to see which nodes you use, then switch to enforce mode with that allowlist.

Audit log

All tool invocations are logged as JSON lines to ~/.comfyui-mcp/audit.log:

# Watch the audit log in real time
tail -f ~/.comfyui-mcp/audit.log | python -m json.tool

# Find all workflows that used dangerous nodes
grep '"warnings":\[' ~/.comfyui-mcp/audit.log | grep -v '"warnings":\[\]'

Sensitive fields (token, password, secret, api_key, authorization) are automatically redacted from log entries.

Security

Threat model

Threat

Impact

Mitigation

Arbitrary code execution via workflow nodes

Critical

Workflow inspector (audit/enforce mode)

Path traversal via file operations

High

Path sanitizer blocks .., null bytes, encoded attacks, absolute paths

Denial of service via request flooding

Medium

Token-bucket rate limiter per tool category

Credential leakage in logs

Medium

Automatic redaction of token, password, secret, api_key, authorization

Information disclosure via API

Low

Dangerous endpoints (/userdata, /free) never proxied; /system_stats whitelist-filtered by comfyui_get_system_info

MITM on ComfyUI connection

Medium

Configurable TLS verification

Security controls by component

Workflow Inspector (security/inspector.py)

  • Parses workflow JSON, extracts node types, checks against configurable blocklist

  • Recursive pattern matching for __import__(), eval(), exec(), os.system(), subprocess in all input values (including nested dicts/lists)

  • Audit mode: logs warnings, allows execution. Enforce mode: blocks unapproved nodes

  • Limitation: static blocklist can be bypassed with obfuscation or unknown custom nodes

Path Sanitizer (security/sanitizer.py)

  • Validates filenames, subfolders, and URL path segments: blocks path traversal, null bytes, absolute paths, control characters

  • URL path segment validation on discovery tools (comfyui_list_models, comfyui_get_model_metadata) prevents folder/filename injection

  • Allowlist-based extension filtering (default: .png, .jpg, .jpeg, .webp, .gif, .json)

  • Handles percent-encoded inputs (URL decoding before validation)

  • Enforces max upload size (default 50MB), max filename length (255 chars)

Rate Limiter (security/rate_limit.py)

  • Token-bucket per tool category: workflow (10/min), generation (10/min), file_ops (30/min), read_only (60/min)

  • In-memory only (resets on restart, no distributed support)

HTTP Client (client.py)

  • Configurable TLS verification, connect/read timeouts

  • Retries on connection errors with backoff (3 retries default). HTTP 4xx/5xx errors raised immediately (no retry)

WebSocket Progress (progress.py)

  • On-demand WebSocket connections for real-time execution tracking (step progress, current node, outputs)

  • Automatic HTTP polling fallback if WebSocket connection fails

  • TLS/SSL passthrough for secure ComfyUI connections

  • Per-prompt event filtering (ignores events from other concurrent jobs)

Configuration (config.py)

  • yaml.safe_load only, env var overrides limited to specific keys, Pydantic type validation

Production deployment

For production, run behind a reverse proxy (nginx, Traefik) to add TLS termination, authentication, and CSP headers. No PII is collected. No external telemetry.

Architecture

flowchart TB
    subgraph Client["LLM Client"]
        MC[Claude / AI Assistant]
    end

    subgraph MCP["ComfyUI MCP Server"]
        CONFIG[Config<br/>YAML/env]
        AL[Audit Logger<br/>JSON logs]

        subgraph Security["Security Layers"]
            WI[Workflow Inspector<br/>Dangerous nodes<br/>Suspicious input]
            PS[Path Sanitizer<br/>Traversal block<br/>Extension filter]
            RL[Rate Limiter<br/>Token-bucket]
        end

        subgraph Tools["Tool Groups"]
            TG[generation.py<br/>jobs.py<br/>discovery.py<br/>history.py<br/>files.py]
        end

        API[ComfyUI Client<br/>httpx]
        WS[WebSocket Progress<br/>websockets]
    end

    subgraph ComfyUI["ComfyUI Server"]
        CS[REST API<br/>port 8188]
        CWS[WebSocket<br/>/ws]
    end

    MC <--MCP--> MCP
    CONFIG --> MCP
    AL --> MCP

    MCP --> Security
    Security --> Tools
    Tools --> API
    Tools --> WS
    API --httpx--> CS
    WS --websockets--> CWS

Components

Component

File

Responsibility

Server

server.py

Entry point, wires components, registers tools

Config

config.py

Pydantic settings, YAML loading, env overrides

Client

client.py

Async HTTP client for ComfyUI REST API

Progress

progress.py

WebSocket progress tracking with HTTP polling fallback

Audit

audit.py

Structured JSON logging with redaction

Workflow Inspector

security/inspector.py

Node type detection, dangerous pattern matching

Node Auditor

security/node_auditor.py

Scans installed nodes for dangerous patterns

Path Sanitizer

security/sanitizer.py

Path traversal, extension filtering

Rate Limiter

security/rate_limit.py

Token-bucket per tool category

Download Validator

security/download_validator.py

URL domain/path and extension validation for downloads

Model Checker

security/model_checker.py

Proactive missing model detection in workflows

Model Manager

model_manager.py

Lazy detection of ComfyUI-Model-Manager availability

Development

Project structure

src/comfyui_mcp/
├── server.py              # MCP server entry point, wires all components
├── config.py              # Pydantic settings, YAML loading, env overrides
├── client.py              # Async HTTP client for ComfyUI API
├── progress.py            # WebSocket progress tracking with HTTP polling fallback
├── pagination.py          # Offset-based pagination helper for list tools
├── audit.py               # Structured JSON audit logger
├── model_manager.py       # Lazy Model Manager detection and validation
├── security/
│   ├── inspector.py       # Workflow node inspection (audit/enforce)
│   ├── node_auditor.py    # Scans installed nodes for dangerous patterns
│   ├── sanitizer.py       # File path validation
│   ├── rate_limit.py      # Token-bucket rate limiter
│   ├── download_validator.py  # URL/extension validation for model downloads
│   └── model_checker.py   # Proactive model availability checking
├── workflow/
│   ├── templates.py       # Built-in workflow templates (txt2img, img2img, upscale, etc.)
│   ├── operations.py      # Workflow graph operations (add/remove nodes, connect, etc.)
│   └── validation.py      # Workflow analysis and validation
└── tools/
    ├── generation.py      # generate_image, run_workflow, summarize_workflow
    ├── workflow.py        # create_workflow, modify_workflow, validate_workflow, analyze_workflow
    ├── jobs.py            # get_queue, get_job, cancel_job, interrupt, get_progress
    ├── discovery.py       # list_models, list_nodes, audit_dangerous_nodes, etc.
    ├── history.py         # get_history
    ├── files.py           # upload_image, get_image, list_outputs, upload_mask, get_workflow_from_image
    ├── models.py          # search_models, download_model, get_download_tasks, cancel_download
    └── nodes.py           # search/install/uninstall/update custom nodes

scripts/
├── smoke_test.py             # Operator smoke-test against a live ComfyUI instance
├── compare_evals.py          # Diff two Inspect AI eval runs (PASS/FAIL + per-tag breakdown)
└── run_multimodel_eval.py    # Run one Task against N models in a single invocation

evals/
├── comfyui_mcp_task.py                       # Inspect AI Task definitions (Phase 4, Phase 5)
├── 2026-05-11-comfyui-mcp-v1.jsonl           # Phase 4 dataset (10 static questions, tagged)
└── 2026-05-12-comfyui-mcp-phase5.jsonl       # Phase 5 dataset (5 live-execution questions, tagged)

Run tests

uv sync
uv run pytest -v

Evaluation

The MCP ships with an Inspect AI-based eval harness for measuring how well an LLM uses the tools end-to-end. Two task suites are defined:

  • Phase 4 — 10 static questions exercising templates, presets, the prompting guide, and the workflow validator/summarizer. ~1-6 min per run for cloud-tier models.

  • Phase 5 — 5 live-execution questions exercising multi-step tool chains, state passing, recovery from intentionally broken workflows, and reading structured outputs. Generation questions actually submit work to the connected ComfyUI server (so you need one reachable at $COMFYUI_URL).

Every question is tagged with what it tests (e.g. template, recovery, state-passing, output-reading) so results can be sliced per category.

Run a single model against one suite:

COMFYUI_URL=https://comfyui.example.net uv run inspect eval \
    evals/comfyui_mcp_task.py@comfyui_mcp_phase5 \
    --model ollama/gpt-oss:120b-cloud \
    --log-dir ./logs/phase5
uv run inspect view --log-dir ./logs/phase5

Run one suite against N models in a single invocation (wraps the eval_set() Python API because the CLI's --model flag is single-value by Click's default):

uv run python scripts/run_multimodel_eval.py \
    evals/comfyui_mcp_task.py@comfyui_mcp_phase4 \
    --models ollama/gpt-oss:120b-cloud,ollama/qwen3-coder:480b-cloud,anthropic/claude-sonnet-4-6 \
    --log-dir ./logs/phase4-cross-model

Compare two runs (per-sample PASS/FAIL diff plus a per-tag breakdown when either log has tagged samples):

uv run python scripts/compare_evals.py logs/phase4-before logs/phase4-after

Each path can be either a specific .eval file or a directory (uses the most recent .eval by mtime).

Build and publish

Build the distributable artifacts locally:

uv build
uvx twine check dist/*

Publish a release to PyPI:

# After bumping pyproject.toml [project].version and updating CHANGELOG.md
git tag v2.1.0
git push origin v2.1.0

The GitHub Actions workflow in .github/workflows/pypi.yml builds the sdist and wheel, verifies the metadata, and publishes to PyPI using GitHub Trusted Publishing on tag push. The GitHub Release is created manually after the workflow succeeds (gh release create v<x.y.z>). Before the first release, create the comfyui-mcp-secure project on PyPI, configure a trusted publisher for this repository in the PyPI project settings, and use the pypi GitHub environment.

Smoke test against a live instance

Verify connectivity, Model Manager availability, and download lifecycle against a running ComfyUI server:

# Full test (connectivity + folder listing + download task lifecycle)
uv run python scripts/smoke_test.py

# Quick connectivity + folder check only
uv run python scripts/smoke_test.py --no-download

# Target a different server
uv run python scripts/smoke_test.py --url http://localhost:8188

The download probe uses a tiny (~520 KB) safetensors file from hf-internal-testing/tiny-random-bert. The file is created with a timestamped name and cleaned up automatically on every run.

Docker

A pre-built Docker image is published to the GitHub Container Registry. No need to clone the repo.

docker pull ghcr.io/hybridindie/comfyui_mcp:latest

How it works

The container runs as a non-root app user with uv run comfyui-mcp-secure as its entrypoint, communicating over stdin/stdout (stdio). This makes it compatible with Claude Code, Claude Desktop, and any MCP client. Config is read from /home/app/.comfyui-mcp/config.yaml inside the container — mount your local config directory to provide it, or use environment variables.

Running standalone

# Using the hosted image
docker run --rm -i \
  -e COMFYUI_URL=http://host.docker.internal:8188 \
  -v ~/.comfyui-mcp:/home/app/.comfyui-mcp:ro \
  ghcr.io/hybridindie/comfyui_mcp:latest

# Or build and run locally
docker build -t comfyui-mcp-secure .
docker run --rm -i \
  -e COMFYUI_URL=http://host.docker.internal:8188 \
  -v ~/.comfyui-mcp:/home/app/.comfyui-mcp:ro \
  comfyui-mcp-secure

Linux users: Add --add-host=host.docker.internal:host-gateway if using host.docker.internal.

Docker Compose

A docker-compose.yml is included for persistent deployments:

# Start
COMFYUI_URL=http://your-comfyui:8188 docker compose up -d

# View logs
docker compose logs -f comfyui-mcp-secure

The compose file mounts ./config.yaml and persists audit logs to a named volume:

services:
  comfyui-mcp-secure:
    build: .
    image: comfyui-mcp-secure:latest
    container_name: comfyui-mcp-secure
    environment:
      - COMFYUI_URL=${COMFYUI_URL:-http://comfyui:8188}
      - COMFYUI_SECURITY_MODE=${COMFYUI_SECURITY_MODE:-audit}
    volumes:
      - ./config.yaml:/home/app/.comfyui-mcp/config.yaml:ro
      - comfyui-mcp-secure-data:/home/app/.comfyui-mcp/logs
    restart: unless-stopped

volumes:
  comfyui-mcp-secure-data:

Connecting to Claude Code / Claude Desktop via Docker

See the Docker configuration in Quick Start above. The key points:

  • Use docker run --rm -i (interactive, no detach) so stdio works

  • Mount your config: -v ~/.comfyui-mcp:/home/app/.comfyui-mcp:ro

  • Set COMFYUI_URL to reach your ComfyUI instance from inside the container

  • Use host.docker.internal to reach ComfyUI running on your host machine

  • The GHCR image (ghcr.io/hybridindie/comfyui_mcp:latest) means no local build needed

License

MIT

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
7hResponse time
3dRelease cycle
14Releases (12mo)
Commit activity
Issues opened vs closed

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hybridindie/comfyui_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server