Skip to content

Start

Five minutes from a plain Flutter app to an agent that taps through it. This page covers apps built from Material widgets; a custom design system adds one step, in Configure.

How it fits together

Marionette has two halves. A package in your app opens it to the agent through the Dart VM service, the same channel Flutter DevTools uses. A bridge, the MCP server or the CLI, turns the agent’s tool calls into actions in the app.

Your agent

Claude Code, Cursor, Codex, …

MCP or shell
The bridge

marionette_mcp, or the marionette CLI

VM service
Your app, in debug

marionette_flutter and its binding

The app always needs the package. For the bridge, pick either one: both see and drive the app, and both write the same session reports.

Add the package

Add marionette_flutter to the app, then initialize MarionetteBinding in main(), in debug builds only. For standard Material widgets the defaults are enough.

terminal
flutter pub add marionette_flutter
lib/main.dart
void main() {
  if (kDebugMode) {
    MarionetteBinding.ensureInitialized();
  } else {
    WidgetsFlutterBinding.ensureInitialized();
  }
  runApp(const MyApp());
}

Give your agent the tools

Pick the bridge you prefer. Both see and drive the app, and both write the same session reports.

MCP server

Your agent calls Marionette’s tools. Install the server once, then register it with your agent.

terminal
dart pub global activate marionette_mcp
terminal
claude mcp add --transport stdio marionette -- marionette_mcp
terminal
codex mcp add marionette -- marionette_mcp
.cursor/mcp.json
{
  "mcpServers": {
    "marionette": { "command": "marionette_mcp", "args": [] }
  }
}
terminal
gemini mcp add marionette marionette_mcp
terminal
copilot mcp add marionette -- marionette_mcp
mcp.json
{
  "servers": {
    "marionette": { "command": "marionette_mcp", "args": [] }
  }
}
mcp_config.json
{
  "mcpServers": {
    "marionette": { "command": "marionette_mcp", "args": [] }
  }
}

Prefer a dev dependency to a global tool? Run dart pub add dev:marionette_mcp, and register dart run marionette_mcp as the server command.

CLI

Your agent runs marionette commands in its shell. Install the CLI once; the Agent Skills below teach the agent its commands.

terminal
dart pub global activate marionette_cli

Add the Agent Skills

marionette_flutter ships three Agent Skills: one prepares an app for Marionette, one drives it with either bridge and writes the report, and one turns a verified run into Patrol tests. This documentation then does not have to be in its context. Dart’s package skills tooling installs them into the folder your agent reads.

terminal
$ flutter pub add marionette_flutter   # if it is not a dependency yet
$ dart run skills@ get

Without the Dart toolchain, npx skills add leancodepl/marionette_mcp reads them straight from GitHub. Run the install again after upgrading the package.

Run the app

Start the app in debug mode. Among the first lines, flutter run prints the VM service URI. The agent needs the ws://…/ws part.

flutter run
$ flutter run
…
A Dart VM Service on sdk gphone64 x86 64 is available at: http://127.0.0.1:9101/V3N5aBc=/
The Flutter DevTools debugger and profiler on sdk gphone64 x86 64 is available at:
http://127.0.0.1:9101/V3N5aBc=/devtools/?uri=ws://127.0.0.1:9101/V3N5aBc=/ws

Paste that URI into the prompt yourself, or ask the agent to run the app: it starts flutter run, reads the URI from the output, and connects.

The first prompt

Give the agent a task:

prompt
Connect to ws://127.0.0.1:9101/V3N5aBc=/ws. Open the login screen, sign in
with test@example.com, and check the logs that the sign-in request fired.
prompt
Run the app with flutter run and connect to it. Open the login screen, sign
in with test@example.com, and check the logs that the sign-in request fired.

The agent calls connect, then get_interactive_elements to see the screen, then tap and enter_text with the keys or the text it found, then get_logs for the proof. That is the whole loop: look, act, look again.

For a real task, name the screen, the precondition and the goal. Drive has prompts for common jobs.

Try the example app first

The repository has an example app that is already set up: a custom design system with its configuration, log collection, session reports, device config and a custom navigation extension. It is the lowest risk first run.

terminal
$ git clone https://github.com/leancodepl/marionette_mcp
$ cd marionette_mcp/example
$ flutter run
prompt
Connect to <the ws:// URI from flutter run>. Open the Custom Widgets screen
and list what you see. Which of the design-system widgets can you tap?

Is it working?

  • connect answers with a success message. A version mismatch means marionette_mcp and marionette_flutter differ: upgrade both.
  • get_interactive_elements lists your buttons and fields. If your own widgets are missing, the configuration in Configure is the fix, not a bug.
  • tap changes the screen, and a second get_interactive_elements shows the new one.
  • get_logs returns lines. If it explains how to enable a log collector instead, wire one up in Configure.

FAQ

Does it work in release builds?

No, by design. Marionette reaches the app through the Dart VM service, which exists only in debug and profile builds. Gate the binding on kDebugMode, or on !kReleaseMode to include profile builds, and nothing of it reaches the release binary.

Which agents does it work with?

Any agent with MCP support, such as Claude Code, Codex, Cursor, Gemini CLI, Copilot or Antigravity, through the MCP server. Any agent that runs shell commands, through the marionette CLI. Most agents can use either.

Do I have to change my widgets?

Not for Flutter's own widgets. A custom design system needs isInteractiveElement, and usually extractText, in the configuration. A ValueKey or a Semantics(identifier:) gives the agent a stable target.

Does it replace integration tests or Patrol?

No. Its gestures simulate a user on a best-effort basis, which is right for an agent checking its own work. For a suite that must pass or fail the same way on every CI run, use Patrol.

How does the agent find my app?

You give it the ws:// VM service URI that flutter run prints, for example ws://127.0.0.1:9101/ws. Pasting it into the prompt is the most reliable way.

connect fails with a version mismatch.

marionette_mcp checks that the app runs the same version of marionette_flutter. Upgrade both packages together.

Is it free?

Yes. Marionette is open source under the Apache 2.0 license and maintained by LeanCode.