Scenario Workflow Authoring
Overview
A workflow has two representations: editor_info (the editable node graph: nodes, edges,
inputKeys) and flow (the compiled runnable form). Authoring through MCP means writing the whole
editor_info document: there are no per-node editing tools; every change is a read, modify, write
of the full graph through workflow_create or workflow_update. Never hand-write flow:
workflow_publish compiles editor_info into it and flips status to ready. Editing a ready
workflow's editor_info leaves the stale flow running until you publish again.
Read references/editor-info.md before writing any graph: it holds the
node type vocabulary, the node choice doctrine (when an llm node is legitimate), the edge
direction rule, per-node data contracts, and a validated minimal example. Create, update, publish,
copy and delete live in the tool catalog (scenario_tools_search plus the matching executor, see
the scenario skill). workflows_list, workflow_get and workflow_run are direct tools: scope
and dry_run go in their top-level arguments, never an executor wrapper. Running and pricing: the
scenario-workflows skill. If a sibling skill named here is missing from your available skills, ask
the user to install it (npx skills add scenario-labs/skills --skill <name>); unattended, proceed
from tool schemas and flag the gap.
Quick reference
| Step | Call | Notes |
|---|
| 1. Study a graph | workflow_get on a working workflow | Copy the shape, never ids |
| 2. Model contract | model_schema_get | Handle names and required inputs |
| 3. Author | editor_info + inputs_definition | Per the reference file |
| 4. Create | workflow_create | Non-atomic, see below |
| 5. Publish | workflow_publish | Compiles flow, needs input+output pins |
| 6. Validate | workflow_run with dry_run=true | Prices and runs the real validator |
workflow_create is two calls under the hood: a failed create may still have created a draft whose
id is in the error. Recover with workflow_update on that id; re-creating duplicates. Seed step 1
with workflow_get: it returns the full graph of any workflow whose id you have, public ones
included (an id or app URL the user supplies, or your own team's from workflows_list). To find a
public template, use search with target="workflows", public=true, a keyword query,
limit=3, and your scope; for example, query="image" with raw filter: 'status = "ready"'. Read
ids from workflows, then fetch the chosen graph with workflow_get; search hits are summaries,
not graph documents. Workflow search supports keyword text and filters only, so omit image and
semantic options. Use workflows_list for browsing your saved workflows.
scripts/fetch_workflow_examples.py bulk-exports trimmed
featured-workflow graphs for maintainers (setup in its header).
Worked example: a text-to-image app
recommend with capability: "txt2img" and the user's brief as prompt, following the
scenario skill's next_step discipline, then model_schema_get: its input names become the
model node's handle names, and its required flag marks what must be wired. Use search instead
when the user names a model.
- Author
editor_info: text1 with data.isInput: true, model1 with type: "model",
data.modelId and data.isOutput: true, one edge from model1's input to text1's output
(edges name the downstream node as source, see the reference), inputKeys: ["text1"].
workflow_create with name, editor_info, and inputs_definition naming text1 as a string
input. The published input key is the node id, which is why run inputs have names like text1.
workflow_publish, then workflow_run with dry_run=true to validate and price. Fix the graph
and re-publish if validation fails.
One output per item needs a loop
A multiple-asset input wired straight into a generative model's reference field is one generation
conditioned on every item at once: ten product shots in come back as one image blending them, not
ten edits. Field names and cardinality are per model, so read them off model_schema_get
(array: true marks a list), and read the field's description as well: a few utility tools take a
list and return one output per item, which needs no loop. When each item needs its own result, put
the model inside a forEach over the list and read the outputs from the forEachEnd (wiring in the
reference's forEach section); keep the direct wiring only when the items are meant as joint
references for a single output. Inside the loop the current item feeds an array field as a one-item
list. forEach runs one billed model job per item, but the dry run prices one iteration whatever
the list length (observed: one quote for one or two items, twice that billed for two), so multiply
the quote by the item count before quoting the user.
Migrating a graph from another node tool
A pipeline exported by Weavy, ComfyUI, or another node editor does not import: only Scenario's own
export round-trips. It is translated node by node, then created, published, and dry-run as above.
The mapping table, member resolution, and the report the user gets are in
references/foreign-graph-import.md; read it before touching
such an export, since its first rule is to reduce the file to a table locally rather than paste it
into the conversation.
Common mistakes
- Writing UI palette names as node types: persisted types are the camelCase vocabulary in the
reference, and every generator is
type: "model".
- Wiring edges producer to consumer: persisted edges point the other way.
- Expecting an
editor_info update to change a live app without re-publishing.
- Retrying a failed
workflow_create with a second create instead of workflow_update on the id
from the error.
- Wiring a batch of assets into one model input and expecting one output per item: that is one
generation over all of them; loop with
forEach.
- Publishing with no pins: at least one
data.isInput node listed in inputKeys and one
data.isOutput node.
- Gating a text node in front of a builder or model: a branch skips only the node wired to its
handle, so the consumer stays pending and the job never completes. Gate the node that does the
work, or use a CEL ternary for conditional prompt text, per the reference's
ifElse section.
- Double-quoted CEL literals: they evaluate but corrupt the canvas editor, single quotes only.
- Sending
workflow_id to workflow_copy: get, update, publish, run and delete take workflow_id,
but copy takes source_workflow_id; the copy inherits everything verbatim and needs its own
publish.