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 MCP server that lets a coding agent inspect widgets, tap, type and screenshot a Flutter app while it runs. It is small on purpose, and the custom design system case is where the setup cost lands.
- Who is it for?
- Adopt Marionette MCP if your agent already runs against a debug build and you want runtime interaction without wiring Flutter Driver into the app. Skip it if your team needs scripted, repeatable test suites in CI, or if your UI is a custom design system you are not prepared to annotate for the agent.
- 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 received new commits within the last day.
- 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 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The gap Marionette MCP fills between a code agent and a running app
A coding agent can read your Dart source and reason about a widget tree it has never seen rendered. What it cannot do by default is press the button. Marionette MCP exists to close that loop: it connects an agent to a Flutter app that is already running in debug mode and gives it a small set of actions for inspecting and manipulating the live UI. The README frames the project as "Playwright MCP/Cursor Browser, but for Flutter apps", which is the clearest statement of intent in the repository.
The audience is Flutter developers who already use an MCP-capable agent (the README names Claude Code, Copilot, Cursor and Gemini CLI) and want it to verify its own work. The README's own example prompts are telling: implement a Forgot Password screen, then connect, navigate, tap the link, enter an email, submit, and check the logs that the API call fired. That is smoke testing driven by the same agent that wrote the code. It is not a replacement for a scripted test suite, and the README does not claim it is.
How the bridge works: a binding in the app, an MCP server outside it
There are two halves. Inside the app, you add the marionette_flutter package and initialize MarionetteBinding in main.dart. The README is explicit that this is the required piece. Outside the app, the marionette_mcp package runs as an MCP server over stdio and connects to the app through the Dart VM service URI that flutter run prints to the console, for example ws://127.0.0.1:9101/ws. The agent talks MCP to the server; the server talks to the running app.
The tool surface is deliberately narrow. The README lists get_interactive_elements for inspecting the widget tree, then tap, secondary_tap, double_tap, long_press, swipe, pinch_zoom and scroll_to for input, plus enter_text, press_back_button, take_screenshots, get_logs and hot_reload. The stated reason for keeping it small is prompt hygiene: fewer tools and less returned data means the agent's context stays manageable. That is a design position, not a limitation of the protocol, and it means the agent sees a curated list of interactive elements rather than the entire widget tree.
Because the app can also register its own tools through registerMarionetteExtension, the surface is extensible without the server growing new built-ins. The README gives the examples of navigating by route name, seeding test data and toggling feature flags. A separate marionette_cli package exists for shell-only environments where an MCP client is not available.
Installing Marionette MCP and driving a first tap
The README splits setup into preparing the app and installing the bridge. Start with the app side, since the server is useless without it. Adding the binding package is one command:
flutter pub add marionette_flutterThen main.dart needs to choose the binding based on build mode. The README gives this exact snippet, and the kDebugMode guard is the part that matters: the binding is only installed in debug builds.
void main() {
if (kDebugMode) {
MarionetteBinding.ensureInitialized();
} else {
WidgetsFlutterBinding.ensureInitialized();
}
runApp(const MyApp());
}On the server side, activate the bridge from pub.dev and register it with your agent. The README shows the Claude Code command, and notes that Cursor, Gemini CLI, Copilot and Antigravity are covered in the MCP tools documentation.
dart pub global activate marionette_mcp
claude mcp add --transport stdio marionette -- marionette_mcpNow run the app with flutter run, copy the VM service URI from the console, and ask the agent to connect using that URI. A first useful prompt is the one the README suggests for investigating a dead button: find the element via get_interactive_elements, tap it, then read get_logs. The repository also ships a Dockerfile that installs a pinned version from pub.dev (MARIONETTE_VERSION defaults to 0.6.0) and sets marionette_mcp as the entrypoint over stdio, which is the form a Docker MCP gateway launches.
Custom design systems are where the minimal-change promise breaks
The README's most important warning is easy to skim past. Standard Material widgets work out of the box. If your app uses a custom design system, configuration is required, and without it the agent cannot see or tap your custom buttons and fields. The configuration guide points at a Production Setup Checklist, and there is a separate semantics guide for custom-painted or otherwise rich content that needs to be made readable to an agent.
This is the honest boundary of the project. The pitch is minimal changes to your app, and for a Material app that holds. For a design system with its own button and text field primitives, the agent's view of the UI is only as good as the annotations you add. Budget for that work before you promise a team that the agent will just drive the app.
Two other constraints are structural rather than incidental. The binding is debug-only, so this is a development and smoke-testing tool, not something that runs against a release build or in production. And the README refers to a single-binding rule in the Flutter setup guide, which means the initialization has to be done once and in the right place rather than scattered.
Marionette MCP against Flutter MCP and Patrol
The README draws its own comparison with the official Dart and Flutter MCP server. That server targets development-time work: searching pub.dev, managing dependencies, analyzing code, inspecting runtime errors. It can drive the UI, but through Flutter Driver, which the README says introduces extra instrumentation in your app. Marionette MCP does only runtime interaction and asks for less instrumentation. The README's summary is a division of labour: use Flutter MCP to build the app, use Marionette MCP to test and interact with it.
Patrol is a different kind of tool, and the difference is the shape of the test. Patrol-style integration testing is a scripted suite that runs the same way every time and can gate a pipeline. Marionette MCP is an agent driving an app interactively, deciding what to tap next from what it sees. Those produce different guarantees. A scripted suite is reproducible; an agent session is exploratory. If your requirement is a test that fails the build on a regression, the agent-driven approach is the wrong instrument, and nothing in the README suggests otherwise. If your requirement is letting the agent that just wrote a screen confirm the screen works before handing it to you, that is exactly the case Marionette MCP was built for.
Maintenance, licence and the upgrade cost you are accepting
The repository is not archived, and the last push was on 2026-09-10. Releases are tagged and reasonably spaced: v0.4.0 on 2026-03-16, v0.5.0 on 2026-04-09, v0.6.0 on 2026-06-23. The version is still 0.x, which is the practical signal to weigh. Tool names, the binding API and the extension registration API can change between minor releases, and an agent configuration that hardcodes tool names will need attention when they do.
Upgrading has two moving parts, and they are independent. The marionette_mcp server is a global pub activation, so a bump is a re-activation at a new version. The marionette_flutter binding is a normal dependency in your app's pubspec, so it follows your usual dependency upgrade path. The Dockerfile shows the project's own approach to pinning: MARIONETTE_VERSION is an ARG defaulting to 0.6.0, and the comment notes it can be overridden with --build-arg, with pinning keeping the image reproducible for a given Docker MCP Registry commit. That pattern is worth copying if you containerize the server.
The licence is Apache-2.0, which is a permissive licence with an explicit patent grant and a requirement to preserve notices. That is a general description of the licence text, not advice about your situation; if the distinction matters to your legal team, they should read the LICENSE file in the repository.
Editorial conclusion
Adopt Marionette MCP if your agent already runs against a debug build and you want runtime interaction without wiring Flutter Driver into the app. Skip it if your team needs scripted, repeatable test suites in CI, or if your UI is a custom design system you are not prepared to annotate for the agent. Before committing, verify three things on your own build: that MarionetteBinding.ensureInitialized() is reached only in debug mode, that the VM service URI the agent connects to is the one printed by flutter run, and that your custom buttons and fields appear in get_interactive_elements rather than being invisible to the agent.
Frequently asked questions
Does Marionette MCP work with a Flutter app that uses a custom design system?
Not out of the box. The README states that standard Material widgets work without extra work, but a custom design system requires configuration, otherwise the agent cannot see or tap your custom buttons and fields. The configuration guide points to a Production Setup Checklist as the starting point.
How do I install the Marionette MCP server?
The README's quick start activates it from pub.dev with dart pub global activate marionette_mcp, then registers it with your AI tool. For Claude Code the documented command is claude mcp add --transport stdio marionette -- marionette_mcp.
What is the difference between Marionette MCP and Flutter MCP?
According to the README, the official Dart and Flutter MCP server focuses on development-time tasks such as searching pub.dev, managing dependencies and analyzing code, and drives the UI through Flutter Driver, which adds instrumentation to your app. Marionette MCP focuses only on runtime interaction and requires minimal changes to the app.
Can Marionette MCP run against a release build of my app?
The README's setup snippet initializes MarionetteBinding only inside a kDebugMode check, with WidgetsFlutterBinding used otherwise. That makes the binding a debug-build feature, so the intended use is development and smoke testing rather than release builds.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/leancodepl-marionette-mcp)