feat(mcp): models registry and hash-versioned template cache pipeline#956
Conversation
Replace JSON \b escape sequences that parsed as backspace with correct v2/v3 labels across all locale index files.
…line Isolate MCP index tooling under scripts/mcp/ with deterministic sync, AI enhancement scripts, and dedicated data files for model profiles and template copy keyed by workflow JSON source hash.
📝 WalkthroughWalkthroughThe PR replaces ChangesMCP Index Pipeline
Template Data Fixes
Sequence Diagram(s)sequenceDiagram
participant Developer
participant sync_index.py
participant enhance_models_registry.py
participant enhance_descriptions.py
participant AI_Endpoint
participant index.mcp.json
Developer->>sync_index.py: npm run mcp (or --check)
sync_index.py->>sync_index.py: load index.json, template_cache, overrides, models_registry
sync_index.py->>sync_index.py: scan ComfyUI API nodes → api_node_model_options.json
sync_index.py->>index.mcp.json: write deterministic output
Developer->>enhance_models_registry.py: npm run mcp:models
enhance_models_registry.py->>AI_Endpoint: chat_json_completion (model profile prompt)
AI_Endpoint-->>enhance_models_registry.py: {summary, strengths, capabilities}
enhance_models_registry.py->>enhance_models_registry.py: write models_registry.json
Developer->>enhance_descriptions.py: npm run mcp:ai
enhance_descriptions.py->>AI_Endpoint: chat_completion (template description prompt)
AI_Endpoint-->>enhance_descriptions.py: description text
enhance_descriptions.py->>enhance_descriptions.py: update template_cache.json (source_hash keyed)
enhance_descriptions.py->>index.mcp.json: apply cache overlay and write
Possibly Related PRs
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🔤 Spellcheck Results
|
|
recheck |
1 similar comment
|
recheck |
|
✅ Upload JSON updated - 596 assets |
Introduce template_overrides.json for persistent recommend/freshness pins, enforce Use Cases minimum recommend of low, and refresh index.mcp.json with three new templates plus AI-enhanced descriptions in template_cache.
|
recheck |
1 similar comment
|
recheck |
Add new npm commands for MCP index synchronization and AI enhancements, including `mcp`, `mcp:check`, `mcp:ai`, and `mcp:models`. Update documentation in AGENTS.md, CLAUDE.md, and scripts/README.md to reflect these changes and clarify the MCP index pipeline. Introduce template overrides in template_overrides.json for improved recommendations.
Add Boogu and HappyHorse 1.1 registry entries, introduce registry_aliases.json with lookup_registry_profile for AI description generation, and normalize strengths tier labels from Krea-specific to generic Type labels.
|
recheck |
1 similar comment
|
recheck |
Refactor the enhance_descriptions.py and enhance_models_registry.py scripts to ensure that successful enhancements are immediately written to their respective cache files. This change allows for progress retention during interrupted runs and clarifies the output messages upon completion.
Delete krea_image_models.json and krea_registry_aliases.json as they are no longer needed. Update template descriptions in template_cache.json and index.mcp.json for improved clarity and accuracy, ensuring they align with the latest model capabilities and workflows.
|
recheck |
1 similar comment
|
recheck |
There was a problem hiding this comment.
Actionable comments posted: 24
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@CLAUDE.md`:
- Around line 32-33: Update the stale MCP data-file examples in CLAUDE.md so
they match the current MCP flow: replace references to models_capabilities.json
with models_registry.json, and make sure the surrounding data/ and MCP
documentation examples stay consistent with the files used by the relevant MCP
modules and cache flow. Check the sections around the data directory listing and
the later MCP examples so the filenames align with the current registry naming.
In `@scripts/data/krea_video_models.json`:
- Around line 1-433: This is generated scrape output and should not live under
scripts/data/. Move the krea_video_models snapshot (including scrapedAt and
triggerElement metadata) to scripts/.output/ or a repo-root report location, and
keep scripts/data/ reserved for stable hand-maintained config only. Update
whichever generation/export step produces this artifact so the saved path
matches the repo’s generated-output convention, using the krea_video_models.json
artifact as the target to relocate.
In `@scripts/data/mcp/api_node_model_options.json`:
- Around line 1-629: This scan cache JSON should not live under scripts/data/mcp
because it is regenerated from a local ComfyUI checkout and causes cross-machine
churn. Move the generated output path used by the sync/scan code so the live
cache is written to scripts/.output/ instead, and keep only a checked-in example
fixture if needed. Update the code that produces api_node_model_options.json and
any references to this artifact so they point to the new generated location,
using the existing scan/sync entrypoints that populate the MCP node model
options cache.
In `@scripts/data/mcp/models_registry.json`:
- Around line 92-100: Collapse the case-variant duplicate entries in the MCP
registry so each model name has only one canonical key. In the registry JSON,
deduplicate the repeated pairs like Kling O1/Kling o1, Kling O3/Kling o3, and
None/none by keeping a single normalized entry and merging the correct summary,
strengths, and capabilities into it. Update the affected registry objects in the
same model groups so the lookup layer’s case-insensitive fallback cannot resolve
to ambiguous or conflicting profiles.
In `@scripts/data/mcp/README.md`:
- Line 17: The docs link in README.md points to the wrong relative location,
resolving under scripts/data/mcp/docs instead of the intended scripts/mcp/docs.
Update the markdown link target in the README so it correctly references the
MCP_AI_ENHANCEMENT.md document from the current file’s location, using the
existing README link text and adjusting only the relative path.
In `@scripts/lib/ai/client.py`:
- Around line 51-67: The type annotations in parse_json_response and
chat_json_completion use Any, but it is not imported in this module, causing
Ruff F821. Update the imports in client.py to bring Any in from typing so both
function signatures resolve cleanly.
- Around line 38-43: Wrap all non-HTTP request failures in the AI client so
callers can handle them consistently. Update the request path in the client
function that uses urllib.request.urlopen and json.load to catch
URLError/timeout-style exceptions alongside HTTPError, and re-raise them as
RuntimeError with a clear message. Make sure
scripts/mcp/enhance_models_registry.py can rely on this behavior so a single
flaky model request is skipped and the batch continues.
In `@scripts/maintenance/krea_registry_sync.py`:
- Around line 30-36: The Krea registry sync currently hard-indexes required
fields in the loop, so a single malformed category or model can crash the whole
run. Update the record handling in krea_registry_sync.py around the data
iteration to safely skip entries missing category["name"] or model["name"],
while keeping the rest of the sync processing intact. Use the existing data loop
that builds entries to add the guard/validation before accessing those keys.
In `@scripts/maintenance/sync_registry_from_krea.py`:
- Around line 41-43: The registry merge in sync_registry_from_krea currently
assumes both REGISTRY_FILE and ALIASES_FILE contain object-shaped JSON, so add
early top-level type validation immediately after json.loads in
sync_registry_from_krea. Check that registry is a dict and aliases is a dict
before continuing, and raise a clear failure if either file contains a
list/string or any non-object value; keep the rest of the merge logic unchanged.
In `@scripts/mcp/bootstrap.py`:
- Around line 1-17: The shared bootstrap helper currently lives alongside MCP
CLI entrypoints, but it should be moved into scripts/lib/ as an import-only
module. Relocate install_paths() and the module-level path constants from
bootstrap.py into a shared library module under scripts/lib/, then update any
MCP scripts that import install_paths() to use the new location while keeping
the function behavior unchanged.
In `@scripts/mcp/docs/MCP_AI_ENHANCEMENT.md`:
- Around line 243-250: The table in MCP_AI_ENHANCEMENT.md has a duplicated entry
for scripts/data/mcp/models_registry.json with conflicting descriptions; remove
the redundant row or merge the two descriptions into one authoritative entry.
Update the table around the models registry references so only one
scripts/data/mcp/models_registry.json line remains, and make sure the
surrounding script entries in the MCP documentation section still clearly map
each script to its role.
In `@scripts/mcp/enhance_descriptions.py`:
- Around line 172-178: The `enhance_descriptions.py` startup path loads
`REGISTRY_FILE` without checking it exists first, so a missing
`models_registry.json` can crash instead of exiting cleanly. Add the same
prerequisite guard used in `enhance_models_registry.py` before the
`load_json(REGISTRY_FILE)` call in the main flow, and return a normal CLI error
when the file is absent; use the existing `MCP_FILE`, `REGISTRY_FILE`, and
`load_json` flow to place the check correctly.
In `@scripts/mcp/enhance_models_registry.py`:
- Around line 86-106: The normalize_profile function is treating
raw["strengths"] and raw["capabilities"] as iterable without validating they are
arrays first, which can cause strings or objects to be normalized into nonsense
before persistence. Update normalize_profile to explicitly verify both fields
are lists/arrays before building the strengths and capabilities lists, and raise
a ValueError when either field is the wrong type. Keep the fix localized to
normalize_profile and preserve the existing summary validation and output shape.
In `@scripts/mcp/import_template_cache.py`:
- Around line 40-49: The dry-run summary in import_templates_from_mcp is
under-counting work because it only compares template keys, so refreshed entries
with changed source_hash are missed. Update the counting logic in
import_template_cache.py to compare full template records before and after
migrate_legacy_cache(import_templates_from_mcp(...)) rather than just set
differences on templates keys. Use the existing cache/template structures in the
main import flow so the “Would add/update” total and the printed sample names
include both newly added and refreshed entries.
In `@scripts/mcp/lib/ai_context.py`:
- Around line 86-111: The model usage aggregation in model_template_usage
currently groups templates by the raw template model string, which can miss
registry lookups that expect canonical model keys. Normalize or canonicalize the
model name before adding entries to usage so aliases resolve to the same
registry key, and keep the existing sorting/truncation behavior intact in
model_template_usage and the downstream usage.get(name, []) consumer in
enhance_models_registry.py.
In `@scripts/mcp/lib/template_cache.py`:
- Around line 41-50: The load_template_cache function currently trusts
cache["templates"] after loading, but callers expect it to be a mapping and will
break if the JSON contains a non-dict value. Update load_template_cache to
validate the templates field during load, normalizing any non-dict value back to
an empty mapping before returning, alongside the existing schema_version and
legacy models cleanup. Keep the fix scoped to load_template_cache and the cache
shape it returns so downstream .get(name) lookups remain safe.
- Around line 141-147: The cache seeding logic in template_cache.py is only
persisting entries when description exists, which drops templates that have io
but no description. Update the entry filter in the seeding path so that
cache["templates"][name] is written whenever either description or io is
present, using the existing entry assembly around tpl, entry, and
cache["templates"].
In `@scripts/mcp/lib/template_overrides.py`:
- Around line 42-50: `load_template_overrides` currently only validates the root
JSON object, but `templates` can still be a non-mapping and later break
`sync_index.py` when it calls `.get()` on it. Update `load_template_overrides`
in `template_overrides.py` to validate or normalize the `templates` entry before
returning, alongside the existing `schema_version` and `empty_overrides()`
handling, so `templates` is always a dict (or the function falls back to safe
defaults) regardless of the override file contents.
In `@scripts/mcp/README.md`:
- Around line 9-23: The fenced code block in the README is missing a language
tag, which triggers markdownlint MD040. Update the opening fence in the section
that lists scripts/mcp and scripts/lib/ai to use a specific lexer such as text,
keeping the content unchanged and ensuring the fenced block is consistently
formatted.
In `@scripts/mcp/scan_api_nodes.py`:
- Around line 70-75: The emitted payload in scan_api_nodes.py currently includes
the absolute api_nodes_dir value under source, which leaks workstation-specific
paths into the shared JSON. Update the payload construction in the
scan/serialization flow to remove that absolute source field or replace it with
a stable non-local identifier, while keeping node_count and nodes intact, so
OUTPUT_FILE.write_text only writes portable data.
In `@scripts/mcp/sync_index.py`:
- Around line 537-544: The payload built in sync_index.py is writing the
caller’s absolute ComfyUI path into the shared cache via source in the JSON.
Update the payload construction in the sync logic that writes
API_NODE_OPTIONS_FILE so it does not serialize str(api_nodes_dir); use a stable,
non-local identifier or omit the field entirely if it is only used for
provenance, and keep the rest of the index generation unchanged.
- Around line 316-350: `infer_task_type()` is only deriving the task type from
the workflow name, so tagged workflows like Image to Image or Image to Video can
be misclassified when the filename lacks a matching token. Update
`infer_task_type` in `sync_index.py` to also consult template metadata/tags (as
`infer_task()` already does) before falling back to filename heuristics, and
ensure the task type written into `index.mcp.json` reflects the tag-based
contract for `infer_task`, `infer_task_type`, and the downstream `io` mapping.
- Around line 368-389: The _normalize_io_side function is coercing untrusted
slot["count"] values with int(slot.get("count", 1)), which can abort sync on
null, empty, or invalid text. Update _normalize_io_side to validate and safely
coerce count before using _encode_slot, skipping or defaulting bad entries
instead of raising, and apply the same defensive handling in the other io.count
normalization path referenced by the review.
In `@scripts/README.md`:
- Line 85: The README entry for Krea alias snapshots points to the wrong alias
file path, so update the documentation in the script guide to reference the MCP
alias location used by this rollout. In the affected markdown list item, replace
the `data/krea_registry_aliases.json` reference with
`data/mcp/registry_aliases.json` while keeping the rest of the Krea snapshot
note aligned with the existing seeding docs.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 42593f21-ad16-41db-924d-50200291d0df
📒 Files selected for processing (57)
.claude/skills/managing-mcp-index/SKILL.md.env.example.gitignoreAGENTS.mdCLAUDE.mdpackage.jsonscripts/README.mdscripts/data/krea_video_models.jsonscripts/data/mcp/README.mdscripts/data/mcp/api_node_model_options.jsonscripts/data/mcp/models_registry.jsonscripts/data/mcp/registry_aliases.jsonscripts/data/mcp/template_cache.example.jsonscripts/data/mcp/template_cache.jsonscripts/data/mcp/template_overrides.example.jsonscripts/data/mcp/template_overrides.jsonscripts/lib/ai/__init__.pyscripts/lib/ai/client.pyscripts/lib/ai/config.pyscripts/lib/env.pyscripts/lib/json_format.pyscripts/lib/paths.pyscripts/maintenance/krea_registry_sync.pyscripts/maintenance/sync_registry_from_krea.pyscripts/mcp/README.mdscripts/mcp/__init__.pyscripts/mcp/apply_template_cache.pyscripts/mcp/bootstrap.pyscripts/mcp/docs/MCP_AI_ENHANCEMENT.mdscripts/mcp/enhance_descriptions.pyscripts/mcp/enhance_models_registry.pyscripts/mcp/import_template_cache.pyscripts/mcp/lib/__init__.pyscripts/mcp/lib/ai_context.pyscripts/mcp/lib/comfyui_paths.pyscripts/mcp/lib/freshness_score.pyscripts/mcp/lib/recommend_score.pyscripts/mcp/lib/scan_api_node_models.pyscripts/mcp/lib/template_cache.pyscripts/mcp/lib/template_overrides.pyscripts/mcp/scan_api_nodes.pyscripts/mcp/sync_index.pyscripts/sync/sync_mcp_index.pytemplates/index.ar.jsontemplates/index.es.jsontemplates/index.fa.jsontemplates/index.fr.jsontemplates/index.ja.jsontemplates/index.jsontemplates/index.ko.jsontemplates/index.mcp.jsontemplates/index.pt-BR.jsontemplates/index.ru.jsontemplates/index.tr.jsontemplates/index.zh-TW.jsontemplates/index.zh.jsonworkflow_template_input_files.json
💤 Files with no reviewable changes (2)
- scripts/sync/sync_mcp_index.py
- .gitignore
| │ ├── data/ # i18n.json, whitelist.json, models_capabilities.json | ||
| │ ├── lib/ # Shared modules (locale_index_files, paths) | ||
| │ ├── lib/ # Shared modules (paths, locale_index_files, ai/) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Update stale MCP data-file examples (old name sneaks in, like a ghost in the host).
Line 32 and Line 99 still reference models_capabilities.json, but this PR’s MCP flow documents/uses data/mcp/models_registry.json (+ cache files). Keeping the old filename here can misroute maintenance work.
Suggested doc fix
-| `scripts/data/` # i18n.json, whitelist.json, models_capabilities.json
+| `scripts/data/` # i18n.json, whitelist.json, mcp/models_registry.json, mcp/template_cache.json
...
-| `scripts/data/` | Static config JSON | `i18n.json`, `whitelist.json`, `models_capabilities.json` |
+| `scripts/data/` | Static config JSON | `i18n.json`, `whitelist.json`, `mcp/models_registry.json`, `mcp/template_cache.json` |Also applies to: 99-100
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@CLAUDE.md` around lines 32 - 33, Update the stale MCP data-file examples in
CLAUDE.md so they match the current MCP flow: replace references to
models_capabilities.json with models_registry.json, and make sure the
surrounding data/ and MCP documentation examples stay consistent with the files
used by the relevant MCP modules and cache flow. Check the sections around the
data directory listing and the later MCP examples so the filenames align with
the current registry naming.
| { | ||
| "source": "https://www.krea.ai/video", | ||
| "scrapedAt": "2026-06-22T11:31:26.393Z", | ||
| "triggerElement": { | ||
| "note": "ID is dynamically generated by bits-ui; at scrape time it was bits-c15 (user reported bits-c13)", | ||
| "selector": "[data-dropdown-menu-trigger]", | ||
| "action": "click opens model gallery dialog" | ||
| }, | ||
| "totalCategories": 4, | ||
| "totalModels": 41, | ||
| "categories": [ | ||
| { | ||
| "name": "Fast Models", | ||
| "description": "Affordable models for fast creative exploration.", | ||
| "models": [ | ||
| { | ||
| "name": "Krea Realtime", | ||
| "slug": "krea_rtv_14b", | ||
| "description": "Real-time video generation model. Instant results at very low cost and quality.", | ||
| "capabilities": [], | ||
| "credits": 10 | ||
| }, | ||
| { | ||
| "name": "Hailuo 2.3 Fast", | ||
| "slug": "minimax_hailuo23_fast", | ||
| "description": "Cheapest medium-quality model. Best for most use cases.", | ||
| "capabilities": [ | ||
| "Start frame" | ||
| ], | ||
| "credits": 150 | ||
| }, | ||
| { | ||
| "name": "LTX-2.3 22B", | ||
| "slug": "ltx-23-22b", | ||
| "description": "High-quality Lightricks video generation model with native audio and start and end frame support.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "End frame", | ||
| "Lora" | ||
| ], | ||
| "credits": 150 | ||
| }, | ||
| { | ||
| "name": "Seedance 2.0 Fast", | ||
| "slug": "seedance-2-fast", | ||
| "description": "Faster ByteDance Seedance 2 variant with cinematic motion, optional synchronized audio, tagged image/video/audio references, and support for animating between start and end frames.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "End frame", | ||
| "References" | ||
| ], | ||
| "credits": 900 | ||
| }, | ||
| { | ||
| "name": "Veo 3.1 Lite", | ||
| "slug": "veo31-lite", | ||
| "description": "Faster and more affordable version of the powerful Veo 3.1 model.", | ||
| "capabilities": [ | ||
| "Start frame" | ||
| ], | ||
| "credits": 200 | ||
| }, | ||
| { | ||
| "name": "Grok Imagine", | ||
| "slug": "grok", | ||
| "description": "Fast, high-quality video generation by xAI.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "References" | ||
| ], | ||
| "credits": 300 | ||
| }, | ||
| { | ||
| "name": "LTX-2", | ||
| "slug": "ltx-2-19b", | ||
| "description": "Affordable medium-quality audio-video model from Lightricks with native sound generation.", | ||
| "capabilities": [ | ||
| "Start frame" | ||
| ], | ||
| "credits": 35 | ||
| }, | ||
| { | ||
| "name": "Seedance Pro Fast", | ||
| "slug": "seedance-1-0-pro-fast", | ||
| "description": "Fast and cheap model. Up to 12 seconds.", | ||
| "capabilities": [ | ||
| "Start frame" | ||
| ], | ||
| "credits": 85 | ||
| }, | ||
| { | ||
| "name": "Veo 3.1 Fast", | ||
| "slug": "veo31-fast", | ||
| "description": "Faster and more affordable version of the powerful Veo 3.1 model with audio.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "End frame" | ||
| ], | ||
| "credits": 450 | ||
| } | ||
| ] | ||
| }, | ||
| { | ||
| "name": "Intelligent Models", | ||
| "description": "Frontier models that think deeply to visualize complex ideas.", | ||
| "models": [ | ||
| { | ||
| "name": "Kling 3.0", | ||
| "slug": "kling_30", | ||
| "description": "Latest frontier model from Kling with native audio and extended durations up to 15 seconds.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "End frame" | ||
| ], | ||
| "credits": 600 | ||
| }, | ||
| { | ||
| "name": "Runway Gen-4.5", | ||
| "slug": "runwaygen45", | ||
| "description": "Latest frontier model from Runway with native text-to-video.", | ||
| "capabilities": [ | ||
| "Start frame" | ||
| ], | ||
| "credits": 450 | ||
| }, | ||
| { | ||
| "name": "Seedance 2.0", | ||
| "slug": "seedance-2", | ||
| "description": "ByteDance frontier video model with cinematic motion, optional synchronized audio, tagged image/video/audio references, and support for animating between start and end frames.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "End frame", | ||
| "References" | ||
| ], | ||
| "credits": 1100 | ||
| }, | ||
| { | ||
| "name": "HappyHorse", | ||
| "slug": "happy-horse", | ||
| "description": "HappyHorse generates synchronized audio-video clips from text, image references, or a source video edit instruction.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "References" | ||
| ], | ||
| "credits": 500 | ||
| }, | ||
| { | ||
| "name": "Kling o3", | ||
| "slug": "kling_o3", | ||
| "description": "Advanced reasoning video model (720p). Supports image, element, and video references for precise creative control.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "End frame" | ||
| ], | ||
| "credits": 300 | ||
| }, | ||
| { | ||
| "name": "Kling o3 Pro", | ||
| "slug": "kling_o3_pro", | ||
| "description": "Advanced reasoning video model (1080p). Supports image, element, and video references for precise creative control.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "End frame" | ||
| ], | ||
| "credits": 400 | ||
| }, | ||
| { | ||
| "name": "Veo 3.1", | ||
| "slug": "veo31", | ||
| "description": "Best video model. Highest-quality frontier model with audio and reference images.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "End frame", | ||
| "References" | ||
| ], | ||
| "credits": 1200 | ||
| }, | ||
| { | ||
| "name": "Veo 3", | ||
| "slug": "veo3", | ||
| "description": "Older version of the highest-quality frontier model Veo 3.1.", | ||
| "capabilities": [ | ||
| "Start frame" | ||
| ], | ||
| "credits": 1200 | ||
| }, | ||
| { | ||
| "name": "Veo 3 Fast", | ||
| "slug": "veo3-fast", | ||
| "description": "Older, faster and more affordable version of the leading Veo 3 model with audio.", | ||
| "capabilities": [ | ||
| "Start frame" | ||
| ], | ||
| "credits": 600 | ||
| } | ||
| ] | ||
| }, | ||
| { | ||
| "name": "Quality Models", | ||
| "description": "Explore a wide range of affordable models from around the world.", | ||
| "models": [ | ||
| { | ||
| "name": "Hailuo 2.3", | ||
| "slug": "minimax_hailuo23", | ||
| "description": "Frontier model with dynamic motion.", | ||
| "capabilities": [ | ||
| "Start frame" | ||
| ], | ||
| "credits": 200 | ||
| }, | ||
| { | ||
| "name": "Seedance 1.5 Pro", | ||
| "slug": "seedance-1-5-pro", | ||
| "description": "Latest medium quality model from ByteDance with audio generation and end frame support.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "End frame" | ||
| ], | ||
| "credits": 300 | ||
| }, | ||
| { | ||
| "name": "Kling o1", | ||
| "slug": "kling_omni", | ||
| "description": "Intelligent video model that thinks before generating. Supports image, element, and video references for precise creative control.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "End frame" | ||
| ], | ||
| "credits": 400 | ||
| }, | ||
| { | ||
| "name": "Wan 2.6", | ||
| "slug": "wan26", | ||
| "description": "Medium-quality model from Alibaba with improved quality and multi-shot support.", | ||
| "capabilities": [ | ||
| "Start frame" | ||
| ], | ||
| "credits": 350 | ||
| }, | ||
| { | ||
| "name": "Wan 2.5", | ||
| "slug": "wan25", | ||
| "description": "Latest medium quality model from Alibaba.", | ||
| "capabilities": [ | ||
| "Start frame" | ||
| ], | ||
| "credits": 350 | ||
| }, | ||
| { | ||
| "name": "Kling 2.6", | ||
| "slug": "kling_26", | ||
| "description": "Frontier model from Kling with native audio. Highest quality at a moderate price point.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "End frame" | ||
| ], | ||
| "credits": 250 | ||
| }, | ||
| { | ||
| "name": "Kling 2.5", | ||
| "slug": "kling_25", | ||
| "description": "Next-gen model with improved dynamics and enhanced style adaptation from Kling.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "End frame" | ||
| ], | ||
| "credits": 250 | ||
| }, | ||
| { | ||
| "name": "Kling 2.5 Turbo", | ||
| "slug": "kling_25_turbo", | ||
| "description": "Top-tier text-to-video generation with unparalleled motion fluidity and cinematic visuals.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "End frame" | ||
| ], | ||
| "credits": 250 | ||
| }, | ||
| { | ||
| "name": "Vidu Q2", | ||
| "slug": "vidu_q2", | ||
| "description": "High-quality model with reference images support.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "References" | ||
| ], | ||
| "credits": 200 | ||
| }, | ||
| { | ||
| "name": "Vidu Q3", | ||
| "slug": "vidu_q3", | ||
| "description": "New model excelling at anime.", | ||
| "capabilities": [ | ||
| "Start frame" | ||
| ], | ||
| "credits": 550 | ||
| }, | ||
| { | ||
| "name": "Hailuo 02", | ||
| "slug": "minimax_hailuo02", | ||
| "description": "Frontier model with dynamic motion.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "End frame" | ||
| ], | ||
| "credits": 200 | ||
| }, | ||
| { | ||
| "name": "Seedance Pro", | ||
| "slug": "seedance-1-0-pro", | ||
| "description": "Fast, high-quality model from ByteDance.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "End frame", | ||
| "References" | ||
| ], | ||
| "credits": 200 | ||
| } | ||
| ] | ||
| }, | ||
| { | ||
| "name": "Legacy Models", | ||
| "description": "Blasts from the past. Try out older models from various providers.", | ||
| "models": [ | ||
| { | ||
| "name": "Wan 2.1", | ||
| "slug": "wan", | ||
| "description": "Fastest low-quality model with video Lora support.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "End frame", | ||
| "Lora" | ||
| ], | ||
| "credits": 250 | ||
| }, | ||
| { | ||
| "name": "Wan 2.2", | ||
| "slug": "wan22", | ||
| "description": "Fast, lower-quality model from Alibaba.", | ||
| "capabilities": [ | ||
| "Start frame" | ||
| ], | ||
| "credits": 300 | ||
| }, | ||
| { | ||
| "name": "Kling 2.1", | ||
| "slug": "kling_21", | ||
| "description": "Frontier model with 1080p resolution.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "End frame" | ||
| ], | ||
| "credits": 200 | ||
| }, | ||
| { | ||
| "name": "Runway Gen-4", | ||
| "slug": "runwaygen4", | ||
| "description": "Medium quality model with a focus on cinematic visuals but weaker structure and motion.", | ||
| "capabilities": [ | ||
| "Start frame" | ||
| ], | ||
| "credits": 200 | ||
| }, | ||
| { | ||
| "name": "Runway Gen-3", | ||
| "slug": "runway", | ||
| "description": "Old generation cinematic model with higher consistency.", | ||
| "capabilities": [ | ||
| "Start frame" | ||
| ], | ||
| "credits": 200 | ||
| }, | ||
| { | ||
| "name": "Kling 1.6", | ||
| "slug": "kling_16", | ||
| "description": "Previous generation high-quality model for complex scenes.", | ||
| "capabilities": [ | ||
| "Start frame" | ||
| ], | ||
| "credits": 150 | ||
| }, | ||
| { | ||
| "name": "Kling 2.0", | ||
| "slug": "kling_2", | ||
| "description": "High-quality model with great aesthetics.", | ||
| "capabilities": [ | ||
| "Start frame" | ||
| ], | ||
| "credits": 1000 | ||
| }, | ||
| { | ||
| "name": "Hailuo", | ||
| "slug": "minimax", | ||
| "description": "High-quality model with camera control.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "Camera", | ||
| "Character" | ||
| ], | ||
| "credits": 300 | ||
| }, | ||
| { | ||
| "name": "01-Live", | ||
| "slug": "minimax_live", | ||
| "description": "High-quality model for animating people.", | ||
| "capabilities": [ | ||
| "Start frame" | ||
| ], | ||
| "credits": 300 | ||
| }, | ||
| { | ||
| "name": "Ray 2", | ||
| "slug": "ray2", | ||
| "description": "Older medium model with natural motion from Luma Labs.", | ||
| "capabilities": [ | ||
| "Start frame" | ||
| ], | ||
| "credits": 300 | ||
| }, | ||
| { | ||
| "name": "Kling 1.0 (Pro)", | ||
| "slug": "klingPro", | ||
| "description": "Slow model with high control and 10s duration.", | ||
| "capabilities": [ | ||
| "Start frame", | ||
| "End frame" | ||
| ], | ||
| "credits": null | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
Do not commit generated scrape output under scripts/data/.
This file is generated snapshot data (e.g., scrapedAt, DOM trigger metadata) and violates the scripts/data/*.json rule. Please move generated artifacts to scripts/.output/ (or a repo-root report path) and keep only stable hand-maintained config in scripts/data/.
As per coding guidelines: “Generated JSON must not be committed under scripts/data/; generated output should go to scripts/.output/ or repo-root reports.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/data/krea_video_models.json` around lines 1 - 433, This is generated
scrape output and should not live under scripts/data/. Move the
krea_video_models snapshot (including scrapedAt and triggerElement metadata) to
scripts/.output/ or a repo-root report location, and keep scripts/data/ reserved
for stable hand-maintained config only. Update whichever generation/export step
produces this artifact so the saved path matches the repo’s generated-output
convention, using the krea_video_models.json artifact as the target to relocate.
Source: Coding guidelines
| { | ||
| "source": "/Users/linmoumou/Documents/Github/ComfyUI/comfy_api_nodes", | ||
| "node_count": 89, | ||
| "nodes": { | ||
| "Flux2ImageNode": { | ||
| "node_id": "Flux2ImageNode", | ||
| "class_name": "Flux2ImageNode", | ||
| "display_name": "Flux.2 Image", | ||
| "model_options": ["Flux.2 [pro]", "Flux.2 [max]"], | ||
| "source_file": "nodes_bfl.py" | ||
| }, | ||
| "BriaImageEditNode": { | ||
| "node_id": "BriaImageEditNode", | ||
| "class_name": "BriaImageEditNode", | ||
| "display_name": "Bria FIBO Image Edit", | ||
| "model_options": ["FIBO"], | ||
| "source_file": "nodes_bria.py" | ||
| }, | ||
| "ByteDanceImageNode": { | ||
| "node_id": "ByteDanceImageNode", | ||
| "class_name": "ByteDanceImageNode", | ||
| "display_name": "ByteDance Image", | ||
| "model_options": ["seedream-3-0-t2i-250415"], | ||
| "source_file": "nodes_bytedance.py" | ||
| }, | ||
| "ByteDanceSeedreamNode": { | ||
| "node_id": "ByteDanceSeedreamNode", | ||
| "class_name": "ByteDanceSeedreamNode", | ||
| "display_name": "ByteDance Seedream 4.5 & 5.0", | ||
| "model_options": ["seedream 5.0 lite", "seedream-4-5-251128", "seedream-4-0-250828"], | ||
| "source_file": "nodes_bytedance.py" | ||
| }, | ||
| "ByteDanceSeedreamNodeV2": { | ||
| "node_id": "ByteDanceSeedreamNodeV2", | ||
| "class_name": "ByteDanceSeedreamNodeV2", | ||
| "display_name": "ByteDance Seedream 4.5 & 5.0", | ||
| "model_options": ["seedream 5.0 lite", "seedream-4-5-251128", "seedream-4-0-250828"], | ||
| "source_file": "nodes_bytedance.py" | ||
| }, | ||
| "ByteDanceTextToVideoNode": { | ||
| "node_id": "ByteDanceTextToVideoNode", | ||
| "class_name": "ByteDanceTextToVideoNode", | ||
| "display_name": "ByteDance Text to Video", | ||
| "model_options": ["seedance-1-5-pro-251215", "seedance-1-0-pro-250528", "seedance-1-0-lite-t2v-250428", "seedance-1-0-pro-fast-251015"], | ||
| "source_file": "nodes_bytedance.py" | ||
| }, | ||
| "ByteDanceImageToVideoNode": { | ||
| "node_id": "ByteDanceImageToVideoNode", | ||
| "class_name": "ByteDanceImageToVideoNode", | ||
| "display_name": "ByteDance Image to Video", | ||
| "model_options": ["seedance-1-5-pro-251215", "seedance-1-0-pro-250528", "seedance-1-0-lite-i2v-250428", "seedance-1-0-pro-fast-251015"], | ||
| "source_file": "nodes_bytedance.py" | ||
| }, | ||
| "ByteDanceFirstLastFrameNode": { | ||
| "node_id": "ByteDanceFirstLastFrameNode", | ||
| "class_name": "ByteDanceFirstLastFrameNode", | ||
| "display_name": "ByteDance First-Last-Frame to Video", | ||
| "model_options": ["seedance-1-5-pro-251215", "seedance-1-0-pro-250528", "seedance-1-0-lite-i2v-250428"], | ||
| "source_file": "nodes_bytedance.py" | ||
| }, | ||
| "ByteDanceImageReferenceNode": { | ||
| "node_id": "ByteDanceImageReferenceNode", | ||
| "class_name": "ByteDanceImageReferenceNode", | ||
| "display_name": "ByteDance Reference Images to Video", | ||
| "model_options": ["seedance-1-0-pro-250528", "seedance-1-0-lite-i2v-250428"], | ||
| "source_file": "nodes_bytedance.py" | ||
| }, | ||
| "ByteDance2TextToVideoNode": { | ||
| "node_id": "ByteDance2TextToVideoNode", | ||
| "class_name": "ByteDance2TextToVideoNode", | ||
| "display_name": "ByteDance Seedance 2.0 Text to Video", | ||
| "model_options": ["Seedance 2.0", "Seedance 2.0 Fast"], | ||
| "source_file": "nodes_bytedance.py" | ||
| }, | ||
| "ByteDance2FirstLastFrameNode": { | ||
| "node_id": "ByteDance2FirstLastFrameNode", | ||
| "class_name": "ByteDance2FirstLastFrameNode", | ||
| "display_name": "ByteDance Seedance 2.0 First-Last-Frame to Video", | ||
| "model_options": ["Seedance 2.0", "Seedance 2.0 Fast"], | ||
| "source_file": "nodes_bytedance.py" | ||
| }, | ||
| "ByteDance2ReferenceNode": { | ||
| "node_id": "ByteDance2ReferenceNode", | ||
| "class_name": "ByteDance2ReferenceNode", | ||
| "display_name": "ByteDance Seedance 2.0 Reference to Video", | ||
| "model_options": ["Seedance 2.0", "Seedance 2.0 Fast"], | ||
| "source_file": "nodes_bytedance.py" | ||
| }, | ||
| "ElevenLabsSpeechToText": { | ||
| "node_id": "ElevenLabsSpeechToText", | ||
| "class_name": "ElevenLabsSpeechToText", | ||
| "display_name": "ElevenLabs Speech to Text", | ||
| "model_options": ["scribe_v2"], | ||
| "source_file": "nodes_elevenlabs.py" | ||
| }, | ||
| "ElevenLabsTextToSpeech": { | ||
| "node_id": "ElevenLabsTextToSpeech", | ||
| "class_name": "ElevenLabsTextToSpeech", | ||
| "display_name": "ElevenLabs Text to Speech", | ||
| "model_options": ["eleven_multilingual_v2", "eleven_v3"], | ||
| "source_file": "nodes_elevenlabs.py" | ||
| }, | ||
| "ElevenLabsTextToSoundEffects": { | ||
| "node_id": "ElevenLabsTextToSoundEffects", | ||
| "class_name": "ElevenLabsTextToSoundEffects", | ||
| "display_name": "ElevenLabs Text to Sound Effects", | ||
| "model_options": ["eleven_sfx_v2"], | ||
| "source_file": "nodes_elevenlabs.py" | ||
| }, | ||
| "ElevenLabsSpeechToSpeech": { | ||
| "node_id": "ElevenLabsSpeechToSpeech", | ||
| "class_name": "ElevenLabsSpeechToSpeech", | ||
| "display_name": "ElevenLabs Speech to Speech", | ||
| "model_options": ["eleven_multilingual_sts_v2", "eleven_english_sts_v2"], | ||
| "source_file": "nodes_elevenlabs.py" | ||
| }, | ||
| "ElevenLabsTextToDialogue": { | ||
| "node_id": "ElevenLabsTextToDialogue", | ||
| "class_name": "ElevenLabsTextToDialogue", | ||
| "display_name": "ElevenLabs Text to Dialogue", | ||
| "model_options": ["eleven_v3"], | ||
| "source_file": "nodes_elevenlabs.py" | ||
| }, | ||
| "GeminiNode": { | ||
| "node_id": "GeminiNode", | ||
| "class_name": "GeminiNode", | ||
| "display_name": "Google Gemini", | ||
| "model_options": ["gemini-2.5-pro", "gemini-2.5-flash", "gemini-3-pro-preview", "gemini-3-1-pro", "gemini-3-1-flash-lite"], | ||
| "source_file": "nodes_gemini.py" | ||
| }, | ||
| "GeminiNodeV2": { | ||
| "node_id": "GeminiNodeV2", | ||
| "class_name": "GeminiNodeV2", | ||
| "display_name": "Google Gemini", | ||
| "model_options": ["Gemini 3.1 Pro", "Gemini 3.1 Flash-Lite"], | ||
| "source_file": "nodes_gemini.py" | ||
| }, | ||
| "GeminiImageNode": { | ||
| "node_id": "GeminiImageNode", | ||
| "class_name": "GeminiImage", | ||
| "display_name": "Nano Banana (Google Gemini Image)", | ||
| "model_options": ["gemini-2.5-flash-image"], | ||
| "source_file": "nodes_gemini.py" | ||
| }, | ||
| "GeminiImage2Node": { | ||
| "node_id": "GeminiImage2Node", | ||
| "class_name": "GeminiImage2", | ||
| "display_name": "Nano Banana Pro (Google Gemini Image)", | ||
| "model_options": ["gemini-3-pro-image-preview", "Nano Banana 2 (Gemini 3.1 Flash Image)"], | ||
| "source_file": "nodes_gemini.py" | ||
| }, | ||
| "GeminiNanoBanana2": { | ||
| "node_id": "GeminiNanoBanana2", | ||
| "class_name": "GeminiNanoBanana2", | ||
| "display_name": "Nano Banana 2", | ||
| "model_options": ["Nano Banana 2 (Gemini 3.1 Flash Image)"], | ||
| "source_file": "nodes_gemini.py" | ||
| }, | ||
| "GeminiNanoBanana2V2": { | ||
| "node_id": "GeminiNanoBanana2V2", | ||
| "class_name": "GeminiNanoBanana2V2", | ||
| "display_name": "Nano Banana 2", | ||
| "model_options": ["Nano Banana 2 (Gemini 3.1 Flash Image)"], | ||
| "source_file": "nodes_gemini.py" | ||
| }, | ||
| "GrokImageNode": { | ||
| "node_id": "GrokImageNode", | ||
| "class_name": "GrokImageNode", | ||
| "display_name": "Grok Image", | ||
| "model_options": ["grok-imagine-image-quality", "grok-imagine-image-pro", "grok-imagine-image"], | ||
| "source_file": "nodes_grok.py" | ||
| }, | ||
| "GrokImageEditNode": { | ||
| "node_id": "GrokImageEditNode", | ||
| "class_name": "GrokImageEditNode", | ||
| "display_name": "Grok Image Edit", | ||
| "model_options": ["grok-imagine-image-quality", "grok-imagine-image-pro", "grok-imagine-image"], | ||
| "source_file": "nodes_grok.py" | ||
| }, | ||
| "GrokImageEditNodeV2": { | ||
| "node_id": "GrokImageEditNodeV2", | ||
| "class_name": "GrokImageEditNodeV2", | ||
| "display_name": "Grok Image Edit", | ||
| "model_options": ["grok-imagine-image-quality", "grok-imagine-image-pro", "grok-imagine-image"], | ||
| "source_file": "nodes_grok.py" | ||
| }, | ||
| "GrokVideoNode": { | ||
| "node_id": "GrokVideoNode", | ||
| "class_name": "GrokVideoNode", | ||
| "display_name": "Grok Video", | ||
| "model_options": ["grok-imagine-video", "grok-imagine-video-1.5"], | ||
| "source_file": "nodes_grok.py" | ||
| }, | ||
| "GrokVideoEditNode": { | ||
| "node_id": "GrokVideoEditNode", | ||
| "class_name": "GrokVideoEditNode", | ||
| "display_name": "Grok Video Edit", | ||
| "model_options": ["grok-imagine-video"], | ||
| "source_file": "nodes_grok.py" | ||
| }, | ||
| "GrokVideoReferenceNode": { | ||
| "node_id": "GrokVideoReferenceNode", | ||
| "class_name": "GrokVideoReferenceNode", | ||
| "display_name": "Grok Reference-to-Video", | ||
| "model_options": ["grok-imagine-video"], | ||
| "source_file": "nodes_grok.py" | ||
| }, | ||
| "GrokVideoExtendNode": { | ||
| "node_id": "GrokVideoExtendNode", | ||
| "class_name": "GrokVideoExtendNode", | ||
| "display_name": "Grok Video Extend", | ||
| "model_options": ["grok-imagine-video"], | ||
| "source_file": "nodes_grok.py" | ||
| }, | ||
| "HitPawGeneralImageEnhance": { | ||
| "node_id": "HitPawGeneralImageEnhance", | ||
| "class_name": "HitPawGeneralImageEnhance", | ||
| "display_name": "HitPaw General Image Enhance", | ||
| "model_options": ["generative_portrait", "generative"], | ||
| "source_file": "nodes_hitpaw.py" | ||
| }, | ||
| "HitPawVideoEnhance": { | ||
| "node_id": "HitPawVideoEnhance", | ||
| "class_name": "HitPawVideoEnhance", | ||
| "display_name": "HitPaw Video Enhance", | ||
| "model_options": ["Portrait Restore Model (1x)", "Portrait Restore Model (2x)", "General Restore Model (1x)", "General Restore Model (2x)", "General Restore Model (4x)", "Ultra HD Model (2x)", "Generative Model (1x)"], | ||
| "source_file": "nodes_hitpaw.py" | ||
| }, | ||
| "TencentTextToModelNode": { | ||
| "node_id": "TencentTextToModelNode", | ||
| "class_name": "TencentTextToModelNode", | ||
| "display_name": "Hunyuan3D: Text to Model", | ||
| "model_options": ["3.0", "3.1"], | ||
| "source_file": "nodes_hunyuan3d.py" | ||
| }, | ||
| "TencentImageToModelNode": { | ||
| "node_id": "TencentImageToModelNode", | ||
| "class_name": "TencentImageToModelNode", | ||
| "display_name": "Hunyuan3D: Image(s) to Model", | ||
| "model_options": ["3.0", "3.1"], | ||
| "source_file": "nodes_hunyuan3d.py" | ||
| }, | ||
| "KlingMotionControl": { | ||
| "node_id": "KlingMotionControl", | ||
| "class_name": "MotionControl", | ||
| "display_name": "Kling Motion Control", | ||
| "model_options": ["kling-v3", "kling-v2-6"], | ||
| "source_file": "nodes_kling.py" | ||
| }, | ||
| "KlingVideoNode": { | ||
| "node_id": "KlingVideoNode", | ||
| "class_name": "KlingVideoNode", | ||
| "display_name": "Kling 3.0 Video", | ||
| "model_options": ["kling-v3", "kling-3.0-turbo"], | ||
| "source_file": "nodes_kling.py" | ||
| }, | ||
| "KlingFirstLastFrameNode": { | ||
| "node_id": "KlingFirstLastFrameNode", | ||
| "class_name": "KlingFirstLastFrameNode", | ||
| "display_name": "Kling 3.0 First-Last-Frame to Video", | ||
| "model_options": ["kling-v3"], | ||
| "source_file": "nodes_kling.py" | ||
| }, | ||
| "LtxvApiTextToVideo": { | ||
| "node_id": "LtxvApiTextToVideo", | ||
| "class_name": "TextToVideoNode", | ||
| "display_name": "LTXV Text To Video", | ||
| "model_options": ["LTX-2 (Pro)", "LTX-2 (Fast)"], | ||
| "source_file": "nodes_ltxv.py" | ||
| }, | ||
| "LtxvApiImageToVideo": { | ||
| "node_id": "LtxvApiImageToVideo", | ||
| "class_name": "ImageToVideoNode", | ||
| "display_name": "LTXV Image To Video", | ||
| "model_options": ["LTX-2 (Pro)", "LTX-2 (Fast)"], | ||
| "source_file": "nodes_ltxv.py" | ||
| }, | ||
| "LumaImageNode2": { | ||
| "node_id": "LumaImageNode2", | ||
| "class_name": "LumaImageNode", | ||
| "display_name": "Luma UNI-1 Image", | ||
| "model_options": ["uni-1", "uni-1-max"], | ||
| "source_file": "nodes_luma.py" | ||
| }, | ||
| "LumaImageEditNode2": { | ||
| "node_id": "LumaImageEditNode2", | ||
| "class_name": "LumaImageEditNode", | ||
| "display_name": "Luma UNI-1 Image Edit", | ||
| "model_options": ["uni-1", "uni-1-max"], | ||
| "source_file": "nodes_luma.py" | ||
| }, | ||
| "MeshyTextToModelNode": { | ||
| "node_id": "MeshyTextToModelNode", | ||
| "class_name": "MeshyTextToModelNode", | ||
| "display_name": "Meshy: Text to Model", | ||
| "model_options": ["latest"], | ||
| "source_file": "nodes_meshy.py" | ||
| }, | ||
| "MeshyRefineNode": { | ||
| "node_id": "MeshyRefineNode", | ||
| "class_name": "MeshyRefineNode", | ||
| "display_name": "Meshy: Refine Draft Model", | ||
| "model_options": ["latest"], | ||
| "source_file": "nodes_meshy.py" | ||
| }, | ||
| "MeshyImageToModelNode": { | ||
| "node_id": "MeshyImageToModelNode", | ||
| "class_name": "MeshyImageToModelNode", | ||
| "display_name": "Meshy: Image to Model", | ||
| "model_options": ["latest"], | ||
| "source_file": "nodes_meshy.py" | ||
| }, | ||
| "MeshyMultiImageToModelNode": { | ||
| "node_id": "MeshyMultiImageToModelNode", | ||
| "class_name": "MeshyMultiImageToModelNode", | ||
| "display_name": "Meshy: Multi-Image to Model", | ||
| "model_options": ["latest"], | ||
| "source_file": "nodes_meshy.py" | ||
| }, | ||
| "MeshyTextureNode": { | ||
| "node_id": "MeshyTextureNode", | ||
| "class_name": "MeshyTextureNode", | ||
| "display_name": "Meshy: Texture Model", | ||
| "model_options": ["latest"], | ||
| "source_file": "nodes_meshy.py" | ||
| }, | ||
| "MinimaxTextToVideoNode": { | ||
| "node_id": "MinimaxTextToVideoNode", | ||
| "class_name": "MinimaxTextToVideoNode", | ||
| "display_name": "MiniMax Text to Video", | ||
| "model_options": ["T2V-01", "T2V-01-Director"], | ||
| "source_file": "nodes_minimax.py" | ||
| }, | ||
| "MinimaxImageToVideoNode": { | ||
| "node_id": "MinimaxImageToVideoNode", | ||
| "class_name": "MinimaxImageToVideoNode", | ||
| "display_name": "MiniMax Image to Video", | ||
| "model_options": ["I2V-01-Director", "I2V-01", "I2V-01-live"], | ||
| "source_file": "nodes_minimax.py" | ||
| }, | ||
| "MinimaxSubjectToVideoNode": { | ||
| "node_id": "MinimaxSubjectToVideoNode", | ||
| "class_name": "MinimaxSubjectToVideoNode", | ||
| "display_name": "MiniMax Subject to Video", | ||
| "model_options": ["S2V-01"], | ||
| "source_file": "nodes_minimax.py" | ||
| }, | ||
| "OpenAIGPTImage1": { | ||
| "node_id": "OpenAIGPTImage1", | ||
| "class_name": "OpenAIGPTImage1", | ||
| "display_name": "OpenAI GPT Image 2", | ||
| "model_options": ["gpt-image-1", "gpt-image-1.5", "gpt-image-2"], | ||
| "source_file": "nodes_openai.py" | ||
| }, | ||
| "OpenAIGPTImageNodeV2": { | ||
| "node_id": "OpenAIGPTImageNodeV2", | ||
| "class_name": "OpenAIGPTImageNodeV2", | ||
| "display_name": "OpenAI GPT Image 2", | ||
| "model_options": ["gpt-image-2", "gpt-image-1.5", "gpt-image-1"], | ||
| "source_file": "nodes_openai.py" | ||
| }, | ||
| "RecraftV4TextToImageNode": { | ||
| "node_id": "RecraftV4TextToImageNode", | ||
| "class_name": "RecraftV4TextToImageNode", | ||
| "display_name": "Recraft V4 Text to Image", | ||
| "model_options": ["recraftv4", "recraftv4_pro"], | ||
| "source_file": "nodes_recraft.py" | ||
| }, | ||
| "RecraftV4TextToVectorNode": { | ||
| "node_id": "RecraftV4TextToVectorNode", | ||
| "class_name": "RecraftV4TextToVectorNode", | ||
| "display_name": "Recraft V4 Text to Vector", | ||
| "model_options": ["recraftv4", "recraftv4_pro"], | ||
| "source_file": "nodes_recraft.py" | ||
| }, | ||
| "OpenAIVideoSora2": { | ||
| "node_id": "OpenAIVideoSora2", | ||
| "class_name": "OpenAIVideoSora2", | ||
| "display_name": "OpenAI Sora - Video (DEPRECATED)", | ||
| "model_options": ["sora-2", "sora-2-pro"], | ||
| "source_file": "nodes_sora.py" | ||
| }, | ||
| "StabilityTextToAudio": { | ||
| "node_id": "StabilityTextToAudio", | ||
| "class_name": "StabilityTextToAudio", | ||
| "display_name": "Stability AI Text To Audio", | ||
| "model_options": ["stable-audio-2.5"], | ||
| "source_file": "nodes_stability.py" | ||
| }, | ||
| "StabilityAudioToAudio": { | ||
| "node_id": "StabilityAudioToAudio", | ||
| "class_name": "StabilityAudioToAudio", | ||
| "display_name": "Stability AI Audio To Audio", | ||
| "model_options": ["stable-audio-2.5"], | ||
| "source_file": "nodes_stability.py" | ||
| }, | ||
| "StabilityAudioInpaint": { | ||
| "node_id": "StabilityAudioInpaint", | ||
| "class_name": "StabilityAudioInpaint", | ||
| "display_name": "Stability AI Audio Inpaint", | ||
| "model_options": ["stable-audio-2.5"], | ||
| "source_file": "nodes_stability.py" | ||
| }, | ||
| "TopazImageEnhance": { | ||
| "node_id": "TopazImageEnhance", | ||
| "class_name": "TopazImageEnhance", | ||
| "display_name": "Topaz Image Enhance", | ||
| "model_options": ["Reimagine"], | ||
| "source_file": "nodes_topaz.py" | ||
| }, | ||
| "VeoVideoGenerationNode": { | ||
| "node_id": "VeoVideoGenerationNode", | ||
| "class_name": "VeoVideoGenerationNode", | ||
| "display_name": "Google Veo 2 Video Generation", | ||
| "model_options": ["veo-2.0-generate-001"], | ||
| "source_file": "nodes_veo2.py" | ||
| }, | ||
| "Veo3VideoGenerationNode": { | ||
| "node_id": "Veo3VideoGenerationNode", | ||
| "class_name": "Veo3VideoGenerationNode", | ||
| "display_name": "Google Veo 3 Video Generation", | ||
| "model_options": ["veo-3.1-generate", "veo-3.1-fast-generate", "veo-3.1-lite", "veo-3.0-generate-001", "veo-3.0-fast-generate-001"], | ||
| "source_file": "nodes_veo2.py" | ||
| }, | ||
| "Veo3FirstLastFrameNode": { | ||
| "node_id": "Veo3FirstLastFrameNode", | ||
| "class_name": "Veo3FirstLastFrameNode", | ||
| "display_name": "Google Veo 3 First-Last-Frame to Video", | ||
| "model_options": ["veo-3.1-generate", "veo-3.1-fast-generate", "veo-3.1-lite"], | ||
| "source_file": "nodes_veo2.py" | ||
| }, | ||
| "ViduTextToVideoNode": { | ||
| "node_id": "ViduTextToVideoNode", | ||
| "class_name": "ViduTextToVideoNode", | ||
| "display_name": "Vidu Text To Video Generation", | ||
| "model_options": ["viduq1"], | ||
| "source_file": "nodes_vidu.py" | ||
| }, | ||
| "ViduImageToVideoNode": { | ||
| "node_id": "ViduImageToVideoNode", | ||
| "class_name": "ViduImageToVideoNode", | ||
| "display_name": "Vidu Image To Video Generation", | ||
| "model_options": ["viduq1"], | ||
| "source_file": "nodes_vidu.py" | ||
| }, | ||
| "ViduReferenceVideoNode": { | ||
| "node_id": "ViduReferenceVideoNode", | ||
| "class_name": "ViduReferenceVideoNode", | ||
| "display_name": "Vidu Reference To Video Generation", | ||
| "model_options": ["viduq1"], | ||
| "source_file": "nodes_vidu.py" | ||
| }, | ||
| "ViduStartEndToVideoNode": { | ||
| "node_id": "ViduStartEndToVideoNode", | ||
| "class_name": "ViduStartEndToVideoNode", | ||
| "display_name": "Vidu Start End To Video Generation", | ||
| "model_options": ["viduq1"], | ||
| "source_file": "nodes_vidu.py" | ||
| }, | ||
| "Vidu2TextToVideoNode": { | ||
| "node_id": "Vidu2TextToVideoNode", | ||
| "class_name": "Vidu2TextToVideoNode", | ||
| "display_name": "Vidu2 Text-to-Video Generation", | ||
| "model_options": ["viduq2"], | ||
| "source_file": "nodes_vidu.py" | ||
| }, | ||
| "Vidu2ImageToVideoNode": { | ||
| "node_id": "Vidu2ImageToVideoNode", | ||
| "class_name": "Vidu2ImageToVideoNode", | ||
| "display_name": "Vidu2 Image-to-Video Generation", | ||
| "model_options": ["viduq2-pro-fast", "viduq2-pro", "viduq2-turbo"], | ||
| "source_file": "nodes_vidu.py" | ||
| }, | ||
| "Vidu2ReferenceVideoNode": { | ||
| "node_id": "Vidu2ReferenceVideoNode", | ||
| "class_name": "Vidu2ReferenceVideoNode", | ||
| "display_name": "Vidu2 Reference-to-Video Generation", | ||
| "model_options": ["viduq2"], | ||
| "source_file": "nodes_vidu.py" | ||
| }, | ||
| "Vidu2StartEndToVideoNode": { | ||
| "node_id": "Vidu2StartEndToVideoNode", | ||
| "class_name": "Vidu2StartEndToVideoNode", | ||
| "display_name": "Vidu2 Start/End Frame-to-Video Generation", | ||
| "model_options": ["viduq2-pro-fast", "viduq2-pro", "viduq2-turbo"], | ||
| "source_file": "nodes_vidu.py" | ||
| }, | ||
| "ViduExtendVideoNode": { | ||
| "node_id": "ViduExtendVideoNode", | ||
| "class_name": "ViduExtendVideoNode", | ||
| "display_name": "Vidu Video Extension", | ||
| "model_options": ["viduq2-pro", "viduq2-turbo"], | ||
| "source_file": "nodes_vidu.py" | ||
| }, | ||
| "ViduMultiFrameVideoNode": { | ||
| "node_id": "ViduMultiFrameVideoNode", | ||
| "class_name": "ViduMultiFrameVideoNode", | ||
| "display_name": "Vidu Multi-Frame Video Generation", | ||
| "model_options": ["viduq2-pro", "viduq2-turbo"], | ||
| "source_file": "nodes_vidu.py" | ||
| }, | ||
| "Vidu3TextToVideoNode": { | ||
| "node_id": "Vidu3TextToVideoNode", | ||
| "class_name": "Vidu3TextToVideoNode", | ||
| "display_name": "Vidu Q3 Text-to-Video Generation", | ||
| "model_options": ["viduq3-pro", "viduq3-turbo"], | ||
| "source_file": "nodes_vidu.py" | ||
| }, | ||
| "Vidu3ImageToVideoNode": { | ||
| "node_id": "Vidu3ImageToVideoNode", | ||
| "class_name": "Vidu3ImageToVideoNode", | ||
| "display_name": "Vidu Q3 Image-to-Video Generation", | ||
| "model_options": ["viduq3-pro", "viduq3-turbo"], | ||
| "source_file": "nodes_vidu.py" | ||
| }, | ||
| "Vidu3StartEndToVideoNode": { | ||
| "node_id": "Vidu3StartEndToVideoNode", | ||
| "class_name": "Vidu3StartEndToVideoNode", | ||
| "display_name": "Vidu Q3 Start/End Frame-to-Video Generation", | ||
| "model_options": ["viduq3-pro", "viduq3-turbo"], | ||
| "source_file": "nodes_vidu.py" | ||
| }, | ||
| "WanTextToImageApi": { | ||
| "node_id": "WanTextToImageApi", | ||
| "class_name": "WanTextToImageApi", | ||
| "display_name": "Wan Text to Image", | ||
| "model_options": ["wan2.5-t2i-preview"], | ||
| "source_file": "nodes_wan.py" | ||
| }, | ||
| "WanImageToImageApi": { | ||
| "node_id": "WanImageToImageApi", | ||
| "class_name": "WanImageToImageApi", | ||
| "display_name": "Wan Image to Image", | ||
| "model_options": ["wan2.5-i2i-preview"], | ||
| "source_file": "nodes_wan.py" | ||
| }, | ||
| "WanTextToVideoApi": { | ||
| "node_id": "WanTextToVideoApi", | ||
| "class_name": "WanTextToVideoApi", | ||
| "display_name": "Wan Text to Video", | ||
| "model_options": ["wan2.5-t2v-preview", "wan2.6-t2v"], | ||
| "source_file": "nodes_wan.py" | ||
| }, | ||
| "WanImageToVideoApi": { | ||
| "node_id": "WanImageToVideoApi", | ||
| "class_name": "WanImageToVideoApi", | ||
| "display_name": "Wan Image to Video", | ||
| "model_options": ["wan2.5-i2v-preview", "wan2.6-i2v"], | ||
| "source_file": "nodes_wan.py" | ||
| }, | ||
| "WanReferenceVideoApi": { | ||
| "node_id": "WanReferenceVideoApi", | ||
| "class_name": "WanReferenceVideoApi", | ||
| "display_name": "Wan Reference to Video", | ||
| "model_options": ["wan2.6-r2v"], | ||
| "source_file": "nodes_wan.py" | ||
| }, | ||
| "Wan2TextToVideoApi": { | ||
| "node_id": "Wan2TextToVideoApi", | ||
| "class_name": "Wan2TextToVideoApi", | ||
| "display_name": "Wan 2.7 Text to Video", | ||
| "model_options": ["wan2.7-t2v"], | ||
| "source_file": "nodes_wan.py" | ||
| }, | ||
| "Wan2ImageToVideoApi": { | ||
| "node_id": "Wan2ImageToVideoApi", | ||
| "class_name": "Wan2ImageToVideoApi", | ||
| "display_name": "Wan 2.7 Image to Video", | ||
| "model_options": ["wan2.7-i2v"], | ||
| "source_file": "nodes_wan.py" | ||
| }, | ||
| "Wan2VideoContinuationApi": { | ||
| "node_id": "Wan2VideoContinuationApi", | ||
| "class_name": "Wan2VideoContinuationApi", | ||
| "display_name": "Wan 2.7 Video Continuation", | ||
| "model_options": ["wan2.7-i2v"], | ||
| "source_file": "nodes_wan.py" | ||
| }, | ||
| "Wan2VideoEditApi": { | ||
| "node_id": "Wan2VideoEditApi", | ||
| "class_name": "Wan2VideoEditApi", | ||
| "display_name": "Wan 2.7 Video Edit", | ||
| "model_options": ["wan2.7-videoedit"], | ||
| "source_file": "nodes_wan.py" | ||
| }, | ||
| "Wan2ReferenceVideoApi": { | ||
| "node_id": "Wan2ReferenceVideoApi", | ||
| "class_name": "Wan2ReferenceVideoApi", | ||
| "display_name": "Wan 2.7 Reference to Video", | ||
| "model_options": ["wan2.7-r2v"], | ||
| "source_file": "nodes_wan.py" | ||
| }, | ||
| "HappyHorseTextToVideoApi": { | ||
| "node_id": "HappyHorseTextToVideoApi", | ||
| "class_name": "HappyHorseTextToVideoApi", | ||
| "display_name": "HappyHorse Text to Video", | ||
| "model_options": ["happyhorse-1.0-t2v"], | ||
| "source_file": "nodes_wan.py" | ||
| }, | ||
| "HappyHorseImageToVideoApi": { | ||
| "node_id": "HappyHorseImageToVideoApi", | ||
| "class_name": "HappyHorseImageToVideoApi", | ||
| "display_name": "HappyHorse Image to Video", | ||
| "model_options": ["happyhorse-1.0-i2v"], | ||
| "source_file": "nodes_wan.py" | ||
| }, | ||
| "HappyHorseVideoEditApi": { | ||
| "node_id": "HappyHorseVideoEditApi", | ||
| "class_name": "HappyHorseVideoEditApi", | ||
| "display_name": "HappyHorse Video Edit", | ||
| "model_options": ["happyhorse-1.0-video-edit"], | ||
| "source_file": "nodes_wan.py" | ||
| }, | ||
| "HappyHorseReferenceVideoApi": { | ||
| "node_id": "HappyHorseReferenceVideoApi", | ||
| "class_name": "HappyHorseReferenceVideoApi", | ||
| "display_name": "HappyHorse Reference to Video", | ||
| "model_options": ["happyhorse-1.0-r2v"], | ||
| "source_file": "nodes_wan.py" | ||
| }, | ||
| "WavespeedImageUpscaleNode": { | ||
| "node_id": "WavespeedImageUpscaleNode", | ||
| "class_name": "WavespeedImageUpscaleNode", | ||
| "display_name": "WaveSpeed Image Upscale", | ||
| "model_options": ["SeedVR2", "Ultimate"], | ||
| "source_file": "nodes_wavespeed.py" | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
Move this generated scan cache out of scripts/data/.
This file is regenerated from a local ComfyUI checkout during sync, so checking it into scripts/data/mcp/ violates the repo policy for generated JSON and invites cross-machine churn. Keep an example fixture if you need one, but write the live cache to scripts/.output/ instead. As per coding guidelines, "Generated JSON must not be committed under scripts/data/; generated output should go to scripts/.output/ or repo-root reports instead."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/data/mcp/api_node_model_options.json` around lines 1 - 629, This scan
cache JSON should not live under scripts/data/mcp because it is regenerated from
a local ComfyUI checkout and causes cross-machine churn. Move the generated
output path used by the sync/scan code so the live cache is written to
scripts/.output/ instead, and keep only a checked-in example fixture if needed.
Update the code that produces api_node_model_options.json and any references to
this artifact so they point to the new generated location, using the existing
scan/sync entrypoints that populate the MCP node model options cache.
Source: Coding guidelines
| "Kling O3": { | ||
| "summary": "Video generation with outpainting, image editing, and character reference features.", | ||
| "strengths": [], | ||
| "capabilities": [] | ||
| }, | ||
| "Kling O1": { | ||
| "summary": "Kling Omni image model with support for up to 10 reference images with tags.", | ||
| "strengths": ["Type: Quality"], | ||
| "capabilities": [] |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Collapse the case-variant duplicate registry keys.
This file contains multiple case-folding duplicates (Kling O1/Kling o1, Kling O3/Kling o3, None/none). The MCP registry lookup layer is described as doing case-insensitive fallback, so these doppelgängers make profile resolution ambiguous and can bind the wrong summary to a template.
Also applies to: 247-250, 262-265, 677-680, 747-750
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/data/mcp/models_registry.json` around lines 92 - 100, Collapse the
case-variant duplicate entries in the MCP registry so each model name has only
one canonical key. In the registry JSON, deduplicate the repeated pairs like
Kling O1/Kling o1, Kling O3/Kling o3, and None/none by keeping a single
normalized entry and merging the correct summary, strengths, and capabilities
into it. Update the affected registry objects in the same model groups so the
lookup layer’s case-insensitive fallback cannot resolve to ambiguous or
conflicting profiles.
|
|
||
| **Template overrides:** edit `template_overrides.json` to pin `recommend` or `freshness` for specific templates. Use Cases category never syncs below `low` (no `not_recommended`). | ||
|
|
||
| See [`../mcp/docs/MCP_AI_ENHANCEMENT.md`](../mcp/docs/MCP_AI_ENHANCEMENT.md). |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Fix the relative docs link target.
Line 17 currently points to scripts/data/mcp/docs/... instead of scripts/mcp/docs/...—a tiny path that does a big oopsie.
Suggested fix
-See [`../mcp/docs/MCP_AI_ENHANCEMENT.md`](../mcp/docs/MCP_AI_ENHANCEMENT.md).
+See [`../../mcp/docs/MCP_AI_ENHANCEMENT.md`](../../mcp/docs/MCP_AI_ENHANCEMENT.md).📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| See [`../mcp/docs/MCP_AI_ENHANCEMENT.md`](../mcp/docs/MCP_AI_ENHANCEMENT.md). | |
| See [`../../mcp/docs/MCP_AI_ENHANCEMENT.md`](../../mcp/docs/MCP_AI_ENHANCEMENT.md). |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/data/mcp/README.md` at line 17, The docs link in README.md points to
the wrong relative location, resolving under scripts/data/mcp/docs instead of
the intended scripts/mcp/docs. Update the markdown link target in the README so
it correctly references the MCP_AI_ENHANCEMENT.md document from the current
file’s location, using the existing README link text and adjusting only the
relative path.
| payload = { | ||
| "source": str(api_nodes_dir), | ||
| "node_count": len(index), | ||
| "nodes": index, | ||
| } | ||
| OUTPUT_FILE.write_text(dumps_compact_arrays(payload), encoding="utf-8") |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Drop the absolute source path from the emitted payload.
This CLI writes the local api_nodes_dir into the shared JSON output, so it can reintroduce the same workstation-path leak on every run. A stable identifier is fine; a home-directory breadcrumb is not.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/mcp/scan_api_nodes.py` around lines 70 - 75, The emitted payload in
scan_api_nodes.py currently includes the absolute api_nodes_dir value under
source, which leaks workstation-specific paths into the shared JSON. Update the
payload construction in the scan/serialization flow to remove that absolute
source field or replace it with a stable non-local identifier, while keeping
node_count and nodes intact, so OUTPUT_FILE.write_text only writes portable
data.
| def infer_task_type(name: str) -> str: | ||
| name_l = name.lower() | ||
| if any(x in name_l for x in ["t2v", "txt2vid", "text_to_video", "text-to-video"]): | ||
| return "t2v" | ||
| if any(x in name_l for x in ["i2v", "img2vid", "img_to_vid", "image_to_video", "image-to-video", "r2v", "reference_to_video"]): | ||
| return "i2v" | ||
| if any(x in name_l for x in ["t2i", "text_to_image", "text-to-image"]): | ||
| return "t2i" | ||
| if any(x in name_l for x in ["i2i", "img_edit", "img2img"]): | ||
| return "i2i" | ||
| if any(x in name_l for x in ["segment", "matting"]): | ||
| return "seg" | ||
| if "upscale" in name_l: | ||
| return "upscale" | ||
| if "depth" in name_l: | ||
| return "depth" | ||
| if "remove" in name_l: | ||
| return "remove" | ||
| if any(x in name_l for x in ["frame", "film", "rife", "slowmo", "gimm"]): | ||
| return "frame" | ||
| if "text_to_3d" in name_l or "text-to-3d" in name_l: | ||
| return "t2-3d" | ||
| if any(x in name_l for x in ["img2_3d", "img2-3d", "image_to_3d", "image-to-3d"]): | ||
| return "i2-3d" | ||
| if any(x in name_l for x in ["text_to_audio", "text-to-audio", "text_to_music", "text-to-music"]): | ||
| return "t2a" | ||
| if any(x in name_l for x in ["text_gen", "llm", "chat"]): | ||
| return "llm" | ||
| if any(x in name_l for x in ["rembg", "birefnet", "bi-refnet", "background"]): | ||
| return "remove" | ||
| if "gaussian" in name_l or "splat" in name_l: | ||
| return "3d" | ||
| if "perspective" in name_l: | ||
| return "perspective" | ||
| return "" |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Derive task_type from template metadata, not just the filename.
infer_task() already trusts tags, but infer_task_type() only parses name. If a workflow is tagged Image to Image or Image to Video without an i2i/i2v-style filename token, this falls back to generic text IO and writes the wrong io contract to index.mcp.json—when the name is coy, the IO goes awry.
Also applies to: 617-622
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/mcp/sync_index.py` around lines 316 - 350, `infer_task_type()` is
only deriving the task type from the workflow name, so tagged workflows like
Image to Image or Image to Video can be misclassified when the filename lacks a
matching token. Update `infer_task_type` in `sync_index.py` to also consult
template metadata/tags (as `infer_task()` already does) before falling back to
filename heuristics, and ensure the task type written into `index.mcp.json`
reflects the tag-based contract for `infer_task`, `infer_task_type`, and the
downstream `io` mapping.
| def _normalize_io_side(slots: list | dict | None) -> list[str]: | ||
| """Normalize legacy object/dict slots to string array.""" | ||
| if not slots: | ||
| return [] | ||
| if isinstance(slots, dict): | ||
| return [ | ||
| _encode_slot(typ, "", int(count)) | ||
| for typ, count in slots.items() | ||
| if typ and int(count) > 0 | ||
| ] | ||
| result: list[str] = [] | ||
| for slot in slots: | ||
| if isinstance(slot, str): | ||
| result.append(slot) | ||
| continue | ||
| typ = slot.get("type", "") | ||
| if not typ: | ||
| continue | ||
| role = (slot.get("role") or slot.get("purpose") or "").strip() | ||
| count = int(slot.get("count", 1)) | ||
| result.append(_encode_slot(typ, role, count)) | ||
| return result |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Validate cached io.count values before coercing them.
template_cache.json is AI-populated in this pipeline, so slot["count"] is untrusted input. int(slot.get("count", 1)) will raise on null, "", or non-numeric text and abort the entire sync for one bad cache row.
Also applies to: 646-649
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/mcp/sync_index.py` around lines 368 - 389, The _normalize_io_side
function is coercing untrusted slot["count"] values with int(slot.get("count",
1)), which can abort sync on null, empty, or invalid text. Update
_normalize_io_side to validate and safely coerce count before using
_encode_slot, skipping or defaulting bad entries instead of raising, and apply
the same defensive handling in the other io.count normalization path referenced
by the review.
| payload = { | ||
| "source": str(api_nodes_dir), | ||
| "node_count": len(index), | ||
| "nodes": index, | ||
| } | ||
| API_NODE_OPTIONS_FILE.write_text( | ||
| dumps_compact_arrays(payload), | ||
| encoding="utf-8", |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Don’t serialize the caller’s absolute ComfyUI path into the shared cache.
Writing source: str(api_nodes_dir) bakes a maintainer-local filesystem path into the generated JSON. That leaks workstation details and guarantees noisy diffs whenever someone else runs the sync.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/mcp/sync_index.py` around lines 537 - 544, The payload built in
sync_index.py is writing the caller’s absolute ComfyUI path into the shared
cache via source in the JSON. Update the payload construction in the sync logic
that writes API_NODE_OPTIONS_FILE so it does not serialize str(api_nodes_dir);
use a stable, non-local identifier or omit the field entirely if it is only used
for provenance, and keep the rest of the index generation unchanged.
| - **`data/mcp/api_node_model_options.json`** — Generated cache of API node `model` dropdown options (from ComfyUI source). | ||
| - **`data/mcp/models_registry.json`** — Model profiles (summary, strengths, capabilities) for AI description generation. | ||
| - **`data/mcp/template_cache.json`** — Per-template AI copy, versioned by workflow JSON hash. | ||
| - **`data/krea_registry_aliases.json`**, **`data/krea_*_models.json`** — Temporary Krea snapshots for one-off registry seeding (delete when seeding is complete). |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Fix Krea alias path in docs (right file, right aisle).
Line 85 lists data/krea_registry_aliases.json, but MCP aliases are documented/seeded under data/mcp/registry_aliases.json in this rollout. This path mismatch will send maintainers to the wrong file.
Suggested doc fix
-- **`data/krea_registry_aliases.json`**, **`data/krea_*_models.json`** — Temporary Krea snapshots for one-off registry seeding (delete when seeding is complete).
+- **`data/mcp/registry_aliases.json`**, **`data/krea_*_models.json`** — Temporary Krea snapshot/alias inputs for one-off registry seeding (delete snapshot files when seeding is complete).📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - **`data/krea_registry_aliases.json`**, **`data/krea_*_models.json`** — Temporary Krea snapshots for one-off registry seeding (delete when seeding is complete). | |
| **`data/mcp/registry_aliases.json`**, **`data/krea_*_models.json`** — Temporary Krea snapshot/alias inputs for one-off registry seeding (delete snapshot files when seeding is complete). |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/README.md` at line 85, The README entry for Krea alias snapshots
points to the wrong alias file path, so update the documentation in the script
guide to reference the MCP alias location used by this rollout. In the affected
markdown list item, replace the `data/krea_registry_aliases.json` reference with
`data/mcp/registry_aliases.json` while keeping the rest of the Krea snapshot
note aligned with the existing seeding docs.
Summary
scripts/sync/sync_mcp_index.pyintoscripts/mcp/with a two-step workflow: deterministic sync (sync_index.py) then optional AI enhancement (enhance_models_registry.py,enhance_descriptions.py).scripts/data/mcp/models_registry.jsonfor model profiles (summary, strengths, capabilities) andscripts/data/mcp/template_cache.jsonfor per-template AI copy versioned by workflow JSONsource_hash.scripts/lib/ai/) and.env.examplefor local configuration; refreshtemplates/index.mcp.jsonwith compactioformat and cache merge on sync.Architecture
models_registry.jsontemplate_cache.jsondescription+io, keyed by workflow hashindex.mcp.jsonNotes / follow-ups
template_cache.jsoncurrently seeds from existingindex.mcp.json(PR feat: add index.mcp.json - MCP-readable template index with descriptions #932 copy); most entries are legacy, not freshly AI-generated. Plan to regenerate incrementally viaenhance_descriptions.py.Test plan
python3 scripts/mcp/sync_index.py --checkpython3 scripts/mcp/enhance_descriptions.py --checkpython3 scripts/mcp/enhance_models_registry.py --checkindex.mcp.jsondiff looks correct for MCP consumers