retiform-comfyui-mcp 2.0.0__tar.gz
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.
- retiform_comfyui_mcp-2.0.0/.gitignore +77 -0
- retiform_comfyui_mcp-2.0.0/CHANGELOG.md +138 -0
- retiform_comfyui_mcp-2.0.0/LICENSE +22 -0
- retiform_comfyui_mcp-2.0.0/PKG-INFO +445 -0
- retiform_comfyui_mcp-2.0.0/README.md +394 -0
- retiform_comfyui_mcp-2.0.0/pyproject.toml +100 -0
- retiform_comfyui_mcp-2.0.0/src/comfyui_mcp_server/__init__.py +16 -0
- retiform_comfyui_mcp-2.0.0/src/comfyui_mcp_server/logger.py +63 -0
- retiform_comfyui_mcp-2.0.0/src/comfyui_mcp_server/png_metadata.py +214 -0
- retiform_comfyui_mcp-2.0.0/src/comfyui_mcp_server/server.py +1977 -0
- retiform_comfyui_mcp-2.0.0/src/comfyui_mcp_server/workflows/sdxl_gguf_lora_template_api.json +93 -0
- retiform_comfyui_mcp-2.0.0/src/comfyui_mcp_server/workflows/sdxl_gguf_template_api.json +1 -0
- retiform_comfyui_mcp-2.0.0/src/comfyui_mcp_server/workflows/sdxl_ipadapter_template_api.json +119 -0
- retiform_comfyui_mcp-2.0.0/tests/_mcpdouble.py +95 -0
- retiform_comfyui_mcp-2.0.0/tests/test_randomize.py +398 -0
- retiform_comfyui_mcp-2.0.0/tests/test_real_sdk.py +108 -0
- retiform_comfyui_mcp-2.0.0/tests/test_tools.py +537 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# --- Python -------------------------------------------------------------
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
*.egg-info/
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
.eggs/
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
env/
|
|
13
|
+
.Python
|
|
14
|
+
|
|
15
|
+
# --- Secrets / local env (NEVER commit) ---------------------------------
|
|
16
|
+
.env
|
|
17
|
+
.env.*
|
|
18
|
+
*.env
|
|
19
|
+
.git-credentials
|
|
20
|
+
*.pem
|
|
21
|
+
*.key
|
|
22
|
+
|
|
23
|
+
# --- Model weights: users must source these themselves ------------------
|
|
24
|
+
# The repo ships code, workflows and instructions. It never ships another
|
|
25
|
+
# party's licensed artifacts.
|
|
26
|
+
*.gguf
|
|
27
|
+
*.safetensors
|
|
28
|
+
*.ckpt
|
|
29
|
+
*.pth
|
|
30
|
+
*.onnx
|
|
31
|
+
*.bin
|
|
32
|
+
*.pt
|
|
33
|
+
models/
|
|
34
|
+
loras/
|
|
35
|
+
embeddings/
|
|
36
|
+
checkpoints/
|
|
37
|
+
clip/
|
|
38
|
+
vae/
|
|
39
|
+
controlnet/
|
|
40
|
+
upscale_models/
|
|
41
|
+
|
|
42
|
+
# --- Generated output ---------------------------------------------------
|
|
43
|
+
output/
|
|
44
|
+
outputs/
|
|
45
|
+
comfyui/
|
|
46
|
+
comfyui-temp/
|
|
47
|
+
*.job_params_cache.json
|
|
48
|
+
*.png
|
|
49
|
+
*.jpg
|
|
50
|
+
*.jpeg
|
|
51
|
+
*.webp
|
|
52
|
+
*.gif
|
|
53
|
+
*.pdf
|
|
54
|
+
|
|
55
|
+
# --- Runtime ------------------------------------------------------------
|
|
56
|
+
*.log
|
|
57
|
+
logs/
|
|
58
|
+
*.pid
|
|
59
|
+
*.tmp
|
|
60
|
+
*.bak
|
|
61
|
+
*.old
|
|
62
|
+
*.swp
|
|
63
|
+
|
|
64
|
+
# --- Editors / OS -------------------------------------------------------
|
|
65
|
+
.vscode/
|
|
66
|
+
.idea/
|
|
67
|
+
.DS_Store
|
|
68
|
+
Thumbs.db
|
|
69
|
+
*~
|
|
70
|
+
|
|
71
|
+
# --- Tooling ------------------------------------------------------------
|
|
72
|
+
.serena/
|
|
73
|
+
node_modules/
|
|
74
|
+
package-lock.json
|
|
75
|
+
|
|
76
|
+
# Local deployment audit artifacts (not part of the published package)
|
|
77
|
+
audit/
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [2.0.0] - 2026-11-10
|
|
9
|
+
|
|
10
|
+
First release under the `retiform-comfyui-mcp` distribution name, rebuilt from an earlier
|
|
11
|
+
internal working repository that remains the historical record. This is a
|
|
12
|
+
**major** bump because the package name, distribution name, module path and
|
|
13
|
+
several tool contracts changed.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- **`generate_logo` is now `generate_image`.** This is a general-purpose image
|
|
18
|
+
generation server and the public name said otherwise. Consumers calling the
|
|
19
|
+
old tool name must update the call site.
|
|
20
|
+
- **The provenance payload is keyed `GenerationInfo`** rather than
|
|
21
|
+
`LogoGenInfo`. Images rendered before this change are still read under the
|
|
22
|
+
legacy key, so nothing already on disk becomes opaque; only the current key is
|
|
23
|
+
written to new images.
|
|
24
|
+
- **The IP-Adapter loaders are configurable.** `CLIPVisionLoader` and
|
|
25
|
+
`IPAdapterModelLoader` no longer assume one installation's filenames: both are
|
|
26
|
+
arguments on `generate_ipadapter`, and the module defaults are overridable via
|
|
27
|
+
`COMFYUI_CLIP_VISION_MODEL` and `COMFYUI_IPADAPTER_MODEL`. A different CLIP
|
|
28
|
+
vision tower or adapter weight was previously a silent failure.
|
|
29
|
+
- **Package renamed** from the internal working name to the `retiform-comfyui-mcp`
|
|
30
|
+
distribution, with the module at `comfyui_mcp_server` and a `src/` layout. The
|
|
31
|
+
distribution name and the module name intentionally differ: PyPI rejected
|
|
32
|
+
`comfyui-mcp-server` as too similar to two existing projects, and the module
|
|
33
|
+
path and the `comfyui-mcp-server` console script are kept stable so an install
|
|
34
|
+
change does not break a running configuration.
|
|
35
|
+
- **Requires Python 3.11+**, raised from 3.10. This is forced by the Pillow pin
|
|
36
|
+
below rather than chosen: Pillow 13 ships cp311 wheels and no 3.10 build. The
|
|
37
|
+
cost is nil because 3.10 reached end-of-life on 2026-10-01, five weeks before
|
|
38
|
+
this release.
|
|
39
|
+
- **Pillow floor raised to 13.0.0.** `pip-audit` reported 59 advisories against
|
|
40
|
+
Pillow 12.3.0, five of them critical; the package's only Pillow call is
|
|
41
|
+
`Image.open()` on files supplied by a client, which is exactly the decoder
|
|
42
|
+
surface those advisories describe. The audit is now clean.
|
|
43
|
+
- **`_parse_param` de-duplicated.** It was defined twice, byte-identical, at
|
|
44
|
+
roughly 40 lines each. Now a single definition.
|
|
45
|
+
- **`.gitignore` rewritten.** The previous file used an inline comment on a
|
|
46
|
+
pattern (`package-lock.json # ...`), which git reads as a filename containing
|
|
47
|
+
the comment text — the pattern matched nothing and the file was not ignored.
|
|
48
|
+
Every pattern is now verified with `git check-ignore`.
|
|
49
|
+
- **Logging routed through the logger module.** Twenty-two raw
|
|
50
|
+
`print(..., file=sys.stderr)` calls bypassed the existing logger; they now go
|
|
51
|
+
through `log_debug` / `log_error`, so a normal run is quiet and detail is
|
|
52
|
+
opt-in via `set_log_level("debug")`.
|
|
53
|
+
- **Version is declared once.** `pyproject.toml` now uses
|
|
54
|
+
`dynamic = ["version"]` with `[tool.hatch.version]` reading the package's
|
|
55
|
+
`__version__`. Declaring it in both places previously allowed drift between
|
|
56
|
+
the installed metadata and what the code reported.
|
|
57
|
+
- **Provenance strings derived, not literal.** The generator string stamped into
|
|
58
|
+
every image was a hardcoded version; it now comes from the package version.
|
|
59
|
+
- **Every filesystem root is environment-driven** — `COMFYUI_PATH`,
|
|
60
|
+
`COMFYUI_MODELS_PATH`, `COMFYUI_OUTPUT_PATH`. Paths were hardcoded to the
|
|
61
|
+
machine the project was developed on, which broke every install elsewhere.
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
|
|
65
|
+
- **The MCP handshake advertised the SDK's version, not ours.** `FastMCP`
|
|
66
|
+
accepts no `version` argument in mcp 1.x and does not forward one to the
|
|
67
|
+
low-level server, so `initialize` reported the SDK's own `1.30.0` while the
|
|
68
|
+
package declared `2.0.0`. The version is now set on the inner server after
|
|
69
|
+
construction, which is the attribute the initialize response reads.
|
|
70
|
+
- **VRAM tools never measured the GPU.** `system_check`, `get_vram_usage` and
|
|
71
|
+
`emergency_cleanup` all read `torch.cuda` inside the MCP server's own process.
|
|
72
|
+
That process never allocates a tensor — ComfyUI is a separate PID — so the
|
|
73
|
+
readings were structurally zero: `vram_used_mb: 0` and `reserved: 0` while
|
|
74
|
+
ComfyUI held the memory, and `emergency_cleanup`'s `freed_mb` was always `0`
|
|
75
|
+
by construction. All three now read the driver via `nvidia-smi`.
|
|
76
|
+
- **`emergency_cleanup` now actually frees memory**, by calling ComfyUI's
|
|
77
|
+
`POST /free {"unload_models": true, "free_memory": true}` and reporting real
|
|
78
|
+
before/after figures. An empty `torch.cuda.empty_cache()` in a process that
|
|
79
|
+
holds nothing could never have reclaimed anything.
|
|
80
|
+
- **VRAM is released when a batch finishes** (new `keep_loaded` flag, default
|
|
81
|
+
`false`). Models are held for the duration of a batch — unloading between
|
|
82
|
+
jobs would make every subsequent job pay a full model-load penalty — and
|
|
83
|
+
released at completion.
|
|
84
|
+
- **Workflow templates resolve relative to the package.** Two call sites
|
|
85
|
+
hardcoded an absolute path into a development directory.
|
|
86
|
+
- **`download_model` had no filename sanitization**, so a `../` path could write
|
|
87
|
+
outside the models tree. Now: bare filename only, extension allowlist per
|
|
88
|
+
model type, `http`/`https` only, and an optional bearer token for gated hosts.
|
|
89
|
+
- **A truncated download could leave a corrupt model file** that looks loadable.
|
|
90
|
+
Writes are now atomic via a `.part` file, with an explicit incomplete-download
|
|
91
|
+
error and a size check against `Content-Length`.
|
|
92
|
+
- **`png_metadata` invented provenance.** A partial params dict wrote
|
|
93
|
+
`steps 20` / `euler_ancestral` / `cfg 7.0` / `seed -1` into the embedded A1111
|
|
94
|
+
block, permanently, inside a binary. Unknown values are now rendered as `?`
|
|
95
|
+
and never defaulted.
|
|
96
|
+
- **A `{"min": .., "max": ..}` dict passed as a parameter spec was sent to the
|
|
97
|
+
workflow as a literal**, so randomised batches could run with nothing
|
|
98
|
+
randomised. This now raises with the correct shape named — ranges are
|
|
99
|
+
two-element lists.
|
|
100
|
+
- **Two bare `except:` clauses narrowed** (they swallowed `KeyboardInterrupt`
|
|
101
|
+
and `SystemExit`).
|
|
102
|
+
- **A failed local import degraded silently.** `log_info = None` let the server
|
|
103
|
+
run with no diagnostics; the import now fails loudly.
|
|
104
|
+
|
|
105
|
+
### Removed
|
|
106
|
+
|
|
107
|
+
- `torch` is no longer a dependency. It was roughly 2GB of CUDA used only to
|
|
108
|
+
produce a number about the wrong process.
|
|
109
|
+
- Client deliverables, a live client brief, a brand template directory, a
|
|
110
|
+
downloader script carrying an API token, four unused evaluation modules, and
|
|
111
|
+
six generations of superseded batch scripts — none of which belong in a
|
|
112
|
+
published package.
|
|
113
|
+
- No model weights, LoRAs, embeddings, CLIP or VAE files are distributed. Users
|
|
114
|
+
source those themselves.
|
|
115
|
+
|
|
116
|
+
### Added
|
|
117
|
+
|
|
118
|
+
- `CHANGELOG.md`, `pyproject.toml` with `src/` layout, and an MIT `LICENSE` with
|
|
119
|
+
joint copyright.
|
|
120
|
+
- Test suite: 90 tests, up from 34. The previous suite covered none of the
|
|
121
|
+
thirteen tools and passed vacuously, because it replaced the MCP SDK with a
|
|
122
|
+
`MagicMock` whose `@tool()` decorator returned mocks instead of functions.
|
|
123
|
+
Tests now use a real pass-through double that rejects unknown constructor
|
|
124
|
+
keywords against the real SDK's signature, so an argument the SDK does not
|
|
125
|
+
accept fails under test instead of only at import in a user's environment.
|
|
126
|
+
- Tests that fail the build rather than the user: version single-sourcing,
|
|
127
|
+
filesystem-path absoluteness, parameter-spec rejection, download contract,
|
|
128
|
+
and VRAM release semantics.
|
|
129
|
+
- Tests that run against the **real MCP SDK** rather than the pass-through
|
|
130
|
+
test double: an import in a subprocess, a live stdio handshake, and the tool
|
|
131
|
+
count a client actually receives. This is the instrument that catches a wrong
|
|
132
|
+
assumption about the dependency, which no mock can.
|
|
133
|
+
- `.github/FUNDING.yml` and a Sponsors section in the README, carrying the
|
|
134
|
+
GitHub Sponsors link and the self-custody rails. Both travel inside the
|
|
135
|
+
wheel, since the dist-info metadata embeds the README, so the addresses reach
|
|
136
|
+
anyone reading the package from a registry.
|
|
137
|
+
|
|
138
|
+
[2.0.0]: https://github.com/retiform-labs/comfyui-mcp-server/releases/tag/v2.0.0
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 David Newman <david@retiform.dev>
|
|
4
|
+
Copyright (c) 2026 Guan <guan@retiform.dev>
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: retiform-comfyui-mcp
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Agentic ComfyUI image generation over the Model Context Protocol: batch discovery, LoRA shaping, and provenance.
|
|
5
|
+
Project-URL: Homepage, https://retiform.dev
|
|
6
|
+
Project-URL: Repository, https://github.com/retiform-labs/comfyui-mcp-server
|
|
7
|
+
Project-URL: Issues, https://github.com/retiform-labs/comfyui-mcp-server/issues
|
|
8
|
+
Author-email: David Newman <david@retiform.dev>, Guan <guan@retiform.dev>
|
|
9
|
+
License: MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2026 David Newman <david@retiform.dev>
|
|
12
|
+
Copyright (c) 2026 Guan <guan@retiform.dev>
|
|
13
|
+
|
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
in the Software without restriction, including without limitation the rights
|
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
19
|
+
furnished to do so, subject to the following conditions:
|
|
20
|
+
|
|
21
|
+
The above copyright notice and this permission notice shall be included in all
|
|
22
|
+
copies or substantial portions of the Software.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
27
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
28
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
29
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30
|
+
SOFTWARE.
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Keywords: agent,comfyui,image-generation,lora,mcp,model-context-protocol,stable-diffusion
|
|
33
|
+
Classifier: Development Status :: 4 - Beta
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
40
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
41
|
+
Requires-Python: >=3.11
|
|
42
|
+
Requires-Dist: mcp<2,>=1.27.0
|
|
43
|
+
Requires-Dist: pillow>=13.0.0
|
|
44
|
+
Requires-Dist: psutil>=5.9.0
|
|
45
|
+
Requires-Dist: requests>=2.31.0
|
|
46
|
+
Provides-Extra: dev
|
|
47
|
+
Requires-Dist: mypy>=1.11; extra == 'dev'
|
|
48
|
+
Requires-Dist: pip-audit>=2.7; extra == 'dev'
|
|
49
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
50
|
+
Description-Content-Type: text/markdown
|
|
51
|
+
|
|
52
|
+
# comfyui-mcp-server
|
|
53
|
+
|
|
54
|
+
An MCP server that gives an agent direct control of a running
|
|
55
|
+
[ComfyUI](https://github.com/comfyanonymous/ComfyUI) instance: submit workflows,
|
|
56
|
+
run randomised batch discovery, shape LoRA stacks, and read back the provenance
|
|
57
|
+
of every image produced.
|
|
58
|
+
|
|
59
|
+
Built for **iterative aesthetic discovery** -- generate wide, curate narrow, and
|
|
60
|
+
keep a verifiable record of the parameters that produced the survivors.
|
|
61
|
+
|
|
62
|
+
> **Not affiliated with** `joenorton/comfyui-mcp-server` or any other project of
|
|
63
|
+
> a similar name. This is an independent implementation (`@retiform-labs`), and
|
|
64
|
+
> its tool surface is specific to the workflow templates it ships.
|
|
65
|
+
|
|
66
|
+
## What it does
|
|
67
|
+
|
|
68
|
+
Thirteen tools, split into four groups:
|
|
69
|
+
|
|
70
|
+
**Lifecycle** -- `system_check` · `start_comfyui` · `stop_comfyui` · `get_comfyui_status`
|
|
71
|
+
|
|
72
|
+
**Generation** -- `generate_image` · `generate_ipadapter` · `get_job_status`
|
|
73
|
+
|
|
74
|
+
**Batch** -- `batch_generate` · `batch_generate_ipadapter`
|
|
75
|
+
|
|
76
|
+
**Hardware** -- `list_models` · `download_model` · `get_vram_usage` · `emergency_cleanup`
|
|
77
|
+
|
|
78
|
+
### Randomised batch discovery
|
|
79
|
+
|
|
80
|
+
The point of the batch tools is *divergence*, not repetition. Pass a `randomize`
|
|
81
|
+
spec and each job in the batch draws its own parameters:
|
|
82
|
+
|
|
83
|
+
```jsonc
|
|
84
|
+
{
|
|
85
|
+
"randomize": {
|
|
86
|
+
"seed": "random", // fresh seed per job, deduplicated
|
|
87
|
+
"sampler": "random", // from a curated safe pool
|
|
88
|
+
"cfg_scale": [5.0, 9.0], // uniform in range
|
|
89
|
+
"loras": {"mode": "auto", "count": [1, 3], "weight": [0.25, 0.7]}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Ranges are **2-element lists** `[min, max]`. A dict like `{"min": 1, "max": 5}` is
|
|
95
|
+
rejected with an error rather than silently sent to the workflow as a literal --
|
|
96
|
+
that mistake is easy to make and produces a batch where nothing varies.
|
|
97
|
+
|
|
98
|
+
### LoRA stack shape matters more than the sum
|
|
99
|
+
|
|
100
|
+
Two LoRAs at equal weight average two aesthetics into a muddle. A stack
|
|
101
|
+
generally wants **one dominant adapter and one supporting it**:
|
|
102
|
+
|
|
103
|
+
- dominant: `0.55` -- `0.77`
|
|
104
|
+
- supporting: `0.25` -- `0.40`
|
|
105
|
+
- optional style pin: `~0.45`
|
|
106
|
+
|
|
107
|
+
A total weight around `1.3` is normal and is **not** a ceiling -- well-shaped
|
|
108
|
+
stacks run above it and produce the best results.
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
## Tool reference
|
|
112
|
+
|
|
113
|
+
Every tool returns JSON. `generate_*` and `batch_*` are **asynchronous**: they
|
|
114
|
+
queue work and return a job id immediately, because a diffusion run takes far
|
|
115
|
+
longer than any sane request timeout. Poll `get_job_status` for the result.
|
|
116
|
+
|
|
117
|
+
### Lifecycle
|
|
118
|
+
|
|
119
|
+
#### `system_check()`
|
|
120
|
+
|
|
121
|
+
Reports GPU name, driver version, total and free VRAM, and whether ComfyUI is
|
|
122
|
+
responding. Reads `nvidia-smi`, so it sees the whole GPU -- including memory
|
|
123
|
+
held by ComfyUI, which a torch call inside this process never could.
|
|
124
|
+
|
|
125
|
+
```jsonc
|
|
126
|
+
// no arguments
|
|
127
|
+
{"gpu": "NVIDIA GeForce RTX 4090", "vram_total_mb": 24564,
|
|
128
|
+
"vram_free_mb": 3703, "comfyui_running": true, "comfyui_version": "0.x"}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
#### `start_comfyui(lowvram=True, fp16=True, port=8188)`
|
|
132
|
+
|
|
133
|
+
Launches ComfyUI if it is not already running. `lowvram` is the safe default for
|
|
134
|
+
cards under ~8 GB.
|
|
135
|
+
|
|
136
|
+
```jsonc
|
|
137
|
+
{"success": true, "pid": 41233, "port": 8188, "already_running": false}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### `stop_comfyui(force=False)`
|
|
141
|
+
|
|
142
|
+
Stops the ComfyUI process. `force=True` sends SIGKILL when a normal shutdown
|
|
143
|
+
hangs.
|
|
144
|
+
|
|
145
|
+
#### `get_comfyui_status()`
|
|
146
|
+
|
|
147
|
+
Lightweight liveness check -- cheaper than `system_check` because it never shells
|
|
148
|
+
out to the driver.
|
|
149
|
+
|
|
150
|
+
```jsonc
|
|
151
|
+
{"running": true, "port": 8188, "queue_remaining": 0}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Generation
|
|
155
|
+
|
|
156
|
+
#### `generate_image(...)`
|
|
157
|
+
|
|
158
|
+
Text-to-image through the packaged SDXL workflow.
|
|
159
|
+
|
|
160
|
+
| Parameter | Default | Notes |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| `prompt` | *required* | What to generate |
|
|
163
|
+
| `negative_prompt` | `"text, watermark, blurry, low quality"` | What to avoid |
|
|
164
|
+
| `negative_embedding` | `None` | A textual-inversion embedding name |
|
|
165
|
+
| `width` / `height` | `1024` / `1024` | |
|
|
166
|
+
| `steps` | `20` | |
|
|
167
|
+
| `cfg_scale` | `7.0` | |
|
|
168
|
+
| `sampler` | `"euler_ancestral"` | |
|
|
169
|
+
| `scheduler` | `"normal"` | |
|
|
170
|
+
| `lora` | `None` | One LoRA filename from `models/loras/` |
|
|
171
|
+
| `lora_weight` | `0.8` | |
|
|
172
|
+
| `loras` | `None` | Several at once: `[{"name": ..., "weight": ...}]` |
|
|
173
|
+
| `seed` | `None` | Omit for a random seed |
|
|
174
|
+
| `output_format` | `"png"` | PNG carries provenance metadata |
|
|
175
|
+
|
|
176
|
+
```jsonc
|
|
177
|
+
{"prompt": "a geometric fox mark, single flat ink colour, high contrast",
|
|
178
|
+
"width": 1024, "height": 1024, "steps": 30, "seed": 1777732588}
|
|
179
|
+
// -> {"job_id": "a3f9...", "status": "queued", "params": {...}}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
#### `generate_ipadapter(...)`
|
|
183
|
+
|
|
184
|
+
Reference-guided generation. Feeds 1--4 images into the model to steer style,
|
|
185
|
+
composition or content.
|
|
186
|
+
|
|
187
|
+
| Parameter | Default | Notes |
|
|
188
|
+
|---|---|---|
|
|
189
|
+
| `prompt` | *required* | |
|
|
190
|
+
| `reference_images` | *required* | 1--4 filenames from `comfyui/input/` |
|
|
191
|
+
| `reference_weight` | `0.6` | How hard the references pull |
|
|
192
|
+
| `weight_type` | `"style transfer"` | Also `"linear"`, `"ease in"`, `"strong style transfer"` |
|
|
193
|
+
| `start_at` / `end_at` | `0.0` / `1.0` | Fraction of sampling the adapter applies over |
|
|
194
|
+
| `embeds_scaling` | `"V only"` | |
|
|
195
|
+
| `width` / `height` | `512` / `512` | |
|
|
196
|
+
| `steps` | `10` | |
|
|
197
|
+
| `cfg_scale` | `7.5` | |
|
|
198
|
+
| `sampler` / `scheduler` | `"dpmpp_2m"` / `"karras"` | |
|
|
199
|
+
| `clip_vision_model` | env default | Override the CLIP vision tower |
|
|
200
|
+
| `ipadapter_model` | env default | Override the IP-Adapter weight |
|
|
201
|
+
|
|
202
|
+
```jsonc
|
|
203
|
+
{"prompt": "the reference mark, redrawn as a woodcut",
|
|
204
|
+
"reference_images": ["ref-a.png", "ref-b.png"],
|
|
205
|
+
"reference_weight": 0.65, "weight_type": "style transfer"}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
> **Reference images must already exist in ComfyUI's `input/` directory.** This
|
|
209
|
+
> tool does not upload files; it names them. A missing file is a raised error,
|
|
210
|
+
> not a silent no-op.
|
|
211
|
+
|
|
212
|
+
#### `get_job_status(job_id)`
|
|
213
|
+
|
|
214
|
+
Polls a queued job. Returns `queued`, `running`, `completed` or `failed`, and on
|
|
215
|
+
completion the output image filenames with their provenance payload.
|
|
216
|
+
|
|
217
|
+
```jsonc
|
|
218
|
+
{"job_id": "a3f9...", "status": "completed",
|
|
219
|
+
"images": [{"filename": "image_output_00042_.png", "subfolder": "", "type": "output"}],
|
|
220
|
+
"params": {"seed": 1777732588, "sampler": "euler_ancestral", "steps": 30, ...}}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Batch
|
|
224
|
+
|
|
225
|
+
#### `batch_generate(prompts, shared_params=None, randomize=None, deduplicate_seeds=True, priority="normal", keep_loaded=False)`
|
|
226
|
+
|
|
227
|
+
Queues one job per prompt, optionally randomising parameters across the batch.
|
|
228
|
+
|
|
229
|
+
- `prompts` -- a list of positive prompt strings, one per job.
|
|
230
|
+
- `shared_params` -- static parameters applied to every job (accepts an object
|
|
231
|
+
or a JSON string).
|
|
232
|
+
- `randomize` -- per-job draws; overrides `shared_params` where it names the same key.
|
|
233
|
+
- `deduplicate_seeds` -- guarantees no two jobs share a seed.
|
|
234
|
+
- `keep_loaded` -- leave the model resident after the batch. See *VRAM behaviour*.
|
|
235
|
+
|
|
236
|
+
```jsonc
|
|
237
|
+
{"prompts": ["a fox mark", "an owl mark", "a wave mark"],
|
|
238
|
+
"shared_params": {"width": 1024, "height": 1024, "steps": 24},
|
|
239
|
+
"randomize": {"seed": "random", "cfg_scale": [5.0, 9.0],
|
|
240
|
+
"loras": {"mode": "auto", "count": [1, 2], "weight": [0.25, 0.7]}}}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Randomisation spec forms:
|
|
244
|
+
|
|
245
|
+
| Form | Meaning |
|
|
246
|
+
|---|---|
|
|
247
|
+
| `"random"` | Draw from a curated safe pool |
|
|
248
|
+
| `"random_unsafe"` | Draw from the full pool, including values that often break |
|
|
249
|
+
| `[min, max]` | Uniform draw in a numeric range |
|
|
250
|
+
| `{"mode": "auto", "count": [1,3], "weight": [0.25,0.7]}` | LoRA stack selection |
|
|
251
|
+
| scalar (e.g. `24`) | Fixed at that value |
|
|
252
|
+
|
|
253
|
+
#### `batch_generate_ipadapter(prompts, reference_images, ...)`
|
|
254
|
+
|
|
255
|
+
The reference-guided equivalent, taking the same batch arguments plus the
|
|
256
|
+
IP-Adapter parameters from `generate_ipadapter`.
|
|
257
|
+
|
|
258
|
+
### Hardware
|
|
259
|
+
|
|
260
|
+
#### `list_models(model_type="all")`
|
|
261
|
+
|
|
262
|
+
Lists what is actually on disk, with sizes. `model_type` filters to one of
|
|
263
|
+
`unet`, `clip`, `vae`, `loras`, `ipadapter`, `clip_vision`.
|
|
264
|
+
|
|
265
|
+
```jsonc
|
|
266
|
+
{"model_type": "loras"}
|
|
267
|
+
// -> [{"name": "copperplate_v2.safetensors", "type": "loras",
|
|
268
|
+
// "size_mb": 172.4, "format": "safetensors"}]
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
#### `download_model(url, model_type, filename=None, expected_sha256=None, token=None)`
|
|
272
|
+
|
|
273
|
+
Downloads from a Civitai-style URL. Writes atomically -- a partial download never
|
|
274
|
+
lands under the final name -- and sanitises `filename` to a single path
|
|
275
|
+
component, so a crafted URL cannot write outside the model directory.
|
|
276
|
+
|
|
277
|
+
```jsonc
|
|
278
|
+
{"url": "https://civitai.com/api/download/models/12345",
|
|
279
|
+
"model_type": "loras", "filename": "copperplate_v2.safetensors"}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
#### `get_vram_usage()`
|
|
283
|
+
|
|
284
|
+
Current GPU memory as the driver reports it. Reads `nvidia-smi` rather than this
|
|
285
|
+
process's CUDA context, which never allocates and would always read zero.
|
|
286
|
+
|
|
287
|
+
```jsonc
|
|
288
|
+
{"used_mb": 9216, "total_mb": 24564, "free_mb": 15348}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
#### `emergency_cleanup(restart_comfyui=False)`
|
|
292
|
+
|
|
293
|
+
Frees GPU memory by asking ComfyUI to unload its models, then re-reads the driver
|
|
294
|
+
to report what was **actually** released. Useful before handing the GPU to a
|
|
295
|
+
non-ComfyUI job.
|
|
296
|
+
|
|
297
|
+
```jsonc
|
|
298
|
+
{"models_unloaded": ["sdxl-base"], "vram_freed_mb": 9001,
|
|
299
|
+
"vram_before_mb": 9216, "vram_after_mb": 215}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
> `restart_comfyui=True` is the heavier option: it stops and restarts the server.
|
|
303
|
+
> Use it when a normal unload does not recover the memory.
|
|
304
|
+
|
|
305
|
+
## Requirements
|
|
306
|
+
|
|
307
|
+
- **Python 3.11+**
|
|
308
|
+
- **A running ComfyUI instance** with the nodes your workflows use
|
|
309
|
+
(`UnetLoaderGGUF` and the IP-Adapter nodes, for the shipped templates)
|
|
310
|
+
- **`nvidia-smi`** on `PATH` for the VRAM tools (NVIDIA drivers)
|
|
311
|
+
- **Models, LoRAs, embeddings and CLIP/VAE weights are NOT included.** Source
|
|
312
|
+
them yourself and place them where ComfyUI expects them. This repository ships
|
|
313
|
+
code, workflow templates and instructions -- never another party's licensed
|
|
314
|
+
artifacts.
|
|
315
|
+
|
|
316
|
+
The IP-Adapter templates reference these filenames; substitute your own:
|
|
317
|
+
|
|
318
|
+
| Node | Default filename | Override with |
|
|
319
|
+
|---|---|---|
|
|
320
|
+
| `CLIPVisionLoader` | `CLIP-ViT-H-14-laion2B-s32B-b79K.safetensors` | `clip_vision_model=` or `COMFYUI_CLIP_VISION_MODEL` |
|
|
321
|
+
| `IPAdapterModelLoader` | `ip-adapter-plus_sdxl_vit-h.bin` | `ipadapter_model=` or `COMFYUI_IPADAPTER_MODEL` |
|
|
322
|
+
|
|
323
|
+
These are **defaults, not requirements.** If your install uses a different CLIP
|
|
324
|
+
vision tower or IP-Adapter weight, pass the filename to `generate_ipadapter` or
|
|
325
|
+
set the environment variable -- no code change, and no failure to diagnose
|
|
326
|
+
silently.
|
|
327
|
+
|
|
328
|
+
A negative embedding is optional; the templates accept one via the
|
|
329
|
+
`negative_embedding` argument (e.g. `DeepNegative_xl_v1`).
|
|
330
|
+
|
|
331
|
+
## Install
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
pip install retiform-comfyui-mcp
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
> The **distribution** is `retiform-comfyui-mcp`; the installed **module** is
|
|
338
|
+
> `comfyui_mcp_server` and the **console script** is `comfyui-mcp-server`. The
|
|
339
|
+
> three names differ deliberately: PyPI rejects the bare `comfyui-mcp-server`
|
|
340
|
+
> as too similar to two existing projects, and keeping the module path and the
|
|
341
|
+
> script name stable means an install never breaks a working MCP client config.
|
|
342
|
+
|
|
343
|
+
Or from source:
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
git clone https://github.com/retiform-labs/comfyui-mcp-server
|
|
347
|
+
cd comfyui-mcp-server
|
|
348
|
+
pip install -e ".[dev]"
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
> **`mcp` is pinned below 2.0** -- in the MCP SDK 2.x line,
|
|
352
|
+
> `mcp.server.fastmcp` is a compatibility stub that raises on import. Do not
|
|
353
|
+
> widen that pin without migrating the server to `MCPServer`.
|
|
354
|
+
|
|
355
|
+
## Configure
|
|
356
|
+
|
|
357
|
+
All configuration is environment-driven. Nothing is assumed about where ComfyUI
|
|
358
|
+
lives.
|
|
359
|
+
|
|
360
|
+
| Variable | Default | Meaning |
|
|
361
|
+
|---|---|---|
|
|
362
|
+
| `COMFYUI_HOST` | `127.0.0.1` | ComfyUI host |
|
|
363
|
+
| `COMFYUI_PORT` | `8188` | ComfyUI port |
|
|
364
|
+
| `COMFYUI_PATH` | `~/ComfyUI` | ComfyUI install root (the dir with `main.py`) |
|
|
365
|
+
| `COMFYUI_MODELS_PATH` | `$COMFYUI_PATH/models` | Models root |
|
|
366
|
+
| `COMFYUI_OUTPUT_PATH` | `$COMFYUI_PATH/output` | Where renders land |
|
|
367
|
+
| `COMFYUI_CLIP_VISION_MODEL` | `CLIP-ViT-H-14-laion2B-s32B-b79K.safetensors` | CLIP vision tower for reference-guided generation |
|
|
368
|
+
| `COMFYUI_IPADAPTER_MODEL` | `ip-adapter-plus_sdxl_vit-h.bin` | IP-Adapter weight (plus variant) |
|
|
369
|
+
| `COMFYUI_IPADAPTER_BASIC_MODEL` | `ip-adapter_sdxl_vit-h.bin` | IP-Adapter weight used by the VRAM-saving fallback |
|
|
370
|
+
| `COMFYUI_DOWNLOAD_TOKEN` | *(none)* | Bearer token for gated model hosts, so it need not travel through a tool argument |
|
|
371
|
+
|
|
372
|
+
### MCP client
|
|
373
|
+
|
|
374
|
+
```jsonc
|
|
375
|
+
{
|
|
376
|
+
"mcpServers": {
|
|
377
|
+
"comfyui": {
|
|
378
|
+
"command": "comfyui-mcp-server",
|
|
379
|
+
"env": { "COMFYUI_PATH": "/opt/ComfyUI" }
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## VRAM behaviour
|
|
386
|
+
|
|
387
|
+
The MCP server is a **separate process** from ComfyUI. It never allocates CUDA
|
|
388
|
+
tensors, so `torch.cuda.memory_allocated()` inside it is structurally zero no
|
|
389
|
+
matter what ComfyUI is holding. This server therefore reads VRAM from the
|
|
390
|
+
driver (`nvidia-smi`) and releases it by asking ComfyUI to unload --
|
|
391
|
+
|
|
392
|
+
- `get_vram_usage` -- live used/free/total from the driver
|
|
393
|
+
- `emergency_cleanup` -- `POST /free {"unload_models": true, "free_memory": true}`
|
|
394
|
+
on ComfyUI, reporting real before/after MB and `freed_mb`
|
|
395
|
+
|
|
396
|
+
`torch` is deliberately **not** a dependency. It cost ~2GB of CUDA to produce a
|
|
397
|
+
number that measured the wrong process.
|
|
398
|
+
|
|
399
|
+
Unloading after every job is usually the wrong trade: the next job then pays a
|
|
400
|
+
full model-load penalty. If you release between jobs, release at **batch**
|
|
401
|
+
boundaries.
|
|
402
|
+
|
|
403
|
+
## Provenance
|
|
404
|
+
|
|
405
|
+
Generated PNGs carry an A1111-compatible `parameters` block plus a structured
|
|
406
|
+
`GenerationInfo` record. Values that were not supplied are written as `?` and are
|
|
407
|
+
**never** defaulted -- an invented parameter in an embedded block is a permanent,
|
|
408
|
+
machine-readable lie.
|
|
409
|
+
|
|
410
|
+
Images rendered before the rename carry the same payload under the key
|
|
411
|
+
`LogoGenInfo`. Both keys are read; only the current one is written.
|
|
412
|
+
|
|
413
|
+
## Development
|
|
414
|
+
|
|
415
|
+
```bash
|
|
416
|
+
pip install -e ".[dev]"
|
|
417
|
+
python -m unittest discover -s tests -v
|
|
418
|
+
ruff check src tests
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
Nothing under `src/` may contain an absolute filesystem path; a test enforces
|
|
422
|
+
this, because a package that only works on its author's machine is broken for
|
|
423
|
+
everyone else.
|
|
424
|
+
|
|
425
|
+
## License
|
|
426
|
+
|
|
427
|
+
MIT -- see [LICENSE](LICENSE).
|
|
428
|
+
|
|
429
|
+
Copyright (c) 2026 David Newman and Guan.
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## Sponsors
|
|
434
|
+
|
|
435
|
+
If comfyui-mcp-server is useful to you, consider supporting its continued development:
|
|
436
|
+
|
|
437
|
+
- **[GitHub Sponsors](https://github.com/sponsors/guan-tends)**
|
|
438
|
+
- **Bitcoin:** `bc1q0gd3mwjg3zy9sghv22kmpg823vss4c0zzdzg24`
|
|
439
|
+
- **Solana:** `Eu8wQcW68TKMs1a6eqzZu8znzU52QLqQugAMG8uCD6y6`
|
|
440
|
+
- **Ethereum / EVM:** `0x2733ff7c865C56d565a99BE1DC11B81cc76850A5`
|
|
441
|
+
- **XRP Ledger:** `r4X6e7McAQj7e8vBCeued1RYu4mCJrREDG`
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
Crafted with ❤️ by [Retiform Labs](https://retiform.dev)
|