# API (/api)

# Stunt Double API [#stunt-double-api]

Stunt Double exposes its platform through a **Model Context Protocol (MCP)**
server at `https://app.stuntdouble.io/api/mcp` (Streamable HTTP, OAuth 2.1).
Drive your workspace from Claude, your own agents or any MCP client, without
writing a bespoke integration.

- **[MCP server](/mcp)**: authentication, scopes and what a connection can reach.
- **[Connect](/mcp/connect)**: set up Claude, Cursor, ChatGPT and other clients.
- **[Tools](/mcp/tools)**: every tool, prompt and resource, grouped by what it acts on.

## Public docs server [#public-docs-server]

Agents that haven't signed in can still connect. A read-only MCP server at
`https://www.stuntdouble.io/api/docs-mcp` needs no authentication and exposes
the documentation as resources. It has no tools: to act on a workspace,
connect to the product server above.

## Versioning [#versioning]

A client negotiates the MCP protocol version at `initialize`. Breaking changes
ship alongside the old behavior, never in place, and deprecations are announced
in the [changelog](https://www.stuntdouble.io/changelog) at least 90 days
before removal.

## Discovery [#discovery]

| What                  | Where                                                    |
| --------------------- | -------------------------------------------------------- |
| MCP manifest          | `https://www.stuntdouble.io/.well-known/mcp.json`        |
| AI Catalog            | `https://www.stuntdouble.io/.well-known/ai-catalog.json` |
| OpenAPI spec          | `https://www.stuntdouble.io/openapi.json`                |
| Site index for agents | [`/llms.txt`](/llms.txt)                                 |


# Concepts (/concepts)

# Core concepts [#core-concepts]

Stunt Double is organized around a small number of building blocks. This page
explains each one and how they fit together.

<ConceptMap />

| Term | In one line |
| --- | --- |
| Workspace | Your team: members, billing and every project |
| Project | One product or surface, and everything that tests it |
| Actor | An AI user with instructions and knowledge that drives a real browser |
| Checklist | Plain-language checks an actor verifies, each with evidence |
| Interview | A structured study run with persona participants, ending in a report |
| Automation | A graph of steps that runs checklists on a schedule or an event |
| Comment | A pin a real visitor drops on your page through the feedback widget |
| Insight | A notable observation surfaced from a run |

## Workspaces and projects [#workspaces-and-projects]

A **workspace** is the top-level container for your team. It owns members,
billing, and every project. A **project** sits inside a workspace and groups the
actors, checklists, and runs for one product or surface. Teams working on
several products keep them separated by using a project per product.

## Actors [#actors]

An **actor** is an AI assistant you create. It is defined by its instructions
and its knowledge:

- **Instructions** describe the actor's role and how it should behave.
- **Knowledge** is the reference material the actor can draw on, such as facts
  about your product or test credentials.

Under the hood, an actor drives a real browser and works through your product
the way a person would, observing the page, taking an action, observing the
result, and repeating until the task is done. It can also read email sent to
its own inbox, which is what makes signup and verification flows testable.

## Checklists [#checklists]

A **checklist** is an automated verification workflow. You describe the steps you
want checked in plain language, and an actor runs through them in a hosted
browser. Each step is reported as passed or failed, with a screenshot and a
short explanation.

<Screenshot
  name="checklist-run"
  alt="A checklist run: a failed check with the actor's explanation and a screenshot of the error, above the passed checks."
  caption="A checklist run. Each result carries what the actor did and what it saw."
/>

Checklists are the fastest way to catch regressions in flows that are tedious to
test by hand, such as signup, checkout, or onboarding.

## Interviews [#interviews]

An **interview** is a structured conversation Stunt Double runs with your users.
You define sections and questions, the platform conducts the interview, and it
produces a report summarizing what it learned. Interviews are useful for user
research and feedback gathering at a scale that manual interviews cannot reach.

## Workflows [#workflows]

A **workflow** (an **automation** in the dashboard) chains steps into a single repeatable automation. Where a
checklist verifies a flow, a workflow is meant to get something done end to end,
and can combine several actions into one run.

<Screenshot
  name="automation-builder"
  alt="The automation flow editor: a GitHub trigger, a Run Checklist step, and a condition that branches to a GitHub comment or a notification."
  caption="An automation is a graph of steps, started by a trigger: a schedule, a webhook, or an event from GitHub, Vercel, Slack, Linear or Figma."
/>

## Comments and the feedback widget [#comments-and-the-feedback-widget]

Actors report what an AI persona experienced. **Comments** cover the other half:
what a real visitor experienced. The **feedback widget** is a small script you
add to your own site, letting visitors drop a pin anywhere on a page and leave a
comment in the place it applies to.

Each comment arrives with a screenshot, the page URL, the pin position, and
device details, and lands in the project's Comments view grouped by page. From
there you reply in the thread and move each one through **new**, **reviewed**,
**resolved**, or **dismissed**.

<Callout variant="note" title="Security boundary">
  The widget is identified by a per-project API key that lives in your page source, so the real
  access control is the **domain allowlist** on the project: the widget API only answers requests
  from domains you have approved. Workspace admins can narrow that further, or switch the widget off
  across the whole workspace, from Settings → Security.
</Callout>

See **[Collect stakeholder feedback on a prototype](/tutorials/feedback-widget)**
for installation and triage.

## Insights [#insights]

As actors work, Stunt Double surfaces **insights**: notable observations drawn
from a run, such as a confusing step, a broken link, or an unexpected result.
Insights turn raw session recordings into something you can act on without
watching every replay.

## Issues and goals [#issues-and-goals]

Each project has an **Issues** tab. Problems your runs and reviewers have
already found appear there on their own: failed checks, open design review
notes, untriaged comments and insight findings, each linked back to its
evidence. Turn one into a tracked issue in a click, then move it through open,
in progress, resolved or dismissed. The highest severity sits at the top.

**Goals** say what a project is trying to achieve, such as "New visitors finish
sign-up without help". Link insights and feedback to the goal they support, so
the evidence for each goal sits in one place. A goal is active until you mark
it completed or archive it.

## Recordings and replays [#recordings-and-replays]

Everything an actor does is recorded. For any run you can replay the session,
step through each action, and see the screenshot captured at that moment. This
is what makes a failed checklist or a surprising interview answer easy to
diagnose.

## Next steps [#next-steps]

Ready to automate this? See the **[API overview](/api)** and the
**[MCP server](/mcp)**, and connect the tools your team already uses from
**[Integrations](/integrations)**.


# Quickstart (/getting-started)

# Quickstart [#quickstart]

This guide takes you from a new account to your first checklist run.

<PageFacts
  items={[
    { label: "Time", value: "About 5 minutes" },
    { label: "Who", value: "Anyone in a workspace" },
    { label: "You need", value: "A URL an actor can reach" },
  ]}
/>

<Steps>
<Step>

### Create a workspace [#create-a-workspace]

When you first sign in, Stunt Double asks you to create a **workspace**. It
holds your projects, members and billing, so name it after your team or
company.

Sign-in is passwordless: enter the six-digit code sent to your email, or
continue with Google.

</Step>
<Step>

### Create a project [#create-a-project]

Inside the workspace, create a **project** for the product or surface you
want to test. A project keeps related actors, checklists and runs together, so
a team with several products keeps them apart.

<Screenshot
  name="dashboard"
  alt="A project in the Stunt Double dashboard: the sidebar lists interviews, comments, checklists and automations, beside the project's checklists and their latest results."
  caption="A project's home. Everything you create from here on lives inside it."
/>

</Step>
<Step>

### Create an actor [#create-an-actor]

An **actor** is an AI user that does the work. Open **Actors** in the
sidebar, choose **Create actor**, and pick a template or start from scratch.
Then give it:

- **Instructions** that describe who it is and how it behaves.
- **Knowledge** it can draw on: facts about your product, test accounts,
  conventions to follow.

Out of the box an actor drives a real browser and reads email sent to its own
inbox, so it works through your product the way a person would. For a first
run, a one-paragraph description of your audience is plenty.

<Screenshot
  name="actor-create"
  alt="An actor's Overview tab, with its name, description, instructions and location."
  caption="Name the actor by role, not as a person. Instructions say who it is; knowledge is what it knows."
/>

</Step>
<Step>

### Write and run a checklist [#write-and-run-a-checklist]

In the project, open **Checklists** and choose **Create checklist**. A
checklist is a set of checks written in plain language, for example:

1. Open the pricing page.
2. Confirm the Pro plan shows a monthly and an annual price.
3. Start sign-up and confirm it reaches the email step.

<Screenshot
  name="checklist-editor"
  alt="A checklist being edited, with three plain-language checks listed in order and a Run button."
  caption="One check per outcome. The actor works out the clicks."
/>

Create the checklist, then press **Run** on its page. The actor opens a
hosted browser and works through each check.

</Step>
<Step>

### Review the run [#review-the-run]

Every run is recorded. Open it to replay the session, inspect each check's
result and screenshot, and see any **insights** the platform surfaced.

<Screenshot
  name="checklist-run"
  alt="A finished checklist run: two checks passed and one failed, with the failed check open to show its screenshot and the actor's explanation."
  caption="A failed check opens on the moment it failed, with the actor's reasoning beside the screenshot."
/>

</Step>
</Steps>

## Next steps [#next-steps]

<CardGrid
  items={[
    {
      icon: "actor",
      title: "Build an actor that behaves like your audience",
      description: "Instructions and knowledge that make an actor act like a real member of your audience.",
      href: "/tutorials/first-actor",
    },
    {
      icon: "checklist",
      title: "Write checklists that catch real regressions",
      description: "Check outcomes, not clicks, and read the evidence behind the badge.",
      href: "/tutorials/effective-checklists",
    },
    {
      icon: "plug",
      title: "Connect your tools",
      description: "Run checks on pull requests, deploys and design changes.",
      href: "/integrations",
    },
    {
      icon: "terminal",
      title: "Drive it from an assistant",
      description: "Connect Claude, Cursor or ChatGPT through the MCP server.",
      href: "/mcp",
    },
  ]}
/>


# Claude Design (/guides/claude-design)

Claude is where a growing share of design work starts: interactive artifacts,
landing pages, dashboards, and full prototypes sketched in a conversation. The
gap is validation. Claude can generate ten variants of a flow, but it cannot
tell you which one a distracted first-time user actually completes. With the
Stunt Double connector, the same conversation that produced the design can also
test it.

<Callout variant="who">
  Designers and PMs designing in Claude (artifacts, Claude design previews), and
  anyone using Claude as their daily assistant who wants research and QA on tap.
</Callout>

## Setup (once) [#setup-once]

Pick whichever fits how you use Claude:

- **Claude Desktop, one click**: in the app, open **Integrations** > Claude,
  select **Download .mcpb** and open the file with Claude Desktop (needs
  Node 18 or later).
- **Claude (web or desktop), manual**: in Claude, open Settings > Connectors >
  Add and paste the URL `https://app.stuntdouble.io/api/mcp`.

See **[Claude](/mcp/clients/claude)** and **[MCP](/mcp)** for more detail.

The first time Claude uses a Stunt Double tool, a browser window opens to sign
in and authorize. No API keys.

<FlowStrip
  nodes={[
    { label: 'Add the connector', sub: 'One click or a URL' },
    { label: 'Authorize in browser', sub: 'No API keys' },
    { label: 'Prompts appear', sub: 'validate_design and friends' },
  ]}
/>

Once connected, the workflow prompts (`validate_design`, `run_user_research`,
`triage_feedback`, and friends) are available from the prompt picker, and
Claude will also reach for the tools unprompted when a testable URL shows up in
conversation.

## Workflow: design in Claude, validate with personas, iterate [#workflow-design-in-claude-validate-with-personas-iterate]

1. **Design as usual.** Ask Claude for the artifact: a pricing page, an
   onboarding flow, a dashboard.
2. **Publish it.** Use Claude's publish/share option on the artifact so it has
   a public URL. Stunt Double actors need a reachable URL; a published
   artifact is exactly that.
3. **Validate in the same chat.** Use the `validate_design` prompt, or just
   say:

   > "Run this past 3 Stunt Double personas: a rushed mobile user, a skeptical
   > CFO, and someone unfamiliar with the product. Can they work out what we
   > sell and what it costs?"

   Claude creates an interview against the published URL, defines the
   personas, launches it, and pulls the report back into the conversation.

4. **Iterate with evidence.** Findings arrive as themes, task success, and
   transcript quotes. Because the design lives in the same chat, Claude can
   apply the changes to the artifact immediately and re-run the identical
   interview for a before/after comparison.

<FlowStrip
  loop
  nodes={[
    { label: 'Generate', sub: 'Ask Claude for the artifact' },
    { label: 'Publish', sub: 'Get a public URL' },
    { label: 'Test', sub: 'Personas try to use it' },
    { label: 'Revise', sub: 'Fold findings back in' },
  ]}
/>

This loop (generate, publish, test, revise) typically fits inside a single
working session, which is the point: validation happens while the design is
still cheap to change.

<TextureBreak />

## Workflow: research without a design [#workflow-research-without-a-design]

Stunt Double is also useful in Claude before anything is designed:

- **Concept and comprehension checks** against competitors or your current
  production site: "Interview 4 personas on the competitor's site: where do
  they get lost in checkout?"
- **Feedback triage**: "Triage the new feedback on the marketing site project"
  pulls in feedback pins (with screenshots and device info), clusters them,
  and can reproduce suspected bugs with a checklist run.
- **Standing guardrails**: "Set up daily checks on signup and checkout" uses
  the `setup_guardrails` prompt to create checklists plus a scheduled
  workflow, so this becomes ambient rather than on-demand.

<Pullquote>
  Personas beat testers. Ask for real texture, not "a user", and the variety is
  what surfaces the interesting failures.
</Pullquote>

## Tips [#tips]

- **Personas beat testers.** Ask for personas with real texture (background,
  goals, patience, device) rather than "a user". Variety across participants
  is what surfaces the interesting failures.
- **Tasks beat questions.** "Find the price of the annual plan and start
  checkout" produces observable evidence; "do you like this page?" produces
  pleasantries.
- **Keep the interview.** Re-running the same interview after changes gives
  you a longitudinal signal, not a one-off anecdote.
- Runs take a few minutes; Claude polls and reports when done. Watch the
  browser session recordings in the dashboard at
  [app.stuntdouble.io](https://app.stuntdouble.io) when you want to see
  exactly what a persona did.


# Code changes (/guides/codebases)

Unit tests prove the code does what the code says. Stunt Double proves a user
can still do what they came for. Wired into a coding agent and GitHub, it
closes the loop between "the diff looks right" and "the flow works": every
change that reaches a preview deployment can be walked end to end by an AI
persona before merge, with results reported where engineers already look.

<Callout variant="who">
  Engineers using Claude Code, Cursor, Windsurf, or any MCP-capable coding agent, and teams that
  want user-level verification in their PR flow.
</Callout>

## Setup [#setup]

1. **Connect your coding agent** (plugin preferred, MCP config as a fallback):

   **Claude Code:** add the server, or install the plugin (server plus skills and
   agents):

   ```bash
   claude mcp add --transport http stuntdouble https://app.stuntdouble.io/api/mcp
   ```

   ```bash
   claude plugin marketplace add stunt-double/stuntdouble-mcp
   claude plugin install stuntdouble@stuntdouble
   ```

   **Cursor:** install the
   [Stunt Double Cursor plugin](https://github.com/stunt-double/stuntdouble-mcp)
   (MCP server plus skills, rules and agents). From the
   [Cursor marketplace](https://cursor.com/marketplace) once listed, or add the
   repo as a team marketplace (Dashboard → Plugins → Team Marketplaces → Add
   Marketplace → Import from Repo), then install `stuntdouble` from Customize.

   **MCP only** (Cursor without the plugin, Windsurf, or any other JSON-config
   client):

   ```json
   // .cursor/mcp.json or ~/.cursor/mcp.json
   { "mcpServers": { "stuntdouble": { "url": "https://app.stuntdouble.io/api/mcp" } } }
   ```

   You authorize via browser on first use; no API keys. **[MCP](/mcp)** has
   setup for more clients.

2. **Connect GitHub** to your workspace (**Integrations** > GitHub >
   **Connect GitHub**, then **Install GitHub App** for the repositories you
   choose) so agents can read PRs and post results, and workflows can trigger
   on GitHub events. See **[GitHub](/integrations/github)**.

3. **Teach your agent the habit.** Add a note to your repo's agent
   instructions (`CLAUDE.md`, `.cursorrules`, etc.) so verification happens by
   default, not on request:

   ```markdown
   ## Verification

   After a change is deployed to a preview URL, verify it with Stunt Double
   (use the verify_change MCP prompt): run a checklist over the affected user
   flows and report per-check results with evidence. Post the results on the
   pull request. If a checklist for the flow already exists, re-run it rather
   than creating a new one.
   ```

## Workflow: verify a PR on its preview deployment [#workflow-verify-a-pr-on-its-preview-deployment]

The `verify_change` prompt encodes the whole loop, or drive it
conversationally:

> “The preview for this branch is at my-branch-preview.vercel.app. Verify the
> signup changes and post results on the PR.”

<FlowStrip
  nodes={[
    { label: 'Preview deploys', sub: 'PR opens a URL' },
    { label: 'Checklist runs', sub: 'Actor walks the flow' },
    { label: 'Fix and re-run', sub: 'Agent reads evidence' },
    { label: 'Verdict on the PR', sub: 'Pass/fail per check' },
  ]}
/>

Your agent will:

1. Create (or reuse) a checklist against the preview URL, with instructions
   derived from the diff and 4 to 8 pass/fail checks: the new behavior,
   adjacent flows, and error states.
2. Run it; an actor drives a real browser through the flows and records
   evidence per check.
3. On failures, read the evidence, propose or apply the fix, and re-run.
4. Post a verdict comment on the PR: pass/fail per check with evidence
   highlights.

Since the coding agent both wrote the change and read the failure evidence,
the fix loop is tight: no repro steps, no handoff.

<TextureBreak />

## Workflow: checks that run themselves [#workflow-checks-that-run-themselves]

Checklists persist, so verification compounds into a regression net:

- **On deploys**: a workflow with a Vercel or GitHub event trigger re-runs the
  critical checklists on every deploy or PR, with notification steps for
  failures and GitHub comment / actor PR review steps to report inline.
- **On a schedule**: cron-triggered workflows (daily against production) catch
  regressions that arrive via dependencies, config, or content rather than
  code.

Set both up in one shot with the `setup_guardrails` prompt. After a few PRs
you accumulate a library of user-flow checks that no one has to remember to
run.

## Workflow: from bug report to fix without a human repro [#workflow-from-bug-report-to-fix-without-a-human-repro]

Feedback captured by Stunt Double (pinned to a page, with screenshot and
device info) is agent-readable:

> “Triage the new feedback on the dashboard project; reproduce anything that
> looks like a bug.”

The agent clusters reports, reproduces suspects with targeted checklist runs,
fixes what it can in the codebase, verifies on the preview, and updates the
feedback status. The `triage_feedback` prompt packages this.

## Tips [#tips]

- Actors have their own email inboxes, so flows behind OTP or magic-link auth
  are testable end to end.
- Keep checks user-phrased ("a new user can complete signup with a magic
  link") rather than DOM-phrased ("the submit button is enabled"); they
  survive refactors.
- Small focused checklists beat one giant one: clearer failure signal, cheaper
  re-runs.
- Runs are async and take a few minutes; agents should poll for results
  rather than assume completion.


# Stakeholder feedback (/guides/feedback-widget)

AI personas tell you what a careful, unhurried user would hit. Real visitors
tell you what actually happened to them, on their device, on the version you
shipped this morning. The feedback widget closes that gap: one script tag on
your own site, and anyone looking at a page can drop a pin on the exact spot
that confused them and say why.

What arrives is not a support ticket with "the page is broken" in it. It is a
comment anchored to a coordinate, with a screenshot of the page as that person
saw it, the URL, the viewport, and which version of the page they were on.

<Callout variant="who">
  PMs and designers running prototype and staging reviews, founders collecting
  feedback from early customers, and any team tired of screenshots pasted into
  Slack with no idea which page or build they came from.
</Callout>

## Setup, in about five minutes [#setup-in-about-five-minutes]

<FlowStrip
  nodes={[
    { label: 'Enable', sub: 'Project -> Comments' },
    { label: 'Install', sub: 'One script tag' },
    { label: 'Allow', sub: 'Your domains only' },
    { label: 'Triage', sub: 'Reply and resolve' },
  ]}
/>

Enable **Comments** on the project, paste one script tag before `</body>` (or
hand the install prompt to a coding agent), and the install screen flips to
**Widget detected** once a page loads it. Then set your allowed domains before
you point anyone at it. The
[integration reference](/integrations/feedback-widget) has the snippet and
every setting.

## What a visitor does [#what-a-visitor-does]

Press **`c`** or click the floating button, click the thing you mean, type the
comment, send. Name and email are remembered on their browser, so the second
comment is just the comment. Email is optional and only used to tell them when
someone replies or marks their comment resolved.

If you enable **Show comments on page**, existing pins render for everyone, so a
group review turns into a shared thread rather than five people reporting the
same button. Turn it off and the widget still collects, it just does not
display.

<Pullquote>
  The pin is the point. A comment attached to a coordinate and a screenshot
  needs no reproduction steps.
</Pullquote>

## Three ways teams use it [#three-ways-teams-use-it]

<RoleGrid
  items={[
    { role: 'Staging review', gets: 'Require sign in, and the whole team leaves notes in context instead of in Slack.' },
    { role: 'Prototype testing', gets: 'Put a rough build in front of ten early users and watch where the pins cluster.' },
    { role: 'Live product', gets: 'A standing feedback channel on the pages that matter, grouped by page and version.' },
  ]}
/>

**Internal review on staging.** Switch on **Require sign in** and only signed-in
members of your workspace can read or leave comments. Reviewers sign in through
a popup from the widget, and their replies are badged **Team** on the page. It
is the cheapest possible design-review tool: no seats to provision, no file to
upload, and the thing under review is the actual product.

**Prototype and beta testing.** Ship the rough version to a handful of real
users with the widget on. Where pins cluster is your friction, and you have the
screenshot to prove it. This pairs directly with persona interviews: run an
interview to find what a careful user hits, run the widget to find what a
hurried one does.

**A standing channel on production.** Leave it installed with sign-in off and
comments hidden from other visitors. People report what bothers them where it
bothers them, and it lands in a queue rather than a support inbox.

## Triage without losing the thread [#triage-without-losing-the-thread]

Comments are grouped **by page**, because that is how fixes get made. Each group
shows every pin left on that page, and you collapse the ones you have handled.

<Screenshot
  name="feedback"
  alt="The project Comments view with comments grouped by page, each showing a screenshot of where it was pinned, its status and replies."
  caption="Comments grouped by page, each with the screenshot and element it was pinned to."
/>

Open one and you get the screenshot with the pin in place, the device and
viewport, the reply thread, and the status. Reply and the visitor is notified if
they left an email, with your reply appearing on the page for them. Move it
through **new**, **reviewed**, **resolved**, or **dismissed**; resolving notifies
the author, which is the difference between collecting feedback and closing a
loop. After a round of fixes, **Resolve all** clears the open queue in one go.

### Versions, so old comments stay readable [#versions-so-old-comments-stay-readable]

Pages change, and a comment about a button you deleted is noise unless you know
which build it was about. Every comment records a hash of the page content, and
comments are grouped into **v1**, **v2**, and so on, with the newest badged
**Latest**. Select a version to see only the comments left on it.

If you would rather tie versions to your releases than to page content, set
`data-sd-version` on the script tag to a build ID, release tag, or commit SHA.
The widget then uses that verbatim.

## Who can actually use it [#who-can-actually-use-it]

The API key in your page source is an identifier, not a secret. The domain
allowlist decides which sites the widget answers, **Require sign in** limits it
to workspace members, and workspace admins can narrow or switch it off for
every project. See
[Control who can use it](/integrations/feedback-widget#control-who-can-use-it).

<TextureBreak />

## Feeding comments back into verification [#feeding-comments-back-into-verification]

A comment tells you something is wrong once. A checklist tells you whether it is
still wrong on every deploy. The move that pays off is turning the recurring
ones into checks:

```text
1. Read the open comments for a page (list_feedback, status: "new")
2. Reproduce: run a checklist against that page with the reported flow
3. Fix, then keep the checklist and wire it into a deploy-triggered workflow
4. Resolve the comment (update_feedback_status) so the author is notified
```

All three feedback tools are on the MCP server, so an agent can work that loop
with you: "read the new comments on the pricing page, reproduce the top one with
a checklist, and tell me what you find." See the
**[codebases guide](/guides/codebases)** for the deploy-trigger side, and the
**[validation playbook](/guides/validation)** for turning findings into standing
checks.

## Tips [#tips]

- Install on staging first, with **Require sign in** on. You get the value
  immediately and nothing is exposed while you are still deciding.
- Only ask for an email if you intend to reply. Collecting addresses and going
  quiet is worse than not collecting them.
- Set `data-sd-version` if you deploy several times a day. Content hashing works,
  but grouping by release is unambiguous when you are reading back a week later.
- Resolve aggressively. An open queue that reflects genuinely outstanding work
  gets looked at; one that has become an archive does not.
- Turn off the `c` shortcut if your own page already uses it. It is one toggle,
  and clashing shortcuts confuse visitors more than they help.


# Figma Make (/guides/figma-make)

Figma Make turns a prompt or a design into a working web app and publishes it
to a real URL. That published URL is exactly what Stunt Double actors are
built to test, which makes Make plus Stunt Double the fastest idea-to-evidence
loop available: describe a product, publish it, and have AI personas try to
use it, all before an engineer touches it.

<Callout variant="who">
  Designers and PMs prototyping in Figma Make, and founders validating concepts
  before committing to a build.
</Callout>

## Setup [#setup]

- Add the Stunt Double MCP connector to the agent you use around Make (Claude,
  Claude Code, Cursor): `https://app.stuntdouble.io/api/mcp`. Sign in when
  prompted the first time. **[MCP](/mcp)** covers each client.
- Optionally connect Figma from **Integrations** at
  [app.stuntdouble.io](https://app.stuntdouble.io) so workflows can also react
  to Figma events. See **[Figma](/integrations/figma)**.

## The loop: build, publish, test, revise [#the-loop-build-publish-test-revise]

<FlowStrip
  loop
  nodes={[
    { label: 'Build in Make', sub: 'Rough is fine' },
    { label: 'Publish', sub: 'Get the live URL' },
    { label: 'Test with personas', sub: 'Tasks and questions' },
    { label: 'Revise in Make', sub: 'Republish, re-run' },
  ]}
/>

1. **Build in Make.** Get the prototype to the point where the core flow
   exists, even roughly. Real (fake) content beats lorem ipsum: personas read
   the page like users do.
2. **Publish.** Use Make's publish action to get the live site URL. Stunt
   Double needs nothing else; there is no SDK to embed.
3. **Test with personas.** From your agent, use the `validate_design` prompt
   with the published URL, or be specific:

   > "Interview 4 personas against my published Make site: a power user of a
   > competitor, a non-technical small business owner, a price-sensitive
   > student on mobile, and an accessibility-conscious user. Brief: can they
   > understand what this does and complete signup?"

   The agent builds the interview (tasks plus comprehension questions),
   attaches the personas with mixed devices, launches, and returns the report.

4. **Revise in Make.** Take the top friction points back into Make (or let
   your agent rewrite the Make prompt with the findings folded in), republish,
   and re-run the same interview. Because the discussion guide and personas
   are unchanged, differences between rounds are attributable to the design
   change.

Two or three cycles of this typically settles the big questions
(comprehension, navigation, willingness to proceed) in an afternoon.

<Pullquote>
  Test the ugly version. The strongest signal comes before you polish; personas
  are immune to embarrassment.
</Pullquote>

## When the prototype graduates [#when-the-prototype-graduates]

Make prototypes tend to become real products. The assets you built during
validation come along:

- The **project** in Stunt Double just gets a new URL (staging, then
  production).
- The **interviews** re-run against the real product, giving continuity of
  evidence from prototype to launch.
- Add **checklists** for the flows as they harden, and a scheduled or
  deploy-triggered **workflow** (`setup_guardrails` prompt) so the validated
  behavior stays validated. See the
  **[codebases guide](/guides/codebases)** for the engineering-side loop.

## Tips [#tips]

- Test the ugly version. The strongest signal comes before you polish;
  personas are immune to embarrassment.
- One flow per round. "Can they sign up" and "do they understand pricing" are
  separate studies; mixing them muddies the report.
- Watch a session recording in the dashboard at least once per round; seeing a
  persona hesitate is more persuasive to stakeholders than any summary.
- Make sites are public by default when published, so there are no auth
  hurdles. If you add auth later, actors handle email flows: they have their
  own inboxes for OTP and magic-link sign-ins.


# Figma prototypes (/guides/figma)

Figma is where the design decisions happen; Stunt Double is how you find out
whether users can actually follow them. The integration works at two levels:
agent-driven testing of prototypes (via MCP), and event-driven automation that
re-checks work whenever a file changes (via the Figma connection).

<Callout variant="who">
  Designers validating flows before handoff, design-systems teams guarding
  consistency, and PMs who want usability signal attached to the file, not
  buried in a separate report.
</Callout>

## Setup [#setup]

1. **Connect Figma and link files to projects**, as
   **[Figma](/integrations/figma#set-it-up)** describes. That turns on Figma
   triggers for workflows and keeps runs, feedback and interviews attached to
   the design source.
2. **For agent workflows**, add the Stunt Double MCP connector to whichever
   agent you use alongside Figma (Claude, Claude Code, Cursor):
   `https://app.stuntdouble.io/api/mcp` (setup per client in
   **[MCP](/mcp)**). If you also run Figma's own MCP
   server, the two compose well: Figma MCP reads and edits the design, Stunt
   Double MCP tests it.

<FlowStrip
  nodes={[
    { label: 'Figma MCP', sub: 'Reads and edits the design' },
    { label: 'Stunt Double MCP', sub: 'Tests the rendered result' },
    { label: 'Evidence', sub: 'Transcripts on the file' },
  ]}
/>

## Workflow: test a prototype with personas [#workflow-test-a-prototype-with-personas]

Actors drive real browsers, so anything viewable at a URL is testable. For
Figma that means:

- **Prototype share links** (`figma.com/proto/...`) set to "anyone with the
  link can view". Good for click-through flow testing: can a persona find
  their way from the landing frame to the goal?
- **Figma Make published sites** for anything with real interaction. See the
  dedicated **[Figma Make guide](/guides/figma-make)**.

From your agent:

> “Use the validate_design prompt on my prototype link with personas for our
> two main segments. Focus on whether the new onboarding order makes sense.”

The agent creates an interview, adds task items ("Get from the welcome screen
to creating your first project"), attaches varied personas (mix devices;
prototypes often fail on mobile first), launches, and returns the synthesized
report with transcripts as evidence.

<Callout variant="note" title="Reading results on prototypes">
  Personas can only follow the interactions you wired. A "could not proceed"
  finding sometimes means a missing hotspot rather than a design flaw; the
  transcript makes it obvious which.
</Callout>

## Workflow: re-check automatically when the design changes [#workflow-re-check-automatically-when-the-design-changes]

This is the predictive half. Once Figma is connected, workflows can trigger on
Figma events:

1. Create checklists for the flows the design feeds (staging or production
   URLs).
2. Create a workflow with a Figma event trigger and steps that run those
   checklists, plus a notification step.
3. Activate it.

<FlowStrip
  nodes={[
    { label: 'File update', sub: 'or a library publish' },
    { label: 'Workflow fires', sub: 'Figma event trigger' },
    { label: 'Checklists run', sub: 'Against staging or prod' },
    { label: 'Team notified', sub: 'Drift, within minutes' },
  ]}
/>

Now a designer shipping a file update or publishing a library triggers a
re-check of the affected product flows, and discrepancies between the design
intent and the built product surface within minutes of the change, not at the
next QA pass. An agent can set this whole thing up from the `setup_guardrails`
prompt.

<TextureBreak />

## Workflow: brand and design-system guardianship [#workflow-brand-and-design-system-guardianship]

Give an actor your brand guidelines as knowledge (voice, spacing rules, color
usage, component dos and don'ts) and use it to run checklists like:

- "Every page uses the product font stack; no default serif fallbacks
  anywhere."
- "Primary CTAs use the brand primary color and sentence case."
- "Empty states show the illustration style, not stock icons."

Run these against staging on a schedule or on library publishes, and the
design system stops drifting silently. The `check_brand` and
`check_design_system` MCP prompts encode this whole workflow; the
**[validation guide](/guides/validation)** covers the pattern in depth,
including compliance and cross-surface continuity audits.

## Tips [#tips]

- Keep prototype links public-viewable for the duration of a test; personas
  do not have Figma accounts.
- Name interviews after the design iteration ("Onboarding v3, post-crit") so
  before/after comparisons are easy.
- Feedback captured in Stunt Double carries page URL, pin position, and
  screenshots, which maps naturally back to frames when you triage with the
  `triage_feedback` prompt.


# Guides (/guides)

# Guides [#guides]

How Stunt Double plugs into the tools where the work already happens.

<CardGrid
  items={[
    {
      title: 'Design in Claude, validate with personas',
      description: 'Design an artifact in Claude, publish it, and test it with AI personas without leaving the conversation.',
      href: '/guides/claude-design',
    },
    {
      title: 'Test Figma prototypes with AI personas',
      description: 'Walk prototypes with personas and re-check the built flows automatically when a file changes.',
      href: '/guides/figma',
    },
    {
      title: 'The fastest idea-to-evidence loop with Figma Make',
      description: 'Publish a Make site and have AI personas try to use it before an engineer touches it.',
      href: '/guides/figma-make',
    },
    {
      title: 'Verify every change with Claude Code, Cursor, and GitHub',
      description: 'Wire Stunt Double into your coding agent so every preview deployment is walked by a persona before merge.',
      href: '/guides/codebases',
    },
    {
      title: 'Collect comments straight from your site',
      description: 'Install the feedback widget so visitors pin comments in context, then triage them alongside what your actors find.',
      href: '/guides/feedback-widget',
    },
    {
      title: 'Continuous validation: brand, compliance, continuity, design system',
      description: 'Turn brand, tone, legal, continuity, and design-system standards into checks that run on every deploy.',
      href: '/guides/validation',
    },
  ]}
/>


# Continuous validation (/guides/validation)

Most teams think of Stunt Double as flow verification: can a user sign up, can
they check out. The same machinery enforces standards. An actor that can read
every page like a customer can also read every page like a brand editor, a
compliance reviewer, or a design-system maintainer, and unlike those humans,
it can do it on every deploy.

<Callout variant="who">
  Brand and content teams, legal and compliance owners, design-system
  maintainers, and PMs responsible for keeping many surfaces coherent.
</Callout>

## The guardian pattern [#the-guardian-pattern]

Every validation workflow follows the same three steps, and the MCP prompts
(`check_brand`, `check_design_system`, `check_compliance`,
`check_continuity`) encode them:

<FlowStrip
  nodes={[
    { label: 'Codify', sub: 'Rules as actor knowledge' },
    { label: 'Assert', sub: 'Observable checklist checks' },
    { label: 'Automate', sub: 'Re-run on every change' },
  ]}
/>

1. **Codify the standard as actor knowledge.** Store the rules on a dedicated
   reviewer actor (brand and tone guidelines, design tokens and component
   rules, the list of regulatory requirements). Stored knowledge is what makes
   audits repeatable: every future run enforces the same standard, not
   whatever was in the prompt that day.
2. **Assert the standard with observable checks.** Each rule becomes a
   checklist check a reader could verify on the rendered page. Phrase checks
   in terms of what is visible, not how it is implemented.
3. **Automate the re-run.** A standard that is checked once decays. Wire the
   checklists into a workflow: schedule (weekly brand sweep), deploy trigger
   (compliance on every release), or Figma events (design-system audit when
   the library is published).

The four standards below all follow that pattern. They differ only in what the
reviewer actor knows and what its checks look for:

<RoleGrid
  items={[
    { role: 'Brand & tone', gets: 'Voice rules read against every page, with the offending copy quoted.' },
    { role: 'Design system', gets: 'Tokens and component rules checked visually on the rendered product.' },
    { role: 'Compliance', gets: 'Behavioral checks (consent, disclosures) as evidence for counsel.' },
    { role: 'Continuity', gets: 'Prices, names, and promises reconciled across every surface.' },
  ]}
/>

## Brand and tone of voice [#brand-and-tone-of-voice]

Give the reviewer actor your voice rules and let it read the site the way a
customer does:

- "Headlines use sentence case."
- "Copy addresses the reader as you, never the user."
- "Error messages state what happened and offer a next step."
- "No unexplained jargon on pricing or onboarding pages."

Findings come back grouped by page with the offending copy quoted as evidence
and a suggested rewrite in the correct voice. Because the rules live on the
actor, a rebrand is one knowledge update away from being enforced everywhere.

## Design-system adherence [#design-system-adherence]

Actors see the rendered product the way a user does, so checks are phrased
visually rather than in terms of code:

- "Body text uses the product font stack; no serif fallbacks anywhere."
- "Primary CTAs use the brand primary color and sentence case."
- "Form fields show a visible focus state."
- "Empty states use the illustration style, not stock icons."

The natural trigger is a Figma event: when the design library is published or
a file changes, the audit re-runs against staging and drift between the system
and the shipped product surfaces within minutes. See the
**[Figma guide](/guides/figma)** for the connection setup.

## Legal and compliance [#legal-and-compliance]

Compliance checks exercise behavior, not just presence:

- "A cookie banner appears before any non-essential tracking, and the reject
  option works."
- "Privacy policy and terms are reachable from every page footer."
- "Prices include mandatory taxes, or state clearly that they do not."
- "Required disclaimers appear adjacent to the claims they qualify."
- "Marketing emails contain a working unsubscribe link" (actors have their own
  inboxes, so email flows are testable end to end).

<Callout variant="note" title="Two rules keep this honest">
  Your team supplies which regimes apply (the actor never guesses
  jurisdictions), and results are evidence for counsel to review, presented as
  observations against the stated requirements rather than legal conclusions.
  Every finding carries the exact page content as evidence.
</Callout>

## Continuity across surfaces [#continuity-across-surfaces]

Pricing, terminology, feature names, and promises drift apart across the
marketing site, the product, the docs, and emails, and customers are usually
the first to notice. The continuity workflow runs mirrored checks per surface
("record the price, billing period, and plan names shown") plus cross-surface
assertions ("the price at checkout matches the pricing page"), then reports a
discrepancy table ranked by customer impact.

For promise-versus-product continuity, an interview beats a checklist: give a
persona the task "the homepage says setup takes two minutes; sign up and see
if that holds" and read the transcript.

<TextureBreak />

## Example actors and workflows [#example-actors-and-workflows]

Each example below is a complete setup: the reviewer actor and its knowledge,
the checklist that asserts the standard, and the workflow that re-runs it. Build
them from the dashboard, or ask Claude to create them over MCP in one line, for
example "Set up a weekly brand sweep on acme.com against these voice rules...".
Swap the URLs, rules, and channels for your own.

### Brand and tone of voice [#brand-and-tone-of-voice-1]

```text
Actor      Brand Guardian
           "A meticulous brand editor. Read every page as a first-time
            visitor and flag anything that breaks our voice."
Knowledge  Voice and tone guidelines
           - Headlines use sentence case, no trailing full stops.
           - Address the reader as "you"; never "the user".
           - No exclamation marks in product UI or buttons.
           - Errors say what happened, then the next step.
Checklist  Brand sweep: marketing pages   (run by Brand Guardian, url: acme.com)
           Instructions: Visit the homepage, pricing, and onboarding. Read each
             as a first-time visitor and judge the copy against your knowledge.
           Checks:
           - Every headline uses sentence case.
           - Copy addresses the reader as "you", never "the user".
           - No exclamation marks in buttons, labels, or errors.
           - Every error message states what happened and a next step.
Workflow   Weekly brand sweep
           Trigger: schedule "0 9 * * 1"   (Mondays, 9am)
           Steps:   run_checklist  ->  notification (#brand)
```

### Design-system adherence [#design-system-adherence-1]

```text
Actor      Design-System Guardian
           "A design-system maintainer. Judge the rendered product against our
            tokens and component rules; report what a user would see."
Knowledge  Design system rules
           - Body text: Inter; headings: Inter Display. No serif fallbacks.
           - Primary buttons: brand primary (#4F46E5), sentence case, 8px radius.
           - Inputs show a 2px focus ring in brand primary.
           - Empty states use the line-illustration set, never stock icons.
Checklist  DS adherence: core app   (run by Design-System Guardian, url: staging)
           Instructions: Walk the dashboard, a form, and one empty state.
           Checks:
           - Body and heading text use the product font stack, no serif fallback.
           - Primary CTAs use the brand primary colour and sentence case.
           - Focus states are visible on every input.
           - Empty states use the illustration style, not stock icons.
Workflow   DS drift check on library publish
           Trigger: figma_event   (library publish / file update)
           Steps:   run_checklist  ->  notification (#design-system)
```

### Legal and compliance [#legal-and-compliance-1]

```text
Actor      Compliance Reviewer
           "A compliance analyst. Check the product against the requirements in
            your knowledge and report evidence for counsel; never draw legal
            conclusions and never assume a jurisdiction."
Knowledge  Applicable requirements (supplied by the team)
           - GDPR / ePrivacy: consent before non-essential cookies; reject works.
           - Privacy policy and terms reachable site-wide.
           - Displayed prices include GST or state that they exclude it.
           - Marketing emails carry a working unsubscribe link.
Checklist  Compliance sweep: acquisition   (run by Compliance Reviewer, url: acme.com)
           Instructions: Load the site fresh (no prior consent). Exercise the
             cookie banner, footer links, pricing, and a marketing email.
           Checks:
           - A cookie banner appears before any non-essential tracking fires.
           - The banner's reject option works and is honoured.
           - Privacy policy and terms are reachable from every page footer.
           - Displayed prices include GST or clearly state they exclude it.
           - The latest marketing email has a working unsubscribe link.
Workflow   Compliance gate on deploy
           Trigger: vercel_event   (production deploy succeeded)
           Steps:   run_checklist  ->  notification (#compliance + email counsel)
```

### Continuity across surfaces [#continuity-across-surfaces-1]

```text
Actor      Continuity Reviewer
           "Compare what we say across surfaces. Record exact values; flag any
            surface that disagrees with the others."
Checklist  Pricing continuity: marketing   (run by Continuity Reviewer, url: acme.com/pricing)
           Checks:
           - Record the price, billing period, and plan names shown.
Checklist  Pricing continuity: checkout    (run by Continuity Reviewer, url: app.acme.com)
           Instructions: Start checkout for each plan.
           Checks:
           - The price and plan name at checkout match the pricing page.
           - Feature names match the marketing site (no renamed or dropped ones).
Workflow   Weekly continuity check
           Trigger: schedule "0 8 * * 1"
           Steps:   run_checklist (marketing) -> run_checklist (checkout)
                    -> notification (#growth)
```

For promise-versus-product continuity, an interview is a better fit than a
checklist: attach a persona and give it the task "the homepage says setup takes
two minutes; sign up and see if that holds", then read the transcript.

## Where this fits in the other workflows [#where-this-fits-in-the-other-workflows]

- **Codebases**: run a compliance or brand audit against the preview
  deployment alongside change verification, so a PR that breaks a disclosure
  or ships off-brand copy is caught before merge. See the
  **[codebases guide](/guides/codebases)**.
- **Figma**: a design-system audit on Figma events closes the loop between the
  library and production. See the **[Figma guide](/guides/figma)**.
- **Claude**: all four prompts are available from the prompt picker once the
  connector is added; "audit our site against the tone of voice guide" is a
  one-line ask. See the **[Claude design guide](/guides/claude-design)**.

## Tips [#tips]

- One standard per checklist. A brand sweep and a compliance sweep produce
  different reviewers' worth of findings; keep the failure signals separate.
- Keep rules observable. "Feels premium" is not checkable; "no more than one
  font family per page" is.
- Start with the standard you already argue about in review. The first audit
  usually pays for itself by ending a recurring debate with evidence.
- Session recordings in the dashboard show exactly what the actor saw when it
  flagged a deviation, which settles disputes quickly.


# Improve your score (/index-ranking/improve-your-score)

# Check and improve a site's score [#check-and-improve-a-sites-score]

A score tells you where you stand. The sessions behind it tell you why. This
guide goes from a report to a ranked list of fixes, then re-scores the site
once they ship.

You can do all of it at [index.stuntdouble.io](https://index.stuntdouble.io),
or ask your AI assistant to do it through the [MCP server](/mcp), which ships
the `check-agent-readiness` [skill](/mcp/skills).

<Steps>
<Step>

### Read the report [#read-the-report]

Open the site's report, or call `get_index_report` with its domain. If your
Stunt Double project is linked to an Index domain, pass its `project_id`
instead. Start with the lowest categories and providers.

</Step>
<Step>

### Read the sessions behind the weak spots [#read-the-sessions-behind-the-weak-spots]

Filter the sessions by category or provider (`list_index_sessions` with
`category` or `provider`) and read what the agents actually ran into: a cookie
wall before the cart, help content that only renders with JavaScript, a
product page with no structured data. Fix what the sessions show, not what you
guess.

</Step>
<Step>

### Compare with your sector [#compare-with-your-sector]

Browse the leaderboard for your sector (`search_index_domains` with `sector`)
or look up named competitors, and read the reports worth contrasting.

</Step>
<Step>

### Rank the fixes [#rank-the-fixes]

1. Failing probe checks with a high weight.
2. Frictions that recur across providers.
3. Issues that only one provider hits.

Tie every fix to a failing check or a session friction. For legacy pages that
can't be rebuilt soon, [WAO](/stuntkit/wao) repairs the accessibility tree
agents read without changing the page.

</Step>
<Step>

### Re-score once the fixes ship [#re-score-once-the-fixes-ship]

Re-run the domain (`request_index_rerun`) for fresh probes and about 24 agent
sessions. Only the domain's owner can re-run it. Poll
`list_index_sessions` with the returned `run_id` until no session is running,
then compare the new report with the old one, category by category.

</Step>
</Steps>

<Callout variant="tip" title="Keep the fix from regressing">
  Before the re-run, confirm the fix on a preview deployment with a
  [checklist](/concepts#checklists), then run that checklist on every deploy
  with an [automation](/concepts#workflows).
</Callout>

## Next steps [#next-steps]

- **[Connect the MCP server](/mcp/connect)**
- **[Make any site readable by AI agents](/stuntkit/wao)**


# Stunt Double Index (/index-ranking)

# Stunt Double Index [#stunt-double-index]

More of your visitors arrive through an AI agent: someone asks ChatGPT, Claude,
Gemini or Perplexity to compare products, find an answer or check out on their
behalf. The [Stunt Double Index](https://index.stuntdouble.io) measures how that
goes on your site, and ranks it against everyone else.

Each tracked domain gets a score out of 100, a band and a rank, overall and
within its sector. Index data is public, so you can read any tracked site's
report, including your competitors'.

## How a site is scored [#how-a-site-is-scored]

A score comes from two kinds of evidence:

- **Probes.** HTTP checks that look at what agents depend on, such as whether
  pages are server-rendered, structured data is present and agents are allowed
  in. Each failing check is listed with its weight.
- **Agent sessions.** Live sessions, one per AI provider and benchmark task,
  where an agent tries a real job on the site. Each session records what the
  agent tried, its rubric evidence, a summary and the frictions it hit.

A full run is the probes plus about 24 agent sessions. Stunt Double runs the
sessions on the same [browser toolset](/stuntkit/browser-toolset) its actors
use.

## What it measures [#what-it-measures]

| Category              | Can an agent...                                    |
| --------------------- | -------------------------------------------------- |
| Brand awareness       | Recognize the brand and what it offers             |
| Discovery             | Find the site and the right page for a task        |
| Information retrieval | Read the facts it needs from the site              |
| Market ranking        | Place the site among its competitors               |
| Accuracy              | Report what the site says without getting it wrong |
| Checkout              | Get through to buying, on a person's behalf        |
| Delegated access      | Act for a signed-in person                         |
| Support               | Find help content and get an answer                |

The report also breaks the score down by provider, so you can see when one
agent struggles where the others don't.

<Callout variant="note" title="No score?">
  A site can be tracked without a score when the checks were blocked or
  unanswered, or the site opted out. The report says which.
</Callout>

## Add or claim a site [#add-or-claim-a-site]

Anyone signed in can add a site that isn't tracked yet at
[index.stuntdouble.io](https://index.stuntdouble.io). A new site gets light
probes by default, with an option to add live agent sessions.

Claim your domain to re-score it after you ship changes.

## Next steps [#next-steps]

- **[Check and improve a site's score](/index-ranking/improve-your-score)**
- **[Browse the leaderboard](https://index.stuntdouble.io)**
- **[Make any site readable by AI agents](/stuntkit/wao)**


# Overview (/)

# Stunt Double documentation [#stunt-double-documentation]

Stunt Double gives you authentic user insights from autonomous AI agents,
backed by evidence. **Actors**, AI users you describe, drive a real browser
through your product and come back with a recording, screenshots and the steps
behind every result.

<Screenshot
  name="checklist-run"
  alt="A checklist run in Stunt Double: each check marked passed or failed, with the screenshot the actor took at that step and its explanation."
  caption="A checklist run. Every check carries the screenshot and the reasoning behind its result."
/>

## What do you want to do? [#what-do-you-want-to-do]

<CardGrid
  columns={2}
  items={[
    {
      icon: "rocket",
      title: "Run my first check in five minutes",
      description: "Create a workspace, an actor and a checklist, then read the run.",
      href: "/getting-started",
    },
    {
      icon: "checklist",
      title: "Catch regressions before users do",
      description: "Write checks around outcomes and read the evidence behind each result.",
      href: "/tutorials/effective-checklists",
    },
    {
      icon: "interview",
      title: "Find out how people get on with a design",
      description: "Run an interview with persona participants and act on the report.",
      href: "/tutorials/running-an-interview",
    },
    {
      icon: "automation",
      title: "Re-check every deploy automatically",
      description: "Chain checklists into an automation that runs on a schedule, a PR or a deploy.",
      href: "/tutorials/building-workflows",
    },
    {
      icon: "feedback",
      title: "Collect comments on a prototype",
      description: "Add the feedback widget so reviewers pin comments where they apply.",
      href: "/tutorials/feedback-widget",
    },
    {
      icon: "terminal",
      title: "Drive it from Claude, Cursor or ChatGPT",
      description: "Connect the MCP server and run checklists and interviews from a conversation.",
      href: "/mcp",
    },
  ]}
/>

## How the pieces fit together [#how-the-pieces-fit-together]

A workspace holds your team, and a project holds one product. Inside it,
actors do three kinds of work, and every run comes back as evidence.

<ConceptMap />

Read **[Core concepts](/concepts)** for each building block in detail.

## Browse by section [#browse-by-section]

<CardGrid
  columns={3}
  items={[
    {
      title: "Tutorials",
      description: "Step by step, from your first actor to a review in Slack.",
      href: "/tutorials",
    },
    {
      title: "Guides by workflow",
      description: "Validate designs from Figma, Claude and your codebase before they reach users.",
      href: "/guides",
    },
    {
      title: "Integrations",
      description: "GitHub, Vercel, Slack, Linear, Figma, webhooks and self-hosted workers.",
      href: "/integrations",
    },
    {
      title: "MCP server",
      description: "Endpoint, clients, scopes, and every tool, prompt and resource.",
      href: "/mcp",
    },
    {
      title: "API",
      description: "OAuth scopes, versioning and what you can automate.",
      href: "/api",
    },
    {
      title: "StuntKit",
      description: "The open source browser tools and icons behind Stunt Double.",
      href: "/stuntkit",
    },
  ]}
/>

<Callout variant="tip" title="You don't need to start in the dashboard">
  Connect **[Slack](/integrations/slack)** and a link shared with a request for
  feedback is reviewed in the thread it was posted in. Or connect an assistant
  through **[MCP](/mcp)** and ask it to run a checklist.
</Callout>

## Getting help [#getting-help]

Press <kbd>⌘</kbd> <kbd>K</kbd> (<kbd>Ctrl</kbd> <kbd>K</kbd> on Windows and Linux) to search every page. If something here is
unclear or missing, reach out from the
[support page](https://www.stuntdouble.io/support).


# Desktop app (/integrations/desktop-app)

# Desktop app [#desktop-app]

The desktop app is Stunt Double in its own Mac window. It can also turn your
Mac into a worker, so actors browse with this computer's Chrome, including its
VPN access and machine-credential sign-ins.

## Before you start [#before-you-start]

- **macOS.** macOS 10.15 or later, on Apple silicon or Intel.
- **Chrome.** Google Chrome installed, if you want to use the Mac as a
  worker.
- **Plan.** The app itself works on any plan. Using it as a worker needs the
  Workers entitlement, which comes with the Enterprise plan.

## Install it [#install-it]

1. Go to [stuntdouble.io](https://stuntdouble.io) and click **Download for
   Mac**.
2. Open the download and drag Stunt Double into **Applications**.
3. Open the app and sign in with your Stunt Double account.

The app updates itself when a new version ships.

## Use this Mac as a worker [#use-this-mac-as-a-worker]

1. In the app, open **Settings**, then **Workers**.
2. Find **Use this device as a worker** and click **Enable this device**.
3. The card changes to **Connected using this machine's browser.** The
   worker shows in the list as **Your device**.
4. Assign actors to it. Open an actor, find **Self-hosted worker** and choose
   this device under **Worker**.

The browser runs inside the app, not as a window on your desktop. Click **Open
device console** to watch what an actor is doing, sign in to an internal site
or take control of the browser. Sign-ins you make there persist for later
runs.

Enabling is remembered. When the app restarts, including after an update, the
worker reconnects by itself.

To stop, click **Disable** on the same card.

## Troubleshooting [#troubleshooting]

**There is no "Use this device as a worker" card.** The card only shows inside
the desktop app, in a workspace with the Workers entitlement.

**The card asks you to install Google Chrome.** The worker drives Chrome.
Install it, then click **Enable this device** again.

**The card offers "Pair with" your workspace.** This Mac is paired with a
different workspace, or its worker was deleted. Click **Pair with** to pair it
here.

**The card says to update the app.** That version cannot manage a worker yet.
Update the app and reopen **Workers**.

**A run fails because the browser profile is in use.** Only one browser can
use the profile at a time. Close any Stunt Double worker running on the same
Mac from the command line, or a sign-in window left open, and try again.


# Feedback widget (/integrations/feedback-widget)

# Feedback widget [#feedback-widget]

Add one script tag to your site and anyone looking at a page can drop a pin on
the spot that confused them and say why. Each comment arrives in your project
with a screenshot of the page as they saw it, the URL and the viewport.

This page is the reference for installing and locking it down. For a
walkthrough on a prototype, read the [tutorial](/tutorials/feedback-widget); for
running reviews with it, read the [guide](/guides/feedback-widget).

## Before you start [#before-you-start]

- **Workspace limits.** Owners and admins set a workspace-wide ceiling for
  the widget under **Settings**, then **Security**.
- **A site you control.** You need to add a script tag to its HTML.

## Set it up [#set-it-up]

1. Open the project and go to **Comments**.
2. Follow the install step. Copy the snippet and paste it just before
   `</body>`, or choose **With Claude** to hand the job to a coding agent.
3. Load your site. The install step shows **Widget detected** once the widget
   checks in.
4. If the widget also runs somewhere other than your project URL, such as a
   staging or custom domain, add it under **Allowed domains** in the project's
   feedback settings. Requests from anywhere else are refused.

## The snippet [#the-snippet]

```html
<script src="https://app.stuntdouble.io/api/feedback/widget"
  data-sd-key="YOUR_PROJECT_KEY"
  data-sd-url="https://app.stuntdouble.io"
  defer></script>
```

- **`data-sd-key`** (required) is your project's feedback API key. Without it
  the widget logs a warning and does nothing.
- **`data-sd-url`** (required in practice) points at Stunt Double. It must be
  `https`; the widget refuses to run otherwise.
- **`data-sd-version`** (optional) pins comments to a version string you
  control, such as a build ID, release tag or commit SHA. Without it, comments
  are grouped by page content.

There is no package to install and no build step. Screenshot capture loads
`html2canvas` on demand from a pinned, integrity-checked URL, so one tag is
enough.

## Control who can use it [#control-who-can-use-it]

The API key sits in your page source, so treat it as an identifier, not a
secret. The real access control is the domain allowlist, which every widget
endpoint checks before it answers.

| Setting                     | What it does                                                                                                                                                                |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Allowed domains**         | Hosts the widget may run on, beyond your project URL (always accepted). Up to 20. An entry matches an exact host; `*.example.com` covers every subdomain, such as previews. |
| **Allow local development** | Lets `localhost` and private network addresses use the widget. On by default.                                                                                               |
| **Require sign in**         | Only signed-in members of this workspace can read or leave comments. Use it for staging and internal reviews.                                                              |
| **Toolbar visible**         | Shows or hides the feedback button on your site                                                                                                                             |
| **Show comments on page**   | Shows existing comment pins on the page                                                                                                                                     |
| **Keyboard shortcut**       | Lets visitors press `c` to start placing a comment                                                                                                                          |
| **API Key**                 | **Rotate** issues a new key and stops every install of the old one until its snippet is updated                                                                             |

The allowlist **fails closed**: a project with no URL and an empty allowlist
serves nothing at all, and the settings page says so, so you can tell "locked
down" apart from "broken".

Workspace admins hold a ceiling above every project under **Settings**, then
**Security**: a workspace-wide **allowed origins** list that projects can only
narrow, and a **Feedback widget** switch that turns the widget off for every
project, including sites where the key is already installed.

## Remove it [#remove-it]

Delete the script tag from your site. To stop an old copy of the snippet
working anywhere, rotate the API key.

## Troubleshooting [#troubleshooting]

**"Widget detected" never appears.** Check the snippet is on the page you
loaded, and that the domain is your project URL or under **Allowed
domains**. For `localhost`, turn on **Allow local development**.

**The widget stopped loading after a key rotation.** Every copy of the snippet
needs the new key. Copy the snippet again from **Comments**.

**The widget will not load even though the domain is allowed.** An owner or
admin may have limited the widget workspace-wide under **Settings**, then
**Security**.


# Figma plugin (/integrations/figma-plugin)

# Figma plugin [#figma-plugin]

The Stunt Double plugin brings actors into the Figma canvas. Select a frame
and actors review it, run a checklist against it, or talk it through with you,
and the findings land as numbered pins next to the design.

<Callout variant="note" title="Not in the Figma Community yet">
  The plugin is not listed in the Figma Community yet, so there is no public
  install link. This page describes how it works so you know what to expect when
  it arrives.
</Callout>

## What it does [#what-it-does]

The plugin has four tabs:

- **Review.** Select one or more frames and ask actors for design feedback.
  The plugin exports the frames and the reviewers judge the image. Click
  **Place on canvas** to draw numbered pins and a findings panel beside the
  frame, **Resolve** findings as you fix them, and click **Re-check frame** to
  ask the reviewers what is fixed. **Open in dashboard** takes you to the full
  review.
- **Checklists.** Run one of the project's checklists against the selected
  frames and read the results.
- **Feedback.** Create and triage feedback on the design, filtered to **This
  file only** or across the project.
- **Chat.** Talk to one of your actors about the selected frames, with
  **Attach frame snapshot** to send the frame along.

Pick a project in the plugin's header first: reviews, checklists and feedback
all belong to a project.

## Before you start [#before-you-start]

- **A Stunt Double account** with access to the workspace and project you want
  to work in.
- **Actors in the workspace.** Reviews and chat need at least one actor.
  Create one in the dashboard if the plugin says **No actors in this workspace
  yet.**
- **A project.** The plugin works inside a project, so create one in the
  dashboard first.

## Connect the plugin [#connect-the-plugin]

The plugin signs in through your browser, so it never asks for a password.

1. Open the plugin in Figma and click **Connect to Stunt Double**. A browser
   tab opens and the plugin reads **Approve the request in your browser.
   Waiting...**
2. In the browser, sign in to Stunt Double if you are not already. The
   **Connect the Figma plugin** page lists what the plugin will be able to do.
3. Click **Approve**. The plugin switches to **Connected to Stunt Double**.

To cancel, click **Deny** in the browser or **Cancel** in the plugin.

## What the plugin can access [#what-the-plugin-can-access]

When you approve, the plugin can:

- See your workspaces, projects and actors
- Run checklists and view run results
- Create and triage feedback on your designs
- Chat with your actors about selected frames

Approving issues a plugin token that lasts 90 days and is stored in Figma on
your machine. The plugin can only reach `app.stuntdouble.io` and
`stuntdouble.io`.

The plugin does not need the [Figma workspace connection](/integrations/figma),
and the connection does not need the plugin. Use either or both.

## Disconnect [#disconnect]

Click **Sign out** in the plugin. That removes the token from Figma on your
machine. The token also expires on its own after 90 days, and the plugin asks
you to connect again.

## Troubleshooting [#troubleshooting]

**"The connect request expired. Try again."** The browser approval took too
long. Click **Connect to Stunt Double** again and approve within a few
minutes.

**"The request was denied."** Someone clicked **Deny** in the browser. Start
the connection again and click **Approve**.

**The browser says the connect request is no longer valid.** The request was
already used or has expired. Return to Figma and start the connection again.

**"Select one or more frames first."** Reviews and checklists run on frames,
not groups or pages. Select at least one frame on the canvas.

**"Pick a project in the header before running a review."** Choose a project
from the plugin's header. If the list is empty, create a project in the
dashboard.

**"Could not reach Stunt Double."** Check your network connection. Figma only
lets the plugin talk to stuntdouble.io, so a network that blocks it will stop
the plugin.


# Figma (/integrations/figma)

# Figma [#figma]

Connect Figma and actors can read the frames, components and tokens in your
linked files, work through your prototypes the way a user would, and start
automations when a file is saved, commented on, versioned or published as a
library.

This page covers the workspace connection, set up in the dashboard. To review
frames and run checklists from inside Figma, see the
[Figma plugin](/integrations/figma-plugin). For the testing workflow once you
are connected, read the [Figma guide](/guides/figma).

## What it does [#what-it-does]

- **Design context.** Actors read frames, components and tokens from the
  design file linked to their project.
- **Prototype walkthroughs.** Actors work through your prototypes the way
  real users would.
- **Per-project files.** Each project links its own design file, so runs,
  feedback and interviews stay attached to the design they came from.
- **Workflow triggers.** Automations can start on four Figma events: **File
  Update**, **File Comment**, **File Version Update** and **Library Publish**.

## Before you start [#before-you-start]

- **Role.** Any member can connect their own Figma account. Turning Figma on
  for the workspace takes an owner or admin.
- **Plan.** Figma does not need a particular plan.
- **Figma access.** The Figma account you connect needs access to the files
  you want actors to read.

## Set it up [#set-it-up]

1. In [app.stuntdouble.io](https://app.stuntdouble.io), open **Integrations**
   from the sidebar and click **Figma**.
2. On **Your account**, click **Connect Figma** and approve Stunt Double in
   Figma. The step changes to **Connected as** with your Figma name.
3. On the step named after your workspace, an owner or admin turns the switch
   on. It reads **On. Actors, checklists and automations here can use it.**
4. On **Projects**, click **Choose for a project** and pick a project. Its
   **Settings** open on the **Integrations** tab: choose the project's design
   file there. Repeat for each project that works from a Figma file.

To re-check work when a file changes, create an automation, choose a Figma
trigger, then pick from **Figma Events**.

## What Stunt Double can access [#what-stunt-double-can-access]

Stunt Double asks Figma for these scopes:

| Scope                 | Used for                                                     |
| --------------------- | ------------------------------------------------------------ |
| `current_user:read`   | Showing which Figma account is connected                     |
| `file_content:read`   | Reading frames, components and tokens from linked files      |
| `file_metadata:read`  | Reading file names and versions                              |
| `file_comments:read`  | Reading comments, including for the **File Comment** trigger |
| `file_comments:write` | Writing comments on linked files                             |

## Disconnect [#disconnect]

Open **Integrations**, click **Figma**, then click **Disconnect** on **Your
account**. The **Disconnect Figma?** dialog lists every workspace that loses
Figma. Confirm, and those workspaces stop using it until someone connects
again.

To stop one workspace from using Figma and leave the rest, an owner or admin
turns off the workspace switch instead. You can also remove Stunt Double from
the connected apps in your Figma account settings, which revokes access from
Figma's side.

## Troubleshooting [#troubleshooting]

**The workspace step reads "Off. Ask a workspace owner or admin to turn it
on."** Only owners and admins can flip the switch. Ask one of them to open
the Figma card, or send them this page.

**The Projects step is greyed out.** It waits for the first two steps. Connect
your account and turn the workspace switch on first.

**Actors cannot see a file.** Check that the Figma account you connected can
open that file, and that the file is linked on the project's **Integrations**
tab.

**Automations do not start on file changes.** Confirm the automation is on,
its trigger lists the right **Figma Events**, and Figma is still on for the
workspace.


# GitHub (/integrations/github)

# GitHub [#github]

Connect GitHub and Stunt Double works where code review already happens.
Actors review pull requests with their own knowledge and instructions,
automations start on pull requests, pushes, issues and releases, and results
land on the pull request as comments.

For the full loop of preview deploys, checklists and PR reports, read the
[codebases guide](/guides/codebases).

## What it does [#what-it-does]

- **Actor PR reviews.** Assign actors to review pull requests and post
  suggestions from their knowledge and instructions.
- **Workflow triggers.** Pull request, review, push, issue, check suite and
  release events start automations automatically.
- **PR comments.** Workflow results and checklist summaries post directly on
  the pull request.

## Before you start [#before-you-start]

- **Role.** Any member can connect their GitHub account. Turning GitHub on for
  the workspace and choosing watched repositories takes an owner or admin.
- **Plan.** GitHub does not need a particular plan.
- **GitHub permissions.** Installing the Stunt Double GitHub App on an
  organization's repositories needs permission to install apps there, usually
  an organization owner. If that is someone else, send them the install link
  (see [below](#hand-setup-to-someone-else)).

## Set it up [#set-it-up]

GitHub takes one extra step compared with other integrations: signing in tells
Stunt Double who you are, and installing the GitHub App decides which
repositories it can see.

1. In [app.stuntdouble.io](https://app.stuntdouble.io), open **Integrations**
   from the sidebar and click **GitHub**.
2. On **Your account**, click **Connect GitHub** and authorize Stunt Double in
   GitHub.
3. Still on **Your account**, click **Install GitHub App**. In GitHub, choose
   the account or organization, then pick all repositories or only the ones
   Stunt Double should see. The step changes to **Connected as** with your
   GitHub name.
4. On the step named after your workspace, an owner or admin turns the switch
   on.
5. Under **Settings**, find **Watched repositories** and click **Add
   repository**. Pick each repository that automations and actor PR reviews
   should listen to, then click **Done**.
6. On **Projects**, click **Choose for a project** and pick a project. Its
   **Settings** open on the **Integrations** tab: choose the project's
   repository there.

To start an automation from GitHub, create an automation, choose a GitHub
trigger, and pick the events: **PR Opened**, **PR Merged**, **PR Updated**,
**Review Requested**, **Push**, **Issue Opened**, **Check Suite Completed**,
**Release Published** and more.

### Watched repositories versus project repositories [#watched-repositories-versus-project-repositories]

**Watched repositories** are a workspace setting: GitHub events from these
repositories reach your automations and actor PR reviews. A **project's
repository** says which codebase that project works with. A repository can
be both.

### Hand setup to someone else [#hand-setup-to-someone-else]

At the foot of the GitHub page, the **Install link** card has a link that
walks someone through installing the GitHub App, turning it on for this
workspace and choosing repositories. It picks up wherever they left off. Send
it to the organization owner who can install apps.

<Screenshot
  name="integrations"
  alt="The GitHub integration page once set up: connected as an account, switched on for the workspace, ready to choose a repository for each project."
  caption="GitHub once it is set up. Choose for a project opens the project's settings, where you pick its repository."
/>

## What Stunt Double can access [#what-stunt-double-can-access]

GitHub access comes in two parts.

**Your account (OAuth).** Stunt Double requests:

| Scope        | Used for                                                   |
| ------------ | ---------------------------------------------------------- |
| `read:user`  | Showing which GitHub account is connected                  |
| `user:email` | Reading the email address on your GitHub account  |
| `repo`       | Reading repositories and posting comments on pull requests |

**The GitHub App.** The app can only see the repositories you chose when you
installed it. Change that list at any time with **Manage app access** next to
**Watched repositories**, or from the app's settings in GitHub.

<Callout variant="note" title="About the repo scope">
  GitHub's `repo` scope covers private repositories as well as public ones.
  Choosing specific repositories when you install the GitHub App keeps what
  Stunt Double watches to the ones you pick.
</Callout>

## Disconnect [#disconnect]

- **Stop watching one repository.** Under **Watched repositories**, click the
  delete icon beside it (owners and admins).
- **Stop one workspace using GitHub.** An owner or admin turns off the
  workspace switch.
- **Remove GitHub from your account.** Click **Disconnect** on **Your
  account**. The **Disconnect GitHub?** dialog lists every workspace that loses
  GitHub. Confirm to remove it.
- **Remove the app from GitHub.** Uninstall the Stunt Double GitHub App from
  your account or organization settings in GitHub. Stunt Double stops seeing
  every repository at once.

## Troubleshooting [#troubleshooting]

**"Install the GitHub App on the repositories Stunt Double should see."** You
signed in but have not installed the app yet. Click **Install GitHub App**.

**"No repositories found. Install the GitHub App on at least one
repository."** The app is installed with no repositories. Click **Manage app
access** and add some.

**"The GitHub App installation is no longer linked to this account."** The app
was uninstalled or moved. Click **Reconnect GitHub App**.

**"Couldn't load repositories from GitHub."** The installation may have been
revoked in GitHub. Click **Reconnect GitHub App**, then **Retry**.

**Automations do not start on a pull request.** Check that the repository is
under **Watched repositories**, the automation is on, and its trigger includes
that event.

**A repository is missing from the picker.** Stunt Double only lists
repositories the GitHub App can see. Add it with **Manage app access**.


# Integrations (/integrations)

# Integrations [#integrations]

A design gets updated in Figma, a pull request opens on GitHub, a preview
lands on Vercel. Each of those is a moment when someone should try the flow
before it ships. Integrations let Stunt Double act at that moment instead of
waiting for someone to remember to start a run.

Actors drive real browsers through real products and report back with
evidence. Anything with a reachable URL can be tested: production, staging, a
Vercel preview, a published Figma Make prototype, a Claude design artifact or
a v0 link. Integrations decide when that happens and where the results land.

## How Stunt Double plugs in [#how-stunt-double-plugs-in]

Three mechanisms connect Stunt Double to your other tools:

1. **The MCP server** at `https://app.stuntdouble.io/api/mcp`. Any MCP client
   (Claude, Claude Code, Cursor, Windsurf, ChatGPT and others) gets the full
   toolset: create and run checklists, launch persona interviews, triage
   feedback and comment on pull requests. Client setup lives in the
   [MCP section](/mcp).
2. **Connections and event triggers.** Connect GitHub, Figma, Linear, Slack or
   Vercel and automations can start on pull requests, deploys, design updates,
   Linear issues and Slack messages, so checks re-run without anyone asking.
3. **The dashboard** at [app.stuntdouble.io](https://app.stuntdouble.io), for
   anything visual: watching session recordings, reading interview reports and
   managing actors and integrations.

## How every connection works [#how-every-connection-works]

Every integration lives on the **Integrations** page of your workspace. Open
the sidebar, choose **Integrations**, then click a card. Each one is set up in
the same three steps, in the same order, so learning one teaches you all of
them.

<FlowStrip
  nodes={[
    {
      label: "Your account",
      sub: "Connect the account Stunt Double should use",
    },
    { label: "This workspace", sub: "An owner or admin turns it on" },
    {
      label: "Projects",
      sub: "Each project picks its file, repository, team or channel",
    },
  ]}
/>

1. **Your account.** Click **Connect** followed by the tool's name (for
   example **Connect GitHub**) and approve Stunt Double on the provider's
   site. The step then reads **Connected as** with your account name, and
   lists the workspaces already using it.
2. **Your workspace.** The second step carries your workspace's name and a
   switch. An owner or admin turns it on, and the step reads **On. Actors,
   checklists and automations here can use it.** Members see **Off. Ask a
   workspace owner or admin to turn it on.** until an admin does.
3. **Projects.** Click **Choose for a project** and pick a project. That opens
   the project's **Settings**, on the **Integrations** tab, where you choose
   the design file, repository, Linear team, Slack channel or Vercel project
   it works with. If the workspace has no projects yet, the button reads
   **Create a project**.

<Screenshot
  name="integrations"
  alt="The GitHub integration page: the account step reads Connected as jordan-lee, the workspace switch is on, and the projects step offers Choose for a project, beside a list of what GitHub unlocks."
  caption="Every integration is the same three steps. A finished step shows a tick; the next one to do is the first without."
/>

A step that depends on the one before it stays greyed out until that step is
done, so the next thing to do is always the first step without a tick.

Once an integration is on, its page grows a **Settings** section for anything
that applies to the whole workspace, such as GitHub's watched repositories or
the Linear agent's identity.

<Callout variant="note" title="One account, several workspaces">
  Your connection belongs to your account, not to one workspace. Turn it on in
  as many workspaces as you like, and each one shows up on step one as a place
  the connection is used.
</Callout>

### Disconnecting [#disconnecting]

To remove a connection, open the integration and click **Disconnect** on the
**Your account** step. A dialog titled **Disconnect** followed by the tool's
name lists every workspace that loses the integration. Confirm, and those
workspaces stop using it until someone connects again.

To stop one workspace from using a connection while others keep it, an owner
or admin turns off the switch on step two instead. Slack works a little
differently: see [Slack](/integrations/slack) for **Remove** versus
**Disconnect**.

### Handing setup to someone else [#handing-setup-to-someone-else]

GitHub, Linear and Slack each show an **Install link** card at the foot of
their page. Copy the link and send it to whoever should finish setup: a
teammate who owns the GitHub organization, or an admin who has to flip the
workspace switch. The link walks them through every step for that workspace
and picks up wherever they left off.

## Every integration [#every-integration]

| Integration                                              | What it does                                                                   | Who sets it up                                                             | What Stunt Double requests                                                                                                                                                                                                                |
| -------------------------------------------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [GitHub](/integrations/github)                           | Actor pull request reviews, PR comments, automations on GitHub events          | Anyone connects; an owner or admin turns it on                             | OAuth: `read:user`, `user:email`, `repo`. GitHub App: access to the repositories you choose                                                                                                                                               |
| [Vercel](/integrations/vercel)                           | Automations on deployment events                                               | Anyone connects, or install from the Vercel Marketplace                    | The permissions the Vercel integration asks for at install                                                                                                                                                                                |
| [Figma](/integrations/figma)                             | Design context for actors, automations on file events                          | Anyone connects; an owner or admin turns it on                             | `current_user:read`, `file_content:read`, `file_metadata:read`, `file_comments:read`, `file_comments:write`                                                                                                                               |
| [Figma plugin](/integrations/figma-plugin)               | Design reviews, checklists, feedback and actor chat inside Figma               | Each designer, from Figma                                                  | A plugin token valid for 90 days. The plugin only talks to stuntdouble.io                                                                                                                                                                 |
| [Linear](/integrations/linear)                           | The Stunt Double agent on Linear issues, automations on issue events           | Anyone connects; an owner or admin turns it on and picks the agent's actor | `read`, `write`, `issues:create`, `comments:create`, `app:assignable`, `app:mentionable`                                                                                                                                                  |
| [Slack](/integrations/slack)                             | Design reviews in threads, notifications, a workspace agent in direct messages | An owner or admin adds it to the workspace                                 | `channels:read`, `groups:read`, `channels:history`, `groups:history`, `chat:write`, `chat:write.customize`, `mcp:connect`, `commands`, `users:read`, `app_mentions:read`, `incoming-webhook`, `assistant:write`, `im:history`, `im:write` |
| [Webhooks](/integrations/webhooks)                       | Start an automation from any system that can send an HTTP request              | Anyone who can edit automations                                            | A secret per automation, sent as `X-Webhook-Secret`                                                                                                                                                                                       |
| [Feedback widget](/integrations/feedback-widget)         | Collect comments on your live site into a project                              | Project members; owners and admins set workspace limits                    | A project key that identifies the project, limited to your allowed domains                                                                                                                                                                |
| [Self-hosted workers](/integrations/self-hosted-workers) | Run actors on your own network, against sites the public internet cannot reach | Owners and admins, on Enterprise                                           | A worker API key, shown once                                                                                                                                                                                                              |
| [Desktop app](/integrations/desktop-app)                 | Use your Mac as a worker                                                       | Owners and admins, where workers are available                             | Your Stunt Double sign-in                                                                                                                                                                                                                 |

MCP clients (Claude, Claude Code, Cursor, Windsurf, ChatGPT) request
`mcp:read`, `mcp:write` and `mcp:run`. Their setup is in the [MCP section](/mcp).

## Guides by workflow [#guides-by-workflow]

The pages above cover setup. The **[guides](/guides)** cover what to do once you
are connected: designing in Claude, testing Figma prototypes and Make sites,
verifying code changes, and standing validation.

## Where each role feels it [#where-each-role-feels-it]

- **Designers** find usability problems while the design is still cheap to
  change: persona interviews against prototypes, with transcripts and
  evidence.
- **Product managers** get research at decision speed: a structured study
  with a brief, a discussion guide, varied personas and a synthesized report,
  plus feedback triage that clusters and reproduces what users report.
- **Engineers** get a user-level regression net: checklists that walk real
  flows on preview deployments, results as PR comments, and re-runs on every
  deploy.
- **Leadership** gets scheduled checks over the critical paths (signup,
  checkout, the core action), with failures reaching Slack or Linear before
  they reach support.
- **Brand, legal and design-system owners** get enforcement instead of
  periodic review: standards written down once and checked on every deploy or
  design update.


# Linear (/integrations/linear)

# Linear [#linear]

Connect Linear and Stunt Double becomes a teammate on your issues. Assign an
issue to Stunt Double or mention it in a comment, and the agent works on it
and replies in the issue. Automations can start on the same events and post
their results back to the issue.

## What it does [#what-it-does]

- **Issue triage.** Actors triage, comment on and update issues.
- **The Stunt Double agent.** Delegate an issue to Stunt Double, or mention it
  in a comment, and the agent answers on the issue. Pick an actor for it to
  act as, and it uses that actor's model and MCP servers.
- **Workflow triggers.** Automations can start on **Issue Assigned** (an issue
  is assigned to Stunt Double) and **Issue Mentioned** (Stunt Double is
  mentioned in an issue comment).
- **Two-way updates.** Workflow results post back to the Linear issue.

## Before you start [#before-you-start]

- **Role.** Any member can connect Linear. Turning it on for the workspace and
  choosing the agent's actor takes an owner or admin.
- **Plan.** Linear does not need a particular plan.

## Set it up [#set-it-up]

1. In [app.stuntdouble.io](https://app.stuntdouble.io), open **Integrations**
   from the sidebar and click **Linear**.
2. On **Your account**, click **Connect Linear** and approve Stunt Double in
   Linear. The step changes to **Connected as** with your Linear name.
3. On the step named after your workspace, an owner or admin turns the switch
   on.
4. On **Projects**, click **Choose for a project** and pick a project. Its
   **Settings** open on the **Integrations** tab: choose the project's Linear
   team there.
5. Under **Settings**, an owner or admin sets **Agent identity**. Pick an
   actor for the agent to act as, or leave it on **No actor** for the default
   model and built-in tools.

Then try it: open an issue in Linear and assign it to Stunt Double, or mention
Stunt Double in a comment.

To start an automation from Linear, create an automation, choose a Linear
trigger, and pick from **Linear Events**. You can limit it to one team.

### Hand setup to someone else [#hand-setup-to-someone-else]

At the foot of the Linear page, the **Install link** card has a link that
walks someone through connecting Linear, turning it on for this workspace,
choosing a team and the agent's actor. It picks up wherever they left off.

## What Stunt Double can access [#what-stunt-double-can-access]

Stunt Double asks Linear for these scopes:

| Scope             | Used for                                     |
| ----------------- | -------------------------------------------- |
| `read`            | Reading issues, teams and comments           |
| `write`           | Updating issues the agent works on           |
| `issues:create`   | Creating issues                              |
| `comments:create` | Replying on issues                           |
| `app:assignable`  | Letting you assign issues to Stunt Double    |
| `app:mentionable` | Letting you mention Stunt Double in comments |

<Callout variant="note" title="What the agent can use">
  With an actor set under **Agent identity**, the agent also uses that actor's
  model and MCP servers, including any credentials the actor holds. Choose an
  actor whose access you are happy to see used on Linear issues.
</Callout>

## Disconnect [#disconnect]

- **Stop one workspace using Linear.** An owner or admin turns off the
  workspace switch.
- **Remove Linear from your account.** Click **Disconnect** on **Your
  account**. The **Disconnect Linear?** dialog lists every workspace that loses
  Linear. Confirm to remove it.
- **Remove the app from Linear.** You can also remove Stunt Double from the
  installed apps in your Linear workspace settings.

## Troubleshooting [#troubleshooting]

**You cannot assign an issue to Stunt Double.** Check that Linear is on for
the workspace and the connection still shows **Connected as**. If it was
disconnected, connect it again.

**The agent replies with only its built-in tools.** **Agent identity** is set
to **No actor**. An owner or admin can pick an actor under **Settings**.

**Agent identity is missing.** It only appears to owners and admins, and only
once Linear is connected and on for the workspace.

**Linear will not let you install the app.** Adding apps to a Linear workspace can be limited to Linear admins. Ask one to connect, or send them the install link.

**An automation does not start.** Check that it is on, that its trigger
includes **Issue Assigned** or **Issue Mentioned**, and that any team filter
matches the issue's team.


# Self-hosted workers (/integrations/self-hosted-workers)

# Self-hosted workers [#self-hosted-workers]

Your staging site sits behind a VPN, so a hosted browser cannot reach it. A
self-hosted worker runs on a machine inside your network and drives the
browser from there. It connects out to Stunt Double, so you open no inbound
ports.

For a full walkthrough, read
[Test a product behind your firewall](/tutorials/self-hosted-worker).

## What it does [#what-it-does]

- **Reach internal products.** Actors assigned to a worker browse from your
  network, so they can open staging, intranet and local dev servers.
- **Outbound only.** The worker connects to Stunt Double. Nothing connects in.
- **Domain controls.** Each worker can be limited to the domains actors need.
- **Mixed fleets.** Actors without a worker keep using Stunt Double's cloud
  browser, so one workspace can use both.

## Before you start [#before-you-start]

- **Plan.** Self-hosted workers are on the Enterprise plan only. On other
  plans, **Settings**, then **Workers**, shows **Enterprise feature**.
- **Role.** Only workspace owners and admins can set workers up.
- **A host.** A machine inside your network with Docker that can reach both
  the product you want to test and `app.stuntdouble.io`.

## Set it up [#set-it-up]

1. In [app.stuntdouble.io](https://app.stuntdouble.io), open **Settings**,
   then **Workers**. The page is marked **Experimental**.
2. Turn on **Enable for this workspace**.
3. Click **Add worker**, enter a **Name** (and a description if you like),
   then click **Create worker**.
4. Copy the Docker command shown under **Recommended: Docker**. The API key in
   it is shown once, so copy it before you dismiss the panel.
5. Run the command on your host. It looks like this, with your own key:

   ```bash
   # Run the worker (auto-restarts on failure; auto-updates via watchtower)
   docker run -d --name sd-worker --restart unless-stopped \
     --label com.centurylinklabs.watchtower.enable=true \
     -e SD_WORKER_API_KEY=<your-api-key> \
     ghcr.io/stunt-double/worker:latest

   # Enable auto-updates (one-time per machine; safe to re-run)
   docker rm -f sd-watchtower 2>/dev/null; docker pull ghcr.io/nicholas-fedor/watchtower:1.19.0 && docker run -d --name sd-watchtower --restart unless-stopped \
     -v /var/run/docker.sock:/var/run/docker.sock \
     ghcr.io/nicholas-fedor/watchtower:1.19.0 --label-enable --cleanup --interval 300
   ```

   The second command installs Watchtower, which pulls and restarts the worker
   when a new `:latest` ships. Skip it if you would rather update by hand.

6. Back on **Workers**, the worker's status changes to **Connected** once it
   checks in. Enter an internal URL and click **Test connection**. The test
   runs in the worker's browser, so a pass means actors can reach that URL.
7. Assign actors to it. Open an actor, find **Self-hosted worker** and choose
   it under **Worker**. Pick **Any self-hosted worker** to use whichever one is
   online when a run starts.

For docker compose or other container setups, expand **API key only** to copy
the bare key and set it as `SD_WORKER_API_KEY` yourself.

### Sites behind a VPN or machine-credential SSO [#sites-behind-a-vpn-or-machine-credential-sso]

The panel also shows a command for **System browser mode**. Instead of a
headless browser in a container, the worker opens that machine's own Chrome
with a dedicated profile, so the company VPN, device certificates and proxy
settings all apply. Run it on the machine that has VPN access, not inside
Docker, and sign in to your VPN portal or SSO once in the window that opens.
On a Mac, the [desktop app](/integrations/desktop-app) does the same with one
click.

## Control what it can reach [#control-what-it-can-reach]

Each worker has an **Access** setting:

- **Allow all domains.** The default. The worker may reach any host.
- **Allow specific domains.** Only the hosts you list, and their subdomains.
- **Block specific domains.** Any host except the ones you list, and their
  subdomains.

On a network where reach matters, switch to **Allow specific domains** and
list only what actors need.

## Remove a worker [#remove-a-worker]

Delete the worker from **Workers**. Actors assigned to it are unassigned and
go back to the cloud browser. Then stop the container on your host:

```bash
docker rm -f sd-worker
```

## Troubleshooting [#troubleshooting]

**The worker stays Offline.** Check the container is running
(`docker ps`) and read its logs (`docker logs sd-worker`). The host needs
outbound HTTPS to `app.stuntdouble.io`.

**Test connection says "No response from worker. Is it running?"** The worker
has not checked in. Start the container, wait for **Connected**, then test
again.

**Test connection fails on your internal URL.** The worker is up but cannot
reach that host. Check DNS and routing from the host, and the worker's
**Access** setting.

**You lost the API key.** It is only shown once. Delete the worker and create
a new one.

**Settings shows "Enterprise feature".** Workers need the Enterprise plan.
Contact us to upgrade.


# Slack (/integrations/slack)

# Slack [#slack]

Someone posts a staging link in the design channel and asks what everyone
thinks. With Slack connected, Stunt Double answers in the thread: it opens the
page in a real browser, reviews it and replies with ranked findings. The same
Slack agent takes direct messages, answers questions about your workspace and
starts checklists and automations for you.

For the full design review walkthrough, read
[Review designs from Slack](/tutorials/slack-design-review).

## What it does [#what-it-does]

- **Direct messages.** Anyone in your Slack team can DM Stunt Double. Send a
  link and ask for feedback and it runs a design review. Ask about the
  workspace and it can start checklists and automations.
- **Design reviews in channels.** Once design review is set up for a channel,
  Stunt Double picks up messages that carry a link and ask for feedback, and
  replies in the thread.
- **Notifications.** Each project can post to its own Slack channel.
- **Workflow triggers.** Automations can start from Slack events with the
  **Slack Event** trigger.

## Before you start [#before-you-start]

- **Role.** Any member can install the Slack agent. Adding it to a Stunt
  Double workspace, or removing it, takes an owner or admin.
- **Plan.** Slack does not need a particular plan.
- **Slack access.** You need permission to install apps in your Slack team.
  If your Slack admins approve apps first, ask them to approve Stunt Double.

## Set it up [#set-it-up]

1. In [app.stuntdouble.io](https://app.stuntdouble.io), open **Integrations**
   from the sidebar and click **Slack**.
2. On **Your account**, click **Install Slack agent** and approve Stunt Double
   in Slack. The step changes to **Installed by you for** your Slack team.
3. On the step named after your workspace, an owner or admin clicks **Add**
   followed by your Slack team name. The step reads **On, through** your Slack
   team. One install serves every workspace it is added to, so a second
   workspace only needs **Add**, not another install.
4. On **Projects**, click **Choose for a project** and pick a project. Its
   **Settings** open on the **Integrations** tab: choose the project's
   **channel** there.
5. To have Stunt Double pick up review requests in a channel, open **Design
   reviews**, then **Settings**, and choose the channels to watch.

Setting Slack up for someone else? The card at the bottom of the Slack panel
gives you a link to share. It walks them through installing the Slack agent
for this workspace and choosing where notifications go.

## What Stunt Double can access [#what-stunt-double-can-access]

The Slack agent asks for these scopes:

| Scope                                | Used for                                            |
| ------------------------------------ | --------------------------------------------------- |
| `channels:read`, `groups:read`       | Listing channels to choose from                     |
| `channels:history`, `groups:history` | Reading messages in channels it watches for reviews |
| `app_mentions:read`                  | Answering when someone @mentions Stunt Double       |
| `chat:write`, `chat:write.customize` | Posting replies, reviews and notifications          |
| `incoming-webhook`                   | Posting notifications to the channel you choose     |
| `im:history`, `im:write`             | Reading and answering direct messages               |
| `assistant:write`                    | Replying as an assistant in Slack's agent view      |
| `commands`                           | Handling Stunt Double's slash commands              |
| `users:read`                         | Showing who asked                                   |
| `mcp:connect`                        | Letting Slack reach Stunt Double's MCP server    |

Installs from before direct messages were supported lack `im:history` and
`im:write`. Under **Settings**, **Direct messages** shows **Reinstall the
Slack agent to turn this on** for those. Install again to grant them.

## Remove versus disconnect [#remove-versus-disconnect]

Slack has two ways out, and they reach different distances.

- **Remove** takes Slack out of one Stunt Double workspace. An owner or admin
  clicks **Remove** on the workspace step and confirms **Remove Slack from**
  the workspace name. Notifications and the Slack agent stop for that
  workspace. The install stays, so other workspaces keep using it and this one
  can be added back without installing again.
- **Disconnect** removes the install from your account, which takes it out of
  every workspace that uses it. The person who installed it clicks
  **Disconnect** on **Your account**, and the **Disconnect Slack?** dialog
  names each workspace that loses it. Notifications and the agent stop in all
  of them until someone installs it again.

To stop Slack in one workspace and leave the rest running, use Remove.

## Troubleshooting [#troubleshooting]

**A red banner reads "Slack has stopped working for" your workspace.** Slack
stopped renewing the install's token. Click **Reinstall the Slack agent**.
Reinstalling keeps every workspace that uses it.

**The workspace step reads "Not added here yet. Ask a workspace admin to add
it."** The Slack agent is installed, but only owners and admins can add it to
a workspace. Ask one of them to open the Slack card and click **Add**.

**Your account reads "Slack is linked to your account, but the Slack agent
install is gone."** The app was removed from Slack's side. Click **Install
Slack agent** to install it again.

**Stunt Double does not reply in a channel.** DMs work as soon as Slack is
added. Channels need design review turned on for them under **Design
reviews**, then **Settings**. Check that Stunt Double has been invited to the
channel too.

**Direct messages do not arrive.** Check **Direct messages** under
**Settings** on the Slack panel. If it asks you to reinstall, the install
predates DM support.


# Vercel (/integrations/vercel)

# Vercel [#vercel]

A preview deployment finishes and the link lands in a pull request. With
Vercel connected, that deploy can start an automation: actors open the
preview, run your checklists and report back while the change is still in
review.

## What it does [#what-it-does]

- **Deployment triggers.** Automations start when a deployment is created,
  succeeds, fails, is canceled, becomes ready or is promoted to production.
- **Deployment context.** Actors get the deployment's status and metadata,
  so they test the build that just shipped.
- **Per-project links.** Each Stunt Double project links its own Vercel
  project.

## Before you start [#before-you-start]

- **Role.** Any member can connect their own Vercel account. Turning Vercel
  on for the workspace takes an owner or admin.
- **Plan.** Installing the integration is free. Runs it starts count toward
  your Stunt Double plan as usual.
- **Vercel access.** You need permission to add integrations to the Vercel
  team that owns your projects.

## Set it up [#set-it-up]

There are two ways in. Both end with the same per-project link.

### From Stunt Double [#from-stunt-double]

1. In [app.stuntdouble.io](https://app.stuntdouble.io), open **Integrations**
   from the sidebar and click **Vercel**.
2. On **Your account**, click **Connect Vercel**. Vercel opens and asks which
   team and projects to install the integration on. Approve it.
3. On the step named after your workspace, an owner or admin turns the switch
   on. It reads **On. Actors, checklists and automations here can use it.**
4. On **Projects**, click **Choose for a project** and pick a project. Its
   **Settings** open on the **Integrations** tab: choose the project's
   **Vercel project** there.

### From the Vercel Marketplace [#from-the-vercel-marketplace]

1. Find Stunt Double in the Vercel Marketplace and install it on your Vercel
   team.
2. Open the Vercel card under **Integrations**. **Your account** reads
   **Installed from the Vercel Marketplace**, and the workspace step reads
   **On for everyone in this workspace.** Nobody needs to connect their own
   account.
3. On **Projects**, click **Choose for a project** and pick the **Vercel
   project** for each Stunt Double project.

### Start an automation on deploy [#start-an-automation-on-deploy]

1. Open **Automations** and create a new one.
2. Under **Trigger Type**, choose **Vercel Event**.
3. Pick one or more events: **Deployment Created**, **Deployment Succeeded**,
   **Deployment Error**, **Deployment Canceled**, **Deployment Ready** or
   **Deployment Promoted**.
4. Under **Deployment Environment**, choose **Any environment**, **Production
   only** or **Preview only**.
5. Optionally, set a **Project Filter** to one Vercel project. Leave it empty
   to trigger for all of them.
6. Click **Create automation**, then add the checklists it should run.

**Deployment Succeeded** fires once the build completes and checks pass, which
is the usual choice for testing previews.

## What Stunt Double can access [#what-stunt-double-can-access]

Vercel sets the integration's permissions, and shows them on the install
screen before you approve. Stunt Double uses them to list your projects, read
deployment status and metadata, and receive deployment events.

## Disconnect [#disconnect]

Open **Integrations**, click **Vercel**, then click **Disconnect** on **Your
account**. The **Disconnect Vercel?** dialog lists every workspace that loses
Vercel. Confirm, and automations with a **Vercel Event** trigger stop firing
in those workspaces.

To stop one workspace from using Vercel and leave the rest, an owner or admin
turns off the workspace switch instead. A Marketplace install is removed from
the integrations page of your Vercel team.

## Troubleshooting [#troubleshooting]

**Vercel shows "Set up Vercel" next to Vercel Event in the trigger list.**
Vercel is not connected for this workspace yet. Choosing the trigger opens the
connect dialog.

**The workspace step reads "Off. Ask a workspace owner or admin to turn it
on."** Only owners and admins can flip the switch.

**An automation does not start on deploy.** Check that the automation is on,
that its events and **Deployment Environment** match the deploy, and that any
**Project Filter** names the right Vercel project. The integration also has to
be installed on the Vercel team that owns that project.

**Actors cannot open a preview.** Vercel's deployment protection blocks
anonymous visitors to previews. Give actors a way past it, such as a
protection bypass, or test a deployment they can reach.


# Webhooks (/integrations/webhooks)

# Webhooks [#webhooks]

Your CI pipeline finishes a staging deploy, but it is not on Vercel and there
is no built-in trigger for it. A webhook trigger covers that case: the pipeline
sends one HTTP request, and the automation runs its checklists against the
build that just went out.

This page is a reference for the **Webhook** trigger. For building the rest of
an automation, read [Automate multi-step workflows](/tutorials/building-workflows).

## Before you start [#before-you-start]

- **Role.** Any workspace member who can create automations.
- **Plan.** Webhook runs count toward your plan like any other run. A request
  is refused if the workspace is out of runs or over its budget cap.

## Create a webhook trigger [#create-a-webhook-trigger]

1. Open **Automations** and create a new one.
2. Choose the **Project**, and give it a **Name**.
3. Under **Trigger Type**, choose **Webhook**. A webhook secret is generated
   when the automation is created.
4. Click **Create automation**, then add the steps it should run.
5. Open the automation's **Settings** tab. Under **Trigger configuration**,
   copy the **Webhook URL** and the **Webhook secret**.

Treat the secret like a password. Store it in your CI system's secret store,
not in a repository.

## Send the request [#send-the-request]

Send a `POST` to the **Webhook URL** with the secret in the `X-Webhook-Secret`
header. The request body is ignored, so you can send none.

```bash
curl -X POST "$STUNTDOUBLE_WEBHOOK_URL" \
  -H "X-Webhook-Secret: $STUNTDOUBLE_WEBHOOK_SECRET"
```

Here `STUNTDOUBLE_WEBHOOK_URL` and `STUNTDOUBLE_WEBHOOK_SECRET` are variables
you set in your own CI, holding the two values you copied.

The same request from a GitHub Actions step:

```yaml
- name: Run Stunt Double checks
  run: |
    curl --fail -X POST "${{ secrets.STUNTDOUBLE_WEBHOOK_URL }}" \
      -H "X-Webhook-Secret: ${{ secrets.STUNTDOUBLE_WEBHOOK_SECRET }}"
```

## Responses [#responses]

A successful request starts a run and returns its id:

```json
{ "run_id": "3f2b8c1e-..." }
```

The run then shows on the automation's runs list like any other.

| Status | Meaning                                                            |
| ------ | ------------------------------------------------------------------ |
| `200`  | The run started. The body carries `run_id`.                        |
| `400`  | The automation is turned off, or its trigger is not **Webhook**.   |
| `401`  | The `X-Webhook-Secret` header is missing or the secret is wrong.   |
| `402`  | The workspace has no runs left on its plan, or hit its budget cap. |
| `404`  | No automation exists at that URL.                                  |

Errors return a JSON body with an `error` message.

## Turn it off [#turn-it-off]

To stop accepting requests, turn the automation off. Requests then return
`400` until you turn it back on. Deleting the automation makes its URL return
`404`.

## Troubleshooting [#troubleshooting]

**`401` with the right secret.** Check the header name is exactly
`X-Webhook-Secret`, and that your CI is not adding quotes or a trailing
newline to the stored value.

**`400` straight after creating it.** The automation has to be on. Check its
status, and that **Trigger Type** is still **Webhook**.

**The run starts but does nothing useful.** The webhook only starts the
automation. Check that it has steps, and that its checklists point at the
environment your pipeline just deployed.


# Agents (/mcp/agents)

The [Claude Code plugin](/mcp/clients/claude-code-plugin) installs these subagents. Each one knows which Stunt Double tools to call for its job and how to report back.

## design-reviewer [#design-reviewer]

Facilitate design reviews and co-creation sessions using Stunt Double actors and conversations.

You are a design review facilitator that uses Stunt Double to bring user perspectives into design discussions. You help UX designers and PMs validate design decisions by simulating how different user personas would experience proposed changes.

### When to use [#when-to-use]

- During design critiques or reviews to get simulated user reactions
- When evaluating competing design options (A/B exploration)
- Before handoff to engineering to validate the proposed flow
- When a designer wants quick persona-based feedback on a prototype or mockup

### How to run a design review session [#how-to-run-a-design-review-session]

#### 1. Set up the review context [#1-set-up-the-review-context]

```
list_workspaces() → find the workspace for this project
list_actors(workspace_id) → see which personas are available
```

Pick 2-4 actors representing different user segments (e.g., power user, first-time visitor, accessibility-dependent user).

#### 2. Put the design in front of each persona [#2-put-the-design-in-front-of-each-persona]

```
create_interview(workspace_id, project_id, name: "Design review: <feature name>",
                 target_url: <prototype or preview URL>, research_brief: …)
add_interview_section(interview_id, title) → one per topic
add_interview_item(section_id, type: "task" | "question", prompt_text: …)
add_interview_participant(interview_id, actor_id=…) → one per selected actor
launch_interview(interview_id) → async
```

Describe the proposed design in the research brief, then ask each participant to walk the experience and react. Anything with a reachable URL works: a prototype, a preview deployment, a published design link.

#### 3. Gather and synthesize feedback [#3-gather-and-synthesize-feedback]

```
get_interview_report(interview_id) → summary, themes, recommendations
get_interview_participant(participant_id) → verbatim transcript evidence
list_feedback(project_id) → check for related historical feedback
```

Actor chats started in the dashboard are readable with `list_conversations` and `get_conversation`; MCP cannot open a new one.

Summarize themes across personas: what worked, what confused them, what they'd expect instead.

#### 4. Document findings [#4-document-findings]

Create a summary with:

- **Consensus points**: things all personas agreed on
- **Friction points**: steps where one or more personas struggled
- **Divergent reactions**: where different user types had different needs
- **Recommended changes**: prioritized list based on severity and user impact

### Example session [#example-session]

&#62; **Design review: New onboarding wizard**
&#62;
&#62; I created conversations with 3 actors: "Emma - First-time SaaS user", "Carlos - Enterprise admin migrating from competitor", and "Priya - Developer setting up via API".
&#62;
&#62; Key findings:
&#62;
&#62; - Emma found step 2 (workspace setup) confusing: she didn't understand "workspace" vs "project"
&#62; - Carlos wanted a bulk import option at step 3 that doesn't exist yet
&#62; - Priya skipped the wizard entirely and asked for a CLI/API quickstart link
&#62;
&#62; Recommendation: Add contextual help tooltips in step 2, add a "Skip to API docs" link in the header, and consider a bulk import flow for enterprise users.

### Tips [#tips]

- Use `add_actor_knowledge` to give actors context about your product before the review (feature docs, current screenshots, user research), and record the design standards themselves as guidelines (`add_project_guideline`) so every review and run is held to them
- Create a dedicated actor for edge-case users (low bandwidth, screen reader, non-English speaker) to catch accessibility gaps
- Compare responses across participants to find universal vs segment-specific issues

## feedback-triager [#feedback-triager]

Triage, prioritize, and manage Stunt Double feedback to drive UX improvements.

You are a feedback management agent that helps teams systematically process Stunt Double feedback. You categorize issues, identify patterns, prioritize fixes, and track resolution.

### When to use [#when-to-use-1]

- During regular feedback review sessions (weekly triage)
- When a spike in new feedback arrives after a deployment
- When prioritizing the next sprint's UX improvements
- When closing the loop on resolved issues

### Triage workflow [#triage-workflow]

#### 1. Pull new feedback [#1-pull-new-feedback]

```
list_feedback(project_id, status: "new") → get untriaged feedback
```

#### 2. Review each item [#2-review-each-item]

```
get_feedback(feedback_id) → read full details, screenshots, actor context, and replies
```

For each item, assess:

- **Validity**: Is this a real UX issue or expected behavior?
- **Severity**: Does it block the user, cause significant friction, or is it cosmetic?
- **Scope**: Does it affect one persona type or multiple?
- **Reproducibility**: Can it be triggered reliably?

#### 3. Categorize and update status [#3-categorize-and-update-status]

```
update_feedback_status(feedback_id, status: "reviewed") → mark as triaged
update_feedback_status(feedback_id, status: "dismissed") → dismiss false positives
update_feedback_status(feedback_id, status: "resolved") → close fixed issues
```

#### 4. Cross-reference with actors and workflows [#4-cross-reference-with-actors-and-workflows]

```
list_actors(workspace_id) → check which actor types are affected
get_actor(actor_id) → understand the persona that surfaced the issue
list_workflows(workspace_id) → find workflows that cover the affected journey
```

#### 5. Identify patterns [#5-identify-patterns]

Group feedback by:

- **Area**: Which part of the product (onboarding, checkout, settings, etc.)
- **Actor type**: Which personas are most affected
- **Recurrence**: Issues that appear across multiple workflow runs
- **Trend**: New issues vs regressions vs long-standing problems

### Example triage summary [#example-triage-summary]

&#62; **Weekly feedback triage: March 28, 2026**
&#62;
&#62; **New items reviewed:** 9
&#62;
&#62; | ID     | Summary                                       | Severity | Actor               | Status    |
&#62; | ------ | --------------------------------------------- | -------- | ------------------- | --------- |
&#62; | FB-201 | Search returns no results for partial queries | Major    | Power User (Alex)   | reviewed  |
&#62; | FB-202 | Modal close button not keyboard-accessible    | Major    | Accessibility (Sam) | reviewed  |
&#62; | FB-203 | Success toast disappears too quickly          | Minor    | New User (Emma)     | reviewed  |
&#62; | FB-204 | Duplicate of FB-198 (form validation)         |: |: | dismissed |
&#62; | FB-205 | Settings page loads slowly on mobile          | Major    | Mobile User (Ravi)  | reviewed  |
&#62;
&#62; **Patterns noticed:**
&#62;
&#62; - 3 of 9 items relate to keyboard/accessibility: consider an accessibility audit
&#62; - Search issues appearing for the second sprint in a row: needs dedicated workflow
&#62;
&#62; **Recommended actions:**
&#62;
&#62; 1. Create an "Accessibility Navigation" checklist covering keyboard and screen reader flows
&#62; 2. Add a search-focused workflow with multiple query patterns
&#62; 3. Prioritize FB-202 and FB-205 for next sprint

### Tips [#tips-1]

- Process feedback regularly: a weekly cadence prevents backlog buildup
- Use status transitions consistently: new → reviewed → resolved (or dismissed)
- Create actors for underrepresented user segments when feedback reveals blind spots
- Link feedback patterns to workflow coverage gaps and create new workflows to prevent recurrence

## product-researcher [#product-researcher]

Gather user insights and validate product hypotheses using Stunt Double actors, conversations, and feedback.

You are a product research agent that helps PMs and designers gather qualitative insights by conversing with Stunt Double actors and analyzing feedback patterns. You turn AI persona interactions into actionable product intelligence.

### When to use [#when-to-use-2]

- When exploring a new feature idea and need quick user perspective validation
- When analyzing patterns in existing feedback to prioritize the roadmap
- When building user journey maps and need persona-driven walkthroughs
- When preparing for a stakeholder review and need data-backed UX insights

### Research workflows [#research-workflows]

#### Exploratory research: "Would users want this?" [#exploratory-research-would-users-want-this]

Probe a feature concept with a short interview across diverse actors:

```
list_actors(workspace_id) → find relevant personas
create_interview(workspace_id, project_id, name, target_url, research_brief)
add_interview_section(interview_id, title: "Concept exploration")
add_interview_item(section_id, type: "question", prompt_text: "…")
add_interview_participant(interview_id, actor_id=…) → 3-5 varied personas
launch_interview(interview_id) → poll, then get_interview_report(interview_id)
```

Ask open-ended questions: "How would you expect X to work?", "What would you do if you encountered Y?", "What's missing from your current experience?"

Every participant answers the same guide, so the report can synthesise themes across personas rather than leaving you to compare transcripts by hand. Chats an actor has already had are readable with `list_conversations` / `get_conversation`; starting a new chat is a dashboard action, not an MCP one.

#### Feedback analysis: "What are users struggling with?" [#feedback-analysis-what-are-users-struggling-with]

Mine existing feedback for patterns:

```
list_feedback(project_id) → get all feedback, newest first
list_feedback(project_id, status: "new") → focus on untriaged items
get_feedback(feedback_id) → read full details and replies
```

Categorize feedback by:

- **Theme** (navigation, performance, comprehension, trust)
- **Severity** (blocker, painful, annoying, cosmetic)
- **User segment** (which actor types are affected)
- **Frequency** (how many actors hit the same issue)

#### Journey mapping: "How do different users experience this flow?" [#journey-mapping-how-do-different-users-experience-this-flow]

Run actors through a flow and document their experience:

```
list_workflows(workspace_id) → find journey workflows
run_workflow(workflow_id) → execute the journey
get_workflow_run(run_id) → get step-by-step results
```

Build a journey map showing where each persona succeeds, hesitates, or fails.

#### Concept testing: "Which option do users prefer?" [#concept-testing-which-option-do-users-prefer]

Put the alternatives in front of the same panel and compare:

```
create_interview(…, name: "Concept test: Option A vs B", target_url: <option A>)
add_interview_item(section_id, type: "task", prompt_text: "…", expected_evidence: "…")
add_interview_participant(interview_id, persona_spec={…}) → per segment
launch_interview(interview_id) → get_interview_report(interview_id)
```

Give each option its own section (or its own interview against that option's URL), ask participants to evaluate each and explain their preference, then look for patterns across personas in the report.

#### Structured interviews: "Run a small panel through a discussion guide" [#structured-interviews-run-a-small-panel-through-a-discussion-guide]

When you need a repeatable, comparable research round (rather than one-off conversations), use the **Interviews** tools. A panel of 3–5 participants runs through the same sections + questions/tasks against a live URL, and Stunt Double synthesises themes and recommendations for you:

```
create_interview(workspace_id, project_id, name, target_url, research_brief)
add_interview_section(interview_id, title)            # one section per topic
add_interview_item(section_id, type, prompt_text)     # questions or browser tasks
add_interview_participant(interview_id, actor_id=…)   # or persona_spec={…}
launch_interview(interview_id)                        # async run
get_interview_report(interview_id)                    # summary, themes, recs
```

Use this when you want side-by-side comparison across personas: e.g. evaluating a new pricing page, an onboarding flow, or a redesigned dashboard.

### Example research summary [#example-research-summary]

&#62; **Research: Should we add a team dashboard?**
&#62;
&#62; Interviewed 4 actors across segments:
&#62;
&#62; - **Enterprise admin (Carlos):** "Absolutely. I need to see who's active, what projects are running, and where bottlenecks are. I check this daily."
&#62; - **Solo creator (Maya):** "Not useful for me: I'm the only person. I'd rather have a personal productivity view."
&#62; - **Team lead (Jordan):** "Yes, but only if it shows actionable data. Don't just show me charts: show me what needs my attention."
&#62; - **New user (Emma):** "I don't know what a dashboard would show me yet. I'm still figuring out the basics."
&#62;
&#62; **Insight:** Strong demand from team/enterprise segments, but solo users see no value. Consider a role-based default view. Start with an "attention needed" widget rather than a full analytics dashboard.
&#62;
&#62; **Feedback data:** 12 feedback items mention "team visibility" or "who did what": 8 from enterprise actors, 4 from team leads.

### Tips [#tips-2]

- Use `add_actor_knowledge` to brief actors on your product context before a research round, and `list_project_guidelines` to see the standing rules the product is already held to
- Create actors representing underserved segments to explore expansion opportunities
- Cross-reference interview findings with workflow run data for quantitative backing
- Use `update_feedback_status` to mark feedback as "reviewed" once it's been incorporated into research

## qa-engineer [#qa-engineer]

Run and monitor Stunt Double checklists and workflows for QA validation of user-facing features.

You are a QA-focused agent that uses Stunt Double to systematically validate user journeys and catch regressions. You run checklists and workflows, monitor results, and surface issues with clear reproduction context.

### When to use [#when-to-use-3]

- Before a release to run the full validation suite
- After deploying to staging to smoke-test critical paths
- When investigating a reported bug to see if Stunt Double actors can reproduce it
- To verify a fix by re-running the workflow that originally caught the issue

### QA validation workflow [#qa-validation-workflow]

#### 1. Identify what to test [#1-identify-what-to-test]

```
list_workspaces() → find the workspace
list_workflows(workspace_id) → see available journey tests
list_checklists(workspace_id) → see available quality checks
```

Choose workflows for end-to-end journey validation and checklists for point-in-time quality gates.

#### 2. Run the validation suite [#2-run-the-validation-suite]

```
run_workflow(workflow_id) → trigger each relevant workflow (async, returns run_id)
run_checklist(checklist_id) → trigger each relevant checklist (async, returns run_id)
```

Trigger multiple runs in parallel for efficiency. Each returns a run ID to poll.

#### 3. Monitor and collect results [#3-monitor-and-collect-results]

```
get_workflow_run(run_id) → poll until status is complete, check step-level results
get_checklist_run(run_id) → poll until status is complete, check per-check results
```

#### 4. Triage failures [#4-triage-failures]

```
get_feedback(feedback_id) → inspect any feedback generated during the run
list_feedback(project_id, status: "new") → check for new issues surfaced
```

For each failure, document:

- **What failed**: the specific step or check
- **Expected vs actual**: what should have happened
- **Actor context**: which persona hit the issue and why their profile matters
- **Severity**: blocker, major, minor, or cosmetic

#### 5. Verify fixes [#5-verify-fixes]

After a fix is deployed, re-run the specific workflow or checklist that caught the issue:

```
run_workflow(workflow_id) → re-run the failing workflow
get_workflow_run(run_id) → confirm all steps now pass
update_feedback_status(feedback_id, status: "resolved") → close the feedback item
```

### Example QA report [#example-qa-report]

&#62; **QA run: v2.4.0 staging validation**
&#62;
&#62; Ran 4 workflows and 2 checklists against staging.
&#62;
&#62; | Validation              | Status | Details                                         |
&#62; | ----------------------- | ------ | ----------------------------------------------- |
&#62; | Signup → First Project  | PASS   | All 6 steps completed                           |
&#62; | Checkout Flow           | FAIL   | Step 4: payment form timeout after 30s          |
&#62; | Settings Update         | PASS   | All 4 steps completed                           |
&#62; | Invite Team Member      | PASS   | All 3 steps completed                           |
&#62; | Accessibility Checklist | FAIL   | 2/8 checks failed (contrast ratio, focus order) |
&#62; | Performance Checklist   | PASS   | All checks within thresholds                    |
&#62;
&#62; **Blockers:** Payment form timeout must be fixed before release.
&#62; **Action items:** Fix contrast ratio on settings page, review focus order on modal dialogs.

### Tips [#tips-3]

- Run workflows with different actors to test the same journey from multiple user perspectives
- Use `get_workflow(workflow_id)`, which returns `recent_runs`, to compare current results against previous runs and spot regressions
- Schedule workflows on a regular cadence using `create_workflow` with `trigger_type: "schedule"` for continuous validation

## ux-friction-reviewer [#ux-friction-reviewer]

Review code changes for potential UX friction using Stunt Double personas and feedback.

You are a UX-focused reviewer that uses Stunt Double to identify friction in user journeys. You bridge code changes to real-world user impact by leveraging AI personas, workflows, and feedback data.

### Review focus [#review-focus]

1. **Identify user-facing changes** that could introduce friction (confusing flows, missing feedback, broken paths, error states without recovery).
2. **Cross-reference with existing feedback** for known issues in the affected area.
3. **Suggest running specific workflows or checklists** to validate the changes.
4. **Flag accessibility and usability concerns** that AI personas might encounter.
5. **Recommend creating new actors or workflows** when coverage gaps are found.

### How to use Stunt Double tools [#how-to-use-stunt-double-tools]

#### Check existing feedback for the affected area [#check-existing-feedback-for-the-affected-area]

```
list_feedback(project_id) → scan for issues related to changed files
get_feedback(feedback_id) → read full details of relevant submissions
```

#### Run validation against staging [#run-validation-against-staging]

```
list_workspaces() → find the workspace
list_workflows(workspace_id) → find journey workflows covering the changed flow
list_checklists(workspace_id) → find quality checklists for the area
run_workflow(workflow_id) or run_checklist(checklist_id) → trigger a run
get_workflow_run(run_id) or get_checklist_run(run_id) → poll until complete
```

#### Review actor coverage [#review-actor-coverage]

```
list_actors(workspace_id) → check if personas cover the affected user types
get_actor(actor_id) → inspect system prompt and capabilities for relevance
```

#### Suggest new coverage when gaps are found [#suggest-new-coverage-when-gaps-are-found]

```
create_actor(workspace_id, name, description) → propose a new persona
create_workflow(workspace_id, name, trigger_type) → propose a new journey test
```

### Example review comment [#example-review-comment]

&#62; This PR changes the checkout error handling. I checked Stunt Double feedback and found 3 open issues related to payment failures (FB-102, FB-107, FB-115). I ran the "Checkout Happy Path" workflow against staging (steps 1-4 passed but step 5 (error recovery) now shows a blank screen instead of the retry prompt. Recommend fixing the error state before merging. I also noticed there's no actor representing a user with a saved but expired card) consider creating one for ongoing coverage.

---

Edit on GitHub: [design-reviewer.md](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/agents/design-reviewer.md), [feedback-triager.md](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/agents/feedback-triager.md), [product-researcher.md](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/agents/product-researcher.md), [qa-engineer.md](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/agents/qa-engineer.md), [ux-friction-reviewer.md](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/agents/ux-friction-reviewer.md)


# ChatGPT and Codex (/mcp/clients/chatgpt-and-codex)

The repository also carries an OpenAI plugin manifest (`.openai-plugin/plugin.json`, in the [Agent Plugins](https://agent-plugins.org) format) with the directory listing, review test cases and release notes. `node scripts/build-openai-plugin.mjs` checks it against OpenAI's limits and writes `dist/stuntdouble-openai-plugin.zip` (in the Codex layout the portal accepts: the manifest as `.codex-plugin/plugin.json`, a `.mcp.json` for the hosted server, the skills and the logos), which is what goes into the [plugin submission portal](https://developers.openai.com/plugins/deploy/submission).

Until it is published in the directory, connect it in ChatGPT developer mode (**Settings → Apps → Advanced**) as a custom connector with:

```
https://app.stuntdouble.io/api/mcp
```

The checklist run and Index report tools render as cards in ChatGPT (and in other MCP Apps hosts, Claude among them).

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/README.md)


# Claude Code plugin (/mcp/clients/claude-code-plugin)

The repository is also a Claude plugin: it bundles the hosted MCP server (`.mcp.json`) with the skills in `skills/` and the agents in `agents/`.

```bash
claude plugin marketplace add stunt-double/stuntdouble-mcp
claude plugin install stuntdouble@stuntdouble
```

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/README.md)


# Claude Code (/mcp/clients/claude-code)

```bash
claude mcp add --transport http stuntdouble https://app.stuntdouble.io/api/mcp
```

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/README.md)


# Claude (/mcp/clients/claude)

Go to **Settings → Connectors → Add custom connector** and paste:

```
https://app.stuntdouble.io/api/mcp
```

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/README.md)


# Codex CLI (/mcp/clients/codex-cli)

```bash
codex mcp add stuntdouble --url https://app.stuntdouble.io/api/mcp
codex mcp login stuntdouble
```

Or add it to `~/.codex/config.toml` by hand and then run `codex mcp login stuntdouble`:

```toml
[mcp_servers.stuntdouble]
url = "https://app.stuntdouble.io/api/mcp"
```

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/README.md)


# Cursor (/mcp/clients/cursor)

**Install as a plugin (recommended):** once published, install **Stunt Double** from the [Cursor marketplace](https://cursor.com/marketplace). For a team marketplace or local clone, add this repo as a marketplace and install the `stuntdouble` plugin; Cursor discovers the MCP server, skills, rules and agents from the default folders.

**MCP only (no skills, rules or agents):** add the block below to a Cursor MCP config:

- **Project-local:** `.cursor/mcp.json` at the root of your project.
- **Global (all projects):** `~/.cursor/mcp.json` on macOS/Linux (see [Cursor MCP docs](https://cursor.com/docs/mcp.md) for your OS).

```json
{
  "mcpServers": {
    "stuntdouble": {
      "url": "https://app.stuntdouble.io/api/mcp"
    }
  }
}
```

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/README.md)


# Gemini CLI (/mcp/clients/gemini-cli)

```bash
gemini mcp add --transport http stuntdouble https://app.stuntdouble.io/api/mcp
```

Or add it to `~/.gemini/settings.json` (or `.gemini/settings.json` in a project):

```json
{
  "mcpServers": {
    "stuntdouble": {
      "httpUrl": "https://app.stuntdouble.io/api/mcp"
    }
  }
}
```

Run `/mcp auth stuntdouble` inside Gemini CLI to sign in.

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/README.md)


# OpenCode (/mcp/clients/opencode)

Add it to `opencode.json` in a project (or `~/.config/opencode/opencode.json`), then run `opencode mcp auth stuntdouble`:

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "stuntdouble": {
      "type": "remote",
      "url": "https://app.stuntdouble.io/api/mcp"
    }
  }
}
```

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/README.md)


# Other clients (/mcp/clients/other-clients)

Any client that speaks Streamable HTTP and OAuth can connect to `https://app.stuntdouble.io/api/mcp` directly. For a client that only runs local (stdio) servers, bridge it with [`mcp-remote`](https://www.npmjs.com/package/mcp-remote), which handles the OAuth sign-in:

```json
{
  "mcpServers": {
    "stuntdouble": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://app.stuntdouble.io/api/mcp"]
    }
  }
}
```

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/README.md)


# VS Code (/mcp/clients/vs-code)

```bash
code --add-mcp '{"name":"stuntdouble","type":"http","url":"https://app.stuntdouble.io/api/mcp"}'
```

Or add it to `.vscode/mcp.json` in a workspace (or run **MCP: Open User Configuration** for every workspace):

```json
{
  "servers": {
    "stuntdouble": {
      "type": "http",
      "url": "https://app.stuntdouble.io/api/mcp"
    }
  }
}
```

The tools appear in Copilot Chat's agent mode.

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/README.md)


# Windsurf (/mcp/clients/windsurf)

Add it to `~/.codeium/windsurf/mcp_config.json` (or **Windsurf Settings → Cascade → MCP Servers → View raw config**):

```json
{
  "mcpServers": {
    "stuntdouble": {
      "serverUrl": "https://app.stuntdouble.io/api/mcp"
    }
  }
}
```

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/README.md)


# Connect (/mcp/connect)

Every client connects to the same endpoint, `https://app.stuntdouble.io/api/mcp`, and signs in with OAuth the first time it calls a tool. No API keys. The app gives you a shorter path for the clients below. For the rest, follow the [client pages](/mcp/clients/claude-code).

## Claude Desktop [#claude-desktop]

1. In your workspace, open **Integrations** and choose **Claude**.
2. Select **Download .mcpb** and open the file. Claude Desktop installs the server as an extension. It needs Node 18 or later on your machine.
3. Call any Stunt Double tool. Claude opens a browser window so you can sign in and approve the connection.

If you would rather not install an extension, select **Add it manually instead**. In Claude, go to **Settings**, then **Connectors**, choose **Add**, and paste the endpoint URL. The [Claude page](/mcp/clients/claude) covers the same steps for Claude on the web and mobile.

For Claude Code, run the command on the [Claude Code page](/mcp/clients/claude-code) instead.

## Cursor, Windsurf and other MCP clients [#cursor-windsurf-and-other-mcp-clients]

Open **Integrations** and choose **MCP**. Install Stunt Double from the Cursor marketplace, or import `stunt-double/stuntdouble-mcp` into your team marketplace. To add only the server, put it in `.cursor/mcp.json` under `mcpServers.stuntdouble.url`, as the [Cursor and Windsurf page](/mcp/clients/cursor-windsurf) shows.

## ChatGPT [#chatgpt]

The Stunt Double app is not in the ChatGPT app directory yet. You can connect it in developer mode:

1. In ChatGPT, open **Settings**, then **Apps**, then **Advanced**, and turn on developer mode.
2. Create a custom connector and paste the endpoint URL.
3. Sign in to Stunt Double when ChatGPT asks.

Checklist runs and Stunt Double Index results show up as cards in the conversation.

## What a client can do [#what-a-client-can-do]

A client asks for some or all of three scopes: `mcp:read`, `mcp:write` and `mcp:run`. A client that asks for none gets all three. Whatever it holds, it acts as you and reaches only the workspaces you belong to. The [overview](/mcp#authentication) explains each scope, and [tools](/mcp/tools) lists what they unlock.

## Revoke access [#revoke-access]

Go to **Account**, then **Security**, and find the client under **connected apps**. Revoke it there to end that client's access to your account.


# MCP server (/mcp)

# Stunt Double MCP Server [#stunt-double-mcp-server]

Deploy AI user personas to validate user journeys at scale. Find UX friction before real users do.

[Stunt Double](https://stuntdouble.io) deploys AI agents with realistic user personas to validate user journeys at scale. Create actors, run automated workflows and checklists against any web app, and surface friction points before real users encounter them. Integrates with Claude, Linear, GitHub, and Slack.

&#60;p align="center"&#62;
  &#60;img src="./assets/demo.gif" alt="Asking Claude for the latest activity in a Stunt Double workspace, and Claude listing its checklists through the MCP server" width="100%"&#62;
&#60;/p&#62;

## Connect a client [#connect-a-client]

- [Claude Code](/mcp/clients/claude-code)
- [Claude Code plugin](/mcp/clients/claude-code-plugin)
- [Claude (web, Desktop, mobile)](/mcp/clients/claude)
- [ChatGPT and Codex](/mcp/clients/chatgpt-and-codex)
- [Codex CLI](/mcp/clients/codex-cli)
- [Gemini CLI](/mcp/clients/gemini-cli)
- [VS Code (GitHub Copilot)](/mcp/clients/vs-code)
- [OpenCode](/mcp/clients/opencode)
- [Windsurf](/mcp/clients/windsurf)
- [Other clients](/mcp/clients/other-clients)
- [Cursor](/mcp/clients/cursor)
- [From the Stunt Double app](/mcp/connect)

## Authentication [#authentication]

Authentication is handled automatically via OAuth 2.1 with PKCE. The first time your AI client connects, a browser window will open for you to sign in and authorise access to your Stunt Double account. No API keys or tokens required.

For **Cursor**, the OAuth redirect URI is fixed to `cursor://anysphere.cursor-mcp/oauth/callback` ([docs](https://cursor.com/docs/mcp.md)).

### What a connection can reach [#what-a-connection-can-reach]

The server acts as you, never more. Every tool resolves the workspace it is
being asked about and checks your membership of it before doing anything, so a
connection reaches exactly the workspaces `list_workspaces` returns for you, and
an archived workspace reaches nothing. A workspace you are not a member of
answers the same way one that does not exist does: "not found".

That holds for ids too, not just the `workspace_id` you pass. A tool that takes
another object's id (a checklist for an automation step, an actor for an
interview participant) checks that object belongs to the same workspace before
storing or running it, and refuses with "not found in this workspace" otherwise.
Refusals never say which workspace an id does belong to.

Scopes narrow this further, never widen it. The consent screen names what the
connection asked for, and a token granted `mcp:read` is not shown the write or
run tools at all: they are absent from `tools/list` rather than present and
failing.

| Scope       | What it allows                                                                                                                                            |
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mcp:read`  | Read your workspaces and their contents                                                                                                                   |
| `mcp:write` | Create and edit content in your workspaces                                                                                                                |
| `mcp:run`   | Start checklist runs, automation runs and interviews, which consume the workspace run allowance, and re-run Stunt Double Index scores for domains you own |

## Example prompts [#example-prompts]

Four prompts that exercise the core of the server once it is connected:

1. **Verify a flow:** "Create a checklist that signs up for a new account on https://demo-checkout-stunt-double.vercel.app, adds an item to the basket and reaches payment, then run it and tell me which checks failed."
2. **Run a user interview:** "Set up an interview with three personas (a first-time shopper, a returning customer and a screen reader user) about our pricing page, launch it, and summarise the report."
3. **Check agent readiness:** "How well can AI agents use stripe.com according to the Stunt Double Index, and which categories are dragging its score down?"
4. **Triage feedback:** "Summarise the open feedback on my main project, group it into themes, and mark anything already fixed as resolved."

## Privacy Policy [#privacy-policy]

The server is hosted by Stunt Double and acts as the signed-in user. It reads and writes only the workspaces that user belongs to, within the OAuth scopes they grant (`mcp:read`, `mcp:write`, `mcp:run`). It does not read your conversation with the AI client beyond the arguments passed to each tool call, and it does not access the client's memory, chat history or files.

Data created through the server (projects, actors, checklists, runs, interviews, feedback) is stored in your Stunt Double workspace and handled under the [Stunt Double Privacy Policy](https://www.stuntdouble.io/privacy), which covers collection, use, storage, sub-processors, retention and your rights. Revoke a connection at any time by disconnecting it in your AI client.

## Support [#support]

- Email: [support@stuntdouble.io](mailto:support@stuntdouble.io)
- Help centre and docs: [stuntdouble.io/support](https://www.stuntdouble.io/support)
- MCP reference: [docs.stuntdouble.io/api/mcp](https://docs.stuntdouble.io/api/mcp)
- Security reports: see [SECURITY.md](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/SECURITY.md)

## Transport [#transport]

This server uses [Streamable HTTP](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http) transport. The endpoint is:

```
https://app.stuntdouble.io/api/mcp
```

## MCP Registry [#mcp-registry]

The server is listed in the official [MCP Registry](https://registry.modelcontextprotocol.io) as `io.stuntdouble/mcp-server`, so registry-backed clients and directories can find it by name:

```bash
curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=io.stuntdouble/mcp-server"
```

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/README.md)


# Cursor rules (/mcp/rules)

The Cursor plugin installs these rules. To add one by hand, save it in your project's `.cursor/rules` folder.

## review-checklist [#review-checklist]

Checklist for validating UX changes with Stunt Double before shipping.

```md title="review-checklist.mdc"
---
description: Checklist for validating UX changes with Stunt Double before shipping
alwaysApply: false
globs:
  - "**/*"
---

review-checklist:

Before merging:
- Run relevant Stunt Double checklists against staging before merging UX changes.
- Verify that existing workflows still pass after modifying user-facing flows.
- Check Stunt Double feedback (`list_feedback`) for regressions related to the changed area.

Coverage:
- Ensure new user journeys have corresponding workflows or checklist items for ongoing validation.
- If a new user-facing feature is added, check that at least one actor persona covers the target user type.
- If no actor exists for the affected user segment, recommend creating one via `create_actor`.

After merging:
- Re-run affected workflows against the deployed environment to confirm the fix.
- Update feedback status to "resolved" for any issues addressed by the change.
- Review workflow run history (`get_workflow`, which returns `recent_runs`) to confirm no regressions.
```

## stuntdouble-basics [#stuntdouble-basics]

Core guidelines for working with the Stunt Double MCP server. Cursor applies it to every request.

```md title="stuntdouble-basics.mdc"
---
description: Core guidelines for working with the Stunt Double MCP server
alwaysApply: true
---

stuntdouble-basics:

- Stunt Double deploys AI agents with realistic user personas to validate user journeys at scale.
- Authentication is handled automatically via OAuth 2.1 with PKCE. No API keys or tokens are needed.
- Always specify a workspace when creating or listing resources. Use `list_workspaces` to find available workspaces.

Finding things:
- `search` is the fastest way to locate anything in a workspace: projects, actors, checklists, interviews, automations, issues, goals, feedback, actor knowledge, insights, design reviews, project resources and conversations, all in one ranked call.
- Prefer it over listing an entity type and filtering the list yourself. Narrow with `types` and `project_id` when you already know the shape of what you want.
- Search before you create. It is how you find the actor, checklist, or workflow that already covers the job instead of adding a near-duplicate.
- Search only ever returns entities from workspaces you are a member of; the boundary is enforced server-side, not by the filters you pass.

Actors:
- Actors represent AI user personas. Create actors with clear names (format: "FirstName - Role"), descriptions, and system prompts.
- Give actors knowledge via `add_actor_knowledge` so they understand your product context.
- Conversations are read-only over MCP (`list_conversations`, `get_conversation`); an actor chat is started from the dashboard. To probe actors for research, design reviews or concept testing, run an interview: it puts every persona through the same guide and synthesises the answers, which one-off chats cannot do.
- Create diverse actors covering different segments: new users, power users, enterprise, accessibility, mobile.

Workflows and checklists:
- Use workflows for multi-step user journey validation (signup, checkout, onboarding flows).
- Use checklists for point-in-time quality checks (accessibility, performance, content).
- Workflow and checklist runs are asynchronous. After triggering a run, poll `get_workflow_run` or `get_checklist_run` for results.
- Use `create_workflow` with trigger_type "schedule" or "webhook" for automated continuous validation, then `add_workflow_step` once per step, in the order they should run: a workflow with no steps does nothing when it fires.
- A workflow's steps run by following the connections between them, which the step tools maintain. `get_workflow` returns `execution_order` and `unreachable_step_ids`: check them before activating, because a step nothing reaches is a step that never runs.

Feedback:
- Review feedback items surfaced by Stunt Double and update their status as issues are triaged or resolved.
- Status lifecycle: new → reviewed → resolved (or dismissed for false positives).
- Cross-reference feedback with actors and workflows to identify patterns and coverage gaps.

Interviews:
- Interviews are structured user research rounds: a discussion guide (sections of questions and tasks) is run against a target URL by AI participants (existing actors or ad-hoc personas).
- Build the guide with `add_interview_section` and `add_interview_item` before launching. Each section should focus on one topic and contain ordered `question` or `task` items.
- Add participants with `add_interview_participant`: pass `actor_id` to reuse an existing actor, or `persona_spec` (`name`, `bio`, `traits`) for an ad-hoc persona.
- `launch_interview` is async: it returns a trigger run ID and flips the interview to `running`. Participants update independently; use `get_interview` and `get_interview_participant` to follow progress.
- Once participants complete, the synthesis task produces a report (summary, themes, recommendations, per-question rollup). Fetch it with `get_interview_report`, or call `regenerate_interview_report` after editing transcripts or re-running participants.

Guidelines (the standards a workspace holds itself to):
- A guideline is a standing rule (design system, tone of voice, brand, content, accessibility, compliance, security, performance, shared knowledge). It is owned by the **workspace** and attached to the projects it applies to, so one rule holds for several projects without being retyped or edited in several places.
- Whatever is in force for a project is rendered into every checklist run, design review, interview and triage for it. Read `list_project_guidelines` before writing checks, interview questions or design feedback, and assert the rules rather than restating or contradicting them.
- Two switches decide whether a rule is in force: the library's and the project attachment's. `list_project_guidelines` folds them into one `enabled`.
- Write a rule once with `add_project_guideline` (library plus this project) or `add_workspace_guideline` (library only), then hold other projects to the same rule with `set_project_guideline` rather than adding a near-duplicate. Check `list_workspace_guidelines` first.
- `update_workspace_guideline` with `apply_to_design_reviews` carries a rule to every design review in the workspace, including the Slack and Linear reviews that have no project to attach it through.
- Guidelines are project-wide standards; `add_actor_knowledge` is for what one actor needs to remember.

Standards and guardrails:
- The same checklist and workflow machinery enforces standards, not just flows: brand and tone of voice, design-system adherence, legal and compliance requirements, and cross-surface continuity (pricing, terminology, promises).
- The pattern is always the same: codify the standard as a guideline, assert it with observable checklist checks (phrased as things a user could verify on the rendered page), then re-run on a schedule or on deploy and design events with `create_workflow`.
- Schedules store a timezone alongside the cron. Pass `{ cron, timezone }` in `trigger_config` using the timezone from `get_me`, or the run lands at that hour in UTC.
- Anything with a reachable URL can be tested: production, staging, Vercel/Netlify previews, Figma Make published prototypes, Claude design artifacts, v0 links.

Stunt Double Index (how AI agents experience websites):
- When someone asks how well AI agents can find, understand or act on a site (agent readiness, AI visibility, whether an agent can check out or get support there), read its Index report with `get_index_report` rather than guessing. Pass a `domain`, or a `project_id` to use the domain linked to that project.
- `list_index_sessions` explains a score: each session is one AI provider attempting one benchmark task, with evidence, a summary and the frictions it hit. `search_index_domains` finds a site or compares it against the leaderboard and its sector.
- Index data is public, so any tracked site can be read. `request_index_rerun` is different: it spends about 24 agent sessions, and only the domain's owner can call it. Use it after the site has shipped changes, not to refresh a report that is already current.

Workspace and project scope:
- `get_workspace` reports the admin-set security controls under `settings` (public sharing, feedback widget, self-hosted workers, network policy). They are ceilings: a feature switched off there cannot be switched back on for one project, so check before promising a shared report link or a widget install.
- A project is archived, never deleted. An archived project (and every checklist, interview and feedback item under it) reads as missing from these tools; restoring one is a dashboard action.
- `list_project_mcp_servers` shows which MCP servers a project's runs can reach. A tool that is not listed is not available to the run. Registering and attaching servers are workspace-admin actions in the dashboard.

Prompts (slash-command recipes):
- The server ships self-contained prompt recipes; most clients surface them as slash commands. Reach for them instead of assembling tool calls by hand.
- `validate_design`, `verify_change`, `run_user_research`, `triage_feedback`, `setup_guardrails`, `check_brand`, `check_design_system`, `check_compliance`, `check_continuity`, and `stuntdouble_guide` (orientation + full tool catalogue).
```

---

Edit on GitHub: [review-checklist.mdc](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/rules/review-checklist.mdc), [stuntdouble-basics.mdc](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/rules/stuntdouble-basics.mdc)


# Check agent readiness (/mcp/skills/check-agent-readiness)

## When to use [#when-to-use]

- Someone asks how agent-ready, AI-visible or "agent-friendly" a site is, or whether an AI agent could check out, get support or find a product there
- A team wants to know why agents struggle on their site, and what to fix first
- A site has shipped changes meant to help agents (structured data, llms.txt, accessible checkout, fewer bot walls) and needs re-scoring
- You want to benchmark a site against competitors or its sector

## Instructions [#instructions]

1. **Find the site:**

   - `search_index_domains(query)` with part of the domain or name. If it is not tracked, say so: anyone can add it at index.stuntdouble.io.
   - If the user works in a Stunt Double project linked to an Index domain, pass `project_id` to the tools below instead of a domain.

2. **Read the report:**

   - `get_index_report(domain)` returns the overall score out of 100, its band and rank, per-category scores (brand awareness, discovery, information retrieval, market ranking, accuracy, checkout, delegated access, support), per-provider scores, the frictions agents hit and the failing probe checks.
   - A null score comes with `unscored_reason` and `unscored_note` (checks blocked or unanswered, or the site opted out). Report that reason instead of a number.

3. **Explain the weak spots from evidence:**

   - Pick the lowest categories and providers, then `list_index_sessions(domain, category=…)` or `provider=…` to read the sessions behind them: what the agent tried, its rubric evidence, a summary and its frictions.
   - Quote what agents actually ran into. Do not guess at causes the sessions do not show.

4. **Compare (optional):**

   - `search_index_domains(sector=…)` for the sector leaderboard, or query named competitors, then `get_index_report` on the ones worth contrasting.

5. **Recommend fixes:**

   - Rank by impact: failing probe checks with high weight first, then frictions that recur across providers, then single-provider issues.
   - Tie every recommendation to a failing check or a session friction.

6. **Re-score after fixes ship:**
   - `request_index_rerun(domain)` starts fresh probes and about 24 agent sessions. Only the domain's owner can call it.
   - Poll `list_index_sessions(domain, run_id)` about every 60 seconds until no session is running, then `get_index_report(domain)` and report the change per category.

## Example flow [#example-flow]

```
search_index_domains(query="acme")
get_index_report(domain="acme.com")                    # 54/100, checkout 21, support 38
list_index_sessions(domain="acme.com", category="checkout")
search_index_domains(sector="ecommerce", limit=10)     # where acme sits among peers

# after the fixes ship
request_index_rerun(domain="acme.com")                 # returns run_id
list_index_sessions(domain="acme.com", run_id="…")     # poll until finished
get_index_report(domain="acme.com")                    # compare with the earlier score
```

## Example output [#example-output]

```
acme.com: 54/100 (rank 212, ecommerce)

Weakest: Checkout 21, Support 38
- Checkout: 5 of 6 providers stalled at the cookie wall before the cart (sessions: claude, openai, gemini…)
- Support: no help content reachable without JavaScript; failing check "support page server-rendered" (weight 3)

Fix first
1. Let the cart load behind the consent banner (affects every provider)
2. Server-render /help and link it from the footer
3. Add Product structured data on PDPs (failing check, weight 2)
```

## Tips [#tips]

- **Index data is public.** The read tools work on any tracked site, so competitor comparisons need no access.
- **Re-runs are expensive.** Only re-run after a change has shipped, never to refresh a report that is already current.
- **Pair with checklists.** Once a fix is chosen, `verify-change` can confirm it on a preview before the re-run, and `setup-guardrails` keeps it from regressing.

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/skills/check-agent-readiness/SKILL.md)


# Check brand (/mcp/skills/check-brand)

## When to use [#when-to-use]

- You have brand and tone-of-voice guidelines and want to know where the live product breaks them
- Copy has drifted as pages shipped independently and you want a consistent voice again
- You want brand adherence enforced continuously, not audited once

## Instructions [#instructions]

1. **Resolve context:**
   - `list_workspaces()` → pick the workspace
   - `list_projects(workspace_id)` → match the URL to a project, or let `create_checklist` find/create it

2. **Set up a brand-guardian actor and record the rules:**
   - `list_actors(workspace_id)`, else `create_actor(...)` described as a meticulous brand and copy reviewer
   - `list_workspace_guidelines(workspace_id)`: the standard may already be written down; attach it with `set_project_guideline(project_id, guideline_id)` rather than typing a second copy
   - Otherwise `add_project_guideline(project_id, category: "tone_of_voice" | "brand", content, title)`, one rule per guideline so a finding can name the rule it breaks. Guidelines are workspace-owned and reach every checklist run, design review, interview and triage for the project
   - Use `add_actor_knowledge(actor_id, ...)` for what only this reviewer needs to remember, not for the standard itself
   - If no guidelines were supplied, ask for them, or draft a short rule set from the strongest existing pages and confirm it before auditing

3. **Create a checklist per surface in scope:**
   - `create_checklist(...)` for each page or flow (default scope: homepage, one core product flow, one high-traffic marketing page)
   - Phrase each check as an observable assertion a reader could verify on the rendered page:
     - "Headlines use sentence case"
     - "Copy addresses the reader as you, never the user"
     - "Error messages state what happened and offer a next step"
     - "No unexplained jargon on pricing"
   - 4–8 checks per checklist

4. **Run and collect evidence:**
   - `run_checklist(checklist_id)` → poll `get_checklist_run(run_id)` until terminal; read the evidence per check

5. **Report deviations grouped by page:**
   - Each with the broken rule, the offending copy or element as evidence, and a suggested rewrite in the correct voice

6. **Make it standing:**
   - Offer `create_workflow` on a schedule or deploy trigger so the brand stays on-voice as pages change (see `setup-guardrails`)

## Example output [#example-output]

```
Brand & voice audit: acme.com

Pricing page:
  [FAIL] "The User can upgrade at any time": addresses reader in third person.
         Rewrite: "You can upgrade any time."
  [FAIL] Headline "GET STARTED NOW!!!": all caps + exclamation, breaks sentence-case + no-exclamation rules.
         Rewrite: "Get started"

Onboarding:
  [PASS] Sentence-case headlines throughout
  [FAIL] Error "Invalid input": states no cause and no next step.
         Rewrite: "That email is already in use. Try signing in instead."

3 deviations across 2 pages. Offer: schedule this checklist weekly to hold the voice.
```

## Tips [#tips]

- **Store the rules on the actor, not just in the checklist.** Actor knowledge makes every future run enforce the same standard automatically.
- **Checks must be reader-observable.** Actors see the rendered page, so phrase rules by what a customer would read, not by CSS or component names.
- **One checklist per surface** keeps failures attributable to a page.

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/skills/check-brand/SKILL.md)


# Check compliance (/mcp/skills/check-compliance)

## When to use [#when-to-use]

- You need evidence of whether a product meets stated legal or regulatory requirements
- A release or content change may have affected consent, disclosures, or required flows
- You want compliance regressions caught continuously, not discovered in an audit

## Important framing [#important-framing]

This produces **evidence for the team and their counsel to review**. It is not legal advice. Present findings as observations against the stated requirements, never as legal conclusions. Do not guess which jurisdictions or regimes apply: ask.

## Instructions [#instructions]

1. **Resolve context:**
   - `list_workspaces()` → pick the workspace
   - `list_projects(workspace_id)` → match the URL to a project, or let `create_checklist` find/create it

2. **Confirm the requirements in scope:**
   - If none were supplied, ask which regimes apply (e.g. GDPR cookie consent, AU Consumer Law pricing, financial-services disclaimers) before running

3. **Set up a compliance-reviewer actor and record the requirements:**
   - `list_actors(workspace_id)`, else `create_actor(...)`
   - `add_project_guideline(project_id, category: "compliance", content, title)`, one obligation per guideline so a finding can name the requirement it breaks. Check `list_workspace_guidelines(workspace_id)` first: an obligation that applies to several products is one rule attached to each with `set_project_guideline`

4. **Create a checklist that exercises behaviour, not just presence:**
   - "A cookie banner appears before any non-essential tracking and the reject option works"
   - "Privacy policy and terms are reachable from every page footer"
   - "Prices include mandatory taxes or state clearly that they do not"
   - "Required disclaimers appear adjacent to the claims they qualify"
   - "The signup flow states how personal data will be used"
   - If email flows are in scope, actors have their own inboxes: include "Marketing emails contain a working unsubscribe link"

5. **Run and keep the evidence:**
   - `run_checklist(checklist_id)` → poll `get_checklist_run(run_id)` until terminal
   - On anything ambiguous, quote exactly what the page showed

6. **Report per requirement: met, not met, or unclear:**
   - Each with evidence, and a list of items for counsel to review

7. **Offer standing coverage:**
   - `create_workflow`: a schedule catches regressions from content edits; a deploy trigger catches them from releases (see `setup-guardrails`)

## Example output [#example-output]

```
Compliance evidence: acme.com (scope: GDPR cookie consent, price display)
Note: observations for counsel to review, not legal advice.

[NOT MET] Analytics (GA) fires on load before the cookie banner is shown. Reject button then does not stop it (evidence: network trace, screenshot).
[MET]     Privacy policy and terms linked in the footer on every audited page.
[UNCLEAR] Prices shown as "from $19" with no tax statement: quote: "from $19 / month". Flag for counsel: is a tax-inclusive/exclusive note required here?
[MET]     Signup states data use with a link to the privacy policy.

For counsel: items 1 and 3. Offer: schedule this weekly to catch consent regressions from content edits.
```

## Tips [#tips]

- **Never state a legal conclusion.** Report met / not met / unclear against the stated requirements and hand ambiguous items to counsel with a verbatim quote.
- **Test behaviour, not just presence.** "The reject button actually stops tracking" is stronger evidence than "a cookie banner exists".
- **Use the actor inbox** for unsubscribe and email-consent checks when email flows are in scope.

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/skills/check-compliance/SKILL.md)


# Check continuity (/mcp/skills/check-continuity)

## When to use [#when-to-use]

- Marketing, product, docs, and emails may have drifted out of sync (pricing, plan names, feature terminology, promises)
- A pricing or naming change shipped on one surface but maybe not the others
- You want cross-surface drift caught as content ships, not by customers

## Instructions [#instructions]

1. **Resolve context:**
   - `list_workspaces()` → pick the workspace
   - `list_projects(workspace_id)` → match the surfaces to a project, or let `create_checklist` find/create it

2. **Ensure a reviewer actor exists:**
   - `list_actors(workspace_id)`, else `create_actor(...)`

3. **Create a checklist per surface with mirrored extraction checks:**
   - Record the same facts on each surface so they can be compared:
     - "Record the price, billing period, and plan names shown"
     - "Record what the main feature is called on this page"
   - Plus cross-surface assertions on the primary surface:
     - "The price at checkout matches the price on the pricing page"
     - "The feature is named consistently; it is never called by an old name"

4. **For promise-versus-product continuity, use an interview:**
   - `create_interview(...)` with a task that walks from the marketing claim to using the feature ("The homepage says setup takes two minutes; sign up and see if that holds"), a persona participant, and `launch_interview`
   - Poll and read `get_interview_report`

5. **Run everything and compare recorded values across surfaces:**
   - `run_checklist` per surface → poll `get_checklist_run` until terminal

6. **Report a discrepancy table:**
   - Each item, what every surface shows, which surfaces disagree, with evidence
   - Rank by customer impact (billing mismatches first)

7. **Offer standing coverage:**
   - A scheduled `create_workflow` so drift is caught as content ships (see `setup-guardrails`)

## Example output [#example-output]

```
Continuity check: marketing site vs app vs docs

| Item              | Marketing        | App (checkout)   | Docs             | Disagree?     |
| ----------------- | ---------------- | ---------------- | ---------------- | ------------- |
| Pro price / month | $29              | $39              | $29              | App ≠ others  |
| Plan names        | Starter/Pro/Team | Starter/Pro/Team | Starter/Pro/Biz  | Docs: "Biz"   |
| Core feature name | "Flows"          | "Flows"          | "Workflows"      | Docs: old name|
| Setup promise     | "2-minute setup" | ~6 min in interview transcript |    | Promise > reality |

Ranked: billing mismatch first (App charges $39 vs $29 advertised). Then docs plan-name + feature-name drift.
Offer: schedule this so drift is caught as content ships.
```

## Tips [#tips]

- **Mirror the extraction checks** across surfaces so the recorded values line up cleanly for comparison.
- **Billing mismatches rank first**: a price that disagrees between the pricing page and checkout is the highest-impact discrepancy.
- **Interviews beat checklists for promises.** A task that walks from the marketing claim to the real experience captures whether the promise holds.

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/skills/check-continuity/SKILL.md)


# Check design system (/mcp/skills/check-design-system)

## When to use [#when-to-use]

- You have a design system (tokens, component rules, a Figma library) and want to know where the live product drifts from it
- Screens shipped independently and visual consistency has eroded
- You want design-system adherence enforced when the library is published or files change

## Instructions [#instructions]

1. **Resolve context:**
   - `list_workspaces()` → pick the workspace
   - `list_projects(workspace_id)` → match the URL to a project, or let `create_checklist` find/create it

2. **Set up a design-guardian actor and record the rules:**
   - `list_actors(workspace_id)`, else `create_actor(...)` described as a design-system reviewer
   - `list_workspace_guidelines(workspace_id)` first, then either attach an existing rule with `set_project_guideline(project_id, guideline_id)` or write one with `add_project_guideline(project_id, category: "design_system", content, title)`. Token values and component dos and don'ts belong here: guidelines reach every run and design review for the project
   - `update_workspace_guideline(workspace_id, guideline_id, apply_to_design_reviews: true)` when the standard should also hold for design reviews raised from Slack or Linear, which carry no project
   - If no rules were supplied, ask for the key tokens and component rules, or derive a draft from the most polished screens and confirm it

3. **Create a checklist per surface:**
   - Actors see the rendered product the way a user does, so phrase checks visually, not in terms of code or class names:
     - "Body text uses the product font stack, no serif fallbacks anywhere"
     - "Primary CTAs use the brand primary colour and sentence case"
     - "Form fields show a visible focus state"
     - "Empty states use the illustration style, not stock icons"
     - "Spacing between sections is consistent across pages"

4. **Run and collect evidence:**
   - `run_checklist(checklist_id)` → poll `get_checklist_run(run_id)` until terminal; collect evidence for each deviation

5. **Report deviations by page:**
   - With screenshots or extracted evidence and the rule each one breaks

6. **Wire it into the design workflow:**
   - If the workspace has Figma connected, `create_workflow` with a `figma_event` trigger so the audit re-runs when the design library is published or files change
   - Otherwise offer a schedule or deploy trigger (see `setup-guardrails`)

## Example output [#example-output]

```
Design-system audit: app.acme.com

Dashboard:
  [FAIL] Card titles render in Georgia (serif): should use Inter. Font stack is missing on /dashboard.
  [PASS] Primary buttons use brand primary #4F46E5
  [FAIL] Section spacing 12px here vs 24px on Settings: inconsistent vertical rhythm

Settings:
  [PASS] Focus states visible on all inputs
  [FAIL] Empty "no integrations" state uses a stock plug icon, not the house illustration set

4 deviations. If Figma is connected, offer a figma_event workflow so this re-runs on library publish.
```

## Tips [#tips]

- **Phrase checks visually.** The actor cannot see class names or tokens: only the rendered result. "Body text is the product sans-serif" works; "uses `--font-body`" does not.
- **Store rules as actor knowledge** so every run enforces the same system.
- **A `figma_event` trigger closes the loop**: the audit re-runs the moment the library changes.

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/skills/check-design-system/SKILL.md)


# Create actor panel (/mcp/skills/create-actor-panel)

## When to use [#when-to-use]

- When setting up a new user persona for journey testing
- When you need an actor representing a specific user segment (enterprise admin, first-time user, accessibility user, etc.)
- When expanding test coverage to include underrepresented user types
- When a design review or feedback triage reveals a missing persona

## Instructions [#instructions]

1. **Identify the workspace:**
   - `list_workspaces()` → find the target workspace

2. **Check existing actors to avoid duplicates:**
   - `list_actors(workspace_id)` → review current personas

3. **Create the actor:**
   - `create_actor(workspace_id, name, description)` → create with a clear, human name and description
   - Name format: "FirstName - Role/Trait" (e.g., "Emma - First-time SaaS user")
   - Description should capture goals, frustrations, and technical comfort level

4. **Configure the actor's identity:**
   - `update_actor(actor_id, system_prompt, capabilities)` → set the system prompt that defines how this persona behaves, thinks, and reacts
   - System prompt should include: background, goals, pain points, technical skill level, and communication style

5. **Add knowledge entries:**
   - `add_actor_knowledge(actor_id, title, content)` → give the actor context about your product
   - Common knowledge: product docs, feature guides, pricing pages, FAQ content
   - Persona-specific knowledge: their company context, past experience, specific needs

6. **Verify the setup:**
   - `get_actor(actor_id)` → confirm system prompt and capabilities
   - `list_actor_knowledge(actor_id)` → confirm knowledge entries
   - Exercise one actor for real before relying on the panel: a small `create_checklist` plus `run_checklist` against the product shows whether the persona behaves as written

## Example actor configurations [#example-actor-configurations]

### First-time user [#first-time-user]

```
Name: "Emma - First-time SaaS user"
Description: "Non-technical user trying a project management tool for the first time.
Works at a small marketing agency. Comfortable with email and spreadsheets but
hasn't used specialized SaaS tools before."
System prompt: "You are Emma, a 28-year-old marketing coordinator at a 10-person agency.
You've been asked by your boss to find a project management tool. You're not very
technical: you mainly use Google Sheets and email. You get frustrated by jargon
and prefer clear, simple interfaces. You'll abandon a flow if it takes more than
a few minutes without showing value."
```

### Enterprise admin [#enterprise-admin]

```
Name: "Carlos - Enterprise IT admin"
Description: "Technical user responsible for evaluating and deploying tools across a
500-person organization. Cares about SSO, permissions, audit logs, and compliance."
System prompt: "You are Carlos, a 42-year-old IT director at a mid-size financial
services company. You evaluate SaaS tools for security, compliance, and scalability.
You need SSO/SAML, role-based access control, and audit logs. You'll reject any
tool that can't explain its data handling. You're thorough and ask pointed questions."
```

### Accessibility user [#accessibility-user]

```
Name: "Sam - Screen reader user"
Description: "Visually impaired user who navigates entirely with a screen reader and
keyboard. Tests for WCAG compliance and accessible UX patterns."
System prompt: "You are Sam, a 35-year-old software developer who is blind. You use
NVDA screen reader with Firefox. You navigate by headings, landmarks, and tab order.
You expect proper ARIA labels, logical focus management, and meaningful alt text.
You get frustrated by unlabeled buttons, focus traps, and content that only makes
sense visually."
```

## Tips [#tips]

- Create at least 3-4 diverse actors per product to cover different user segments
- Give actors realistic constraints (slow internet, mobile-only, non-English speaker) for edge case coverage
- Update actor knowledge when your product changes so personas stay current
- Use `remove_actor_knowledge` to clean up outdated knowledge entries

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/skills/create-actor-panel/SKILL.md)


# Design review (/mcp/skills/design-review)

## When to use [#when-to-use]

- When evaluating a new design, flow, or prototype before implementation
- When comparing two or more design options
- Before engineering handoff to validate the proposed UX
- When a PM or designer wants diverse user perspectives on a concept

## Instructions [#instructions]

1. **Set up the workspace and select actors:**
   - `list_workspaces()` → find the workspace
   - `list_actors(workspace_id)` → review available personas
   - Pick 3-5 actors representing different user segments (new user, power user, enterprise, accessibility, mobile, etc.)

2. **Brief the actors (if needed):**
   - `add_actor_knowledge(actor_id, title, content)` → share design context, screenshots, feature descriptions, or prototype links with each actor
   - This ensures actors have the right context to give informed feedback

3. **Run the review as an interview:**
   - `create_interview(workspace_id, project_id, name: "Design review: <feature>", target_url, research_brief)` → describe the proposed design clearly in the brief: what it does, how the user would interact with it, and what the key decision points are
   - `add_interview_section(interview_id, title)` then `add_interview_item(section_id, type, prompt_text)` → mix `task` items ("Find the annual price and start checkout") with specific questions ("Would you understand what this button does?", "What would you expect to happen next?", "Is anything confusing or missing?")
   - `add_interview_participant(interview_id, actor_id)` → one per selected actor, or `persona_spec` for an ad-hoc persona
   - `launch_interview(interview_id)` → async, poll `get_interview(interview_id)` until terminal

4. **Collect and read responses:**
   - `get_interview_report(interview_id)` → summary, themes, recommendations, per-question rollup
   - `get_interview_participant(participant_id)` → verbatim transcript evidence for a finding
   - Conversations are read-only over MCP: `list_conversations` / `get_conversation` read chats started in the dashboard

5. **Cross-reference with existing data:**
   - `list_feedback(project_id)` → check if the design addresses known issues
   - `list_workflows(workspace_id)` → verify that existing workflows cover the new flow

6. **Synthesize findings:**
   - Group feedback into themes
   - Note where actors agree (strong signal) vs disagree (segment-specific)
   - Identify friction points, confusion, and missing affordances
   - Recommend specific design changes with supporting quotes from actors

## Example output [#example-output]

```
Design Review: New Dashboard Layout

Actors consulted:
  - Emma (first-time user)
  - Carlos (enterprise admin)
  - Priya (developer/API user)
  - Sam (screen reader user)

Findings:

  Strong consensus:
  - All actors found the left sidebar navigation intuitive
  - All actors liked the quick-action buttons at the top

  Friction points:
  - Emma: "I don't know what 'Workflows' means. Can it say 'Automations' or have a subtitle?"
  - Sam: "The dashboard cards don't have heading levels. I can't navigate by headings."
  - Carlos: "I need to see team activity, not just my own. Add a team toggle."

  Split opinions:
  - Priya preferred a data-dense layout; Emma preferred the card-based layout
  - Recommendation: offer a "compact view" toggle for power users

  Existing feedback addressed:
  - FB-145 ("can't find settings"): new sidebar placement resolves this
  - FB-162 ("too many clicks to create project"): quick-action button addresses this

Recommendations:
  1. Add descriptive subtitles to sidebar items for new users
  2. Add proper heading structure (h2/h3) to dashboard cards for screen readers
  3. Add team/personal toggle for enterprise users
  4. Consider compact view option for power users
```

## Tips [#tips]

- Ask participants both open-ended ("What stands out to you?") and specific ("Would you click this button?") questions
- Include at least one accessibility-focused actor in every design review
- Use transcript quotes as evidence in design documents and stakeholder presentations
- Keep the interview. Re-running it after the design changes gives a like-for-like comparison, which a one-off review cannot

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/skills/design-review/SKILL.md)


# Skills (/mcp/skills)

The skills in `skills/` install into Claude Code, Cursor, Codex, OpenCode and other agents that read `SKILL.md` files, using the [skills CLI](https://skills.sh). They drive the MCP server above, so connect that too.

```bash
npx skills add stunt-double/stuntdouble-mcp                          # pick from the list
npx skills add stunt-double/stuntdouble-mcp --skill verify-change     # just one
```

- [`check-agent-readiness`](/mcp/skills/check-agent-readiness): Check how well AI agents and assistants can find, understand and act on a website using the Stunt Double Index, explain the score from real agent sessions, compare against peers, and re-score after fixes.
- [`check-brand`](/mcp/skills/check-brand): Audit a product or site against brand and tone-of-voice guidelines: a Stunt Double actor reads every in-scope page like a customer and flags copy, voice, and presentation that break the rules, with evidence.
- [`check-compliance`](/mcp/skills/check-compliance): Check a product against legal and compliance requirements on Stunt Double (cookie consent, privacy and terms access, required disclosures, claim substantiation, unsubscribe flows) and collect evidence for counsel to review.
- [`check-continuity`](/mcp/skills/check-continuity): Check continuity across surfaces on Stunt Double: pricing, terminology, feature names, and promises should match between marketing site, product, docs, and emails. Actors walk each surface and the mismatches surface with evidence.
- [`check-design-system`](/mcp/skills/check-design-system): Audit a live product against its design system on Stunt Double: typography, colour usage, spacing, component and iconography rules, checked on rendered pages with evidence, and re-checked automatically on library publishes.
- [`create-actor-panel`](/mcp/skills/create-actor-panel): Create and configure a Stunt Double actor (AI persona) with knowledge entries for realistic user simulation.
- [`design-review`](/mcp/skills/design-review): Run a design review session by gathering feedback from multiple Stunt Double actors on a proposed design or flow.
- [`maintain-automations`](/mcp/skills/maintain-automations): Change an existing Stunt Double automation or checklist without losing run history: retime triggers, edit and rewire workflow steps, build condition branches, update checks in place, and pause or retire what is no longer needed.
- [`run-qa-suite`](/mcp/skills/run-qa-suite): Run the full Stunt Double QA suite (all workflows and checklists) and produce a release-readiness report.
- [`run-user-interview`](/mcp/skills/run-user-interview): Plan, configure, and launch a structured user interview with AI participants on Stunt Double, then read back the synthesised report.
- [`run-ux-validation`](/mcp/skills/run-ux-validation): Validate a user journey by running Stunt Double workflows or checklists and reporting the results.
- [`setup-guardrails`](/mcp/skills/setup-guardrails): Stand up continuous guardrails on Stunt Double: checklists for critical user flows plus a workflow that re-runs them on a schedule or on deploy/PR events, so regressions surface before users do.
- [`triage-feedback`](/mcp/skills/triage-feedback): Review, categorize, and manage Stunt Double feedback submissions across projects.
- [`verify-change`](/mcp/skills/verify-change): Verify a shipped or previewed code change by running a Stunt Double actor through the affected user flows, and optionally report results on the pull request.

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/README.md)


# Maintain automations (/mcp/skills/maintain-automations)

## When to use [#when-to-use]

- A workflow needs a different schedule, trigger, notification or set of checklists
- A checklist's checks drifted from the product (renamed buttons, new flow steps) and keep failing for the wrong reason
- You want a branch: notify only on failure, or run a follow-up only when a condition holds
- An automation or checklist is obsolete

## Instructions [#instructions]

1. **Find what exists:**

   - `search(workspace_id, query)` or `list_workflows(workspace_id)` / `list_checklists(workspace_id)`
   - `get_workflow(workflow_id)` shows the steps, their connections, `execution_order` (the order a run actually takes) and `unreachable_step_ids`. Read it before changing anything.

2. **Change the trigger or details:**

   - `update_workflow(workflow_id, name?, description?, trigger_config?)`. For a schedule, send `{ cron, timezone }` with the timezone from `get_me`, so the hour is the user's hour. The schedule syncs on save.

3. **Edit steps:**

   - `update_workflow_step(step_id, config)` replaces the config in full: send every key the step needs, not just the changed one.
   - `add_workflow_step(...)` appends and connects a new step; `remove_workflow_step(step_id)` removes one and repoints whatever led into it at whatever it led to.
   - `reorder_workflow_steps(workflow_id, step_ids)` sets one linear order. Pass every step id: one left out is disconnected. It flattens branches, so rebuild any fork afterwards.

4. **Build or repair a branch:**

   - `connect_workflow_steps(workflow_id, source_step_id, target_step_id, branch)`. On a condition step, `branch: 'true'` is the path when it holds and `'false'` when it does not; every other step uses `null`.
   - A step appended after a condition lands on its `true` path. The `false` handle is unconnected by default, so a condition that does not hold ends the run. Connect it when that outcome should do something too.
   - `source_step_id: null` means the trigger. `target_step_id: null` disconnects that handle.

5. **Update a checklist in place:**

   - `get_checklist(checklist_id)` first, then `update_checklist(checklist_id, checks=[…])` sending each existing check with its `id`. A check that keeps its id keeps its run history; one dropped from the list is deleted with its past results.
   - Also adjustable: `instructions`, `url`, `actor_id`, `pass_threshold`, `run_mode` (`code` replays a recorded run).

6. **Verify before leaving it:**

   - `get_workflow(workflow_id)` again: every step in `execution_order`, nothing in `unreachable_step_ids`.
   - `run_workflow(workflow_id)` or `run_checklist(checklist_id)` once and confirm the result is what the change intended.

7. **Pause or retire:**
   - Prefer `toggle_workflow(workflow_id)` to pause. `delete_workflow` and `delete_checklist` discard all run history, so use them only when the user asks for deletion.

## Example flow [#example-flow]

```
get_workflow(workflow_id)                          # read execution_order first

# Move the daily run to 7am the user's time
update_workflow(workflow_id, trigger_config={ cron: "0 7 * * *", timezone: "Europe/London" })

# Notify only when something fails
add_workflow_step(workflow_id, step_type="condition",
  config={ field: "previous_step.failed", operator: "gt", value: 0 })     # appended after the checklists
add_workflow_step(workflow_id, step_type="notification",
  config={ channel: "email", recipients: { type: "all_members" }, template: "failed" })   # lands on the true path
# Ends quietly when nothing failed; to report a pass too, connect the false handle:
# connect_workflow_steps(workflow_id, source_step_id=condition_id, target_step_id=pass_notice_id, branch="false")

# Reword a check without losing its history
get_checklist(checklist_id)
update_checklist(checklist_id, checks=[{ id: "…", … }, …])

get_workflow(workflow_id)                          # nothing unreachable
run_workflow(workflow_id)
```

## Tips [#tips]

- **Read, change, read again.** `get_workflow` before and after any edit is the cheapest way to catch a step no run will reach.
- **Keep check ids.** Rewording a check under its old id keeps it comparable with earlier runs; a new id starts its history over.
- **Pause, don't delete.** Deleting is irreversible and loses the history that shows when something regressed.

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/skills/maintain-automations/SKILL.md)


# Run QA suite (/mcp/skills/run-qa-suite)

## When to use [#when-to-use]

- Before a release to validate all critical user journeys
- After a major refactor to check for regressions
- On a scheduled cadence (weekly, per-sprint) for continuous quality monitoring
- When onboarding a new team member who wants to see current product quality

## Instructions [#instructions]

1. **Discover the workspace and all validations:**
   - `list_workspaces()` → find the target workspace
   - `list_workflows(workspace_id)` → get all journey workflows
   - `list_checklists(workspace_id)` → get all quality checklists

2. **Trigger all runs:**
   - `run_workflow(workflow_id)` → trigger each workflow (collect all run IDs)
   - `run_checklist(checklist_id)` → trigger each checklist (collect all run IDs)
   - Trigger all runs in parallel for speed

3. **Poll all runs to completion:**
   - `get_workflow_run(run_id)` → poll each workflow run
   - `get_checklist_run(run_id)` → poll each checklist run
   - Wait until all runs have completed

4. **Compile the QA report:**
   - List every workflow and checklist with pass/fail
   - For failures: include step name, actor, and what went wrong
   - Compare against previous runs if available: `get_workflow(workflow_id)` returns `recent_runs`

5. **Check for new feedback generated:**
   - `list_feedback(project_id, status: "new")` → see if runs surfaced new issues
   - Include any new feedback in the report

6. **Produce a release-readiness verdict:**
   - **GO**: all workflows and checklists pass, no new blockers
   - **CONDITIONAL**: minor issues found but no blockers, document known issues
   - **NO-GO**: blocker-level failures, list what must be fixed

## Example report [#example-report]

```
QA Suite Report: v2.5.0 Release Candidate
Environment: staging.example.com
Date: 2026-03-28

WORKFLOWS (4 total):
  [PASS] Signup → First Project ............ 6/6 steps
  [PASS] Checkout Flow .................... 5/5 steps
  [FAIL] Team Onboarding .................. 4/7 steps (step 5: invite email not received)
  [PASS] Account Settings ................. 4/4 steps

CHECKLISTS (3 total):
  [PASS] Core Accessibility ............... 10/10 checks
  [PASS] Performance Budget ............... 5/5 checks
  [FAIL] Content Quality .................. 7/8 checks (placeholder text on billing page)

VERDICT: CONDITIONAL
  - 1 workflow failure (team invite email delivery): non-blocking, known infra issue
  - 1 checklist failure (placeholder text): cosmetic, can ship with known issue

NEW FEEDBACK: 2 items surfaced during this run
  - FB-401: Invite flow shows "undefined" in success message
  - FB-402: Billing page has "Lorem ipsum" in FAQ section

RECOMMENDATION: Safe to release with documented known issues.
Fix invite email and billing placeholder in next patch.
```

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/skills/run-qa-suite/SKILL.md)


# Run user interview (/mcp/skills/run-user-interview)

## When to use [#when-to-use]

- When you want qualitative research on a specific user journey or concept before shipping
- When you need a discussion guide run against a live URL by several realistic participants
- When a checklist or workflow surfaces something that needs deeper "why" exploration
- When you want a one-pass report (summary, themes, recommendations, per-question rollup) from a small panel

## Instructions [#instructions]

1. **Find the workspace and project:**
   - `list_workspaces()` → pick the workspace
   - `list_projects(workspace_id)` → pick the project the interview is about

2. **Create the interview:**
   - `create_interview(workspace_id, project_id, name, target_url, research_brief?)`
   - `name` (short, descriptive (e.g. "Onboarding clarity) week 1")
   - `target_url`: the live page participants should land on
   - `research_brief`: 1–3 paragraphs of context (what you're trying to learn, decisions it will inform)

3. **Build the discussion guide:**
   - For each topic, `add_interview_section(interview_id, title, intro_script?)`
   - For each section, add 2–5 items with `add_interview_item(section_id, type, prompt_text, expected_evidence?)`
     - `type: "question"`: open-ended prompt the participant answers in their own words
     - `type: "task"`: instruction the participant performs in the browser ("Sign up for a free account")
     - `expected_evidence`: optional note about what a successful answer looks like
   - Aim for 1–3 sections, 3–8 items total: interviews longer than ~15 items get expensive and noisy

4. **Recruit participants:**
   - For each participant, `add_interview_participant(interview_id, …)`:
     - Existing actor: pass `actor_id` (use `list_actors` to find one: useful when you've already shaped the persona via `create-actor-panel`)
     - Ad-hoc persona: pass `persona_spec` with `name`, `bio`, and 3–5 `traits`
   - 3–5 participants is the sweet spot. Vary segment, device, and locale for coverage.

5. **Launch the round:**
   - `launch_interview(interview_id)` → returns a trigger run ID; interview flips to `running`
   - This is async. Participants run in parallel and update independently.

6. **Watch progress:**
   - `get_interview(interview_id)` → shows status of every participant (`pending` / `running` / `completed` / `failed`)
   - For any participant you want to inspect, `get_interview_participant(participant_id)` → full transcript with interviewer turns, participant turns, and tool actions (clicks, navigations, screenshots)

7. **Read the report:**
   - Once all participants land, the synthesis task produces a report automatically
   - `get_interview_report(interview_id)` → `{ summary, themes, recommendations, per_question_rollup }`
   - If you change participants or fix a flaky run with `get_interview_participant`, call `regenerate_interview_report(interview_id)` to re-synthesise from the latest transcripts

## Example flow [#example-flow]

```
# 1. Set up
list_workspaces()
list_projects(workspace_id="…")

# 2. Create
create_interview(
  workspace_id="…",
  project_id="…",
  name="Pricing page comprehension",
  target_url="https://acme.com/pricing",
  research_brief="Validate the new tiered pricing page. Looking for confusion between Pro and Team tiers, and whether the value props land for first-time visitors."
)
# → returns { id: "interview-…" }

# 3. Guide
add_interview_section(interview_id, title="First impressions")
add_interview_item(section_id, type="task", prompt_text="Open the pricing page and tell me what you think this product does.")
add_interview_item(section_id, type="question", prompt_text="Which plan would you pick and why?")

add_interview_section(interview_id, title="Tier comparison")
add_interview_item(section_id, type="task", prompt_text="Find the difference between Pro and Team and describe it in your own words.")

# 4. Participants
add_interview_participant(interview_id, actor_id="emma-first-time-saas-user")
add_interview_participant(interview_id, persona_spec={
  name: "Priya Shah",
  bio: "Engineering manager at a 40-person startup evaluating tools for her team.",
  traits: ["budget-conscious", "values team features", "compares 3 tools before deciding"]
})

# 5. Launch
launch_interview(interview_id)

# 6. Read
get_interview(interview_id)          # progress
get_interview_participant(p_id)      # individual transcripts
get_interview_report(interview_id)   # synthesised themes + recommendations
```

## Tips [#tips]

- **Pair with `create-actor-panel`.** Create reusable actors once and attach them to many interviews: the persona stays consistent across rounds, and you can compare results over time.
- **Keep the guide short.** 3–5 questions per section, 2–3 sections. Long guides produce noisy reports.
- **Tasks beat questions for journey research.** A `task` puts the participant in the browser; the transcript captures what they actually did. Use `question` items for reactions and "why" after a task.
- **Mix segments.** One first-time user, one power user, one accessibility-focused participant exposes friction faster than five clones of the same persona.
- **Retry failed participants individually** (via the dashboard) before regenerating the report: the synthesis is only as good as the transcripts it summarises.

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/skills/run-user-interview/SKILL.md)


# Run UX validation (/mcp/skills/run-ux-validation)

## When to use [#when-to-use]

- Before merging changes that affect user-facing flows
- After deploying to a staging environment
- When investigating reported UX friction
- As a pre-release quality gate

## Instructions [#instructions]

1. **Find the workspace and available validations:**
   - `list_workspaces()` → identify the target workspace
   - `list_workflows(workspace_id)` → find journey-level workflow tests
   - `list_checklists(workspace_id)` → find point-in-time quality checks

2. **Choose what to run:**
   - Workflows for end-to-end journeys (signup, checkout, onboarding)
   - Checklists for quality gates (accessibility, performance, content)
   - Run both if doing a full pre-release validation

3. **Trigger runs:**
   - `run_workflow(workflow_id)` → returns a run ID (async)
   - `run_checklist(checklist_id)` → returns a run ID (async)
   - You can trigger multiple runs in parallel

4. **Poll for results:**
   - `get_workflow_run(run_id)` → check status and step-level results
   - `get_checklist_run(run_id)` → check status and per-check results
   - Poll every few seconds until status shows complete

5. **Report results:**
   - List each workflow/checklist with pass/fail status
   - For failures, include the specific step that failed and what happened
   - Note which actor (persona) encountered the issue

6. **Follow up on failures:**
   - `list_feedback(project_id)` → check for related feedback
   - `get_feedback(feedback_id)` → read full context on issues
   - `update_feedback_status(feedback_id, status: "reviewed")` → mark triaged items
   - Suggest code changes or flag for the team

## Example output [#example-output]

```
UX Validation Results: staging (2026-03-28)

Workflows:
  [PASS] Signup → First Project (6/6 steps)
  [FAIL] Checkout Flow (failed at step 4: payment form timeout)
  [PASS] Team Invite (3/3 steps)

Checklists:
  [PASS] Performance (5/5 checks)
  [FAIL] Accessibility (6/8 checks: contrast ratio, focus order failed)

Summary: 2 failures found. Payment form timeout is a blocker.
Recommend fixing before release.
```

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/skills/run-ux-validation/SKILL.md)


# Set up guardrails (/mcp/skills/setup-guardrails)

## When to use [#when-to-use]

- A flow is business-critical (signup, sign-in, checkout, password reset) and must not silently break
- You want regressions caught on every deploy or on a schedule, not by customers
- You already verified a change (see `verify-change`) and want to keep that coverage standing

## Instructions [#instructions]

1. **Resolve context:**
   - `list_workspaces()` → pick the workspace
   - `list_projects(workspace_id)` → match the product URL to a project, or let `create_checklist` find/create it

2. **Ensure an actor exists:**
   - `list_actors(workspace_id)`, else `create_actor(...)`

3. **One checklist per flow:**
   - `create_checklist(...)` per critical flow, each with focused instructions and 3–6 crisp pass/fail checks
   - Small, focused checklists give clearer failure signals than one giant checklist

4. **Record any standard the flows are held to:**
   - `list_project_guidelines(project_id)` to read what the project is already held to, then `add_project_guideline(...)` for anything missing
   - A guideline reaches every run, review and interview for the project, so the checks can assert it instead of restating it in each checklist

5. **Confirm a green baseline before automating:**
   - `run_checklist(checklist_id)` for each, poll `get_checklist_run(run_id)` until terminal
   - Fix flaky checks now: a noisy guardrail gets ignored

6. **Automate re-runs with a workflow:**
   - `create_workflow(...)` with a trigger:
     - `schedule` with a cron (daily is a good default), passing `{ cron, timezone }` in `trigger_config` using the timezone from `get_me` so the hour means the user's hour, or
     - event triggers (`vercel_event`, `github_event`) to run on deploys and pull requests when those connections are configured in the workspace
   - `add_workflow_step(workflow_id, step_type, config)`: one `run_checklist` step per checklist, plus a `notification` step so failures reach the team. Each call appends and connects, so call them in the order the steps should run
   - `get_workflow(workflow_id)` to check `execution_order` before activating: anything in `unreachable_step_ids` is a step no run will reach
   - `toggle_workflow(workflow_id)` to activate

7. **Confirm coverage to the user:**
   - What is covered, when it runs, where failures are reported, and how to extend it (add a checklist, then add a step)
   - Manage everything later at app.stuntdouble.io

## Example flow [#example-flow]

```
list_workspaces()
list_projects(workspace_id="…")

# One checklist per critical flow
create_checklist(url="https://acme.com", name="Signup", instructions="…", checks=[…])   # 3–6 checks
create_checklist(url="https://acme.com", name="Checkout", instructions="…", checks=[…])

# Baseline
run_checklist(signup_id); get_checklist_run(run_id)   # confirm green
run_checklist(checkout_id); get_checklist_run(run_id)

# Automate
create_workflow(name="Critical flows", trigger_type="schedule", cron="0 8 * * *")
add_workflow_step(workflow_id, step_type="run_checklist", config={ checklist_id: signup_id })
add_workflow_step(workflow_id, step_type="run_checklist", config={ checklist_id: checkout_id })
add_workflow_step(workflow_id, step_type="notification", config={ channel: "email", recipients: { type: "all_members" }, template: "failed" })
get_workflow(workflow_id)      # execution_order covers every step, nothing unreachable
toggle_workflow(workflow_id)   # activate
```

## Tips [#tips]

- **Baseline first.** Never automate a red or flaky checklist: you will just train the team to ignore alerts.
- **Prefer deploy/PR triggers** when the connection exists; a schedule is the fallback that also catches content-only regressions.
- **The standards skills build on this.** `check-brand`, `check-design-system`, `check-compliance`, and `check-continuity` all end by wiring their checklists into a workflow the same way.

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/skills/setup-guardrails/SKILL.md)


# Triage feedback (/mcp/skills/triage-feedback)

## When to use [#when-to-use]

- During weekly feedback review sessions
- After a deployment to check for new issues surfaced by actors
- When prioritizing UX improvements for the next sprint
- When closing the loop on fixed issues

## Instructions [#instructions]

1. **Pull untriaged feedback:**
   - `list_feedback(project_id, status: "new")` → get all new feedback items

2. **Review each item in detail:**
   - `get_feedback(feedback_id)` → read full details including actor context, screenshots, and evidence

3. **Assess each item:**
   - **Is it valid?**: Real UX issue vs expected behavior or test artifact
   - **How severe?**: Blocker (can't proceed), Major (significant friction), Minor (annoyance), Cosmetic (polish)
   - **Who's affected?**: One persona type or multiple
   - **Is it new?**: First occurrence or repeat of a known issue

4. **Update status:**
   - `update_feedback_status(feedback_id, status: "reviewed")` → triaged and needs action
   - `update_feedback_status(feedback_id, status: "resolved")` → issue has been fixed
   - `update_feedback_status(feedback_id, status: "dismissed")` → false positive or not actionable

5. **Cross-reference for patterns:**
   - `list_actors(workspace_id)` → check which personas are most affected
   - `list_workflows(workspace_id)` → identify workflows that cover the affected areas
   - Look for clusters: multiple feedback items about the same flow, same actor type, or same area

6. **Summarize findings:**
   - Total items reviewed, by status
   - Top patterns or clusters
   - Recommended actions (new workflows, actor updates, code fixes)

## Example output [#example-output]

```
Feedback Triage: March 28, 2026

Reviewed: 7 new items

Triaged:
  [reviewed] FB-301: Search returns empty for partial queries (Major, Power User)
  [reviewed] FB-302: Modal not keyboard-dismissable (Major, Accessibility User)
  [reviewed] FB-303: Onboarding tooltip overlaps CTA button (Minor, New User)
  [dismissed] FB-304: Duplicate of FB-298
  [reviewed] FB-305: Slow load on settings page (Major, Mobile User)
  [dismissed] FB-306: Expected behavior: empty state on fresh account
  [reviewed] FB-307: Confusing label on export button (Minor, Enterprise Admin)

Patterns:
  - 2 accessibility items this week (up from 0 last week): consider an a11y checklist
  - Search issues recurring: needs a dedicated search workflow

Actions:
  1. Fix FB-302 keyboard trap before next release
  2. Create "Keyboard Navigation" checklist
  3. Add search-specific workflow with varied query patterns
```

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/skills/triage-feedback/SKILL.md)


# Verify change (/mcp/skills/verify-change)

## When to use [#when-to-use]

- A code change has landed on a preview deployment, staging, or production and needs checking before merge or release
- A PR has a preview URL and you want evidence the change works and nothing adjacent regressed
- You want a reusable regression suite for a flow, not a one-off manual pass

## Instructions [#instructions]

1. **Resolve context:**
   - `list_workspaces()` → pick the workspace (use it silently if there is only one)
   - `list_projects(workspace_id)` → match the preview URL or product name against an existing project. Tools that take a `url` (like `create_checklist`) will find or create the project for you.
   - If you start from a pull request rather than a diff, `list_pull_requests(workspace_id, owner, repo, state="open")` finds it and `get_pull_request(workspace_id, owner, repo, pr_number)` gives its title, branches and change stats, which tell you which flows to exercise. These need the workspace's GitHub connection. The preview URL usually comes from the user or the deploy, since the PR details do not include it.

2. **Pick an actor:**
   - `list_actors(workspace_id)` → reuse a QA-style actor if one exists
   - `create_actor(...)` if the workspace has none; a plain "QA tester" actor is fine

3. **Create a checklist against the preview URL:**
   - `create_checklist(...)` with the preview URL as the host
   - `instructions`: describe how to exercise the changed flows as a real user would
   - `checks`: specific pass/fail statements covering: the new behaviour works, adjacent flows did not regress, and error states behave. 4–8 checks is the sweet spot.

4. **Run and poll:**
   - `run_checklist(checklist_id)` → returns a run ID (async)
   - `get_checklist_run(run_id)` → poll every 30–60s until terminal. Each check result carries evidence.

5. **Report:**
   - If any check fails, summarise the failure with its evidence and the likely fix. If you can edit the code, offer to fix and re-run the same checklist against the updated preview.
   - If a pull request is associated with the change, post the results with `comment_on_pr` (owner/repo#number): a short verdict line, then a table of checks with pass/fail and evidence highlights.

6. **Keep it as a regression suite:**
   - Suggest re-running the checklist on the next change to this area, or wire it to a schedule or GitHub events with `create_workflow` (see the `setup-guardrails` skill).

## Example output [#example-output]

```
Change verification: preview deploy (signup validation rework)

[PASS] New inline validation shows on blur, not just submit
[PASS] Valid email + password creates the account and lands on /welcome
[FAIL] Password < 8 chars: form submits anyway, no error shown (evidence: screenshot, network 200)
[PASS] Existing OAuth sign-in still works
[PASS] Duplicate email shows the "account exists" message

Verdict: 1 blocker. Client-side length check is bypassable: server accepts the weak password.
Posted to acme/web#482. Re-run this checklist after the fix.
```

## Tips [#tips]

- **Small, focused checklists give clearer signals.** Cover one change area per checklist rather than one giant list.
- **Write checks a human could verify on the rendered page.** Actors see the product like a user does, so phrase checks by observable behaviour, not code.
- **Pair with `setup-guardrails`** once the checklist is green, to keep the flow covered on every future deploy.

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/skills/verify-change/SKILL.md)


# Tools (/mcp/tools)

### Account [#account]

| Tool     | Description                                                                          |
| -------- | ------------------------------------------------------------------------------------ |
| `get_me` | The account this connection acts as: id, email, name, timezone, notification channel |

Notification channel and timezone are account settings rather than workspace ones, so
they are the same answer in every workspace. Pass the `timezone` when creating a
scheduled workflow, or "every weekday at 9" becomes nine in UTC.

### Workspaces [#workspaces]

| Tool                     | Description                                                                        |
| ------------------------ | ---------------------------------------------------------------------------------- |
| `list_workspaces`        | List your workspaces                                                               |
| `get_workspace`          | Get workspace details by ID or slug, including its admin-set controls (`settings`) |
| `list_workspace_members` | List members of a workspace                                                        |

`get_workspace` reports the workspace security controls under `settings`: public
sharing, the feedback widget, self-hosted workers, and the network policy. They are
ceilings set by an admin, so a feature switched off there cannot be switched back on for
a single project.

### Search [#search]

| Tool     | Description                                                                                                                                                                                                             |
| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `search` | Search a workspace across projects, actors, checklists, interviews, automations, issues, goals, feedback, actor knowledge, insights, design reviews, project resources and conversations. Ranked, with optional filters |

`search` takes a `workspace_id` plus an optional `query`, `types` filter, `project_id`
filter and `limit` (max 50). Terms are matched as prefixes, so a partial word is enough.
Omit `query` to browse the most recently updated items. Results carry the `id` you need
for the matching getter, so it is usually cheaper than listing an entity type and
filtering the list yourself. Reach for it before creating anything, to find the actor or
checklist that already covers the job.

### Projects [#projects]

| Tool                       | Description                                                                |
| -------------------------- | -------------------------------------------------------------------------- |
| `list_projects`            | List projects in a workspace                                               |
| `get_project`              | Get a project (the product tracked by checklists, workflows, feedback)     |
| `create_project`           | Create a project (a product to track with checklists, workflows, feedback) |
| `list_project_mcp_servers` | The MCP servers this project's runs can reach                              |

A project is archived, never deleted, and an archived project reads as missing from
every tool here. Registering an MCP server and attaching it to a project are
workspace-admin actions in the dashboard; `list_project_mcp_servers` is how you check
what tools a run will actually have before writing a checklist that depends on one.

### Guidelines [#guidelines]

Standing rules the team holds the product to: design system, tone of voice, brand,
content, accessibility, compliance, security, performance, or shared knowledge. A
guideline is owned by the **workspace** and attached to the projects it applies to, so
one rule can hold for every project without being retyped. Whatever is in force is
rendered into every checklist run, design review, interview and triage for that project.

| Tool                         | Description                                                                     |
| ---------------------------- | ------------------------------------------------------------------------------- |
| `list_workspace_guidelines`  | The workspace library, with how many projects hold each rule                    |
| `add_workspace_guideline`    | Add a rule to the library, optionally attaching it to projects                  |
| `update_workspace_guideline` | Edit a rule, switch it off, or apply it to every design review in the workspace |
| `remove_workspace_guideline` | Remove a rule from the library, detaching it from every project                 |
| `list_project_guidelines`    | The rules this project is held to                                               |
| `add_project_guideline`      | Record a rule and hold this project to it                                       |
| `set_project_guideline`      | Attach a library rule to a project, detach it, or switch it off there           |

Two switches decide whether a rule is in force for a project: the library's `enabled`
and the attachment's. `list_project_guidelines` folds them into one `enabled` so you
never have to reason about both. A rule with `apply_to_design_reviews` set also holds for
design reviews raised from Slack or Linear, which carry no project to attach it through.

Codify a standard as a guideline rather than repeating it in each checklist, and search
the library before writing a new rule: attaching the one that already exists keeps the
team's standard in a single place to edit.

### Actors [#actors]

| Tool           | Description                                                                                                     |
| -------------- | --------------------------------------------------------------------------------------------------------------- |
| `list_actors`  | List active actors in a workspace                                                                               |
| `get_actor`    | Get actor details including system prompt and capabilities                                                      |
| `create_actor` | Create a new actor in a workspace                                                                               |
| `update_actor` | Update actor name, description, system prompt, capabilities, or status. Set status to "archived" to soft-delete |

### Knowledge [#knowledge]

| Tool                     | Description                         |
| ------------------------ | ----------------------------------- |
| `list_actor_knowledge`   | List knowledge entries for an actor |
| `add_actor_knowledge`    | Add a knowledge entry to an actor   |
| `remove_actor_knowledge` | Remove a knowledge entry            |

### Conversations [#conversations]

| Tool                 | Description                                      |
| -------------------- | ------------------------------------------------ |
| `list_conversations` | List conversations, optionally filtered by actor |
| `get_conversation`   | Get a conversation with its messages             |

### Checklists [#checklists]

| Tool                | Description                                                                                |
| ------------------- | ------------------------------------------------------------------------------------------ |
| `list_checklists`   | List checklists in a workspace                                                             |
| `get_checklist`     | Get checklist details, checks, and recent runs                                             |
| `get_checklist_run` | Get a checklist run with per-check results                                                 |
| `run_checklist`     | Trigger a checklist run (async). Returns run ID                                            |
| `create_checklist`  | Create a browser-based QA checklist (host via project or URL, actor, instructions, checks) |
| `update_checklist`  | Update a checklist (pass `checks` to replace the full set)                                 |
| `delete_checklist`  | Delete a checklist and its checks and runs                                                 |

### Workflows [#workflows]

A workflow is a graph, not a list: its steps run by following the connections between them. The step tools maintain those connections, so adding, removing and reordering steps is enough to build one. `connect_workflow_steps` is only needed to branch.

| Tool                     | Description                                                                  |
| ------------------------ | ---------------------------------------------------------------------------- |
| `list_workflows`         | List workflows in a workspace                                                |
| `get_workflow`           | Get a workflow with its steps, edges, the order a run takes, and recent runs |
| `run_workflow`           | Trigger a workflow run (async). Returns run ID                               |
| `get_workflow_run`       | Get a workflow run with step-level details                                   |
| `create_workflow`        | Create a workflow (multi-step automation)                                    |
| `update_workflow`        | Update a workflow's name, description, or trigger                            |
| `toggle_workflow`        | Activate or pause a workflow                                                 |
| `delete_workflow`        | Delete a workflow and its steps and runs                                     |
| `add_workflow_step`      | Add a step and connect it into the run                                       |
| `update_workflow_step`   | Change a step's type or config in place                                      |
| `remove_workflow_step`   | Remove a step and close the gap it leaves                                    |
| `reorder_workflow_steps` | Set the order the steps run in                                               |
| `connect_workflow_steps` | Wire one step to another, for a condition's True and False paths             |

### Feedback [#feedback]

| Tool                     | Description                                |
| ------------------------ | ------------------------------------------ |
| `list_feedback`          | List feedback for a project, newest first  |
| `get_feedback`           | Get a feedback submission with its replies |
| `update_feedback_status` | Update feedback status                     |

### GitHub [#github]

| Tool                 | Description                                                            |
| -------------------- | ---------------------------------------------------------------------- |
| `list_pull_requests` | List pull requests for a GitHub repository                             |
| `get_pull_request`   | Get details for a GitHub pull request (title, author, branches, stats) |
| `comment_on_pr`      | Post a comment on a GitHub pull request                                |

### Interviews [#interviews]

Structured user interviews: actors or generated personas run through a discussion guide (sections + questions/tasks) against a target URL, then Stunt Double synthesises themes and recommendations.

| Tool                          | Description                                                                                |
| ----------------------------- | ------------------------------------------------------------------------------------------ |
| `list_interviews`             | List interviews in a workspace, optionally filtered by project                             |
| `get_interview`               | Get an interview with its discussion guide (sections + items) and participants             |
| `create_interview`            | Create a new interview in a project (name, target URL, research brief)                     |
| `update_interview`            | Update an interview's name, target URL, research brief, or status                          |
| `add_interview_section`       | Add a section to the discussion guide                                                      |
| `add_interview_item`          | Add a question or task to a section                                                        |
| `add_interview_participant`   | Attach a participant, either an existing actor or an ad-hoc `persona_spec`                 |
| `get_interview_participant`   | Get a participant including their full transcript from the run                             |
| `get_interview_report`        | Get the current synthesised report (summary, themes, recommendations, per-question rollup) |
| `launch_interview`            | Launch the interview round (async). Returns the trigger run ID                             |
| `regenerate_interview_report` | Re-run synthesis on existing transcripts (async). Returns the trigger run ID               |

### Prompts [#prompts]

Most MCP clients (Claude, Claude Code, Cursor) surface these as slash commands. Each is a self-contained recipe: which tools to call, in what order, and how to report back.

| Prompt                | Description                                                                                                               |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `validate_design`     | Validate a live design, prototype, or preview URL (Figma Make, Claude artifact, v0, staging) with AI personas             |
| `verify_change`       | Verify a shipped or previewed code change by running an actor through the affected flows, optionally commenting on the PR |
| `run_user_research`   | Run a structured multi-persona interview study and synthesise themes and recommendations                                  |
| `triage_feedback`     | Triage user feedback on a project: cluster it, reproduce issues with an actor, and update statuses                        |
| `setup_guardrails`    | Stand up checklists for critical flows plus a workflow that re-runs them on a schedule or on deploy/PR events             |
| `check_brand`         | Audit a product against brand and tone-of-voice guidelines, flagging deviations with evidence                             |
| `check_design_system` | Audit a live product against its design system (typography, colour, spacing, components) on rendered pages                |
| `check_compliance`    | Check a product against legal and compliance requirements and collect evidence for counsel to review                      |
| `check_continuity`    | Check continuity across surfaces (pricing, terminology, promises) between marketing, product, docs, and emails            |
| `stuntdouble_guide`   | Orientation for Stunt Double: what it does, when to reach for it, and the full tool catalogue                             |

### Resources [#resources]

Read-only context a client can attach without calling a tool. The guide and the connection are always listed; the workspace resources need `mcp:read`, the same as the tools that return that data.

| URI                                                | MIME type          | Description                                                                                   |
| -------------------------------------------------- | ------------------ | --------------------------------------------------------------------------------------------- |
| `stuntdouble://guide`                              | `text/markdown`    | What Stunt Double does, when to reach for it, the full tool catalogue and how to poll a run   |
| `stuntdouble://connection`                         | `application/json` | Who the connection acts as, the scopes it holds, the tools each unlocks and any it lacks      |
| `stuntdouble://workspaces`                         | `application/json` | The workspaces the connection can reach, with your role (same data as `list_workspaces`)      |
| `stuntdouble://workspaces/{workspace_id}/projects` | `application/json` | The live projects in one workspace, most recently opened first (same data as `list_projects`) |

`resources/list` includes one projects entry per workspace, so a client can browse them without expanding the template.

### Stunt Double Index [#stunt-double-index]

The [Stunt Double Index](https://index.stuntdouble.io) is a public ranking of how AI agents experience websites: each tracked domain is scored out of 100 from HTTP probes plus live agent sessions, one per AI provider and benchmark task. Index data is public rather than workspace data, so the read tools reach any tracked site.

| Tool                   | Description                                                                                                                                      |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `get_index_report`     | A site's score, band, rank, category and provider scores, frictions, failing probe checks and when it was last scored                            |
| `list_index_sessions`  | The agent sessions behind a score (newest run by default), with status, score, evidence, a summary and frictions. Filter by provider or category |
| `search_index_domains` | Find a site by domain or name, or browse the leaderboard, optionally by sector                                                                   |
| `request_index_rerun`  | Re-score a domain you own (`mcp:run`). Fresh probes now, about 24 agent sessions over a few minutes                                              |

`get_index_report`, `list_index_sessions` and `request_index_rerun` take a `domain`, or a `project_id` to use the Index domain linked to that project (which checks your membership like any project read). Only the domain's owner can re-run it. After a re-run, poll `list_index_sessions` with the returned `run_id` until the sessions finish, then read `get_index_report`.
(inviting/removing members) is available in the [web dashboard](https://app.stuntdouble.io).

---

[Edit on GitHub](https://github.com/stunt-double/stuntdouble-mcp/blob/v1.14.1/README.md)


# Browser toolset (/stuntkit/browser-toolset)

# Give an AI agent a safe browser [#give-an-ai-agent-a-safe-browser]

Your agent needs to book a demo on a site it has never seen. It reads the
page, finds the form, fills it in, and stops at the button that would start a
paid trial. `@stunt-double/browser-toolset` is the set of tools that makes that last
step a rule rather than a hope.

It is the same code that drives every Stunt Double actor and every
[Stunt Double Index](https://index.stuntdouble.io) session, published from
[StuntKit](/stuntkit).

## What you get [#what-you-get]

- **An executor for Anthropic's browser use toolset.** Claude asks for
  `navigate`, `read_page`, `left_click` or `type`, and the executor runs it
  against your browser and returns the results the API expects.
- **The same tools for any model.** Eleven plain function tools with flat
  parameters, so Claude, Gemini, GPT and open models all get the same
  vocabulary.
- **Safety guards in the tools, not the prompt.** Opt in and the tools refuse
  to pay, subscribe, create accounts or type passwords and card numbers.
- **A screenshot pruner** that keeps long agent loops inside the context
  window.

The tools never call a model themselves. Pages are read as a tree of elements
with `ref` handles, so your agent's own model does all of the reasoning. There
are no runtime dependencies: you bring the browser.

## Install [#install]

The source is public on
[GitHub](https://github.com/stunt-double/stuntkit/tree/main/packages/browser-toolset).
Until the `@stunt-double` scope is available on npm, the package is published
to GitHub Packages, which asks for a token to install any package, public ones
included. Point the scope at GitHub Packages in your project's `.npmrc`, with a
GitHub token that has the `read:packages` scope:

```ini
@stunt-double:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
```

Then install it:

```sh
npm install @stunt-double/browser-toolset
```

To skip the token, build it from the repository and install the tarball:

```sh
git clone https://github.com/stunt-double/stuntkit.git
cd stuntkit && pnpm install
pnpm --filter @stunt-double/browser-toolset build
pnpm --filter @stunt-double/browser-toolset pack
```

Node 20 or later, or any runtime with `Buffer` (Bun, Deno, Cloudflare Workers
with `nodejs_compat`).

## Connect your browser [#connect-your-browser]

The toolset talks to a browser through a small `BrowserDriver` interface:
open a page, take a screenshot, click, type, evaluate a script. Implement it
over Playwright, a hosted browser or a raw CDP client. The repository has a
complete
[Playwright driver](https://github.com/stunt-double/stuntkit/tree/main/packages/browser-toolset/examples)
you can copy.

## Run Claude on the browser use toolset [#run-claude-on-the-browser-use-toolset]

```ts
import Anthropic from '@anthropic-ai/sdk';
import {
  BROWSER_TOOLSET,
  BrowserToolsetExecutor,
  INDEX_SESSION_SAFETY,
  isBrowserToolsetCall,
} from '@stunt-double/browser-toolset';

const client = new Anthropic();
const executor = new BrowserToolsetExecutor(driver, { safety: INDEX_SESSION_SAFETY });
const messages: Anthropic.Messages.MessageParam[] = [{ role: 'user', content: task }];

for (;;) {
  const response = await client.messages.create({
    model: 'claude-opus-5-5',
    max_tokens: 16000,
    tools: [BROWSER_TOOLSET],
    messages,
  });
  messages.push({ role: 'assistant', content: response.content });
  const calls = response.content.filter((b): b is Anthropic.Messages.ToolUseBlock =>
    isBrowserToolsetCall(b)
  );
  if (calls.length === 0) break;
  const { results } = await executor.runTurn(calls);
  messages.push({ role: 'user', content: results });
}
```

## Run any other model on the direct tools [#run-any-other-model-on-the-direct-tools]

```ts
import { DIRECT_TOOL_SPECS, DirectBrowserTools } from '@stunt-double/browser-toolset';

const tools = new DirectBrowserTools(driver, {
  viewport: { width: 1280, height: 800 },
  safety: INDEX_SESSION_SAFETY,
});

// Offer DIRECT_TOOL_SPECS to your model in its SDK's tool format, then for each call:
const result = await tools.run(call.name, call.input);
```

`run` never throws. A failure comes back as text the model can act on, and
every call has a 45 second ceiling.

## Turn on the safety guards [#turn-on-the-safety-guards]

If your agent browses sites that are not yours, turn the guards on.
`INDEX_SESSION_SAFETY` enables all of them:

| Guard                  | What it refuses                                                                                                                                                       |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `refuseSensitiveInput` | Typing into a password or payment card field, including card fields inside Stripe, PayPal, Adyen and other payment iframes, and any value that is a valid card number |
| `blockSubmit`          | Submitting a form that pays, subscribes, signs up or starts a trial, and pressing a payment button such as "Place order" anywhere on the page                         |

A link that only navigates, like a "Sign up" link to the sign-up page, is
always allowed: reaching the form is often the task.

When a guard refuses, the action never reaches the page. The model gets an
ordinary result telling it what was refused, so it reports what it saw
instead of retrying.

<Callout variant="note" title="Guards, not a sandbox">
  The guards read the page the way a person would, so a page built to disguise a payment form can
  still get past them. Pair them with a network block on payment providers (`paymentBlocklistFor`)
  and test accounts that hold no real payment details.
</Callout>

Everything is off by default. An agent testing your own product, filling a
sign-up form with test data or completing a sandbox checkout, should keep
doing exactly that.

## Keep private hosts private [#keep-private-hosts-private]

`navigate` refuses anything but `http` and `https`, and by default refuses
loopback, private and cloud metadata addresses. A page can't talk your agent
into probing the machine the browser runs on. Pass `allowPrivateHosts: true`
when an intranet or a local dev server is the point.

## Next steps [#next-steps]

- **[Read the full API](https://github.com/stunt-double/stuntkit/tree/main/packages/browser-toolset)** on GitHub
- **[See what else is in StuntKit](/stuntkit)**
- **[Test a product behind your firewall](/tutorials/self-hosted-worker)**


# Icons (/stuntkit/icons)

# The Continuity icon pack [#the-continuity-icon-pack]

`@stunt-double/icons` is the icon set across the Stunt Double product and
brand: 235 stroked icons on a 24 unit grid with a 2 unit margin, at stroke
width 1.5 with square caps and mitred joins. It ships as framework-free
geometry, standalone SVG files and React components.

## Install [#install]

Until the `@stunt-double` scope is available on npm, add
`@stunt-double:registry=https://npm.pkg.github.com` and a GitHub token with
`read:packages` to your `.npmrc`.

```sh
npm install @stunt-double/icons
```

No dependencies. React 18 or later is an optional peer, needed only for
`@stunt-double/icons/react`.

## React [#react]

Every icon is a component named in PascalCase with an `Icon` suffix
(`arrow-right` is `ArrowRightIcon`). The props match lucide-react's, so moving
a call site across is a rename of the import.

```tsx
import { ArrowRightIcon } from '@stunt-double/icons/react';

<ArrowRightIcon size={16} />
<ArrowRightIcon title="Next" />
```

| Prop                  | Default        | What                                                                     |
| --------------------- | -------------- | ------------------------------------------------------------------------ |
| `size`                | `24`           | Width and height, in px or any CSS length                                |
| `color`               | `currentColor` | Stroke color                                                             |
| `strokeWidth`         | `1.5`          | Stroke width in grid units                                               |
| `absoluteStrokeWidth` | `false`        | Hold the stroke at `strokeWidth` px whatever the size                    |
| `title`               |                | An accessible name. Without one (or `aria-label`) the icon is decorative |

`iconComponents` maps every kebab-case name to its component, for icons chosen
from data.

## SVG files and geometry [#svg-files-and-geometry]

Each icon is a standalone file stroked in `currentColor`:

```ts
import arrowRight from "@stunt-double/icons/svg/arrow-right.svg";
```

The root entry has no framework code: each icon's elements as data, a manifest
and a serializer.

```ts
import { iconNodes, iconToSvg } from "@stunt-double/icons";

iconToSvg(iconNodes.arrowRight, { size: 32, color: "#111" });
```

`@stunt-double/icons/icons.json` lists every icon with its category, search
tags and the lucide-react names it replaces.

## AI provider marks [#ai-provider-marks]

Filled logos for AI providers and coding agents (Claude, OpenAI, Cursor,
GitHub Copilot, Gemini, v0, Lovable and more), each in a `color` and a `mono`
variant at `sm` (16px), `md` (24px) and `lg` (32px).

```tsx
import { ClaudeLogo, CursorLogo } from '@stunt-double/icons/react';

<ClaudeLogo />
<CursorLogo variant="mono" size="sm" />
```

The marks are from [LobeHub Icons](https://github.com/lobehub/lobe-icons) (MIT)
and are trademarks of their owners: use them to refer to those products, not
to suggest endorsement.

## Next steps [#next-steps]

- **[Read the full API on GitHub](https://github.com/stunt-double/stuntkit/tree/main/packages/icons)**


# StuntKit (/stuntkit)

# StuntKit [#stuntkit]

Give your AI agent a browser it can use safely. StuntKit is the toolkit Stunt
Double runs on every agent session: browser tools for your own agents, plus the
icons, spelling and accessibility packages behind the product.

It is licensed under Apache-2.0, at
[github.com/stunt-double/stuntkit](https://github.com/stunt-double/stuntkit).
StuntKit packages are published under the `@stunt-double` scope, on GitHub
Packages until that scope is available on npm (GitHub Packages asks for a token
even for public packages). The self-hosted worker ships as the Docker image
`ghcr.io/stunt-double/worker:latest`.

## What's in it [#whats-in-it]

<CardGrid
  columns={2}
  items={[
    {
      title: 'Browser tools for AI agents',
      description:
        'Let Claude or any model with function calling read and act on a real page, with guards that refuse payments, sign-ups and passwords.',
      href: '/stuntkit/browser-toolset',
    },
    {
      title: 'Web Agent Optimiser',
      description:
        'A drop-in script that repairs the accessibility tree agents read, so legacy sites work for agents without a rebuild.',
      href: '/stuntkit/wao',
    },
    {
      title: 'Icons',
      description:
        'The Continuity icon pack as geometry, SVG files and React components, plus AI provider marks.',
      href: '/stuntkit/icons',
    },
    {
      title: 'Spelling',
      description: 'Rewrite text into American, British or Canadian spelling, whole words only.',
      href: '/stuntkit/spelling',
    },
  ]}
/>

<div className="overflow-x-auto [&_td:first-child_code]:whitespace-nowrap">

| Package                         | What it does                                                                                                                             | Source                                                                                |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `@stunt-double/browser-toolset` | Browser tools for AI agents over any browser you bring, with opt-in safety guards                                                        | [GitHub](https://github.com/stunt-double/stuntkit/tree/main/packages/browser-toolset) |
| `@stunt-double/icons`           | The Continuity icon pack: 235 stroked icons as geometry, standalone SVG files and React components                                       | [GitHub](https://github.com/stunt-double/stuntkit/tree/main/packages/icons)           |
| `@stunt-double/spelling`        | Whole-word American, British and Canadian spelling localization, from American or British source text                                    | [GitHub](https://github.com/stunt-double/stuntkit/tree/main/packages/spelling)        |
| `@stunt-double/wao`             | Web Agent Optimiser: a drop-in script that repairs the accessibility tree agents read, so legacy sites work for agents without a rebuild | [GitHub](https://github.com/stunt-double/stuntkit/tree/main/packages/wao)             |

</div>

## How to use it [#how-to-use-it]

- **Build your own browser agent.** Add `@stunt-double/browser-toolset` to an
  agent loop, connect it to Playwright, a hosted browser or a CDP client, and
  turn on the guards. See [Give an AI agent a safe browser](/stuntkit/browser-toolset).
- **Run Stunt Double sessions on your own infrastructure.** The self-hosted
  worker image runs actors inside your network, so they can reach staging and
  internal tools. See [Test a product behind your firewall](/tutorials/self-hosted-worker).
- **Make a site work for agents.** Drop the `@stunt-double/wao` script into a
  page to repair the accessibility tree agents read.
- **Match the product in your own UI.** Use `@stunt-double/icons` for the
  Continuity icons and `@stunt-double/spelling` to localize copy.

## Principles [#principles]

- **Rules live in the tools, not the prompt.** A prompt that says "never pay or
  sign up" is a request a model can ignore. When a StuntKit guard refuses, the
  click never reaches the page.
- **Bring your own model and browser.** The tools never call a model and have
  no runtime dependencies. Your agent does the reasoning; you choose the
  browser.
- **Guards are opt-in and readable.** Turn on the ones you need, and read and
  test every rule before an agent acts on a real site.
- **One codebase.** The packages are the same code that drives hosted Stunt
  Double actors and the [Stunt Double Index](https://index.stuntdouble.io).

## Contribute [#contribute]

Issues and pull requests are welcome on the repository. It has a
`CONTRIBUTING.md` with the local setup, and every commit needs a
[Developer Certificate of Origin](https://developercertificate.org) sign-off
(`git commit -s`). Report security issues privately through GitHub rather than
in a public issue.

<Callout variant="tip" title="Using StuntKit with Stunt Double">
  You don't need StuntKit to use Stunt Double: hosted actors already run on it. Reach for it when
  you are building an agent of your own, or when you want to run Stunt Double sessions on your own
  infrastructure.
</Callout>

## Next steps [#next-steps]

- **[Browse StuntKit on GitHub](https://github.com/stunt-double/stuntkit)**
- **[Give an AI agent a safe browser](/stuntkit/browser-toolset)**
- **[Test a product behind your firewall](/tutorials/self-hosted-worker)**


# Spelling (/stuntkit/spelling)

# Localize English spelling [#localize-english-spelling]

`@stunt-double/spelling` rewrites text into British, American or Canadian
spelling, from source written in either American or British English. Stunt
Double's public sites are written in American English and rewritten in the
browser for visitors who read British or Canadian spelling.

```ts
import { localiseSpelling } from "@stunt-double/spelling";

localiseSpelling("Organize the color of the center.", "british");
// 'Organise the colour of the centre.'

localiseSpelling("Analyse the cancelled catalogue.", "american");
// 'Analyze the canceled catalog.'
```

## Install [#install]

Until the `@stunt-double` scope is available on npm, add
`@stunt-double:registry=https://npm.pkg.github.com` and a GitHub token with
`read:packages` to your `.npmrc`.

```sh
npm install @stunt-double/spelling
```

No dependencies. Runs anywhere JavaScript does: browsers, Node 20 or later
and workers.

## How it works [#how-it-works]

Every rewrite is a whole word, looked up exactly in a dictionary generated from
stems and the suffixes they take. Nothing is rewritten by pattern alone:
"promise", "enterprise" and "otherwise" keep their "-ise" in American English,
and "four" and "tour" keep their "-our". A word that isn't in the dictionary
stays as written.

- **Both directions.** A British word left in American copy still comes out
  American.
- **Ambiguous words are left alone.** Words correct in both systems with
  different meanings ("practice", "license", "check", "program") are never
  rewritten towards British.
- **Case is carried.** `Color` becomes `Colour` and `COLOR` becomes `COLOUR`.
- **Identifiers are kept.** A word in a domain, path, address or snake_case
  name (`color.com`, `/center`, `color_scheme`) keeps its spelling.
- **Identity on no change.** When nothing matches, the same string is
  returned, so you can compare by identity to skip a DOM write.

## API [#api]

- **`localiseSpelling(text, system)`** rewrites `text` into `system`
  (`'british' | 'american' | 'canadian'`).
- **`spellingDictionary(system)`** returns the lowercase word map for a system,
  useful for style checks and tooling.

Australia, New Zealand, Ireland and most of the Commonwealth use `'british'`.

## Rewrite a page in the browser [#rewrite-a-page-in-the-browser]

```ts
import { localiseSpelling, type SpellingSystem } from "@stunt-double/spelling";

function localisePage(root: Node, system: SpellingSystem): void {
  const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
  for (let node = walker.nextNode(); node; node = walker.nextNode()) {
    const text = node.nodeValue ?? "";
    const next = localiseSpelling(text, system);
    if (next !== text) node.nodeValue = next;
  }
}
```

Skip `code`, `pre` and anything else where text isn't prose.

## Next steps [#next-steps]

- **[Read the full API on GitHub](https://github.com/stunt-double/stuntkit/tree/main/packages/spelling)**


# Web Agent Optimiser (/stuntkit/wao)

# Make any site readable by AI agents [#make-any-site-readable-by-ai-agents]

Agents read a page through its accessibility tree and through screenshots.
Legacy pages fail the first: a `<div onclick>` is not a button, an icon link
has no name, and a field's label is the table cell beside it. To an agent,
those controls are missing or unnamed.

`@stunt-double/wao` (Web Agent Optimiser) repairs them in place. Before, in
Chromium's aria snapshot of a legacy page:

```yaml
- link:
    - /url: /cart
- cell:
    - textbox
- text: Buy now
```

After WAO:

```yaml
- link "Shopping cart":
    - /url: /cart
- cell:
    - textbox "Email"
- button "Buy now"
```

## How it works [#how-it-works]

- **Attributes only.** WAO never replaces, moves or restyles a node. Event
  handlers, framework state and what a screenshot shows are untouched.
- **Never overwrites the page.** Automatic repairs only fill in attributes the
  page has not set. Only rules you write can override.
- **Reversible.** `restore()` puts every attribute back exactly as it was.
- **Keeps up.** A `MutationObserver` repairs content that single-page apps add
  later, including inside open shadow roots.
- **Small.** About 4 KB gzipped, with no dependencies, and no network requests.

## Install [#install]

As a script tag on any site, serving `dist/wao.global.js` from the package
(also exported as `@stunt-double/wao/script`):

```html
<script src="/wao.global.js" defer></script>
```

Or as a package in a bundled app. Until the `@stunt-double` scope is available
on npm, add `@stunt-double:registry=https://npm.pkg.github.com` and a GitHub
token with `read:packages` to your `.npmrc`.

```sh
npm install @stunt-double/wao
```

```ts
import { optimise } from "@stunt-double/wao";

const wao = optimise();
```

The npm entry has no side effects: nothing runs until you call `optimise`. The
script build runs it once the DOM is ready and exposes the handle as
`window.wao`.

## In an agent's browser [#in-an-agents-browser]

You don't need to own a site to use WAO on it. An agent harness can inject the
script build into every page it opens, so the agent reads the repaired tree.
With Playwright:

```ts
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";

const script = readFileSync(
  fileURLToPath(import.meta.resolve("@stunt-double/wao/script")),
  "utf8",
);

await context.addInitScript({ content: script });
```

With Puppeteer, pass the same content to `page.evaluateOnNewDocument`. Pair it
with the [browser toolset](/stuntkit/browser-toolset), whose `read_page` reads
the same tree.

## What it repairs [#what-it-repairs]

| Repair       | What it does                                                                                                            |
| ------------ | ----------------------------------------------------------------------------------------------------------------------- |
| `clickables` | Gives a non-semantic element with a click handler `role="button"` and `tabindex="0"`, so Enter and Space click it.      |
| `names`      | Names an unnamed control from a tooltip, an icon font class, the text beside a field, a field's `name` or a link's URL. |
| `landmarks`  | Gives a page with no `<main>` `role="main"` on its one obvious candidate.                                               |

Each repaired element is marked with `data-wao`, so an agent can tell inferred
names from ones the page wrote. Switch a repair off with
`optimise({ repairs: { landmarks: false } })`.

## Rules [#rules]

Heuristics can't know what a control means. Rules can, and they override the
page:

```ts
optimise({
  rules: [
    { selector: "#hdr .links", role: "navigation", label: "Primary" },
    {
      selector: ".btn-go",
      label: "Check out",
      description: "Pays for everything in the basket",
    },
  ],
});
```

With the script build, set `window.waoOptions` before the script loads.

## Reports [#reports]

`report()` lists every change in effect and what WAO found but could not fix
(`unnamed-control`, `image-without-alt`, `no-main`). Each issue is a good
candidate for a rule, or for a fix in the site itself.

<Callout variant="note" title="Not an accessibility fix">
  Inferred names are a best guess for agents. A site that people use with
  assistive technology still needs real labels.
</Callout>

## Next steps [#next-steps]

- **[Read the full API on GitHub](https://github.com/stunt-double/stuntkit/tree/main/packages/wao)**
- **[Give an AI agent a safe browser](/stuntkit/browser-toolset)**


# Automate from Claude (/tutorials/automate-with-mcp)

# Automate Stunt Double from Claude [#automate-stunt-double-from-claude]

This tutorial connects Claude and uses it to run your workspace from a
conversation. The **[MCP server](/mcp)** pages cover authentication and every
tool.

## Connect Claude to your workspace [#connect-claude-to-your-workspace]

Follow **[Connect](/mcp/connect)** for Claude Desktop, or the
**[Claude](/mcp/clients/claude)** and **[Claude Code](/mcp/clients/claude-code)**
pages. The first time Claude calls a tool, it runs the OAuth flow and asks you
to sign in to your workspace. Once you approve, Claude can list and call tools
on your behalf for the rest of the session.

## What you can ask Claude to do [#what-you-can-ask-claude-to-do]

With the server connected, you can drive most of the platform in plain
language instead of clicking through the dashboard:

- "Run the checklist for our signup flow and tell me if anything failed."
- "Create an actor for a first-time free-trial user and give it browser
  access."
- "Launch a new round of our onboarding interview and summarize the top
  themes."
- "List the workflows that ran in the last day and flag any failures."

Claude translates each request into the appropriate tool calls, actors,
checklists, workflows, interviews, and reads back the result.

## Scope tokens to least privilege [#scope-tokens-to-least-privilege]

Grant only the scopes a given client needs. There are three: `mcp:read` to
read a workspace, `mcp:write` to create and edit, and `mcp:run` to start runs.
A client that only reports on results needs `mcp:read`, and nothing more. A
client that requests no scope gets all three. See
**[authentication](/mcp#authentication)** for what each covers.

## Next steps [#next-steps]

- **[Automate multi-step workflows](/tutorials/building-workflows)**,
  for automations that don't need a human in the loop at all.
- **[API overview](/api)**, for driving Stunt Double from your
  own tools instead of Claude.


# Build workflows (/tutorials/building-workflows)

# Automate multi-step workflows [#automate-multi-step-workflows]

A checklist verifies one flow in one run. A workflow chains several steps
together into a single repeatable automation, so you can wire up a whole
process instead of a single check.

<Screenshot
  name="automation-builder"
  alt="The automation flow editor: a GitHub pull request trigger runs a checklist, then a condition sends failures to a GitHub comment and passes to a notification."
  caption="An automation is a graph. A condition splits the path: true on the left, false on the right."
/>

## Step types available [#step-types-available]

A workflow is built from steps, each with its own configuration:

- **Run Checklist**, executes an existing checklist and carries its result
  forward.
- **Condition**, branches the workflow based on the result of a previous
  step.
- **Notification**, sends an alert when the workflow reaches that step.
- **Delay**, waits before continuing, useful for giving an async process time
  to finish.
- **GitHub Comment**, posts a comment back to a pull request.
- **Actor PR Review**, has an actor review a pull request and report its
  findings.

## Example: verify a pull request automatically [#example-verify-a-pull-request-automatically]

A common workflow for a product team:

1. **Run Checklist**, runs the checklist for the flow the pull request
   touches.
2. **Condition**, checks whether every check in that run passed.
3. **Actor PR Review**, if the condition passes, has an actor review the
   change directly.
4. **GitHub Comment**, posts the outcome back to the pull request either way,
   so the result is visible without leaving GitHub.

Each step only needs to know about the step before it, which is what makes it
possible to compose a whole review process out of a handful of pieces.

## Toggle a workflow on when you're ready [#toggle-a-workflow-on-when-youre-ready]

A new workflow can be built and tested without affecting anything else.
Toggle it on only once you've run it a few times and are confident in the
result, since an enabled workflow runs automatically going forward rather
than waiting for you to trigger it by hand.

## Next steps [#next-steps]

- **[Turn runs into insights](/tutorials/reviewing-runs)**
- **[Automate Stunt Double from Claude](/tutorials/automate-with-mcp)**


# Write checklists (/tutorials/effective-checklists)

# Write checklists that catch real regressions [#write-checklists-that-catch-real-regressions]

A checklist is only as useful as the checks inside it. This tutorial covers
how to write checks that hold up over time, and how to read a run so a
"failed" badge tells you something you can act on.

<PageFacts
  items={[
    { label: "Time", value: "20 minutes" },
    { label: "You need", value: "An actor and a URL" },
    { label: "Next", value: "Turn runs into insights" },
  ]}
/>

<Screenshot
  name="checklist-editor"
  alt="The new checklist dialog on its Checks step: four plain-language checks in order, each with a check type, when it runs and what happens on failure."
  caption="Each check says what should be true. Under it: its type, when it runs, and whether a failure stops the run."
/>

## Write one check per outcome, not per click [#write-one-check-per-outcome-not-per-click]

A check is a single description of what should be true. It's tempting to
write mechanical, step-by-step instructions, but checks written that way
break the moment your UI changes, even when the underlying behavior didn't:

- Weak: "Click the discount field, type SAVE10, click Apply."
- Strong: "Confirm the cart shows the discounted price after applying code
  SAVE10."

The strong version tells the actor what outcome to verify and leaves it to
work out how to get there, which is exactly what makes checklists resilient
to redesigns.

## Point checks at the moment that matters [#point-checks-at-the-moment-that-matters]

Group checks around the state that actually needs verifying, not the
navigation that gets you there: the confirmation screen after checkout, the
inbox after a signup, the dashboard after a plan change. If a flow has
several outcomes worth confirming, write a check for each one rather than one
check trying to cover all of them.

## Choose what each check is worth [#choose-what-each-check-is-worth]

Not every check deserves to stop a release. Each one has a type, set in the
create dialog or the flow editor's inspector:

- **Pass / fail** is blocking. One failure fails the whole run. Use it for the
  things that must work: checkout completes, the signup email arrives.
- **Weighted** contributes its weight to a run score rather than blocking.
  Weights run from -100 to 100, default 1, so "checkout completes" can outweigh
  "the footer year is current". A weight below zero is a **penalty**: it adds
  nothing when the check passes and deducts that many points when it fails,
  without raising the maximum. Phrase a penalty as what should be true ("no
  placeholder text is left in the copy"), so passed still means good.
- **Informational** is never judged. The actor records what it observed ("the
  headline price is $29 per month") and that text is the deliverable, which is
  how drift in prices, copy or plan names gets caught between runs.

A checklist's **Passing score** decides the verdict for its weighted checks. It
defaults to 100, which reproduces the old all or nothing rule, so a checklist
you already have behaves exactly as it did until you lower it.

The two hurdles are independent on purpose: no blocking check may fail, **and**
the weighted score must reach the threshold. Folding pass/fail checks into the
score would let "the user cannot check out" survive as long as enough cosmetic
checks passed.

A check that errors is inconclusive rather than failed. A check the run never
reached says nothing about your product, so it stays out of the score.

### Writing a scoring rubric as a checklist [#writing-a-scoring-rubric-as-a-checklist]

A rubric like "40 points for the core flow, 30 for clear messaging, 20 for
accessible controls, 10 for visual polish, minus 20 for a blocking error, out
of 100" is five weighted checks: four with those weights and one with a weight
of -20. The maximum is the positive weights only, so the run really is out of 100. Partial credit ("15 if the messaging is only partly clear") is two checks
of 15 rather than one of 30, since a check has one verdict.

The passing score is a percentage, and the editor shows the points it works out
to at the current weights (75% of 100 is 75 points). Under **Grades**, name the
ranges a score can land in, best first: Optimal from 75%, Partial adoption from
37.5%, Pushed back below that. A run then reports its grade next to its score.
Grades only label the score; the passing score still decides whether it passed.

## Order the checks, and let one depend on the last [#order-the-checks-and-let-one-depend-on-the-last]

Checks run top to bottom. Drag a check vertically to reorder it, or use Move up
and Move down in the inspector.

Order matters because a check can depend on how the one before it turned out.
Set its run condition to:

- **Always**, the default: it runs whatever the check before it found.
- **If previous passed**, for a follow-up that only means anything once the
  step before it worked ("the confirmation shows the right total").
- **If previous failed**, for an inspection that is only reachable when the
  step before it broke ("the error message explains what to fix").

Without conditions, both shapes produce a misleading report: one failure
counted twice, or a check that fails because the product worked.

A check whose condition is unmet is recorded as **skipped**, naming the check it
depended on, and left out of the score entirely. It did not fail, and it is not
inconclusive either. A condition is only unmet when the previous check's verdict
contradicts it: an antecedent that errored, or an informational one with no
verdict at all, leaves the condition undecidable, and the check runs. Quietly
testing less is the one outcome a testing product must never produce.

One thing to know before you reach for informational checks: they need the
actor to read the page, so a checklist containing one always runs the full
agent rather than replaying its recorded script.

## Read the evidence, not just the pass/fail badge [#read-the-evidence-not-just-the-passfail-badge]

A run reports a verdict, a score for its weighted checks, and the evidence
behind every check. Each check carries its own screenshot, taken on the page
the actor was actually on when it reached its verdict. A run report has tabs
for:

- **Results**, each check's status and a short explanation.
- **Screenshots**, the page as the actor saw it at each step.
- **Recording**, the full session if you want to watch it play out.
- **Knowledge**, which knowledge entries the actor drew on.

When a check fails, read the explanation and screenshot before deciding
whether it's a real regression or an ambiguous instruction. A surprising
number of "failures" turn out to be a check that needs to be more specific
about what counts as success.

<Screenshot
  name="checklist-run"
  alt="A checklist run report with one failed check open, showing the actor's explanation and the screenshot of the form error, above three passed checks."
  caption="The failed check leads, with what the actor did, what it saw, and the step and time it happened."
/>

## Read a run against the one before it [#read-a-run-against-the-one-before-it]

A finished run is compared with the previous completed run of the same
checklist, under **Since the previous run** on the report. It names what
changed rather than what is red, which is the difference between a report you
read every time and one you stop opening. Informational checks are compared as
text, so "$29 per month" becoming "$39 per month" shows up even though no
pass or fail state moved.

Editing a checklist keeps its history: checks you did not touch keep their ids
and their past results, so a comparison still has something to compare against.

## Re-run before you trust a result [#re-run-before-you-trust-a-result]

An actor working through a hosted browser can hit a transient issue, a slow
page load, a flaky third-party script, just like a human tester can. Before
filing a bug from a single failed run, re-run the checklist once. A result
that reproduces consistently is worth acting on immediately.

## Next steps [#next-steps]

- **[Turn runs into insights](/tutorials/reviewing-runs)**
- **[Automate multi-step workflows](/tutorials/building-workflows)**


# Collect feedback (/tutorials/feedback-widget)

# Collect stakeholder feedback on a prototype [#collect-stakeholder-feedback-on-a-prototype]

Share a prototype and the notes come back as screenshots in Slack, with no way
to tell which page or which build they were about. The feedback widget is one
script tag on the prototype: reviewers pin a comment on the exact spot, and it
arrives with a screenshot and the page context already attached.

## 1. Turn comments on for a project [#1-turn-comments-on-for-a-project]

Open the project holding the prototype and go to **Comments**. If comments have
never been enabled, you get a short explainer and an **Enable comments** button.
Enabling switches the feature on and mints the project's **API key**, which ties
the widget on your site back to this project.

Each project has its own key and its own inbox, so a round of feedback on one
prototype never mixes with another.

## 2. Add the widget to the prototype [#2-add-the-widget-to-the-prototype]

The Comments view then shows the install step with two tabs.

**With Claude** gives you a prompt to paste into Claude, Claude Code, or any
coding assistant. It describes both script tags and where they go, which is
usually the fastest route if an agent already has your repo open.

**Code** gives you the snippet directly. Add it just before the closing
`</body>` tag. Set `data-sd-version` to your build ID if you want each round of
feedback kept apart (see [Prototype versions](#prototype-versions) below). The
[integration reference](/integrations/feedback-widget#the-snippet) explains
every attribute.

There is no package to install and no build step, so this works on a staging
deploy, a preview URL, or a throwaway static build just as well as on your
production site.

### Confirming it works [#confirming-it-works]

The install screen polls while you work. Add the snippet, deploy or reload the
prototype, and visit any page carrying it. Within a few seconds the screen flips
from **Waiting for widget connection** to **Widget detected** and moves you on
to the Comments view. No manual verification step, and nothing to paste back.

If it never flips, the two usual causes are a mistyped key and a domain that is
not allowed yet. Both are covered under [Control who can use
it](#control-who-can-use-it).

## 3. Hand it to your stakeholders [#3-hand-it-to-your-stakeholders]

Before you send the link, decide who should be able to comment.

For an internal review, turn on **Require sign in** under **Comments →
settings**. Only signed-in members of the workspace can then read or leave
comments, which is the right setting for an unreleased prototype on a public
staging URL. Reviewers get a **Sign in to review** button instead of an
anonymous form: it opens a popup, they sign in to Stunt Double as normal, and
the popup hands the widget a short-lived token proving workspace membership.
Their replies are badged **Team**, and their name comes from their account
rather than a form field.

The token lasts an hour, so a long review session may ask for a fresh sign-in.
Because a cross-origin widget cannot read the Stunt Double session cookie, this
token is the only thing that unlocks gated data: the sign-in state the widget
shows is cosmetic on its own.

For a wider round, with customers or people outside the workspace, leave sign-in
off and send the link. Commenting then takes three actions:

1. Click the floating button in the bottom-right corner, or press **`c`**.
2. Click the spot the comment is about. A pin lands there.
3. Type a name, optionally an email, and the comment, then send. **Escape**
   cancels at any point.

The name and email are remembered in that person's browser, so their second
comment only needs the comment itself. The email is optional and only used to
notify them when someone replies to or resolves what they left.

Every submission carries more than the text: a screenshot of the page as it
looked, the page URL, the pin position, viewport size, scroll offset, and basic
device information. That is what makes "this bit is confusing" actionable a week
later.

Keep **Show comments on page** enabled for a group review and existing pins are
drawn on the page, so five stakeholders discuss one thread instead of filing the
same note five times. Turn it off when you want independent reactions: the
widget still collects everything, it just does not show anyone else's.

## 4. Triage what comes back [#4-triage-what-comes-back]

The project's **Comments** view is where feedback lands. It opens on the
**Open** queue with counts across the top: total comments, how many are open,
how many pages they came from, and how many are resolved.

<Screenshot
  name="feedback"
  alt="The project Comments view: counts of comments, open, pages and resolved, filter tabs, and comments grouped by page with a screenshot of where each was pinned."
  caption="Comments arrive grouped by page, each with the screenshot of where it was pinned and the element it was attached to."
/>

Comments are **grouped by page**, because that is how you fix them. Expand a
page to see every pin left on it, collapse the ones you have dealt with, or use
**Collapse all** when the list gets long. Where the pins cluster is where the
prototype is failing.

Opening a comment shows the full detail: the screenshot with the pin in place,
the device and viewport it was left on, the thread of replies, and the status
control. From there you can:

- **Reply** in the thread. If the person left an email they are notified, and
  the reply appears on the page for them under a **Team** badge.
- **Change status** between **new**, **reviewed**, **resolved**, and
  **dismissed**. Resolving notifies the author, which is what turns a round of
  feedback into a closed loop rather than a list nobody hears back about.
- **Resolve all** to clear the whole open queue at once, after a round of fixes.

Resolved and dismissed comments are filed under **Resolved** rather than
deleted, so you can still show why a decision was made three iterations ago.

### Prototype versions [#prototype-versions]

Prototypes change fast, and a comment about a button you have since removed is
noise unless you know which iteration it referred to. The widget hashes the page
content with each comment and groups them into versions: **v1** is the earliest
snapshot that received a comment, and the newest is badged **Latest**. A page
group with more than one version shows a small timeline, and selecting a version
filters the group to the comments left on it.

By default the hash is computed from the rendered page, and recomputed as you
navigate a single-page app. If you would rather tie versions to your own
iterations, set `data-sd-version` on the script tag to a release tag, build ID,
or commit SHA. When it is set, the widget uses it verbatim and never re-hashes.
That is the cleaner option when you are showing the same prototype to a new
group each week.

## Control who can use it [#control-who-can-use-it]

Your API key sits in your page source, so it is an identifier, not a secret.
What controls access is the project's **allowed domains** list under
**Comments**, then settings. For a prototype review:

- Add your preview host, or `*.your-domain.com` if each build gets a new URL.
- Turn on **Require sign in** for staging and internal reviews, so only
  workspace members can read or leave comments.
- **Rotate** the key when you stop maintaining a prototype.

Every setting, the fail-closed rule and the workspace-wide controls are in the
[integration reference](/integrations/feedback-widget#control-who-can-use-it).

## Beyond the prototype [#beyond-the-prototype]

The same install works unchanged once the thing you were prototyping ships.
Leave the snippet in place with sign-in off, and you have a standing feedback
channel on production: people report what bothers them where it bothers them,
grouped by page and version, in a queue rather than a support inbox.

## Working with comments over the API [#working-with-comments-over-the-api]

Feedback is available through the MCP server, so an assistant can triage it with
you: `list_feedback` (filter by status when working the new queue), `get_feedback`
for one item and its replies, and `update_feedback_status` to resolve or dismiss.
Reading needs the `mcp:read` scope and changing a status needs `mcp:write`. See
**[MCP](/mcp)** for connecting a client.

A natural pairing: read the open comments for a page, then run a checklist
against that page to reproduce what your reviewers reported before deciding what
to fix.

## Tips [#tips]

- Install it on the staging build with **Require sign in** on, before anyone
  external sees the prototype. Your team gets a low-friction way to leave notes
  in context, and nothing is exposed publicly.
- Ask for the email field when you actually intend to reply. Silence after a
  feedback round is worse than no widget.
- Set `data-sd-version` to your build ID if you iterate daily. Version grouping
  by content hash is good; grouping by release is unambiguous.
- Resolve aggressively between iterations. The Open queue is only useful if it
  reflects what is genuinely outstanding, and resolved comments are still there
  to look back on.
- Pair it with an interview on the same prototype: actor personas show you what
  a careful user hits, the widget shows you what your stakeholders actually
  noticed.

## Next steps [#next-steps]

- **[Run a user interview](/tutorials/running-an-interview)** to
  put persona participants through the same prototype the stakeholders reviewed.
- **[Write checklists that catch real regressions](/tutorials/effective-checklists)**
  to turn a recurring complaint into a check that runs on every deploy.


# Build an actor (/tutorials/first-actor)

# Build an actor that behaves like your audience [#build-an-actor-that-behaves-like-your-audience]

The **[Quickstart](/getting-started)** has you create a minimal
actor to run one checklist. This tutorial goes further: writing instructions
and knowledge that make an actor behave like a genuine member of your
audience.

<PageFacts
  items={[
    { label: "Time", value: "15 minutes" },
    { label: "You need", value: "A project" },
    { label: "Next", value: "Write checklists" },
  ]}
/>

<Screenshot
  name="actor-create"
  alt="An actor's Overview tab in Stunt Double: name, description, instructions and location, with counts of its conversations, emails and knowledge."
  caption="An actor is named by role and described in plain language. The Knowledge tab holds what it knows."
/>

## Start with a persona, not a checklist [#start-with-a-persona-not-a-checklist]

Instructions work best when they describe who the actor is standing in for,
not what you want it to click. A persona has a goal, a level of product
familiarity, and a tone:

> You are a small business owner evaluating whether to switch invoicing
> software. You are price-sensitive, comfortable with software but impatient
> with jargon, and you will abandon a signup flow if it asks for a credit card
> before showing you the product.

An actor written this way makes the same kind of judgment calls a real
visitor would, rather than mechanically executing steps.

## Give it knowledge to draw on [#give-it-knowledge-to-draw-on]

Knowledge entries are the reference material an actor can pull from mid-run:
facts about your product, test account credentials, brand or tone
guidelines, or answers to questions it might otherwise get wrong. Add a
knowledge entry for anything you would otherwise have to explain to a new
team member on day one.

Keep entries short and specific. A dozen focused entries work better than one
long document, since the actor only needs to draw on what's relevant to the
step it's on.

## What an actor can do [#what-an-actor-can-do]

Out of the box an actor works through your product the way a real visitor
would:

- **Drives a real browser** through your product, observing each page and
  deciding what to do next.
- **Reads email** sent to its own inbox, so it can pick up verification codes
  and magic links during signup and verification flows.
- **Searches the web** when it needs information beyond your product.
- **Remembers context** it has seen before, carrying useful state between runs.

You don't have to wire any of this up. Give the actor a clear persona and the
knowledge it needs, and it draws on whatever is relevant to the step it's on.

## Test it before you rely on it [#test-it-before-you-rely-on-it]

Before building a whole checklist suite around a new actor, run one small
checklist against it and read the result. Confirm it interprets your
instructions the way you intended, then expand from there.

## Next steps [#next-steps]

- **[Write checklists that catch real regressions](/tutorials/effective-checklists)**
- **[Run a user interview](/tutorials/running-an-interview)**


# Tutorials (/tutorials)

# Tutorials [#tutorials]

The **[Quickstart](/getting-started)** gets you from an empty
workspace to a first checklist run, and **[Core concepts](/concepts)**
explains what each building block does. These tutorials go further: practical,
task-oriented guides for getting real value out of each part of the product as
your usage grows.

## The series [#the-series]

<CardGrid
  columns={2}
  items={[
    {
      icon: 'actor',
      title: '1. Build an actor that behaves like your audience',
      description:
        'Write instructions and knowledge that make an actor behave like a real member of your audience.',
      href: '/tutorials/first-actor',
    },
    {
      icon: 'checklist',
      title: '2. Write checklists that catch real regressions',
      description:
        'Structure checks around outcomes, not clicks, and read the evidence behind a pass or fail instead of trusting the badge.',
      href: '/tutorials/effective-checklists',
    },
    {
      icon: 'interview',
      title: '3. Run a user interview',
      description:
        'Structure sections and items, recruit actor personas or real participants, and read an evidence-backed report.',
      href: '/tutorials/running-an-interview',
    },
    {
      icon: 'automation',
      title: '4. Automate multi-step workflows',
      description:
        'Chain checklists, conditions, notifications, and PR reviews into one repeatable automation.',
      href: '/tutorials/building-workflows',
    },
    {
      icon: 'insight',
      title: '5. Turn runs into insights',
      description:
        'Read results, screenshots, and recordings like a teammate would, and see what surfaces automatically as an insight.',
      href: '/tutorials/reviewing-runs',
    },
    {
      icon: 'feedback',
      title: '6. Collect stakeholder feedback on a prototype',
      description:
        'Install the feedback widget so reviewers pin comments in context, then triage, reply, and resolve them from the project.',
      href: '/tutorials/feedback-widget',
    },
    {
      icon: 'users',
      title: '7. Review designs from Slack',
      description:
        'Share a link in Slack and get the screen opened, reviewed, and written up in the thread, then ask the agent to run the rest of your workspace.',
      href: '/tutorials/slack-design-review',
    },
    {
      icon: 'worker',
      title: '8. Test a product behind your firewall',
      description:
        'Run actors against an internal product with a self-hosted worker, no inbound firewall changes required.',
      href: '/tutorials/self-hosted-worker',
    },
    {
      icon: 'terminal',
      title: '9. Automate Stunt Double from Claude',
      description:
        'Connect the MCP server to Claude and drive actors, checklists, and interviews from a conversation.',
      href: '/tutorials/automate-with-mcp',
    },
  ]}
/>

## Next steps [#next-steps]

Each tutorial stands on its own, so start where your team needs it most. To
connect Slack, GitHub, Linear, Figma or Vercel, see
**[Integrations](/integrations)**. To drive Stunt Double from an assistant, see
**[MCP](/mcp)**.


# Review runs (/tutorials/reviewing-runs)

# Turn runs into insights [#turn-runs-into-insights]

Every run an actor completes, whether it's a checklist, an interview round,
or a workflow, is recorded in full. This tutorial covers how to read a run
report and how to use the insights Stunt Double surfaces on top of it.

## Read a run report [#read-a-run-report]

A run report is organized into tabs:

- **Results**, the pass, fail, or error status for each check, with a short
  explanation for each one.
- **Journey**, the pages the actor visited and the emails it read, in order.
- **Screenshots**, the page as the actor saw it at each step, useful for
  spotting a visual regression that a pass or fail badge wouldn't catch.
- **Recording**, the full session, step through it in order or watch it play
  out end to end.
- **Knowledge**, which knowledge entries the actor drew on for that run,
  useful for checking whether it's relying on something out of date.

<Screenshot
  name="checklist-run"
  alt="A checklist run report on the Results tab: checks passed shown as 3 of 4, the failed check with its evidence and screenshot, then the passed checks."
  caption="The Results tab. Needs attention comes first; every row carries its evidence and a screenshot."
/>

Start with Results to get the headline, then drop into Screenshots or the
Recording when a result needs more context than the explanation gives you.

## What surfaces automatically as an insight [#what-surfaces-automatically-as-an-insight]

Not everything worth knowing fits into a pass or fail. As actors work,
Stunt Double surfaces insights: notable observations from a run that fall
outside strict pass/fail, a confusing step, a broken link, an unexpected
result the actor flagged on its own. Insights turn a pile of session
recordings into a short list of things worth your attention, without
requiring anyone to watch every replay.

Review new insights on a regular cadence rather than only after something
breaks; the most useful ones are often things nobody thought to write a check
for.

## Share findings with your team [#share-findings-with-your-team]

Runs and insights are most useful when they reach the people who can act on
them. Connect **[Slack](/integrations/slack)**,
**[Linear](/integrations/linear)** and **[GitHub](/integrations/github)** (all
listed under **[Integrations](/integrations)**) so failed runs and new insights land where your team already works,
instead of waiting to be checked manually.

## Next steps [#next-steps]

- **[Automate multi-step workflows](/tutorials/building-workflows)**
- **[Automate Stunt Double from Claude](/tutorials/automate-with-mcp)**


# Run an interview (/tutorials/running-an-interview)

# Run a user interview [#run-a-user-interview]

An interview is a structured conversation Stunt Double runs with your users
at a scale a manual research process can't match. This tutorial covers how to
structure one, who to recruit, and how to read what comes back.

## Structure sections and items [#structure-sections-and-items]

An interview is organized into sections, and each section holds a series of
items. An item is either:

- A **question**, asked directly, useful for opinions, priorities, or
  recalling past experience.
- A **task**, where the participant is asked to attempt something (in your
  live product or a Figma prototype) and then reflect on it.

Mixing the two in a section works well: a task that puts the participant in a
flow, followed by questions that surface what confused them or what they
expected instead.

## Recruit participants: actor personas or real people [#recruit-participants-actor-personas-or-real-people]

Participants can be AI actor personas, real people, or a mix of both in the
same round. Actor personas give you a fast, repeatable panel for testing a
direction before you're ready to recruit, while real participants give you an
authentic voice you can't get any other way. A common pattern is to run an
actor panel first to catch obvious problems, then bring in real participants
to validate what's left.

## Launch a round [#launch-a-round]

When the guide and participant list are ready, launch a round. Each round is
a complete pass through the guide with every recruited participant. As you
make product changes, launch another round against the same guide to see how
perception shifts, rather than starting from scratch each time.

## Read the report [#read-the-report]

Every round produces a report with:

- **Overview**: the counts and a **summary** of what the round found.
- **Findings**: the patterns that came up across participants, each backed by
  evidence drawn straight from the interview.
- **By question**: what most participants did, where they differed, and the
  outliers.
- **Recommendations**: ranked by priority, ready to track as goals.
- **Usability** and **Transcripts**: the questionnaire scores and every
  participant's session in full.

<Screenshot
  name="interview-report"
  alt="An interview round report on its Overview tab: counts of participants, findings, questions analysed and recommendations, a summary, and the participant list."
  caption="A round's Overview: the counts, a summary of what participants did, then Findings, By question and Recommendations in order."
/>

Because every finding and recommendation is tied to evidence quotes, you can
check a claim against what a participant actually said before acting on it.

## Next steps [#next-steps]

- **[Turn runs into insights](/tutorials/reviewing-runs)**
- **[Automate multi-step workflows](/tutorials/building-workflows)**


# Test behind a firewall (/tutorials/self-hosted-worker)

# Test a product behind your firewall [#test-a-product-behind-your-firewall]

If the product you want to test is only reachable inside your network, a
hosted browser session can't reach it. A self-hosted worker runs on your side
of the firewall instead, so actors can reach an internal product without
opening anything up.

<Callout variant="note" title="Enterprise plan, workspace admins">
  Self-hosted workers are part of the Enterprise plan, and only a workspace
  owner or admin can turn them on and add workers. The page is marked
  **Experimental** in the app.
</Callout>

## Add a worker [#add-a-worker]

1. Open **Settings > Workers** and select **Enable for this workspace**.
2. Select **Add worker**, name it, then **Create worker**.
3. Copy the Docker snippet the app shows. It runs the published image
   `ghcr.io/stunt-double/worker:latest` with your worker's API key in
   `SD_WORKER_API_KEY`. The key is shown once, so store it somewhere safe
   before you close the dialog. The snippet can also add Watchtower to keep
   the image up to date.
4. Run it on a machine inside your network, then select **Test connection**
   to confirm it reaches Stunt Double and your target product.

The worker dials out to Stunt Double rather than waiting for an inbound
connection, so your firewall needs no changes. Your security team can pull
and inspect the image before it runs.

## Control what it can reach [#control-what-it-can-reach]

Each worker has an access setting with three modes:

- **Allow all domains**: no restriction.
- **Allow specific domains**: only the domains you list.
- **Block specific domains**: everything except the domains you list.

A new worker starts on Allow all domains, so it works as soon as it
connects. To restrict it, switch to Allow specific domains and list only what
your actors need to reach.

## Pin actors and checklists to a worker [#pin-actors-and-checklists-to-a-worker]

Once a worker is connected, assign the actors and checklists that need to
reach your internal product to it. Everything else
keeps running on hosted browser sessions as normal, a workspace can mix both
freely.

## Watch connection status live [#watch-connection-status-live]

The Workers page reflects connection status live: a worker's heartbeat flips
its status without needing a page refresh, so you can tell at a glance
whether it's online before kicking off a run that depends on it.

## Run a worker on your Mac [#run-a-worker-on-your-mac]

The Stunt Double desktop app for Mac can act as a worker too. With Workers
enabled, open **Settings > Workers**, select **Use this device as a worker**,
then **Enable this device**.

## Next steps [#next-steps]

- **[Automate multi-step workflows](/tutorials/building-workflows)**
- **[Turn runs into insights](/tutorials/reviewing-runs)**


# Design review in Slack (/tutorials/slack-design-review)

# Review designs from Slack [#review-designs-from-slack]

Someone drops a staging link in the design channel and asks what everyone thinks.
Two days later there is one reply and nobody has opened it. Connect Slack to
Stunt Double and that message gets an answer in the thread: the screen opened in
a real browser, looked at, and written up with the problems ranked.

One Slack app covers two jobs.

**Design review** watches for a message that carries a link and reads like a
request for feedback, then replies in the thread with a summary and prioritized
findings.

**The agent** handles the rest of the conversation. Message Stunt Double
directly, or @mention it in a channel, and it answers questions about your
workspace and starts checklists and workflows for you.

<Screenshot
  name="slack-review"
  alt="A Slack thread where Stunt Double replies to a request for review with what it did, a summary and three findings ranked by severity."
  caption="The review lands in the thread it was asked in: what the actor did, a summary, then findings ranked by severity."
  width={1200}
  height={800}
/>

## 1. Connect Slack [#1-connect-slack]

Open **Integrations** > Slack and select **Install Slack agent**, then **Add**
your Slack team to the workspace (a workspace owner or admin does this step).
That is the whole setup. The Slack app already asks for the permissions it
needs, so nothing has to be wired up by hand. Review settings live under
**Design reviews > Settings**. The **[Slack](/integrations/slack)** page covers
permissions, and the difference between **Remove** (one workspace) and
**Disconnect** (your account, every workspace).

From the moment the connection exists, anyone in your Slack workspace can
message Stunt Double directly and ask for a review. Channels are optional and
covered in [step 4](#4-turn-it-on-in-a-channel).

Figma is the one extra, and only for Figma links. Connect it from **Integrations** ([Figma](/integrations/figma))
and Figma frames are rendered and reviewed. Without it, a Figma link comes back
with a **Connect Figma** button instead of feedback. Web links are reviewed
either way.

If your workspace connected Slack before direct messages were supported, the
page shows **Reconnect Slack**. An installation keeps whatever permissions it
was granted, so Slack quietly delivers nothing rather than reporting an error.
Reconnecting grants the two permissions the direct-message surface needs.

## 2. Ask for a review [#2-ask-for-a-review]

A review starts when a message carries a link **and** reads like a request for
feedback. Both halves matter: people paste links all day, and a review opens a
real browser and calls a vision model.

Phrasings that count as asking:

- Can I get some feedback on this?
- Design review please
- Thoughts on the new onboarding screen?
- wdyt
- Take a look when you get a chance
- Any comments before I ship this?

Where you post decides how explicit you have to be:

| Where                                    | Starts a review when                      |
| ---------------------------------------- | ----------------------------------------- |
| A direct message to Stunt Double         | a link and a request phrase               |
| A channel with design review turned on   | a link, and a request phrase or a mention |
| Any other channel it has been invited to | a link, a mention, and a request phrase   |
| A reply in a thread that holds a review  | the reply is addressed to us, see below   |

The last row is what keeps mentions predictable. "@Stunt Double run the checkout
checklist against `https://...`" is a request for a checklist, so it goes to the
agent rather than coming back as a design review.

A reply inside a thread is different. If the thread already holds a review, a
reply continues it: the reviewer opens the product again, carrying what it
already found plus whatever you just said ("walk the whole checkout", "try it
signed out"). That is how you steer a review without repeating yourself, and it
is why a reply does not need a link or a request phrase.

It also means a reply costs a review. In a channel with design review turned on,
every reply in a review's thread is treated as addressed to us, so a thread where
three people talk through the findings is three more reviews. Somewhere else the
bot has simply been invited, a reply only continues the review when it mentions
the bot or answers a question the bot asked. If you want to talk about a review
rather than extend it, do it outside the thread, or in a channel that does not
have design review turned on.

Up to three links from a single message are reviewed. Anything past that is
ignored, so one message cannot spend an afternoon of browser time.

## 3. What comes back [#3-what-comes-back]

The thread answers immediately, before any work has started: "On it, taking a
look at the link." It stops short of promising feedback on purpose, because the
next thing it says might be a question. That same message is then edited in place
as the review progresses, so the thread holds one message that keeps up to date
rather than a progress log. In a direct message there is no status message at
all: Slack's own loading indicator carries the progress and clears itself when
the review lands.

**The bot may ask one question first.** It opens the first link and looks at it
before deciding, so if the page is behind a sign-in wall, or the request could
mean any of several flows, it turns that same status message into a single short
question with up to three quick-reply buttons. Click one or just reply in the
thread; either way the review starts from your answer. Nothing is left hanging in
the background while it waits, so an unanswered question costs nothing.

Otherwise it reviews straight away. A web page is reviewed by **using** it: the
reviewer drives a real browser, opens menus, fills forms and walks the flow, and
the reply names the screens it went through. Where it cannot do that (a Figma
link, a page that will not open), it falls back to reviewing what it can capture.

When it captures instead of driving, that happens per link:

- A **web page** is opened in a hosted browser and captured from the top down,
  several screens' worth. Deliberately not the full page: a page capture 20,000
  pixels tall is downscaled into an unreadable strip before a model ever sees it,
  which is the worst possible input for a review of the visual design.
- A **Figma link** is rendered through your workspace's Figma connection. A URL
  that names a node renders that frame; without one, the file's first frame.

The reply is a short summary and then the findings, most serious first. Each
finding carries a severity (critical, issue, suggestion, info) and a category
(usability, accessibility, visual, content, interaction, performance). A long
review is trimmed to the top eight findings with a count of what was left out,
so the thread stays readable.

A link that cannot be opened costs that link, not the review: the others still
come back, and the thread says which one failed and why.

## 4. Turn it on in a channel [#4-turn-it-on-in-a-channel]

Direct messages work on their own. A channel is for teams that share work in the
open, where nobody should have to remember to mention a bot.

Under **Design reviews > Settings**, add the channel. Every message
posted there that carries a link and asks for feedback then starts a review, with
no mention needed.

Two things have to be true for that channel to work, and the settings page
checks both against live Slack state rather than a stored flag:

- **The bot is in the channel.** Run `/invite @Stunt Double` in it. Slack only
  delivers messages from channels the app has joined, so without this the channel
  looks configured and silently never fires.
- **The connection can read messages there.** Being mentioned in a channel is a
  different permission from reading it, and public and private channels differ
  again. A connection made before the permission was requested keeps its old
  grant, so the page prompts for a reconnect naming the channel it affects.

**Try it** on the same page posts a request in the channel as the bot and reviews
it, so you can watch the whole flow before asking your team to use it.

Turning a channel off removes the automatic behavior, not the feature: an
@mention with a link and a request still starts a review anywhere the bot has
been invited.

## 5. Ask the agent for anything else [#5-ask-the-agent-for-anything-else]

Anything that is not a link plus a request for feedback goes to the agent. It
runs against one workspace, with the same read and dispatch surface as the
[MCP server](/mcp):

- List the projects, actors, checklists, and workflows in the workspace.
- Start a checklist run and hand back the run it created.
- Start a workflow run.

Both surfaces work: a direct message, or an @mention in any channel it has been
invited to. Replies stream in as they are written rather than landing all at
once.

A thread is one conversation. The first message starts a session that stays warm
for a day, and every follow-up in that thread feeds the same conversation, so
"run that one again" resolves to the checklist you were just discussing. The
direct message also pins a few starting prompts to the top of the Messages tab.

Runs started from Slack are attributed to the person who asked, as long as they
have linked their Slack identity in their notification settings. Everyone else's
runs are attributed to whoever installed the connection, which is worth fixing
before a team leans on this: attribution is what makes a run's history legible a
month later.

## When nothing happens [#when-nothing-happens]

Slack failing quietly is the normal failure here, so work down this list in
order:

1. **The message has no link, or no request phrase.** Both are required on every
   surface. Add "can I get some feedback on this?" and post it again.
2. **It was a reply in a thread nobody has reviewed yet.** A reply only starts a
   review when the thread already holds one. Post the link as a new message.
3. **The bot is not in the channel.** Run `/invite @Stunt Double` there.
4. **The connection is missing a permission.** **Design reviews > Settings** names
   this against each configured channel, with a reconnect button.
5. **The workspace is over a usage limit.** The thread says which limit was hit
   rather than going silent.

Reviews and agent runs are metered against your plan like any other browser work,
plus the tokens the review spends. A review that drives the page uses one browser
session for the whole thing, however many links the message carried; one that
falls back to capturing screens uses one per link.

## Tips [#tips]

- Turn design review on in the channel where work already gets shared, rather
  than making a new one. The point is that nobody changes what they do.
- Ask for the review before the internal critique, not instead of it. It clears
  the obvious contrast, hierarchy, and label problems so your team spends its
  time on the decisions that need judgment.
- Link your Slack identity if you plan to start runs from Slack. Attribution
  costs nothing to set up and cannot be backfilled.
- Send a Figma link with a node selected. The frame you are asking about beats
  the first frame in the file.

## Next steps [#next-steps]

- **[Write checklists that catch real regressions](/tutorials/effective-checklists)**
  to turn a review finding that keeps coming back into a check that runs on every
  deploy.
- **[Collect stakeholder feedback on a prototype](/tutorials/feedback-widget)**
  to gather comments in context from the people reviewing the same screens.
- **[Automate Stunt Double from Claude](/tutorials/automate-with-mcp)**
  for the same read and dispatch surface in an assistant rather than in Slack.
