Identity & guidance
Confirm the signed-in user, load the authoring guides, and report tool friction.
3 tools · get_me · get_context · give_feedback
Reflow is an MCP server your coding agent connects to. The agent drives a real cloud browser in plain language — exploring, reading, verifying — then freezes what worked into flows that run against every PR preview, with your own model key, posting the evidence where you merge.
Reflow is an MCP server. Add it to Claude Code, Cursor, Codex — anything that speaks MCP — and your agent can author flows, run them in real browsers, and bring back the evidence.
One click for the popular ones, one JSON block for everything else. No SDK, no test framework, nothing to learn up front — your agent reads the tools and knows what to do.
{
"mcpServers": {
"reflow": {
"type": "http",
"url": "https://mcp.reflow.io/mcp"
}
}
}No token to paste — the first connection opens browser sign-in (OAuth 2.1).
The server teaches the full workflow through its hand-written tool descriptions. Here are the current capability groups and representative tools your agent can call.
Confirm the signed-in user, load the authoring guides, and report tool friction.
3 tools · get_me · get_context · give_feedback
Create and version flows, inspect tags, follow dependencies, and reuse upstream output.
8 tools · list_flows · create_flow · get_flow_graph · get_flow_memory · save_flow_content
Run flows, tail step events, retrieve screenshots, and complete email-based journeys.
10 tools · create_run · tail_run · get_run_screenshot · get_mailbox · wait_for_message
Open a cloud browser, read its state, drive it with RFL, and capture the viewport.
9 tools · open_session · snapshot · act · screenshot · close_session
Boot approved team images, run shell commands, stream output, and inspect containers.
8 tools · list_images · run_image · exec · tail_exec · get_container
Provider keys and API tokens stay in the dashboard. MCP OAuth never asks for a token to paste.
dashboard boundary
Your agent doesn't just queue runs — it drives a live cloud browser through MCP. Plain language in; snapshots, screenshots, and what the page actually says back out. No selectors, no local Chrome, no playwright scripts to babysit. And because every command is a Reflow step, your agent can rewrite the session transcript into a permanent flow.
⏺ reflow:open_session (target_url: pr-418 preview) → session_07
⏺ reflow:act ("add the first product to the cart") → ✓ cart badge: 1
⏺ reflow:act ("check out with the standard test card") → ✓ on /confirm
⏺ reflow:snapshot → "$24.00 — order #1042"
⏺ reflow:create_flow (transcript rewritten as named step fences) → checkout-happy-path
✓ verified by hand once — deterministic on every PR from now on “Fill out the signup form with realistic values” is a complete, runnable step — and so is “read the verification email and sign in with the magic link”: every run gets its own inbox. Reflow compiles steps to RFL once, then replays them deterministically — a real browser, realistic data, no model in the loop while they pass.
# named step fences
step="fill out the signup form with realistic values"
step="submit the form"
step="read the verification email and sign in"
step="the welcome screen greets the user by name"
# frontmatter invariant
expect role=alert hidden
Full name
City
Verify your email — your-app
https://your-app.dev/auth/magic?token=…
✓ “Welcome, Maya” — signed in, intent satisfied
Declared snapshot checkpoints compare against the approved
baseline for the selected browser. Comparison is perceptual: SSIM weighted the way human
eyes weigh difference, so anti-aliasing and sub-pixel shifts never cry wolf, while a moved
button always does. Intentional changes arrive as a reviewable comparison — one click
approves the new baseline.
✓ capture declared snapshot checkpoints
✓ diff against the baseline from main
◐ 2 regions changed — intentional?
✓ approved — baseline travels with the branch
Snapshots are captured when the page stops changing — adaptive stabilization, not fixed sleeps — and replays are deterministic: same viewport, compiled RFL. A diff means the UI changed, not that the test got flaky.
capturing baseline…
Approve & commit delta◐ visual differences detected
A moved button or a new banner is a warning you review, not a failure. Approve it and the baseline updates in the same PR — nothing to reconcile after merge.
✗ checkout did not succeed
Product breakage fails loud and blocks the merge. Visual review never buries a real failure — the two outcomes are never mixed.
The same flows run against your dev server before a PR even exists. Point
target_url at localhost and the MCP connection opens an
ephemeral, run-scoped tunnel — the cloud browser reaches your machine, nothing is exposed
beyond the run, and your agent verifies the change before you push it.
⏺ reflow:create_run (target_url: http://localhost:3000) → run_201 ⏺ tunnel up: run-201.tunnel.reflow.io → localhost:3000 (closes with the run) ⏺ reflow:tail_run (run_201) → 4/4 steps ✓ · magic link handled · intent satisfied ✓ safe to push — opening the PR
Connect the MCP server and ask your agent to add Reflow. It discovers the tools, writes the flow, runs it against your preview, and reads the evidence.
Reflow posts into the pull request: a merge-blocking check per flow, the proposed fix as a reviewable diff, screenshot deltas against the main baseline — and one button that commits the accepted changes back to the branch.
browse-and-search
Layout changed against the main baseline: a promo banner was added and the cart badge moved. Every stated expectation still holds — this looks intentional. Approving commits the new baseline and the expectation update below.
baseline · main
this PR
delta · 2 regions
## Always true
- the cart badge shows the running item count
- - the page shows no banners above the product grid
+ - a single promo banner may appear above the product grid checkout-happy-path
Failed at “pay with the standard test card”: submitting payment returned
500 from /api/pay and no
confirmation rendered. The expectation
“the confirmation page shows an order number and the correct total” cannot be met.
This looks like a product regression, not a flow problem — nothing to heal.
step 3/3 · pay with the standard test card
POST /api/pay → 500 Internal Server Error
page state: spinner persisted 30s, no confirmation, console: TypeError in pay.ts
✗ checkout did not succeed
Plain-language intent sits beside named ts step="…" fences.
Their bodies can be deterministic RFL, Playwright TypeScript, or empty for an agent-driven
step. The shipped parser runs those fences in document order.
---
name: Checkout — happy path
url: /checkout
invariants:
- 'expect page url~="/confirmation"'
budgets: { tokens: 50000 }
---
A signed-in user buys a single item.
```ts step="open checkout"
open /checkout
```
```ts step="complete checkout"
click role=button name="Checkout"
expect page url~="/confirmation"
```