The wrong graph bills the same. Catch it before you submit.
A Comfy Cloud dry_run returned status: validated on a graph whose VAEDecode read its own output. A provider’s validator answers whether a graph will run — not whether it’s the graph you meant. comfy-preflight checks the difference in the seconds before submission, in-process, where a shell step can’t walk past it.
npx
npx @mcptoolshop/comfy-preflight check graph.json
pip
pip install comfy-preflight
In-process
preflight(graph, register, input_dims=(w, h))
submit(graph) # only if nothing raised
What it checks
Five checks, composed into one verdict. Every one was paid for by a run that got past dry_run.
Link topology
A node input reading its own node, or a link to a node id that is not in the graph. The founding case: a retyped payload with VAEDecode.samples = ["14", 0] that a provider called validated.
The inverted register scan
When a subject declares no style adapter, the claim is not "the weight is 0.0" — it is that no loader node and no card reference exist anywhere. It asserts the mirror image too: a decided weight with no loader is silently inert, and produces base-model output while every log line says otherwise.
Saved is submitted
The saved sidecar and the submitted payload, compared as parsed graphs rather than as text — because a JSON re-dump can differ in whitespace without a value moving.
Generator-legal frame
A Qwen VAE downsamples by 8, so a width of 1066 decodes to 1064 and puts every output 2 px off its control image. The operand is the effective frame, which on an img2img graph lives in the uploaded image rather than in the graph.
The declared envelope
Graph parameters against a cited envelope table, per checkpoint. Advisory, never a halt — a documented band is documentation, and a gate that halts correct work gets disabled by the third person who hits it.
It never fixes your graph
No rewiring, no auto-inserted loader, no rounded frame. It names the defect and the node, and you decide. A graph a gate repaired is a graph nobody reviewed.
Usage
The development door
npx @mcptoolshop/comfy-preflight check graph.json \
--input-dims 1072x1024 --register subject.json
# 0 = nothing halted 1 = HALT 2 = nothing was examined The production gate — in-process, on the submit path
from comfy_preflight import preflight
# Inside the function that submits. Not in a shell step before it.
preflight(graph, register, input_dims=(width, height))
submit(graph) # only reached if nothing raised Over MCP (stdio)
pip install "comfy-preflight[mcp]"
python -m comfy_preflight.mcp_server
# or, with no Python at all:
npx @mcptoolshop/comfy-preflight mcp