Marionette MCP: driving a running Flutter app from an AI agent
MCP server enabling AI agents to interact with Flutter apps at runtime - let them inspect widgets, simulate taps, enter text, scroll, and take screenshots.
At a glance
- What is it?
- Marionette MCP is an Apache-2.0 Dart MCP server that gives an AI agent a small set of runtime actions against a live Flutter app: inspect the widget tree, tap, type, scroll, screenshot, read logs. It is a testing and interaction tool, not a build tool, and it assumes you can edit main.dart and run the app in debug mode.
- Who is it for?
- Adopt Marionette MCP if you want an agent to exercise a debug build of a Flutter app through standard Material widgets and read the logs afterwards, and if you can add marionette_flutter to main.dart. Skip it if your UI is a custom design system you are not prepared to annotate, if you need release-mode or CI automation, or if you want dependency and analyzer help rather than runtime interaction.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 6 days ago.
- What is it written in?
- Mainly Dart, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The gap Marionette MCP fills: an agent that can only read code
An AI agent editing a Flutter codebase can read widgets, edit them, and run the analyzer. What it cannot do without help is find out whether the screen it just wrote actually renders, whether the button responds, or whether the API call fired when the form was submitted. The README frames the project as "Playwright MCP/Cursor Browser, but for Flutter apps", and the comparison is the useful part: browser automation gives a web agent a live surface to poke at, and Flutter has no equivalent by default. Marionette MCP is the bridge for that surface. It is aimed at developers who already work with an agent inside Claude Code, Cursor, Copilot, Gemini CLI, or a similar tool, and who want the agent to close the loop between a code change and observed behaviour in a running app. The README's example prompts are all of that shape: implement a Forgot Password screen, then connect, navigate, tap, type, submit, and check the logs that the API call fired. It is not a general test framework and it does not replace widget tests. It is an interaction layer for an agent that is already in the editor.
Two packages and a VM service URI: how the pieces connect
The architecture has three moving parts. Inside your app, marionette_flutter provides a binding; the README's main.dart snippet calls MarionetteBinding.ensureInitialized() inside an if (kDebugMode) branch and WidgetsFlutterBinding.ensureInitialized() otherwise. That binding is what exposes the app to the outside. Second, the MCP server itself, published as marionette_mcp, is activated as a global Dart executable and registered with your AI tool over stdio. Third, the connection between them runs over the Flutter VM service. The README instructs you to run flutter run, copy the VM service URI printed in the console (the example given is ws://127.0.0.1:9101/ws), and ask the agent to connect using that URI. So the agent does not talk to your app directly; it talks to the MCP server, which talks to the app through the debug VM service. There is also a CLI alternative, marionette_cli, described as being for shell-only or restricted environments, which lets a shell-capable agent drive the same surface without an MCP client. The README states that the tool surface is deliberately small and returns the minimum actionable data, which is a real design decision: fewer, higher-signal tools keep the agent's context from filling with widget dumps.
The tool set an agent actually gets
The README lists the actions: get_interactive_elements to inspect the widget tree, tap, secondary_tap, double_tap, long_press, swipe, pinch_zoom, scroll_to, enter_text, press_back_button, take_screenshots, get_logs, and hot_reload. Two of these deserve attention. get_interactive_elements is the entry point for almost every workflow, because it is how the agent discovers what is on screen and what it can address; the README's own debugging prompt says to find the 'Clear Cache' button via get_interactive_elements, tap it, and analyze the logs. hot_reload matters because it lets the agent change code and see the result without the human restarting the app, which is the difference between a demo and a usable loop. get_logs is not automatic: the README points to a separate log collection guide covering logging, logger, or a custom setup, so log wiring is configuration you own. Beyond the built-in tools, the project supports Custom Extensions through registerMarionetteExtension, which lets the app expose its own actions to the agent, with the README naming route navigation, seeding test data, and toggling feature flags as examples. That extension point is where a team with app-specific workflows would invest.
Getting it running: the five documented steps
The Quick Start is short. Add the Flutter-side binding with flutter pub add marionette_flutter, then initialize it in main.dart as shown above. Install the bridge with dart pub global activate marionette_mcp. Register it with your AI tool; for Claude Code the README gives claude mcp add --transport stdio marionette -- marionette_mcp, and it points to docs/mcp-tools.md for Cursor, Gemini CLI, Copilot, and Antigravity. Run the app in debug mode with flutter run and copy the VM service URI from the console. Then ask the agent to connect with that URI. The README carries an explicit warning that this is the short version and that docs/getting-started.md walks through each step, which is worth taking at face value: the binding has a single-binding rule documented in docs/flutter-setup.md, and getting two bindings initialized in one app is the kind of mistake that produces confusing failures rather than a clear error. The other configuration surface is docs/configuration.md, which covers custom design systems and a Production Setup Checklist and includes a complete main.dart example.
The custom design system wall
This is the limitation that will decide adoption for many teams. The README states that standard Material widgets work out of the box, and then, in a callout, that if your app uses a custom design system, configuration is required, otherwise the agent cannot see or tap your custom buttons and fields. The reason is that the agent's view of the app is built from semantics and widget information, so a button that is a custom-painted rectangle with a GestureDetector and no semantic annotation is invisible to get_interactive_elements. The README points to docs/semantics.md for making custom-painted and rich content readable to agents, and to the Production Setup Checklist in docs/configuration.md. For a team on stock Material, this is a non-issue. For a team with an in-house component library, the setup cost is proportional to how much of that library lacks semantics, and it is work that has to be done before the agent is useful at all. That is a reasonable trade, but it is not a small one, and it is the first thing to measure before rolling the tool out to a team.
Debug-only by design, and the wrong tool for release automation
The main.dart pattern gates the binding behind kDebugMode, and the workflow depends on flutter run and a VM service URI. That places the tool firmly in the development loop. It is not a release-mode automation harness and nothing in the README suggests it is meant to be one: there is no mention of running headless in CI, no mention of a device farm, no mention of parallel runs. If what you need is a regression suite that runs on every pull request without a human and an agent in the loop, this is the wrong layer, and Flutter's existing integration test tooling is the more direct answer. The other boundary is the agent itself. Because the loop is driven by prompts, results depend on the model's ability to interpret get_interactive_elements output and choose the next action. That makes the tool's small surface area a virtue and also a ceiling: anything not expressible as one of the listed actions, or as a Custom Extension you write, is out of reach. The README also links a troubleshooting guide for "common gotchas and limitations", which is a candid signal that the happy path is not the whole story.
How it differs from the official Dart and Flutter MCP server
The README draws the comparison itself, and the distinction is about instrumentation rather than features. The official Dart and Flutter MCP server is described as focused on development-time tasks: searching pub.dev, managing dependencies, analyzing code, and inspecting runtime errors. It can also drive the UI, but the README says it does so through Flutter Driver, which introduces extra instrumentation into your app. Marionette MCP takes the opposite position: it does only runtime interaction, and it does so with what the README calls minimal changes to your app, meaning the binding plus whatever semantics work your custom widgets need. The practical difference is what you install and what you maintain. Flutter Driver-based tooling changes how the app is built and run; Marionette's binding is a debug-mode initialization line. The README's own summary is that you use Flutter MCP to build your app and Marionette MCP to test and interact with it. These are complements, not substitutes, and a team already using the official server for dependency and analyzer work would add this one rather than switch.
Maintenance cost, licence, and what to check before adopting
The project is Apache-2.0, which permits commercial and closed-source use and includes an explicit patent grant; that is a permissive licence, and as always the obligations around notices and attribution should be read in the LICENSE file rather than taken from a summary. The repository shows a steady release cadence through 2026, with v0.6.0 in June, v0.5.0 in April, and v0.4.0 in March, and the version numbers are still in the 0.x range, which in Dart convention signals that the API can move between minor releases. The upgrade cost is therefore not zero, and the surface you would have to re-check after a bump is specific: the MarionetteBinding initialization in main.dart, any registerMarionetteExtension calls, the MCP tool registration command you added to your AI client, and the log wiring behind get_logs. Pin the version of marionette_flutter in pubspec.yaml if you want to control when that check happens. The one thing worth doing before any wider rollout is the exercise the README itself recommends: walk the Production Setup Checklist in docs/configuration.md against a single real screen, confirm get_interactive_elements surfaces the controls you care about, and only then decide whether to extend the setup to the rest of the app.
Editorial conclusion
Adopt Marionette MCP if you want an agent to exercise a debug build of a Flutter app through standard Material widgets and read the logs afterwards, and if you can add marionette_flutter to main.dart. Skip it if your UI is a custom design system you are not prepared to annotate, if you need release-mode or CI automation, or if you want dependency and analyzer help rather than runtime interaction. Before committing, run the Production Setup Checklist in docs/configuration.md against one real screen of your app and confirm that get_interactive_elements returns the buttons and fields you expect.
Community notes