comfyui-mcp 0.3.0 → 0.3.2

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.
@@ -1,457 +0,0 @@
1
- # Generation Tracker & Community Settings
2
-
3
- ## Overview
4
-
5
- A local SQLite database that tracks every generation's settings, counts reuse,
6
- and optionally shares anonymized public-LoRA settings to a Cloudflare backend.
7
- The MCP server queries the local DB to suggest "what worked before" when building
8
- new workflows, and can also fetch community-aggregated settings.
9
-
10
- ---
11
-
12
- ## Architecture
13
-
14
- ```
15
- ┌─────────────────────────────────────────────────────────────┐
16
- │ ComfyUI MCP Server │
17
- │ │
18
- │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
19
- │ │ Workflow │───▶│ Generation │───▶│ Settings │ │
20
- │ │ Executor │ │ Tracker │ │ Advisor │ │
21
- │ │ (existing) │ │ (new) │ │ (new) │ │
22
- │ └──────────────┘ └──────┬───────┘ └──────┬───────┘ │
23
- │ │ │ │
24
- │ ┌──────▼───────┐ ┌─────▼────────┐ │
25
- │ │ SQLite DB │ │ Community │ │
26
- │ │ (local) │ │ API Client │ │
27
- │ │ better- │ │ (optional) │ │
28
- │ │ sqlite3 │ └─────┬────────┘ │
29
- │ └──────────────┘ │ │
30
- └─────────────────────────────────────────────────┼──────────┘
31
-
32
- ┌───────▼─────────┐
33
- │ Cloudflare │
34
- │ Workers + D1 │
35
- │ (community API) │
36
- └─────────────────┘
37
- ```
38
-
39
- ---
40
-
41
- ## Local Database: `generations.db`
42
-
43
- Location: `<comfyui_path>/comfyui-mcp/generations.db`
44
- (Lives alongside the user's ComfyUI data, gitignored.)
45
-
46
- ### Schema
47
-
48
- ```sql
49
- -- Core generation log
50
- CREATE TABLE generations (
51
- id INTEGER PRIMARY KEY AUTOINCREMENT,
52
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
53
-
54
- -- Settings fingerprint (SHA256 of canonical JSON of tracked fields)
55
- settings_hash TEXT NOT NULL,
56
-
57
- -- Model identification (by content hash, not filename)
58
- model_family TEXT NOT NULL, -- 'qwen_image', 'sdxl', 'flux', etc.
59
- model_hash TEXT NOT NULL, -- AutoV2 (first 10 chars of SHA256) of checkpoint/unet
60
- model_name TEXT, -- checkpoint/unet filename (for full-text search, not part of hash)
61
- preset_name TEXT, -- from model-settings.json, or 'custom'
62
-
63
- -- Sampler settings
64
- sampler TEXT NOT NULL,
65
- scheduler TEXT NOT NULL,
66
- steps INTEGER NOT NULL,
67
- cfg REAL NOT NULL,
68
- denoise REAL DEFAULT 1.0,
69
- shift REAL, -- auraflow_shift for Qwen
70
-
71
- -- Resolution (tracked per-generation, NOT part of settings_hash)
72
- width INTEGER NOT NULL,
73
- height INTEGER NOT NULL,
74
-
75
- -- LoRA (nullable — not all gens use LoRAs)
76
- lora_hash TEXT, -- AutoV2 of LoRA file (content-addressed)
77
- lora_name TEXT, -- LoRA filename (for full-text search, not part of hash)
78
- lora_strength REAL,
79
- lora_civitai_id INTEGER, -- NULL = private/unknown, populated after hash lookup
80
-
81
- -- Satisfaction signal
82
- -- Implicit: if the user runs the same settings_hash again, we increment reuse_count
83
- -- Explicit: user can thumbs-up/down via MCP tool (future)
84
- reuse_count INTEGER NOT NULL DEFAULT 1,
85
-
86
- -- Negative prompt hash (for dedup, not stored in full)
87
- neg_prompt_hash TEXT
88
- );
89
-
90
- -- Fast lookups
91
- CREATE INDEX idx_gen_settings_hash ON generations(settings_hash);
92
- CREATE INDEX idx_gen_model_family ON generations(model_family);
93
- CREATE INDEX idx_gen_model_hash ON generations(model_hash);
94
- CREATE INDEX idx_gen_lora_hash ON generations(lora_hash);
95
- CREATE INDEX idx_gen_lora_civitai ON generations(lora_civitai_id);
96
- CREATE INDEX idx_gen_created ON generations(created_at);
97
-
98
- -- File hash cache (avoid re-hashing large .safetensors files)
99
- -- Covers both models (checkpoints, unets) and LoRAs.
100
- -- Keyed on filename + size + mtime — if any change, re-hash.
101
- CREATE TABLE file_hashes (
102
- filename TEXT PRIMARY KEY, -- basename of the file
103
- file_path TEXT NOT NULL, -- full path (local only, never shared)
104
- file_size INTEGER NOT NULL,
105
- file_mtime TEXT NOT NULL,
106
- sha256 TEXT NOT NULL,
107
- autov2 TEXT NOT NULL, -- sha256[:10].toUpperCase()
108
- file_type TEXT NOT NULL, -- 'checkpoint' | 'unet' | 'lora' | 'vae'
109
- civitai_id INTEGER, -- NULL = not found / not checked
110
- civitai_name TEXT,
111
- civitai_model_id INTEGER, -- parent model ID on CivitAI
112
- checked_at TEXT -- last time we queried CivitAI
113
- );
114
- ```
115
-
116
- ### Settings Hash
117
-
118
- The `settings_hash` is a SHA256 of a canonical JSON string containing only
119
- the **settings identity fields**, sorted alphabetically. This is what defines
120
- "the same combo" — resolution is intentionally excluded because the same
121
- sampler/steps/CFG combo works across resolutions and we want those to count
122
- together.
123
-
124
- Models and LoRAs are identified by their **AutoV2 content hash** (first 10 chars
125
- of SHA256), not filenames. This means renamed files, different quantizations of
126
- the same weights, or the same model downloaded from different sources all map to
127
- the same identity.
128
-
129
- ```json
130
- {
131
- "cfg": 4.0,
132
- "denoise": 1.0,
133
- "lora_hash": "A1B2C3D4E5",
134
- "lora_strength": 1.0,
135
- "model_family": "qwen_image",
136
- "model_hash": "F6G7H8I9J0",
137
- "sampler": "euler",
138
- "scheduler": "simple",
139
- "shift": 3.1,
140
- "steps": 4
141
- }
142
- ```
143
-
144
- **Excluded from hash** (tracked per-row but not part of the fingerprint):
145
- - `width`, `height` — resolution is a per-generation choice
146
- - `model_name`, `lora_name` — display names, not identity
147
- - `preset_name` — informational only
148
- - `neg_prompt_hash` — prompt content is never part of settings identity
149
-
150
- When a generation runs with an already-seen `settings_hash`, we UPDATE
151
- `reuse_count += 1` and `created_at` to the latest timestamp instead of inserting
152
- a duplicate row. This keeps the DB compact and gives us a natural popularity signal.
153
-
154
- ---
155
-
156
- ## File Identification Flow
157
-
158
- All models (checkpoints, UNETs) and LoRAs go through the same hashing pipeline.
159
- The `file_hashes` table caches results so we only compute SHA256 once per file version.
160
-
161
- ```
162
- 1. Workflow references a file → extract filename + resolve full path
163
- 2. stat() the file → get size + mtime
164
- 3. Check file_hashes table (by filename + size + mtime)
165
- ├─ Cache hit → use cached autov2 / civitai_id
166
- └─ Cache miss →
167
- a. Compute SHA256 of the .safetensors file
168
- b. Derive AutoV2 = sha256[:10].toUpperCase()
169
- c. Query CivitAI: GET /api/v1/model-versions/by-hash/{autov2}
170
- ├─ Found → store civitai_id, civitai_name, civitai_model_id
171
- └─ Not found → civitai_id = NULL (private/unknown)
172
- d. INSERT into file_hashes cache
173
- 4. Use autov2 as model_hash / lora_hash in the generations row
174
- 5. Use civitai_id for LoRA privacy filtering
175
- ```
176
-
177
- **Privacy rule**: Only generations where `lora_civitai_id IS NOT NULL` (or no LoRA)
178
- are eligible for community sharing. Private LoRAs never leave the machine.
179
-
180
- **Hashing note**: SHA256 of large safetensors files (2-12GB) takes 5-20 seconds.
181
- We run this in a worker thread and cache aggressively. The cache invalidates only
182
- when file size or mtime changes (renamed files keep their hash).
183
-
184
- ---
185
-
186
- ## Settings Advisor (MCP Hook)
187
-
188
- When building a new workflow, the MCP server queries the local DB:
189
-
190
- ```sql
191
- -- "What worked well for this model?"
192
- -- Groups by settings_hash (resolution-agnostic), shows most popular combos
193
- SELECT sampler, scheduler, steps, cfg, shift, denoise,
194
- lora_hash, lora_name, lora_strength,
195
- model_hash, model_name,
196
- reuse_count, preset_name
197
- FROM generations
198
- WHERE model_family = ?
199
- ORDER BY reuse_count DESC, created_at DESC
200
- LIMIT 10;
201
-
202
- -- "What worked well for this specific LoRA?"
203
- SELECT sampler, scheduler, steps, cfg, shift, denoise,
204
- model_hash, model_name, reuse_count
205
- FROM generations
206
- WHERE lora_hash = ?
207
- ORDER BY reuse_count DESC
208
- LIMIT 10;
209
- ```
210
-
211
- The advisor returns the top settings to the LLM as context, letting it
212
- suggest proven combos to the user. Combined with model-settings.json presets,
213
- this creates a feedback loop: defaults → user experiments → local tracking →
214
- better suggestions.
215
-
216
- ---
217
-
218
- ## NPM Scripts
219
-
220
- ### `npm run generations:review`
221
-
222
- Interactive CLI that shows what would be shared:
223
-
224
- ```
225
- $ npm run generations:review
226
-
227
- === Generation Settings Ready to Share ===
228
-
229
- Model Family Sampler/Sched Steps CFG LoRA Uses
230
- ───────────── ──────────────── ───── ──── ──────────────────────────── ────
231
- qwen_image euler/simple 4 1.0 Lightning-4steps (CivitAI) 47
232
- qwen_image euler_a/beta 50 4.0 (none) 12
233
- sdxl dpmpp_2m/karras 25 6.5 (none) 8
234
- illustrious euler_a/simple 22 4.5 (none) 5
235
-
236
- 4 entries (private LoRAs excluded)
237
-
238
- Share these with the community? [y/N]
239
- ```
240
-
241
- ### `npm run generations:stats`
242
-
243
- Local-only stats view:
244
-
245
- ```
246
- $ npm run generations:stats
247
-
248
- Total generations tracked: 234
249
- Unique setting combos: 18
250
- Most used: qwen_image / euler/simple / 4 steps (47 uses)
251
- Models: qwen_image (142), sdxl (52), illustrious (28), flux (12)
252
- ```
253
-
254
- ---
255
-
256
- ## Community Backend (Cloudflare)
257
-
258
- ### Stack
259
-
260
- - **Cloudflare Workers** — API endpoints (free tier: 100k req/day)
261
- - **Cloudflare D1** — SQLite-compatible edge database (free tier: 5M reads/day, 100k writes/day)
262
- - **Rate limiting** — Cloudflare's built-in rate limiting rules
263
-
264
- ### Endpoints
265
-
266
- #### `POST /api/v1/settings/submit`
267
-
268
- Submit anonymized generation settings.
269
-
270
- ```json
271
- // Request
272
- {
273
- "client_version": "0.2.0",
274
- "entries": [
275
- {
276
- "settings_hash": "abc123...",
277
- "model_family": "qwen_image",
278
- "model_hash": "F6G7H8I9J0",
279
- "model_name": "qwen_image_2512_fp8_e4m3fn.safetensors",
280
- "model_civitai_id": 789012,
281
- "sampler": "euler",
282
- "scheduler": "simple",
283
- "steps": 4,
284
- "cfg": 1.0,
285
- "denoise": 1.0,
286
- "shift": 3.1,
287
- "lora_hash": "A1B2C3D4E5",
288
- "lora_name": "Qwen-Image-Lightning-4steps-V1.0.safetensors",
289
- "lora_civitai_id": 123456,
290
- "lora_strength": 1.0,
291
- "reuse_count": 47
292
- }
293
- ]
294
- }
295
-
296
- // Response
297
- { "accepted": 1, "thank_you": true }
298
- ```
299
-
300
- **What is sent**: content hashes (AutoV2), CivitAI IDs, sampler params, reuse counts.
301
-
302
- **What is NOT sent**: prompts, negative prompts, image data, filenames, file paths,
303
- resolutions, private LoRA/model hashes (no CivitAI match), usernames, IP-derived
304
- location, timestamps.
305
-
306
- #### `GET /api/v1/settings/search`
307
-
308
- Query community-aggregated settings.
309
-
310
- ```
311
- GET /api/v1/settings/search?model_family=qwen_image&lora_civitai_id=123456
312
-
313
- // Response
314
- {
315
- "results": [
316
- {
317
- "model_family": "qwen_image",
318
- "model_hash": "F6G7H8I9J0",
319
- "model_name": "qwen_image_2512_fp8_e4m3fn.safetensors",
320
- "model_civitai_id": 789012,
321
- "sampler": "euler",
322
- "scheduler": "simple",
323
- "steps": 4,
324
- "cfg": 1.0,
325
- "denoise": 1.0,
326
- "shift": 3.1,
327
- "lora_hash": "A1B2C3D4E5",
328
- "lora_name": "Qwen-Image-Lightning-4steps-V1.0.safetensors",
329
- "lora_civitai_id": 123456,
330
- "lora_strength": 1.0,
331
- "total_uses": 1247,
332
- "unique_users": 89,
333
- "avg_reuse": 14.0
334
- }
335
- ],
336
- "total": 1,
337
- "cache_ttl": 3600
338
- }
339
- ```
340
-
341
- Searchable by: `model_family`, `model_hash`, `model_civitai_id`, `model_name`,
342
- `lora_hash`, `lora_civitai_id`, `lora_name`, `sampler`, `scheduler`.
343
- All filters are AND-combined. Name fields support `LIKE` / substring matching
344
- (e.g. `?model_name=copax` matches "CopaxTimelessV11.safetensors").
345
-
346
- Rate limit: 60 req/min per IP.
347
-
348
- #### `GET /api/v1/settings/popular`
349
-
350
- Top settings across all users, filterable.
351
-
352
- ```
353
- GET /api/v1/settings/popular?model_family=sdxl&limit=10
354
-
355
- // Response — same shape as search, ordered by total_uses DESC
356
- ```
357
-
358
- ### D1 Schema (Cloudflare)
359
-
360
- ```sql
361
- CREATE TABLE community_settings (
362
- id INTEGER PRIMARY KEY AUTOINCREMENT,
363
- settings_hash TEXT NOT NULL UNIQUE,
364
-
365
- -- Identity (hashes for dedup, names for full-text search)
366
- model_family TEXT NOT NULL,
367
- model_hash TEXT NOT NULL, -- AutoV2 of checkpoint/unet
368
- model_name TEXT, -- e.g. "qwen_image_2512_fp8_e4m3fn.safetensors"
369
- model_civitai_id INTEGER, -- CivitAI model version ID
370
- lora_hash TEXT, -- AutoV2 of LoRA (NULL if none)
371
- lora_name TEXT, -- e.g. "Qwen-Image-Lightning-4steps-V1.0.safetensors"
372
- lora_civitai_id INTEGER, -- CivitAI model version ID
373
- lora_strength REAL,
374
-
375
- -- Settings
376
- sampler TEXT NOT NULL,
377
- scheduler TEXT NOT NULL,
378
- steps INTEGER NOT NULL,
379
- cfg REAL NOT NULL,
380
- denoise REAL,
381
- shift REAL,
382
-
383
- -- Aggregates
384
- total_uses INTEGER NOT NULL DEFAULT 0,
385
- unique_users INTEGER NOT NULL DEFAULT 0,
386
- first_seen TEXT NOT NULL DEFAULT (datetime('now')),
387
- last_seen TEXT NOT NULL DEFAULT (datetime('now'))
388
- );
389
-
390
- CREATE INDEX idx_cs_model ON community_settings(model_family);
391
- CREATE INDEX idx_cs_model_hash ON community_settings(model_hash);
392
- CREATE INDEX idx_cs_lora_hash ON community_settings(lora_hash);
393
- CREATE INDEX idx_cs_lora_civ ON community_settings(lora_civitai_id);
394
- CREATE INDEX idx_cs_popular ON community_settings(total_uses DESC);
395
- ```
396
-
397
- On submit, the worker does an UPSERT keyed on `settings_hash`:
398
- - If new: INSERT with `total_uses = reuse_count`, `unique_users = 1`
399
- - If exists: `total_uses += reuse_count`, `unique_users += 1`
400
-
401
- ---
402
-
403
- ## MCP Tools (New)
404
-
405
- ### `generation_log`
406
- Called automatically when `enqueue_workflow` is invoked.
407
- Extracts settings from the executed workflow and logs to SQLite.
408
- Not exposed to the user — internal hook.
409
-
410
- ### `suggest_settings`
411
- ```
412
- Input: { model_family: "qwen_image", use_case?: "portrait" | "landscape" | ... }
413
- Output: Top local + community settings for the given model, ranked by reuse.
414
- ```
415
-
416
- ### `generation_stats`
417
- ```
418
- Input: { model_family?: string }
419
- Output: Local generation statistics summary.
420
- ```
421
-
422
- ---
423
-
424
- ## Implementation Order
425
-
426
- ### Phase 1: Local tracking (no network)
427
- 1. Add `better-sqlite3` dependency
428
- 2. Create `src/services/generation-tracker.ts` — DB init, log, query
429
- 3. Create `src/services/lora-identifier.ts` — SHA256 hashing + cache
430
- 4. Hook into workflow executor — log after successful run
431
- 5. Add `suggest_settings` MCP tool
432
- 6. Add `npm run generations:stats` script
433
-
434
- ### Phase 2: CivitAI identification
435
- 7. Add CivitAI hash lookup in lora-identifier
436
- 8. Cache results in `lora_hashes` table
437
- 9. Filter shareable vs private in generation log
438
-
439
- ### Phase 3: Community sharing
440
- 10. Stand up Cloudflare Worker + D1
441
- 11. Add `npm run generations:review` script (preview + confirm)
442
- 12. Add submit endpoint client in MCP server
443
- 13. Add `search_community_settings` MCP tool
444
- 14. Rate limiting + privacy review
445
-
446
- ---
447
-
448
- ## Privacy Guarantees
449
-
450
- 1. **Opt-in only** — Nothing is shared without explicit `npm run generations:review` confirmation
451
- 2. **No prompts** — Text prompts and negative prompts are never stored in shareable form
452
- 3. **No images** — No generated images or thumbnails
453
- 4. **No private LoRAs** — Only LoRAs with a verified CivitAI hash are included
454
- 5. **No PII** — No usernames, paths, IPs stored server-side (Cloudflare Workers don't log by default)
455
- 6. **Local-first** — The local DB works fully offline; community features are additive
456
- 7. **Reviewable** — Users see exactly what will be sent before confirming
457
- 8. **No tracking** — No analytics cookies, no device fingerprinting, no session tracking