Edit source
edit_sourceChange the source of a form or a workflow, on its draft. Nothing goes live until publish.
dry_run defaults to true: the first call previews the change and returns diagnostics without committing. Read them, fix your ops, then call again with dry_run: false.
ops is an ordered array of { kind: "edit", anchor, action, text? }: action is replace | insertBefore | insertAfter | delete; anchor is { id } (a field key or block id; prefer it), { find, replaceWith } (an exact substring and what replaces it; no action or text) or { find } (an exact substring, for delete only); text is required for replace, insertBefore and insertAfter. Ids are made by Paperform: never invent one.
Workflows are supported too: call get_help with topic "workflow" first, read the workflow's #source (it ends with a read-only ::: outputs block: each step's saved example output, trimmed; edits to it are ignored), and send only edit ops (path may be omitted): anchor a trigger, step or component on its #id ({ id: "step-1" }; a quoted id such as #"step 1" is anchored with the unquoted string). A rule is written on the step it runs into (after, when, on): change it by replacing that step. Indentation is cosmetic. A dry run returns { diff } — the node and rule ids added, removed and changed — and no save; a commit saves the draft and returns Stepper's validation issues as diagnostics with the line of the node they concern in the source as re-fetched. A committed edit can delete a step or trigger. Confirm with the user before calling this. For a workflow, expected is { version: , version_id: <version_id> } with the values #source's metadata reported; the edit is refused if the workflow changed since. Some of what the editor fills in on setup is filled in here too, from the app's static definition, and only by edit_source and create (update no longer writes a workflow's steps). A step whose params the edit writes (a new step, one moved to another app or action, or one with a param added, changed or removed) gets the declared default of each field it leaves out that has a plain default: text, number, true/false and date fields, multiple-value text fields and single or multiple dropdowns the app's #schema lists. Fields resolve_parameters lists that the #schema does not (computed from the values chosen so far), and object and reference fields, are never filled: write their defaults yourself. When the workflow is published, Stepper checks each step's fields and marks every HTML field (an email body) whose param makes no choice with options { encodeHTML: true }, so the answers a reference inserts are HTML-escaped however the step was written; a publish is refused if a step's fields cannot be checked. A param that says options { encodeHTML: false } keeps answers raw: write that only to insert trusted HTML on purpose, and the dry run warns about it on a field the app's #schema declares html. Plain-text fields get answers as they are. The dry run refuses a required param left out of a step whose params the edit writes, and a trigger type other than the one fetch on the app lists for that trigger. It checks each reference ({{step.path}} in a param, and each when condition) against the referenced step's example: against a loaded sample or test run, a path the example does not offer is a warning naming the closest one it does; against the app's own example, which lists only some keys, only a key nested deeper than the example puts it is noted; a step with no example is not checked. A path in the older list form, with an index between list levels, is warned about with the path Stepper now resolves it as. A commit stores an example on each trigger or step it adds or moves to another app, operation, trigger or connection, without running anything: a trigger gets its first recent item loaded through its connection (or, when there is none, the trigger's example as the editor builds it, else its app's own example), an action its app's own example when the app declares one. Diagnostics with code workflow_example say what was stored, and warn about each step a later step or rule reads that still has no example, with the call that stores one (load_trigger_samples or test_action, with workflow and node); publishing over MCP is refused until each has one. A step nothing reads needs none.
Forms: call get_help with topic "forms" first, read the form's #source (or #outline for its ids), and send only edit ops, anchored on field keys or block ids in that text (path may be omitted). The edit goes to the form's draft; a committed edit returns preview: { url, expires_at }, a five-minute link that renders the draft in a browser; give it to the person after each committed edit, since the form's live link shows the published version until publish puts the draft live. A committed edit can delete a field. Confirm with the user before calling this. For a form, expected is { version: "" } with the version string fetch reported in its metadata (e.g. "abc123:4" or "live:4"), passed back unchanged; the commit is refused if the form changed since.
Example: edit_source({ id: "12:form:abc123", ops: [{ kind: "edit", anchor: { id: "7ep7s" }, action: "insertAfter", text: "@email "Work email"" }] }) → { "dry_run": true, "diagnostics": [] }
Tool call template
Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.
{
"id": 1,
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"arguments": {
"id": "<id>",
"ops": "<ops>"
},
"name": "edit_source"
}
}Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | A form id or a workflow id, exactly as search, list or fetch returned it. | |
| ops | Yes | Ordered edit ops ({ kind: "edit", … }): forms and workflows take no other kind. action is replace | insertBefore | insertAfter | delete. | |
| dry_run | No | Preview without committing. Defaults to true. | |
| message | No | Optional change note. | |
| expected | No | For a form, { version: "<version>" }, the version string from fetch's metadata, unchanged; for a workflow, { version: <v>, version_id: <id> }; refuses the commit if it changed since. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | ||
| diff | No | ||
| dry_run | No | ||
| committed | No | ||
| diagnostics | No | ||
| applied_with_repairs | No |