Skip to main content
Glama

Rig Author

rig_author

Author a multi-step Rig as a graph the runtime executes for real. nodes are the steps, edges {from, to, guard?} wire them; structure_type linear | dag (default) | stateful_graph (loops with guarded edges; an unfired guard skips its target). A step with no binding is a model turn with NO tools. A step that must do something is bound: endpoint + tool + params (a real MCP tools/call — your own tools live at https://rokha.ai/mcp/jsonrpc, e.g. tool 'hunt_report', params {q: '{{input}}'}), url (HTTP fetch), data_op (memory), signet_actor (value under the runner's mandate), questions (a decision gate), sub_rig or doc_input. Template {{input}} and {{upstream.<field>}} inside values; keys ride secret_refs, never literals. Each distinct key is its own saved rig. Pass name, declare input and outputs. Then call rig_run (wait: true) in the same turn and lead with what it produced. Full field reference: tools_catalog {name: 'rig_author'}.

Input Schema

NameRequiredDescriptionDefault
keyNoStorage key; each distinct kebab-case key is its own saved rig (default 'agent-rig').
nameNoThe rig's name; gates Save and Run for a human.
edgesNoDirected edges; add `guard` for a conditional route.
inputNoWhat the rig asks its runner for; fills `{{input}}`.
nodesYesThe steps. Rarer fields (label, agent, model_policy, headers, data_type, doc_input, expects, produces, inputs, pos, state) are in the full reference.
outputsNoWhat the rig declares it produces.
summaryNoOne line on what the flow does.
requirementsNo{model?: {exact | allowed[] | min_tier}, agent?: {kit, tools[]}} a runner must meet.
structure_typeNolinear = a chain; dag (default) = branches and fan-in; stateful_graph = loops with guards.

Output Schema

NameRequiredDescriptionDefault

No arguments

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changed
    • changedOutput schema / (root)
      Previous value: -nullNew value: +{
      +  "additionalProperties": true,
      +  "type": "object"
      +}
  2. Changed51 schema fields changed
    • changedInput schema / properties / edges / description
      Previous value: -"Directed edges between node ids. A plain {from, to} is a data edge; add `guard` to make it a CONDITIONAL route (stateful_graph loops: 'revise until good' = critique → revise guarded on the verdict, plus an exit edge)."New value: +"Directed edges; add `guard` for a conditional route."
    • removedInput schema / properties / edges / items / properties / from / description
      Removed value: -"Source node id"
    • changedInput schema / properties / edges / items / properties / guard / description
      Previous value: -"Take this edge ONLY when the condition holds — {source, op, value?}. `source` is a `{{node.port}}` channel ref (e.g. '{{critique.out}}'); `op` is eq | not_eq | contains | gt | gte | lt | lte | truthy | exists (a bounded interpreter, never eval). e.g. {\"source\": \"{{critique.out}}\", \"op\": \"contains\", \"value\": \"REVISE\"}."New value: +"{source: a `{{node.port}}` ref, op: eq | not_eq | contains | gt | gte | lt | lte | truthy | exists, value?}."
    • removedInput schema / properties / edges / items / properties / guard / properties
      Removed value: -{
      -  "op": {
      -    "enum": [
      -      "eq",
      -      "not_eq",
      -      "contains",
      -      "gt",
      -      "gte",
      -      "lt",
      -      "lte",
      -      "truthy",
      -      "exists"
      -    ],
      -    "type": "string"
      -  },
      -  "source": {
      -    "type": "string"
      -  },
      -  "value": {}
      -}
    • removedInput schema / properties / edges / items / properties / guard / required
      Removed value: -[
      -  "source",
      -  "op"
      -]
    • removedInput schema / properties / edges / items / properties / kind
      Removed value: -{
      -  "description": "'data' (default) | 'control' | 'conditional' (implied when guard is set)",
      -  "enum": [
      -    "data",
      -    "control",
      -    "conditional"
      -  ],
      -  "type": "string"
      -}
    • removedInput schema / properties / edges / items / properties / to / description
      Removed value: -"Destination node id"
    • changedInput schema / properties / input / description
      Previous value: -"WHAT THIS RIG ASKS ITS RUNNER FOR — the labelled input box the Builder canvas and the rig's product page render, and the value that fills every `{{input}}` placeholder at run time. Pass a plain string (the label) or {label, kind?, hint?, required?, multiple?} — kind is text (default) | number | boolean | date | url | email | file | document | markdown | code | image | audio | video | data | table | list | contact | location | secret | embedding | skill | harness | rig | any. The LISTING kinds (skill/harness/rig) mean the value is a PICKED registry listing — the Explore/Audit shape. DECLARE IT whenever the flow works on something the user supplies: without it the rig saves with no input box and only works when something passes `input` to rig_run. e.g. {\"label\": \"token address\", \"kind\": \"text\", \"hint\": \"0x… on chain 4663\"}."New value: +"What the rig asks its runner for; fills `{{input}}`."
    • removedInput schema / properties / inputs
      Removed value: -{
      -  "description": "ADDITIONAL input ports beyond `input` (or the full list on their own) — same shapes as `input`. The FIRST input fills `{{input}}`; a multi-input rig renders one labelled box per port.",
      -  "items": {
      -    "type": [
      -      "string",
      -      "object"
      -    ]
      -  },
      -  "type": "array"
      -}
    • changedInput schema / properties / key / description
      Previous value: -"Rig storage key — EACH DISTINCT KEY IS ITS OWN SAVED RIG (default 'agent-rig' = the Working Rig; use a fresh kebab key per additional rig, e.g. 'news-digest')"New value: +"Storage key; each distinct kebab-case key is its own saved rig (default 'agent-rig')."
    • changedInput schema / properties / name / description
      Previous value: -"THE RIG'S NAME — the Builder's header box, which gates BOTH Save and ▶ Run for a human. Defaults to `summary` so the rig is always nameable; pass a short product-style name ('X Sentiment Digest') when you have one."New value: +"The rig's name; gates Save and Run for a human."
    • changedInput schema / properties / nodes / description
      Previous value: -"Graph nodes. Each is one step."New value: +"The steps. Rarer fields (label, agent, model_policy, headers, data_type, doc_input, expects, produces, inputs, pos, state) are in the full reference."
    • removedInput schema / properties / nodes / items / properties / agent
      Removed value: -{
      -  "description": "The step's ACTOR — a Forge agent's handle (see /api/agents/available). ANY step kind takes one, and the step then runs AS that agent: an instruction or prompt-shaped skill step speaks in its persona (frame, bio, creator harnesses, voice); an `endpoint`+`tool` or `url` step is PERFORMED by it — the bound call becomes the agent's one tool, it calls it and writes the step's output (without an actor the call stays deterministic); sandbox, decision (Jev) and platform-rail steps carry its name only. Several agents across one rig are fine — each step resolves its own. The RUNNER pays (the agent lends its persona + locked-model preference, never its owner's keys or its allowance; a locked model the runner can't pay for runs on the lane default, said out loud). Authorized at RUN time against the RUNNER — their own agent always works, someone else's only while public; a private/missing actor is a typed agent_unavailable error on that step only. Empty = Rokha performs the step, as always.",
      -  "type": "string"
      -}
    • changedInput schema / properties / nodes / items / properties / data_key / description
      Previous value: -"The DATA collection key (data_op steps only). `{{input}}` templates the run input. The runtime FORCES the `data.` prefix and blocks any reach at system harnesses (profile/chat/keys) — e.g. 'data.x-signals.{{input}}'"New value: +"The memory collection."
    • changedInput schema / properties / nodes / items / properties / data_op / description
      Previous value: -"Makes this a DATA step — durable, owner-scoped cross-rig memory. 'write' overwrites, 'append' accumulates across runs, 'read' loads a stored collection into the flow. Writes are PRIVATE by default (never in the public pulse, not forkable). This is how one rig persists a collection a LATER rig reads."New value: +"A durable, owner-scoped memory step on data_key."
    • removedInput schema / properties / nodes / items / properties / data_type
      Removed value: -{
      -  "description": "The harness_type a data write lands as (default 'knowledge'); one of knowledge/strategy/preference/persona/compliance",
      -  "enum": [
      -    "knowledge",
      -    "strategy",
      -    "preference",
      -    "persona",
      -    "compliance"
      -  ],
      -  "type": "string"
      -}
    • removedInput schema / properties / nodes / items / properties / doc_input
      Removed value: -{
      -  "description": "true = LIGHT LISTING step: read this skill's real registry listing + document and hand it forward (no execution, no runtime needed)",
      -  "type": "boolean"
      -}
    • changedInput schema / properties / nodes / items / properties / endpoint / description
      Previous value: -"Live MCP JSON-RPC endpoint URL — the run performs a REAL tools/call here (optional; empty = agentic step)"New value: +"MCP JSON-RPC URL for a real tools/call."
    • removedInput schema / properties / nodes / items / properties / expects
      Removed value: -{
      -  "description": "What this step EXPECTS from upstream, in plain words ('a list of post URLs') — the step's I/O story, shown on the canvas and carried into the published skeleton",
      -  "type": "string"
      -}
    • removedInput schema / properties / nodes / items / properties / headers
      Removed value: -{
      -  "description": "Extra HTTP headers for a `url` fetch (string→string). Combine with secret_refs to inject a key: {\"Authorization\": \"Bearer {{secret:my-api-key}}\"}.",
      -  "type": "object"
      -}
    • changedInput schema / properties / nodes / items / properties / id / description
      Previous value: -"Stable node id used by edges (e.g. 'draft', 'a')"New value: +"Stable id edges refer to."
    • removedInput schema / properties / nodes / items / properties / inputs
      Removed value: -{
      -  "description": "PER-NODE input ports — the labelled landing slots a multi-parent (fan-in) edge binds to. Same shapes as the rig-level `input`.",
      -  "items": {
      -    "type": [
      -      "string",
      -      "object"
      -    ]
      -  },
      -  "type": "array"
      -}
    • changedInput schema / properties / nodes / items / properties / instruction / description
      Previous value: -"Per-step instruction for the agent (optional)"New value: +"What this step must do."
    • removedInput schema / properties / nodes / items / properties / label
      Removed value: -{
      -  "description": "Display label (optional; defaults to the skill)",
      -  "type": "string"
      -}
    • removedInput schema / properties / nodes / items / properties / method / description
      Removed value: -"HTTP method for a `url` fetch: GET (default) or POST"
    • changedInput schema / properties / nodes / items / properties / model / description
      Previous value: -"Optional Anthropic model id to pin for this step's agentic execution (e.g. 'claude-sonnet-4-6'). Non-Haiku models need the owner's own API key; without one, 'preferred' swaps to Haiku and 'required' refuses the step"New value: +"Pin a model id; non-Haiku needs the owner's key."
    • removedInput schema / properties / nodes / items / properties / model_policy
      Removed value: -{
      -  "description": "'preferred' (default — swap to the best usable model when the pin isn't available) or 'required' (the step refuses to run without its model, typed model_unavailable)",
      -  "enum": [
      -    "preferred",
      -    "required"
      -  ],
      -  "type": "string"
      -}
    • changedInput schema / properties / nodes / items / properties / params / description
      Previous value: -"Arguments for the live tool call, or a plain-HTTP fetch's query/body params (optional). USE `{{input}}` ANYWHERE IN A VALUE to thread what the runner typed — {\"address\": \"{{input}}\"} is how a param comes from user input. `{{secret:<alias>}}` injects one of their saved keys (see secret_refs)."New value: +"Call or fetch arguments; `{{input}}`, `{{upstream.<field>}}` and `{{secret:<alias>}}` template inside values."
    • removedInput schema / properties / nodes / items / properties / pos
      Removed value: -{
      -  "description": "Explicit canvas position {x, y} (optional — omitted nodes are auto-laid by topological layer, parallel branches side by side)",
      -  "properties": {
      -    "x": {
      -      "type": "number"
      -    },
      -    "y": {
      -      "type": "number"
      -    }
      -  },
      -  "type": "object"
      -}
    • removedInput schema / properties / nodes / items / properties / produces
      Removed value: -{
      -  "description": "What this step PRODUCES for downstream ('a sentiment table') — same story, output side",
      -  "type": "string"
      -}
    • changedInput schema / properties / nodes / items / properties / questions / description
      Previous value: -"Makes this a DECISION step (the Jev decision model): typed questions answered with calibrated probabilities — no prose, no tools. Shape: {<id>: {type: 'noul' (yes/no → answers.<id>.noul = p) | 'choice' (pick one of named criteria → answers.<id>.choice + confidence + probabilities) | 'score' (ordinal over a criteria LIST → answers.<id>.score + confidence), instructions, criteria}}. Route on it with a guard, e.g. {source:'{{decide.answers.enter.noul}}', op:'gte', value:0.7}. USE for gates, routing, classification, scoring, trade entry/exit calls; NEVER for anything that writes words or uses tools."New value: +"A DECISION step: {<id>: {type: 'noul' | 'choice' | 'score', instructions, criteria}} answered with probabilities; route on it with an edge guard such as {source: '{{decide.answers.enter.noul}}', op: 'gte', value: 0.7}."
    • changedInput schema / properties / nodes / items / properties / secret_refs / description
      Previous value: -"ATTACH THE RUNNER'S OWN SAVED KEYS to this step — the only way a rig calls an API that needs credentials. Each item {alias, as?}: `alias` is a key the user saved in Profile → API KEYS (or a connect alias like 'oauth-x' / 'mcp-<host>'); `as` binds HOW it lands — `param:<key>` (a tools/call argument), `header:<Name>[:<prefix>]` (e.g. 'header:Authorization:Bearer ' — the trailing space is meaningful), or omit `as` and put {{secret:<alias>}} inside any param/header value. The VALUE is resolved server-side at the call and never appears in params, traces, or the published listing — so a published rig is safe to share and each RUNNER supplies their own key."New value: +"The runner's saved keys this step may use: [{alias, as?}]."
    • removedInput schema / properties / nodes / items / properties / secret_refs / items
      Removed value: -{
      -  "properties": {
      -    "alias": {
      -      "type": "string"
      -    },
      -    "as": {
      -      "type": "string"
      -    }
      -  },
      -  "required": [
      -    "alias"
      -  ],
      -  "type": "object"
      -}
    • changedInput schema / properties / nodes / items / properties / signet_action / description
      Previous value: -"What the Signet step does. WRITES (move value, need a mandate + target + value): 'pay'/'transfer' (send SOL or an SPL token), 'swap' (trade via Jupiter), 'x402' (BUY a paid HTTP resource — signet_target = the resource URL, signet_value = your USDC ceiling in base units; Signet does the 402→pay→retry and returns the content). READS (no mandate, no value moved, safe on any step): 'metrics' (stage-shaped budgets/counts/spend for this actor — point a STAGE dashboard at this step), 'mandates' (is a mandate ready for this actor?), 'portfolio' (SOL + SPL balances; `signet_target` optionally names an address), 'price' (a live quote; `signet_target` = mint, `signet_value` = amount). PAPER (no mandate, no actor, NO money moves — real Jupiter quotes, a recorded ledger on the runner's account; output carries paper:true): 'wallet_activity' (a wallet's recent swaps; `signet_target` = the wallet, `signet_value` = limit) · 'paper_positions' / 'paper_reset' (`signet_target` = ledger name) · 'paper_buy' / 'paper_sell' (`signet_target` = ledger; the fills come from the previous step's JSON `buys` / `sells` list; `signet_value` = the default USD per buy or the sell fraction; a paper_buy step passes `sells` through for a following paper_sell). Defaults to 'pay' — the narrowest action — rather than guessing a wider one."New value: +"Writes move value under the mandate; metrics, mandates, portfolio and price read; paper_* record a paper ledger. Default pay."
    • changedInput schema / properties / nodes / items / properties / signet_actor / description
      Previous value: -"Makes this a SIGNET step — the rig acts with VALUE under a mandate the RUNNER already granted to this actor (e.g. `rig:trader`). The step carries NO credentials: no key, no mandate id, no address. The rail resolves (runner, actor) -> mandate, so a published or forked rig carries the strategy and none of the authority — run someone else's copy and it spends THEIR OWN mandate or refuses. If no mandate exists for the actor, the step refuses and names the fix."New value: +"A Signet step under the RUNNER's mandate to this actor, e.g. 'rig:trader'; no credentials in the rig."
    • changedInput schema / properties / nodes / items / properties / signet_target / description
      Previous value: -"Destination address (pay/transfer), the mint to trade into (swap), an address (portfolio) or the mint to price (price). For a WRITE it MUST be on the mandate's allowlist or the rail refuses. Templating: `{{input}}` is the run input and `{{upstream.<field>}}` reads the previous step's output. Not needed for 'metrics' or 'mandates'."New value: +"Address, mint, wallet or paper ledger name; templates allowed."
    • changedInput schema / properties / nodes / items / properties / signet_value / description
      Previous value: -"Amount in BASE units — lamports for SOL, the mint's base units for an SPL token. Never a decimal. Templating: `{{input}}` is the run input, and `{{upstream.<field>}}` reads the PREVIOUS step's output — which is how a decided position size actually reaches the trade (`{{upstream.size_lamports}}`). Hard-coding an amount in a rig whose earlier step computes one is a bug: the strategy's sizing is then calculated and discarded. If the reference does not resolve, the field is EMPTY and the step refuses — a trade sized by a leftover constant is a trade nobody decided. Not needed for a read action."New value: +"Amount in base units, never a decimal; `{{upstream.<field>}}` sizes it."
    • changedInput schema / properties / nodes / items / properties / skill / description
      Previous value: -"Registry skill name to bind this step to (discover via registry_search)"New value: +"Registry skill to bind (registry_search)."
    • removedInput schema / properties / nodes / items / properties / state
      Removed value: -{
      -  "description": "DECISION step only: what to decide about — a template reading {{input}} and {{upstream.<field>}}; empty = the upstream output.",
      -  "type": "string"
      -}
    • changedInput schema / properties / nodes / items / properties / sub_rig / description
      Previous value: -"Make this node a SUB-RIG step — the referenced SAVED rig runs here as a bounded sub-run (one level deep; runner-pays, shared budget, traces nested under this node). Pass {rig_id, name?} or a bare rig-id string (rig_search finds ids). Mutually exclusive with `skill`. ⚠ the canvas renders it as a labeled ⬡ block; its INNER edits go through you."New value: +"A saved rig id to run here as a sub-run."
    • changedInput schema / properties / nodes / items / properties / tool / description
      Previous value: -"Tool name to invoke on the endpoint (optional)"New value: +"Tool name on that endpoint."
    • changedInput schema / properties / nodes / items / properties / url / description
      Previous value: -"Plain-HTTP fetch target — the run performs a REAL HTTP request here via the SSRF-guarded proxy (optional; use with method + params + headers + secret_refs). `{{input}}` substitutes into the PATH or QUERY too (e.g. 'https://api.example.com/tokens/{{input}}'); the host stays fixed."New value: +"HTTP fetch target; `{{input}}` may sit in the path or query."
    • changedInput schema / properties / outputs / description
      Previous value: -"What the rig DECLARES IT PRODUCES — the contract's output side, shown on the canvas Output box and the product page. Same shapes as `input` (required is input-only). e.g. [{\"label\": \"the audit report\", \"kind\": \"markdown\"}]."New value: +"What the rig declares it produces."
    • removedInput schema / properties / outputs / items
      Removed value: -{
      -  "type": [
      -    "string",
      -    "object"
      -  ]
      -}
    • changedInput schema / properties / requirements / description
      Previous value: -"MODEL + AGENT REQUIREMENTS a runner (and an agent hired for this rig) must meet — {model?: {exact?: '<model id>' | allowed?: ['<id>', …] (≤8) , min_tier?: 'haiku'|'sonnet'|'opus'}, agent?: {tools?: ['<tool>', …] (≤32), kit?: '<kit>'}}. exact OR allowed, not both; every id must be a model the platform knows (the model picker's ids). A hired agent qualifies and is priced ON the required model, and the price floor follows that model's cost. e.g. {\"model\": {\"exact\": \"claude-opus-5-5\"}}."New value: +"{model?: {exact | allowed[] | min_tier}, agent?: {kit, tools[]}} a runner must meet."
    • removedInput schema / properties / requirements / properties
      Removed value: -{
      -  "agent": {
      -    "properties": {
      -      "kit": {
      -        "type": "string"
      -      },
      -      "tools": {
      -        "items": {
      -          "type": "string"
      -        },
      -        "type": "array"
      -      }
      -    },
      -    "type": "object"
      -  },
      -  "model": {
      -    "properties": {
      -      "allowed": {
      -        "items": {
      -          "type": "string"
      -        },
      -        "type": "array"
      -      },
      -      "exact": {
      -        "type": "string"
      -      },
      -      "min_tier": {
      -        "enum": [
      -          "haiku",
      -          "sonnet",
      -          "opus"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "type": "object"
      -  }
      -}
    • removedInput schema / properties / state
      Removed value: -{
      -  "description": "SHARED STATE CHANNELS (stateful_graph) — named channels loop iterations read/write via `{{node.port}}` refs; `reducer` merges concurrent fan-out writes: 'last_write' (default) | 'append' | 'add'. Each {id, kind?, reducer?}.",
      -  "items": {
      -    "properties": {
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "type": "string"
      -      },
      -      "reducer": {
      -        "enum": [
      -          "last_write",
      -          "append",
      -          "add"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id"
      -    ],
      -    "type": "object"
      -  },
      -  "type": "array"
      -}
    • changedInput schema / properties / structure_type / description
      Previous value: -"'linear' (a simple chain — what the guided Builder produces), 'dag' (default; acyclic, parallel branches + real fan-in) or 'stateful_graph' (cyclic loops + conditional edge guards — the runtime evaluates guards and skips unfired branches for real; the Builder CANVAS can't edit it yet, so tell the owner edits go through you)"New value: +"linear = a chain; dag (default) = branches and fan-in; stateful_graph = loops with guards."
    • changedInput schema / properties / summary / description
      Previous value: -"Short human description of the flow (shows as the rig's name)"New value: +"One line on what the flow does."
    • removedInput schema / properties / wallet_address
      Removed value: -{
      -  "description": "Wallet address that owns the rig (the scope the run reads)",
      -  "type": "string"
      -}
    • changedInput schema / required
      Previous value: -[
      -  "wallet_address",
      -  "nodes"
      -]New value: +[
      +  "nodes"
      +]
  3. Changed1 schema field changed
    • addedInput schema / properties / requirements
      Added value: +{
      +  "description": "MODEL + AGENT REQUIREMENTS a runner (and an agent hired for this rig) must meet — {model?: {exact?: '<model id>' | allowed?: ['<id>', …] (≤8) , min_tier?: 'haiku'|'sonnet'|'opus'}, agent?: {tools?: ['<tool>', …] (≤32), kit?: '<kit>'}}. exact OR allowed, not both; every id must be a model the platform knows (the model picker's ids). A hired agent qualifies and is priced ON the required model, and the price floor follows that model's cost. e.g. {\"model\": {\"exact\": \"claude-opus-5-5\"}}.",
      +  "properties": {
      +    "agent": {
      +      "properties": {
      +        "kit": {
      +          "type": "string"
      +        },
      +        "tools": {
      +          "items": {
      +            "type": "string"
      +          },
      +          "type": "array"
      +        }
      +      },
      +      "type": "object"
      +    },
      +    "model": {
      +      "properties": {
      +        "allowed": {
      +          "items": {
      +            "type": "string"
      +          },
      +          "type": "array"
      +        },
      +        "exact": {
      +          "type": "string"
      +        },
      +        "min_tier": {
      +          "enum": [
      +            "haiku",
      +            "sonnet",
      +            "opus"
      +          ],
      +          "type": "string"
      +        }
      +      },
      +      "type": "object"
      +    }
      +  },
      +  "type": "object"
      +}
  4. Changed1 schema field changed
    • changedInput schema / properties / nodes / items / properties / agent / description
      Previous value: -"The step's ACTOR — a SUB-AGENT persona handle (an account that toggled agent_mode; see /api/agents/available). The step's agentic/instruction execution runs AS that persona: its frame, bio, persona harnesses and voice. Authorized at RUN time against the RUNNER — their own agent always works, someone else's only while public; a private/missing actor is a typed agent_unavailable step error, never a silent Rokha fallback. Empty = Rokha performs the step, as always."New value: +"The step's ACTOR — a Forge agent's handle (see /api/agents/available). ANY step kind takes one, and the step then runs AS that agent: an instruction or prompt-shaped skill step speaks in its persona (frame, bio, creator harnesses, voice); an `endpoint`+`tool` or `url` step is PERFORMED by it — the bound call becomes the agent's one tool, it calls it and writes the step's output (without an actor the call stays deterministic); sandbox, decision (Jev) and platform-rail steps carry its name only. Several agents across one rig are fine — each step resolves its own. The RUNNER pays (the agent lends its persona + locked-model preference, never its owner's keys or its allowance; a locked model the runner can't pay for runs on the lane default, said out loud). Authorized at RUN time against the RUNNER — their own agent always works, someone else's only while public; a private/missing actor is a typed agent_unavailable error on that step only. Empty = Rokha performs the step, as always."
  5. Changed17 schema fields changed
    • changedInput schema / properties / edges / description
      Previous value: -"Directed edges: [{from, to}] node ids"New value: +"Directed edges between node ids. A plain {from, to} is a data edge; add `guard` to make it a CONDITIONAL route (stateful_graph loops: 'revise until good' = critique → revise guarded on the verdict, plus an exit edge)."
    • addedInput schema / properties / edges / items / properties
      Added value: +{
      +  "from": {
      +    "description": "Source node id",
      +    "type": "string"
      +  },
      +  "guard": {
      +    "description": "Take this edge ONLY when the condition holds — {source, op, value?}. `source` is a `{{node.port}}` channel ref (e.g. '{{critique.out}}'); `op` is eq | not_eq | contains | gt | gte | lt | lte | truthy | exists (a bounded interpreter, never eval). e.g. {\"source\": \"{{critique.out}}\", \"op\": \"contains\", \"value\": \"REVISE\"}.",
      +    "properties": {
      +      "op": {
      +        "enum": [
      +          "eq",
      +          "not_eq",
      +          "contains",
      +          "gt",
      +          "gte",
      +          "lt",
      +          "lte",
      +          "truthy",
      +          "exists"
      +        ],
      +        "type": "string"
      +      },
      +      "source": {
      +        "type": "string"
      +      },
      +      "value": {}
      +    },
      +    "required": [
      +      "source",
      +      "op"
      +    ],
      +    "type": "object"
      +  },
      +  "kind": {
      +    "description": "'data' (default) | 'control' | 'conditional' (implied when guard is set)",
      +    "enum": [
      +      "data",
      +      "control",
      +      "conditional"
      +    ],
      +    "type": "string"
      +  },
      +  "to": {
      +    "description": "Destination node id",
      +    "type": "string"
      +  }
      +}
    • addedInput schema / properties / edges / items / required
      Added value: +[
      +  "from",
      +  "to"
      +]
    • addedInput schema / properties / input
      Added value: +{
      +  "description": "WHAT THIS RIG ASKS ITS RUNNER FOR — the labelled input box the Builder canvas and the rig's product page render, and the value that fills every `{{input}}` placeholder at run time. Pass a plain string (the label) or {label, kind?, hint?, required?, multiple?} — kind is text (default) | number | boolean | date | url | email | file | document | markdown | code | image | audio | video | data | table | list | contact | location | secret | embedding | skill | harness | rig | any. The LISTING kinds (skill/harness/rig) mean the value is a PICKED registry listing — the Explore/Audit shape. DECLARE IT whenever the flow works on something the user supplies: without it the rig saves with no input box and only works when something passes `input` to rig_run. e.g. {\"label\": \"token address\", \"kind\": \"text\", \"hint\": \"0x… on chain 4663\"}.",
      +  "type": [
      +    "string",
      +    "object"
      +  ]
      +}
    • addedInput schema / properties / inputs
      Added value: +{
      +  "description": "ADDITIONAL input ports beyond `input` (or the full list on their own) — same shapes as `input`. The FIRST input fills `{{input}}`; a multi-input rig renders one labelled box per port.",
      +  "items": {
      +    "type": [
      +      "string",
      +      "object"
      +    ]
      +  },
      +  "type": "array"
      +}
    • changedInput schema / properties / key / description
      Previous value: -"Rig storage key (default 'agent-rig')"New value: +"Rig storage key — EACH DISTINCT KEY IS ITS OWN SAVED RIG (default 'agent-rig' = the Working Rig; use a fresh kebab key per additional rig, e.g. 'news-digest')"
    • addedInput schema / properties / name
      Added value: +{
      +  "description": "THE RIG'S NAME — the Builder's header box, which gates BOTH Save and ▶ Run for a human. Defaults to `summary` so the rig is always nameable; pass a short product-style name ('X Sentiment Digest') when you have one.",
      +  "type": "string"
      +}
    • changedInput schema / properties / nodes / description
      Previous value: -"Graph nodes: [{id, skill?, label?, instruction?, doc_input?, model?, model_policy?, endpoint?, tool?, params?}] — endpoint/tool/params wire a node to a LIVE MCP server (real tools/call at run time)"New value: +"Graph nodes. Each is one step."
    • addedInput schema / properties / nodes / items / properties
      Added value: +{
      +  "agent": {
      +    "description": "The step's ACTOR — a SUB-AGENT persona handle (an account that toggled agent_mode; see /api/agents/available). The step's agentic/instruction execution runs AS that persona: its frame, bio, persona harnesses and voice. Authorized at RUN time against the RUNNER — their own agent always works, someone else's only while public; a private/missing actor is a typed agent_unavailable step error, never a silent Rokha fallback. Empty = Rokha performs the step, as always.",
      +    "type": "string"
      +  },
      +  "data_key": {
      +    "description": "The DATA collection key (data_op steps only). `{{input}}` templates the run input. The runtime FORCES the `data.` prefix and blocks any reach at system harnesses (profile/chat/keys) — e.g. 'data.x-signals.{{input}}'",
      +    "type": "string"
      +  },
      +  "data_op": {
      +    "description": "Makes this a DATA step — durable, owner-scoped cross-rig memory. 'write' overwrites, 'append' accumulates across runs, 'read' loads a stored collection into the flow. Writes are PRIVATE by default (never in the public pulse, not forkable). This is how one rig persists a collection a LATER rig reads.",
      +    "enum": [
      +      "write",
      +      "read",
      +      "append"
      +    ],
      +    "type": "string"
      +  },
      +  "data_type": {
      +    "description": "The harness_type a data write lands as (default 'knowledge'); one of knowledge/strategy/preference/persona/compliance",
      +    "enum": [
      +      "knowledge",
      +      "strategy",
      +      "preference",
      +      "persona",
      +      "compliance"
      +    ],
      +    "type": "string"
      +  },
      +  "doc_input": {
      +    "description": "true = LIGHT LISTING step: read this skill's real registry listing + document and hand it forward (no execution, no runtime needed)",
      +    "type": "boolean"
      +  },
      +  "endpoint": {
      +    "description": "Live MCP JSON-RPC endpoint URL — the run performs a REAL tools/call here (optional; empty = agentic step)",
      +    "type": "string"
      +  },
      +  "expects": {
      +    "description": "What this step EXPECTS from upstream, in plain words ('a list of post URLs') — the step's I/O story, shown on the canvas and carried into the published skeleton",
      +    "type": "string"
      +  },
      +  "headers": {
      +    "description": "Extra HTTP headers for a `url` fetch (string→string). Combine with secret_refs to inject a key: {\"Authorization\": \"Bearer {{secret:my-api-key}}\"}.",
      +    "type": "object"
      +  },
      +  "id": {
      +    "description": "Stable node id used by edges (e.g. 'draft', 'a')",
      +    "type": "string"
      +  },
      +  "inputs": {
      +    "description": "PER-NODE input ports — the labelled landing slots a multi-parent (fan-in) edge binds to. Same shapes as the rig-level `input`.",
      +    "items": {
      +      "type": [
      +        "string",
      +        "object"
      +      ]
      +    },
      +    "type": "array"
      +  },
      +  "instruction": {
      +    "description": "Per-step instruction for the agent (optional)",
      +    "type": "string"
      +  },
      +  "label": {
      +    "description": "Display label (optional; defaults to the skill)",
      +    "type": "string"
      +  },
      +  "method": {
      +    "description": "HTTP method for a `url` fetch: GET (default) or POST",
      +    "enum": [
      +      "GET",
      +      "POST"
      +    ],
      +    "type": "string"
      +  },
      +  "model": {
      +    "description": "Optional Anthropic model id to pin for this step's agentic execution (e.g. 'claude-sonnet-4-6'). Non-Haiku models need the owner's own API key; without one, 'preferred' swaps to Haiku and 'required' refuses the step",
      +    "type": "string"
      +  },
      +  "model_policy": {
      +    "description": "'preferred' (default — swap to the best usable model when the pin isn't available) or 'required' (the step refuses to run without its model, typed model_unavailable)",
      +    "enum": [
      +      "preferred",
      +      "required"
      +    ],
      +    "type": "string"
      +  },
      +  "params": {
      +    "description": "Arguments for the live tool call, or a plain-HTTP fetch's query/body params (optional). USE `{{input}}` ANYWHERE IN A VALUE to thread what the runner typed — {\"address\": \"{{input}}\"} is how a param comes from user input. `{{secret:<alias>}}` injects one of their saved keys (see secret_refs).",
      +    "type": "object"
      +  },
      +  "pos": {
      +    "description": "Explicit canvas position {x, y} (optional — omitted nodes are auto-laid by topological layer, parallel branches side by side)",
      +    "properties": {
      +      "x": {
      +        "type": "number"
      +      },
      +      "y": {
      +        "type": "number"
      +      }
      +    },
      +    "type": "object"
      +  },
      +  "produces": {
      +    "description": "What this step PRODUCES for downstream ('a sentiment table') — same story, output side",
      +    "type": "string"
      +  },
      +  "questions": {
      +    "description": "Makes this a DECISION step (the Jev decision model): typed questions answered with calibrated probabilities — no prose, no tools. Shape: {<id>: {type: 'noul' (yes/no → answers.<id>.noul = p) | 'choice' (pick one of named criteria → answers.<id>.choice + confidence + probabilities) | 'score' (ordinal over a criteria LIST → answers.<id>.score + confidence), instructions, criteria}}. Route on it with a guard, e.g. {source:'{{decide.answers.enter.noul}}', op:'gte', value:0.7}. USE for gates, routing, classification, scoring, trade entry/exit calls; NEVER for anything that writes words or uses tools.",
      +    "type": "object"
      +  },
      +  "secret_refs": {
      +    "description": "ATTACH THE RUNNER'S OWN SAVED KEYS to this step — the only way a rig calls an API that needs credentials. Each item {alias, as?}: `alias` is a key the user saved in Profile → API KEYS (or a connect alias like 'oauth-x' / 'mcp-<host>'); `as` binds HOW it lands — `param:<key>` (a tools/call argument), `header:<Name>[:<prefix>]` (e.g. 'header:Authorization:Bearer ' — the trailing space is meaningful), or omit `as` and put {{secret:<alias>}} inside any param/header value. The VALUE is resolved server-side at the call and never appears in params, traces, or the published listing — so a published rig is safe to share and each RUNNER supplies their own key.",
      +    "items": {
      +      "properties": {
      +        "alias": {
      +          "type": "string"
      +        },
      +        "as": {
      +          "type": "string"
      +        }
      +      },
      +      "required": [
      +        "alias"
      +      ],
      +      "type": "object"
      +    },
      +    "type": "array"
      +  },
      +  "signet_action": {
      +    "description": "What the Signet step does. WRITES (move value, need a mandate + target + value): 'pay'/'transfer' (send SOL or an SPL token), 'swap' (trade via Jupiter), 'x402' (BUY a paid HTTP resource — signet_target = the resource URL, signet_value = your USDC ceiling in base units; Signet does the 402→pay→retry and returns the content). READS (no mandate, no value moved, safe on any step): 'metrics' (stage-shaped budgets/counts/spend for this actor — point a STAGE dashboard at this step), 'mandates' (is a mandate ready for this actor?), 'portfolio' (SOL + SPL balances; `signet_target` optionally names an address), 'price' (a live quote; `signet_target` = mint, `signet_value` = amount). PAPER (no mandate, no actor, NO money moves — real Jupiter quotes, a recorded ledger on the runner's account; output carries paper:true): 'wallet_activity' (a wallet's recent swaps; `signet_target` = the wallet, `signet_value` = limit) · 'paper_positions' / 'paper_reset' (`signet_target` = ledger name) · 'paper_buy' / 'paper_sell' (`signet_target` = ledger; the fills come from the previous step's JSON `buys` / `sells` list; `signet_value` = the default USD per buy or the sell fraction; a paper_buy step passes `sells` through for a following paper_sell). Defaults to 'pay' — the narrowest action — rather than guessing a wider one.",
      +    "enum": [
      +      "pay",
      +      "transfer",
      +      "swap",
      +      "x402",
      +      "metrics",
      +      "mandates",
      +      "portfolio",
      +      "price",
      +      "wallet_activity",
      +      "paper_buy",
      +      "paper_sell",
      +      "paper_positions",
      +      "paper_reset"
      +    ],
      +    "type": "string"
      +  },
      +  "signet_actor": {
      +    "description": "Makes this a SIGNET step — the rig acts with VALUE under a mandate the RUNNER already granted to this actor (e.g. `rig:trader`). The step carries NO credentials: no key, no mandate id, no address. The rail resolves (runner, actor) -> mandate, so a published or forked rig carries the strategy and none of the authority — run someone else's copy and it spends THEIR OWN mandate or refuses. If no mandate exists for the actor, the step refuses and names the fix.",
      +    "type": "string"
      +  },
      +  "signet_target": {
      +    "description": "Destination address (pay/transfer), the mint to trade into (swap), an address (portfolio) or the mint to price (price). For a WRITE it MUST be on the mandate's allowlist or the rail refuses. Templating: `{{input}}` is the run input and `{{upstream.<field>}}` reads the previous step's output. Not needed for 'metrics' or 'mandates'.",
      +    "type": "string"
      +  },
      +  "signet_value": {
      +    "description": "Amount in BASE units — lamports for SOL, the mint's base units for an SPL token. Never a decimal. Templating: `{{input}}` is the run input, and `{{upstream.<field>}}` reads the PREVIOUS step's output — which is how a decided position size actually reaches the trade (`{{upstream.size_lamports}}`). Hard-coding an amount in a rig whose earlier step computes one is a bug: the strategy's sizing is then calculated and discarded. If the reference does not resolve, the field is EMPTY and the step refuses — a trade sized by a leftover constant is a trade nobody decided. Not needed for a read action.",
      +    "type": "string"
      +  },
      +  "skill": {
      +    "description": "Registry skill name to bind this step to (discover via registry_search)",
      +    "type": "string"
      +  },
      +  "state": {
      +    "description": "DECISION step only: what to decide about — a template reading {{input}} and {{upstream.<field>}}; empty = the upstream output.",
      +    "type": "string"
      +  },
      +  "sub_rig": {
      +    "description": "Make this node a SUB-RIG step — the referenced SAVED rig runs here as a bounded sub-run (one level deep; runner-pays, shared budget, traces nested under this node). Pass {rig_id, name?} or a bare rig-id string (rig_search finds ids). Mutually exclusive with `skill`. ⚠ the canvas renders it as a labeled ⬡ block; its INNER edits go through you.",
      +    "type": [
      +      "string",
      +      "object"
      +    ]
      +  },
      +  "tool": {
      +    "description": "Tool name to invoke on the endpoint (optional)",
      +    "type": "string"
      +  },
      +  "url": {
      +    "description": "Plain-HTTP fetch target — the run performs a REAL HTTP request here via the SSRF-guarded proxy (optional; use with method + params + headers + secret_refs). `{{input}}` substitutes into the PATH or QUERY too (e.g. 'https://api.example.com/tokens/{{input}}'); the host stays fixed.",
      +    "type": "string"
      +  }
      +}
    • addedInput schema / properties / nodes / items / required
      Added value: +[
      +  "id"
      +]
    • addedInput schema / properties / outputs
      Added value: +{
      +  "description": "What the rig DECLARES IT PRODUCES — the contract's output side, shown on the canvas Output box and the product page. Same shapes as `input` (required is input-only). e.g. [{\"label\": \"the audit report\", \"kind\": \"markdown\"}].",
      +  "items": {
      +    "type": [
      +      "string",
      +      "object"
      +    ]
      +  },
      +  "type": "array"
      +}
    • addedInput schema / properties / state
      Added value: +{
      +  "description": "SHARED STATE CHANNELS (stateful_graph) — named channels loop iterations read/write via `{{node.port}}` refs; `reducer` merges concurrent fan-out writes: 'last_write' (default) | 'append' | 'add'. Each {id, kind?, reducer?}.",
      +  "items": {
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "type": "string"
      +      },
      +      "reducer": {
      +        "enum": [
      +          "last_write",
      +          "append",
      +          "add"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id"
      +    ],
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
    • changedInput schema / properties / structure_type / description
      Previous value: -"dag (default) or stateful_graph"New value: +"'linear' (a simple chain — what the guided Builder produces), 'dag' (default; acyclic, parallel branches + real fan-in) or 'stateful_graph' (cyclic loops + conditional edge guards — the runtime evaluates guards and skips unfired branches for real; the Builder CANVAS can't edit it yet, so tell the owner edits go through you)"
    • changedInput schema / properties / structure_type / enum
      Previous value: -[
      -  "dag",
      -  "stateful_graph"
      -]New value: +[
      +  "linear",
      +  "dag",
      +  "stateful_graph"
      +]
    • changedInput schema / properties / summary / description
      Previous value: -"Short rig summary"New value: +"Short human description of the flow (shows as the rig's name)"
    • changedInput schema / properties / wallet_address / description
      Previous value: -"Owner scope — derived from your auth token over the public MCP door; you don't pass it, and the run door reads the Rig from the same scope."New value: +"Wallet address that owns the rig (the scope the run reads)"
    • changedInput schema / required
      Previous value: -[
      -  "nodes"
      -]New value: +[
      +  "wallet_address",
      +  "nodes"
      +]
  6. First observed

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare it is a non-read-only, non-idempotent, non-destructive write, and the description supplements this well by stressing that bound steps are 'a real MCP tools/call' and that signet steps 'move value under the mandate'. It also warns that secrets must ride secret_refs rather than literals and that an unbound step silently becomes a model turn with no tools. It stops short of describing persistence semantics of saving over an existing key or any approval/abort behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The content is front-loaded with purpose and execution pairing, then flows into node/edge semantics and binding categories. It is dense and occasionally packs multiple rules into one sentence, but almost every clause carries real information for building a valid graph.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex graph-authoring tool with an output schema already covering returns, the description supplies the execution model, binding taxonomy, templating, and the required follow-up call, plus a pointer to tools_catalog for rarer fields. What remains thin is the save/validation lifecycle implied by name and key, which the agent must infer.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline would be 3, but the description adds a conceptual layer the schema does not: it groups the mutually exclusive node binding kinds (endpoint+tool, url, data_op, signet_actor, questions, sub_rig, doc_input) and explains that a node with no binding is a tool-less model turn. It also documents the template syntax for {{input}}, {{upstream.<field>}}, and {{secret:<alias>}} in one place rather than per-field.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Author a multi-step Rig as a graph the runtime executes for real.' It immediately positions the tool against the execution sibling by pointing to rig_run afterwards, so an agent knows this builds while rig_run runs.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives a clear operational path: author, then call rig_run (wait: true) in the same turn. It also explains what each distinct key means for saved rigs. It does not explicitly exclude cases where skill_author or agent_create would be the better sibling, leaving that inference to the agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.