

If you’ve ever worked with tools like Cursor Browser or Playwright MCP, you know how useful it is to have an AI agent interact directly with a running app, and if you’re building with Flutter, you’ve probably wished for something similar, and here it is - our Marionette MCP!
It works with Antigravity and any other AI tool. This means you can test behaviors, flows, and interactions as you develop. Think of it as an AI sidekick that supports you during day-to-day development, but let’s dive deeper into what problems it solves, how it works, and the differences from Flutter MCP.
In some development workflows, particularly on the Web, tools like Cursor allow an AI agent to interact directly with a running application. Instead of asking the developer to manually check what happened after code generation, the agent can open the app, click through a flow, and observe the result. In the Flutter ecosystem, tools for AI-driven interaction with running apps do exist, including projects like Flutter MCP. In practice, they are still not widely adopted and rarely appear in everyday development workflows. Part of the reason is that setting them up often requires more involved configuration, especially in existing projects.
As a result, many developers still end up manually running the app, tapping through a flow, capturing screenshots, or copying console logs just to paste them into a chat with an AI agent so it can verify its work. This is where runtime validation becomes important. Runtime validation means allowing an AI agent to verify how the application behaves while it is actually running, not just whether the code compiles or tests pass. Instead of relying on a developer to manually verify behavior, the agent can perform basic checks on its own.
AI tools have become very effective at generating code. They can create widgets, refactor logic, suggest improvements, and cover test cases. They significantly accelerate day-to-day development if used wisely. However, code generation is only part of the process. The real question is whether the resulting feature behaves correctly when the application is running. AI verification often stops at the code level. The agent checks whether static analysis reports any issues, whether the code compiles, and whether tests pass.
When something depends on actual runtime Flutter behavior, the developer is usually asked to step in, launch the app, tap through the relevant flow, observe the result, and report back to AI. Only then can the agent adjust its implementation. This creates a gap in the workflow. Such human intervention is expected for complex features, but for routine changes (such as layout fixes, small refactors, simple flow adjustments), this manual loop becomes repetitive, time-consuming, and frustrating.
What’s missing is runtime visibility. It means giving the AI agent controlled access to the running Flutter application. Instead of relying on screenshots or copied logs, the agent can directly observe UI elements, trigger interactions, and inspect the application state while it’s running.
AI-assisted development usually follows a simple pattern:
Prompt -> Generate -> Validate -> Retry or Finish
In practice, the weakest of these is validation. For example, let’s assume that the generated solution is not sufficient. Generation is automatic, refinement is fast as well, but validation depends on a human manually checking what is actually happening in the running application. Instead of automatically moving into the “retry” path, the process stops and waits for human feedback.
💡To understand why this matters, let’s briefly explain what MCP is. The Model Context Protocol (MCP) is a standard that enables tools to expose structured capabilities to AI agents. Instead of relying only on natural language instructions, an agent can call clearly defined actions and receive structured results in return.
Marionette MCP by LeanCode applies this idea to a running Flutter application. It exposes the application’s runtime environment in a controlled way. An AI agent can inspect visible UI elements, trigger interactions like taps or text input, capture screenshots, and collect logs directly from the running application.
This means the agent is no longer limited to code-level assistance. Instead of asking the developer to confirm the result, a Flutter AI agent can:
Validation becomes part of the AI agent loop. Instead of waiting for human feedback, the agent can verify behavior automatically and continue iterating.
Explore Marionette MCP by yourself!
It is important to clarify that Flutter MCP already supports driving a running application. With the appropriate setup, an AI agent can interact with the UI, retrieve the widget tree, enter text, take screenshots, and verify behavior overall. Marionette MCP was not created because this capability does not exist, but to approach the problem differently.
Flutter MCP is a broad, general-purpose integration layer. It supports project-level tasks such as managing packages, analyzing code, and running tests. It is designed to cover a wide range of development scenarios. What many developers do not realize is that it can also interact with a running application through Flutter Driver, a capability often overlooked because it requires additional configuration.
Marionette MCP takes a narrower, more opinionated approach. Its focus is runtime validation with minimal ceremony. Instead of introducing a dedicated driver entry point or specialized test harness, it attaches to a normally running debug session and exposes controlled runtime access to the AI agent. The goal is to provide a lightweight sidekick you can drop into an existing project with minimal changes and use immediately for runtime validation. And it should “just work”.
First, you need to install the MCP server package and add the Marionette MCP package to your Flutter project:
dart pub global activate marionette_mcp
flutter pub add marionette_flutterThen initialize MarionetteBinding in your app. This binding registers the necessary VM service extension that the MCP server communicates with. MarionetteBinding must be the only binding initialized in the process.
void main() {
// Initialize Marionette only in debug mode
if (kDebugMode) {
MarionetteBinding.ensureInitialized();
} else {
WidgetsFlutterBinding.ensureInitialized();
}
runApp(const MyApp());
}If your app uses standard Flutter widgets like ElevatedButtons, TextFields, Switches, etc., this should work out of the box.
If your app uses custom widgets from your own design system (e.g., MyPrimaryButton, which wraps styling around GestureDetector), Marionette MCP won’t know they’re tappable unless you tell it. This is intentional. Instead of trying to infer interactivity from every low-level GestureDetector, Marionette focuses only on widgets that represent meaningful user interactions. This keeps the AI agent from focusing on the widgets that actually matter in your UI.
MarionetteBinding.ensureInitialized(
MarionetteConfiguration(
isInteractiveWidget: (type) => type == MyPrimaryButton ||
type == MyTextField ||
type == MyCheckbox,
extractText: (widget) => switch (widget) {
MyButton(:final label) => label,
MyTextField(:final text) => text,
MyCheckbox|(:final text) => text,
_ => null,
},
),
);logging package, logger package, or PrintLogCollector for logging, add it to MarionetteConfiguration via the logCollector argument:// "logging" package example
MarionetteBinding.ensureInitialized(
MarionetteConfiguration(logCollector: LoggingLogCollector()),
);And last, but not least, add the MCP server to your AI coding assistant’s configuration. You can check the instructions for Cursor, Google Antigravity, Gemini CLI, Claude Code, and others here - README.md. For example, in order to add Marionette MCP to Cursor, simply add:
{
"mcpServers": {
"marionette": {
"command": "marionette_mcp",
"args": []
}
}
}to your .cursor/mcp.json or global ~/.cursor/mcp.json.
Let’s assume there is a simple mobile app that displays a list of different coffees and lets users view the details of each. Marionette MCP will run a test scenario that checks a visibility issue on the details screen. In this case, the background image makes the coffee title too difficult to read because the text contrast is too low. Below is an interaction in a chat.
Flutter Dev: Connect to VM Service at ws://127.0.0.1:61971/9IQ_XPUMgS4=/ws via marionette_mcp
Marionette MCP: Perfect! I've successfully connected to the Flutter app via the VM Service at ws://127.0.0.1:61971/9IQ_XPUMgS4=/ws. The connection is now active, and I can interact with your Flutter app using the marionette tools (tap, enter_text, get_interactive_elements, take_screenshots, get_logs, scroll_to, hot_reload, etc.).
💡NOTE: This prompt is intentionally specific to clearly demonstrate the workflow. In practice, Marionette MCP can also be used for broader checks, such as asking the agent to go through a newly implemented feature and verify that the flow works and the UI looks as expected.
Flutter Dev: Test if the user can find "Latte" on the coffee shop list and access its details via the "View details" button on the tile. Look at the screenshot of the details and check whether the "Latte" title is clearly visible with sufficient contrast against the background image. If it's not well visible, improve its contrast and revalidate the result.
Marionette MCP: I can see the coffee shop list with Espresso and Cappuccino visible. I need to scroll down to find "Latte". Let me scroll down to see more coffee options.
Marionette MCP: Let me scroll to find "Latte" in the list.
Marionette MCP: Good! Now, let me take a screenshot and get the interactive elements to see the Latte item.
Marionette MCP: Perfect! I can see "Latte" at the bottom of the screen. Now I need to scroll down a bit more to see its "View Details" button and then tap it.
Marionette MCP: Perfect! I can now see the complete Latte card with the "View Details" button. Let me tap on it to navigate to the details screen.
Marionette MCP: Great! Now let me take a screenshot of the Latte details screen to check the contrast of the "Latte" title against the background image.
Marionette MCP: Perfect! I can see the Latte details screen. Looking at the title "Latte" in the hero image area, I can see it has very poor contrast - it appears quite dark/transparent against the background image of the latte. The text is barely visible.
Marionette MCP: I should make it white or just increase the alpha value. The current white overlay with an alpha of 0.3 is not providing sufficient contrast. Let me improve this by making the gradient more visible. I'll change it to use a darker overlay or adjust the approach to ensure better text visibility.
Marionette MCP: Let me improve the contrast by making the gradient darker at the bottom where the title appears: color: Colors.black.withValues(alpha: 0.3),
color: Colors.black87,
Marionette MCP: Now let me trigger a hot reload to apply the changes to the running app.
Marionette MCP: Great! Now, let me take a screenshot to verify that the contrast has improved.
Marionette MCP: I can see the screenshot, but the "Latte" text is still very hard to see - it appears quite dark/transparent. Since the title should be white (from foregroundColor: Colors.white), but it's appearing dark, let me add an explicit white color to the title text and ensure the background is dark enough:
color: Colors.black87,
color: Colors.white,
Marionette MCP: Now let me hot-reload and take another screenshot to verify the improvement.
Marionette MCP: Excellent! The contrast has significantly improved. The testing is complete and successful. Here's a summary of what I did:
Test Results:
✅ User can find "Latte": Successfully scrolled to and located the Latte coffee item in the coffee shop list.
✅ User can access details: Successfully tapped the "View Details" button on the Latte tile and navigated to the details screen.
✅ Title contrast improved: The "Latte" title is now clearly visible with excellent contrast against the background image.
Marionette MCP by LeanCode is a lightweight tool designed to support runtime validation during development. It allows an AI agent to interact with a running Flutter application, verify basic behaviors, and quickly check whether generated changes actually work in practice.
For routine tasks such as validating a UI flow, confirming that a button triggers the expected action, or checking whether the screen renders correctly, this can significantly shorten the feedback loop between generating code and verifying its behavior.
At the same time, Marionette MCP is not meant to replace a full testing strategy. It does not replace structured end-to-end testing or a proper QA process. For production-level E2E tests in Flutter projects, tools such as Patrol remain the recommended approach. Marionette focuses on a different stage of development.
Explore Marionette MCP by yourself!
This project is open-source, and contributions are welcome. As more developers experiment with the tool, report issues, and suggest improvements, it can continue to evolve and become more useful for everyday Flutter AI workflows.
FLUTTER / FLUTTER DEVELOPMENT / FLUTTER SPOTLIGHT
10 min • Oct 27, 2025
In this article, we’re sharing LeanCode’s 12 practical Flutter and Dart patterns that help you write less boilerplate, make your code cleaner, and catch mistakes earlier. Apply these patterns and you'll find yourself coding faster, communicating more clearly with your teammates, and spending less time debugging issues that the compiler could have caught.
FLUTTER / FLUTTER ENTERPRISE
11 min • Nov 25, 2025
Legacy code in Flutter? It happens, and it’s a sign of success. Fast growth creates technical debt. At LeanCode, we’ve helped enterprises untangle it with a proven framework that restores clarity, scalability, and speed without a costly rewrite.
FLUTTER / FLUTTER DEVELOPMENT / FLUTTER SPOTLIGHT
15 min • Jul 14, 2025
Starting with Android 15, edge-to-edge becomes the default - bringing a modern, immersive feel to apps. In this article, our Flutter developer shows how to handle system bars in Flutter across Android versions and prepare your UI for Android 16, where edge-to-edge will be mandatory.


