verify_feature
Every option and step type the Tada MCP tool accepts: navigation, clicks, captions, drag-and-drop, checkpoints, walking authenticated pages, and driving email flows. (Formerly record_demo — the old name still works.)
verify_feature is the tool your agent calls to walk a feature (it was named
record_demo until 2026-08-24, and that name still works). Your agent calls it with a title
and an ordered list of steps; Tada drives a headless browser through them,
records the screen, verifies the checkpoints and uploads one mp4.
Options
| Option | Type | Default | What it does |
|---|---|---|---|
title | string | required | Shown on the watch page. 1–200 characters. |
steps | array | required | The ordered actions. At least one. |
device | desktop · mobile · tablet | desktop | Viewport preset. |
width / height | number | — | Exact viewport, overriding device. |
taskId | uuid | — | Attach the demo to a Tada task. |
storageState | path | — | A Playwright storageState JSON, so the demo starts already signed in. |
initScript | string | — | JavaScript run in every page before the app’s own scripts. |
dryRun | boolean | false | Record and transcode only, with no upload. Returns the local mp4 path. |
The presets are desktop 1920×1080, tablet 820×1180, mobile 390×844. Use
mobile only for genuinely mobile-first features; a desktop app filmed at 390px
wide is a demo of your responsive breakpoints, not of your feature.
dryRun is the fast loop while you’re getting the steps right: same recording,
same checkpoints, nothing published.
Steps
Every step is { action, ...fields }. Which fields matter depends on the action.
Navigating and interacting
| Action | Fields | Notes |
|---|---|---|
goto | url | Navigate. Supports ${magicLink} from a preceding awaitEmail. |
click | text or selector | Prefer text: visible labels survive refactors that break CSS selectors. |
fill | text (the label) or selector, value | value supports ${inbox}. |
select | text or selector, value | For custom comboboxes: the role=combobox widgets a plain click can’t drive. |
press | key | e.g. "Enter". |
hover | text or selector | |
scroll | value or selector, ms | value is "top", "bottom" or a pixel offset. ms sets the animation duration. It scrolls smoothly, on an ease-out curve, because a teleporting page is unreadable on video. |
drag | text/selector → toText/toSelector, native | See below. |
wait | ms | A plain pause. |
waitForText | text | Wait for text to appear. Not a checkpoint; see expect. |
wait and waitForText are pacing, not proof. Only expect gates the publish.
Narrating
| Action | Fields | Notes |
|---|---|---|
caption | text, ms | Narrates the next action. Defaults to 2500ms. |
Write captions for every meaningful action. A silent demo is a bad demo. Keep them to roughly six words: anything over 90 characters fills the frame, and the tool will tell you when you’ve crossed that line. Detail belongs in the voice-over, not on screen.
Whether captions are shown, spoken, and in which voice comes entirely from your Tada account settings. There’s no agent-side override: your agent writes the words, your dashboard decides how they’re delivered. If you supply no captions at all, Tada injects one from the demo title so every demo carries its mark.
Verifying
| Action | Fields | Notes |
|---|---|---|
expect | text, urlIncludes or selector, ms | Blocks the publish if it fails. Waits up to 8s by default. |
Put one after every meaningful action. Full detail in Verification.
Drag-and-drop
{ "action": "drag", "text": "My card", "toText": "In Progress" }
Tada tries a pointer-based drag first, which is what dnd-kit and
react-beautiful-dnd listen for, and switches automatically to real HTML5
dragstart/dragover/drop events when the source element is
draggable="true". Set native: true only if that auto-detection misses.
Always follow a drag with an expect asserting the item landed. An
unverified drag can silently no-op, and a video of a card that didn’t move is
exactly the kind of thing verification exists to catch.
Keeping credentials off camera
pause and resume take no fields. Between them the browser still performs
every step, but no frames are captured.
[
{ "action": "pause" },
{ "action": "goto", "url": "http://localhost:3000/login" },
{ "action": "fill", "text": "Email", "value": "[email protected]" },
{ "action": "fill", "text": "Password", "value": "hunter2" },
{ "action": "click", "text": "Sign in" },
{ "action": "goto", "url": "http://localhost:3000/tasks" },
{ "action": "resume" },
{ "action": "caption", "text": "Adding a task" }
]
The demo opens on the authed page with no login sequence and no password on
screen. For a longer-lived alternative, storageState starts the run already
signed in with no login steps at all, useful when you want to demo inside an
account that has real data in it.
Demoing an email flow
Magic links and OTP codes are demoable: Tada can mint a disposable inbox and read it mid-recording.
| Action | Fields | Notes |
|---|---|---|
createInbox | — | Mints an inbox. Use its address as ${inbox} in a later fill. |
awaitEmail | text, ms | Waits for the mail. text filters on a subject substring; ms defaults to 20000. |
After awaitEmail, the link from the email is available to a later goto as
${magicLink}:
[
{ "action": "createInbox" },
{ "action": "fill", "text": "Email", "value": "${inbox}" },
{ "action": "click", "text": "Send me a link" },
{ "action": "awaitEmail", "text": "Sign in to" },
{ "action": "goto", "url": "${magicLink}" },
{ "action": "expect", "urlIncludes": "/dashboard" }
]
An email that never arrives is a verification failure, not a skipped step. The demo doesn’t publish.
What comes back
On success: the share URL, the duration, and a ✓ verified marker if the demo
had checkpoints. Alongside it, any notes worth your attention: skipped steps,
captions that ran long, which narration mode was applied, whether a newer
recorder version is out.
On a failed checkpoint: no link, the list of failures, and the path to the local recording. See Verification.
Next: the CLI reference.