{"id":37879320,"url":"https://github.com/leancodepl/marionette_mcp","last_synced_at":"2026-01-16T16:47:21.946Z","repository":{"id":332134560,"uuid":"1130967478","full_name":"leancodepl/marionette_mcp","owner":"leancodepl","description":"MCP server enabling AI agents to interact with Flutter apps at runtime - let them inspect widgets, simulate taps, enter text, scroll, and take screenshots.","archived":false,"fork":false,"pushed_at":"2026-01-13T11:14:41.000Z","size":333,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-13T11:44:42.307Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leancodepl.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-09T09:23:43.000Z","updated_at":"2026-01-13T11:14:46.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/leancodepl/marionette_mcp","commit_stats":null,"previous_names":["leancodepl/marionette-mcp","leancodepl/marionette_mcp"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/leancodepl/marionette_mcp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leancodepl%2Fmarionette_mcp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leancodepl%2Fmarionette_mcp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leancodepl%2Fmarionette_mcp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leancodepl%2Fmarionette_mcp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leancodepl","download_url":"https://codeload.github.com/leancodepl/marionette_mcp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leancodepl%2Fmarionette_mcp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28480081,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T11:59:17.896Z","status":"ssl_error","status_checked_at":"2026-01-16T11:55:55.838Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-16T16:47:21.830Z","updated_at":"2026-01-16T16:47:21.921Z","avatar_url":"https://github.com/leancodepl.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ca href=\"https://leancode.co/?utm_source=github.com\u0026utm_medium=referral\u0026utm_campaign=marionette-mcp\" align=\"center\"\u003e\n  \u003cimg alt=\"marionette_mcp\" src=\"https://github.com/user-attachments/assets/12726942-57b3-4967-a1c8-bea06b397500\" /\u003e\n\u003c/a\u003e\n\n# Marionette MCP\n\n![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)\n[![marionette_mcp pub.dev badge](https://img.shields.io/pub/v/marionette_mcp)](https://pub.dev/packages/marionette_mcp)\n\n**\"Playwright MCP/Cursor Browser, but for Flutter apps\"**\n\nMarionette MCP enables AI agents (like Cursor, Claude Code, etc.) to inspect and interact with running Flutter applications. It provides a bridge between the MCP protocol and the Flutter VM service, allowing agents to see the widget tree, tap elements, enter text, scroll, and capture screenshots for automated smoke testing and interaction.\n\n![](https://github.com/leancodepl/marionette_mcp_attachments/blob/master/promo.gif)\n\n## Marionette MCP vs Flutter MCP\n\nThe official [Dart \u0026 Flutter MCP server](https://docs.flutter.dev/ai/mcp-server) focuses on **development-time** tasks: searching pub.dev, managing dependencies, analyzing code, and inspecting runtime errors. Marionette MCP is complementary - it focuses on **runtime interaction**: tapping buttons, entering text, scrolling, and taking screenshots. Use Flutter MCP to build your app, use Marionette MCP to test and interact with it.\n\n## Quick Start\n\n**Note: Your Flutter app must be prepared to be compatible with this MCP.**\n\n1. **Prepare your Flutter app** - Add the `marionette_flutter` package and initialize `MarionetteBinding` in your `main.dart`.\n2. **Install the MCP server** - Add `marionette_mcp` to your projects `dev_dependencies`.\n3. **Configure your AI tool** - Add the MCP server command (`dart run marionette_mcp`) to your tool's configuration (Cursor, Claude, etc.).\n4. **Run your app in debug mode** - Look for the VM service URI in the console (e.g., `ws://127.0.0.1:12345/ws`).\n5. **Connect and interact** - Ask the AI agent to connect to your app using the URI and start interacting.\n\n## Installation\n\n### 1. Add MCP Server Package\n\nRun the following command to activate the `marionette_mcp` [global tool](https://dart.dev/tools/pub/cmd/pub-global):\n\n```bash\ndart pub global activate marionette_mcp\n```\n\n\u003e [!NOTE]\n\u003e You can also install the package as a dev-dependency using\n\u003e\n\u003e ```bash\n\u003e dart pub add dev:marionette_mcp\n\u003e ```\n\u003e\n\u003e Then invoke the MCP server as `dart run marionette_mcp`.\n\u003e It might be necessary to change the working directory, so that `dart run` is able to find `marionette_mcp`.\n\u003e You can do it like so: `cd ${workspaceFolder}/packages/mypackage \u0026\u0026 dart run marionette_mcp` (it will vary between tooling).\n\u003e\n\u003e If it does not work, we suggest using the global tool method.\n\n### 2. Add Flutter Package\n\nRun the following command in your Flutter app directory:\n\n```bash\nflutter pub add marionette_flutter\n```\n\n## Flutter App Integration\n\nYou need to initialize the `MarionetteBinding` in your app. This binding registers the necessary VM service extensions that the MCP server communicates with.\n\n### Basic Setup\n\nIf your app uses standard Flutter widgets (like `ElevatedButton`, `TextField`, `Text`, etc.), the default configuration works out of the box.\n\n```dart\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter/material.dart';\nimport 'package:marionette_flutter/marionette_flutter.dart';\n\nvoid main() {\n  // Initialize Marionette only in debug mode\n  if (kDebugMode) {\n    MarionetteBinding.ensureInitialized();\n  } else {\n    WidgetsFlutterBinding.ensureInitialized();\n  }\n\n  runApp(const MyApp());\n}\n```\n\n### Log Collection (`get_logs`)\n\nMarionette collects application logs via Dart's [`logging`](https://pub.dev/packages/logging) package by listening to `Logger.root.onRecord`.\nThis means **`logging` is required for the MCP to be able to scrape logs**.\n\nIf your app doesn't use `logging` (or doesn't emit logs via `Logger(...)`), `get_logs` will likely be empty.\nIf you already use another logging solution, you may need to bridge it into `logging` for `get_logs` to work. If you'd like first-class support for another logging solution, please open an issue describing your setup and expectations.\n\n### Custom Design System\n\nIf you use custom widgets in your design system, you can configure Marionette to recognize them as interactive elements or extract text from them.\n\n**Why `isInteractiveWidget`?** A typical Flutter screen has hundreds of widgets in its tree - `Padding`, `Container`, `Column`, `SizedBox`, etc. When the AI agent calls `get_interactive_elements`, Marionette filters this down to only actionable targets: buttons, text fields, switches, sliders, etc. This gives the agent a concise, manageable list instead of an overwhelming dump of layout widgets.\n\nBy default, Marionette recognizes standard Flutter widgets like `ElevatedButton`, `TextField`, and `Switch`. If your app uses custom widgets (e.g., `MyPrimaryButton` that wraps styling around a `GestureDetector`), Marionette won't know they're tappable unless you tell it. The `isInteractiveWidget` callback lets you mark your custom widget types as interactive, so they appear in the element list and can be targeted by `tap` and other tools.\n\n```dart\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter/material.dart';\nimport 'package:marionette_flutter/marionette_flutter.dart';\nimport 'package:my_app/design_system/buttons.dart';\nimport 'package:my_app/design_system/inputs.dart';\n\nvoid main() {\n  if (kDebugMode) {\n    MarionetteBinding.ensureInitialized(\n      MarionetteConfiguration(\n        // Identify your custom interactive widgets\n        isInteractiveWidget: (type) =\u003e\n            type == MyPrimaryButton ||\n            type == MyTextField ||\n            type == MyCheckbox,\n\n        // Extract text from your custom widgets\n        extractText: (widget) {\n          if (widget is MyText) return widget.data;\n          if (widget is MyTextField) return widget.controller?.text;\n          return null;\n        },\n      ),\n    );\n  } else {\n    WidgetsFlutterBinding.ensureInitialized();\n  }\n\n  runApp(const MyApp());\n}\n```\n\n## Tool Configuration\n\nAdd the MCP server to your AI coding assistant's configuration.\n\n### Cursor\n\n[![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=marionette\u0026config=eyJlbnYiOnt9LCJjb21tYW5kIjoibWFyaW9uZXR0ZV9tY3AgIn0%3D)\n\nOr manually add to your project's `.cursor/mcp.json` or your global `~/.cursor/mcp.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"marionette\": {\n      \"command\": \"marionette_mcp\",\n      \"args\": []\n    }\n  }\n}\n```\n\n### Google Antigravity\n\nOpen the MCP store, click “Manage MCP Servers”, then “View raw config” and add to the opened `mcp_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"marionette\": {\n      \"command\": \"marionette_mcp\",\n      \"args\": []\n    }\n  }\n}\n```\n\n### Gemini CLI\n\nAdd to your `~/.gemini/settings.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"marionette\": {\n      \"command\": \"marionette_mcp\",\n      \"args\": []\n    }\n  }\n}\n```\n\n### Claude Code\n\nYou can run the following command to add it:\n\n```bash\nclaude mcp add --transport stdio marionette -- marionette_mcp\n```\n\n### Copilot\nAdd to your `mcp.json`:\n\n```json\n{\n  \"servers\": {\n    \"marionette\": {\n      \"command\": \"marionette_mcp\",\n      \"args\": []\n    }\n  }\n}\n```\n\n## Available Tools\n\nOnce connected, the AI agent has access to these tools:\n\n| Tool | Description |\n|------|-------------|\n| `connect` | Connect to a Flutter app via its VM service URI (e.g., `ws://127.0.0.1:54321/ws`). |\n| `disconnect` | Disconnect from the currently connected app. |\n| `get_interactive_elements` | Returns a list of all interactive UI elements (buttons, inputs, etc.) visible on screen. |\n| `tap` | Taps an element matching a specific key or visible text. |\n| `enter_text` | Enters text into a text field matching a key. |\n| `scroll_to` | Scrolls the view until an element matching a key or text becomes visible. |\n| `get_logs` | Retrieves application logs collected since the last check (**scraped from Dart `logging` / `Logger.root.onRecord`**). |\n| `take_screenshots` | Captures screenshots of all active views and returns them as base64 images. |\n| `hot_reload` | Performs a hot reload of the Flutter app, applying code changes without losing state. |\n\n## Example Scenarios\n\nMarionette MCP shines when used by coding agents to verify their work or explore the app. Here are some real-world scenarios:\n\n### 1. Verify a New Feature\n\n**Context:** You just asked the agent to implement a \"Forgot Password\" flow.\n**Prompt:**\n\u003e \"Now that you've implemented the Forgot Password screen, let's verify it. Connect to the app, navigate to the login screen, tap 'Forgot Password', enter a valid email, and submit. Check the logs to ensure the API call was made successfully.\"\n\n### 2. Post-Refactor Smoke Test\n\n**Context:** You performed a large refactor on the navigation logic.\n**Prompt:**\n\u003e \"I've refactored the routing. Please run a quick smoke test: connect to the app, cycle through all tabs in the bottom navigation bar, and verify that each screen loads without throwing exceptions in the logs.\"\n\n### 3. Debugging UI Issues\n\n**Context:** Users reported a button is unresponsive on the Settings page.\n**Prompt:**\n\u003e \"Investigate the 'Clear Cache' button on the Settings page. Connect to the app, navigate there, find the button using `get_interactive_elements`, tap it, and analyze the logs to see if an error is occurring or if the tap is being ignored.\"\n\n## How It Works\n\n1. **Initialization**: Your Flutter app initializes `MarionetteBinding`, which registers custom VM service extensions (`ext.flutter.marionette.*`).\n2. **Connection**: The MCP server connects to your app's VM Service URL.\n3. **Interaction**: When an AI agent calls a tool (like `tap`), the MCP server translates this into a call to the corresponding VM service extension in your app.\n4. **Execution**: The Flutter app executes the action (e.g., simulates a tap gesture) and returns the result.\n\n## Assumptions \u0026 Limitations\n\n- **Prefer pasting the VM Service URI manually**: While some tooling can sometimes discover or infer the VM Service endpoint, the most reliable workflow is to copy the `ws://.../ws` URI from your `flutter run` output (or DevTools link) and paste it to the agent when calling `connect`.\n\n- **The agent may not know your app**: Marionette can “see” the widget tree and interact with UI elements, but it doesn’t automatically understand your product’s flows, naming conventions, or edge cases. If you want reliable navigation and assertions, provide extra context in the prompt (what screen to reach, expected labels/keys, preconditions, and the goal of the interaction).\n\n- **“Your mileage may vary” interactions**: Some actions are implemented via best-effort simulation of user behavior (gestures, focus, text entry, scrolling). Depending on platform, custom widgets, overlays, or app-specific gesture handling, results may vary. If a flow is flaky, consider exposing clearer widget keys, simplifying hit targets, or adding custom `MarionetteConfiguration` hooks for your design system. And if you hit something that consistently doesn’t behave as expected, a small repro in an issue helps us improve it.\n\n## Troubleshooting\n\n- **\"Not connected to any app\"**: Ensure the AI agent has called `connect` with the valid VM Service URI before using other tools.\n- **Finding the URI**: Run your Flutter app in debug mode (`flutter run`). Look for a line like: `The Flutter DevTools debugger and profiler on iPhone 15 Pro is available at: http://127.0.0.1:9101?uri=ws://127.0.0.1:9101/ws`. Use the `ws://...` part.\n- **Release Mode**: Marionette only works in debug (and profile) mode because it relies on the VM Service. It will not work in release builds.\n- **Elements not found**: Ensure your widgets are visible. If using custom widgets, make sure they are configured in `MarionetteConfiguration`.\n\n---\n\n## 🛠️ Maintained by LeanCode\n\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://leancode.co/?utm_source=github.com\u0026utm_medium=referral\u0026utm_campaign=marionette-mcp\"\u003e\n    \u003cimg src=\"https://leancodepublic.blob.core.windows.net/public/wide.png\" alt=\"LeanCode Logo\" height=\"100\" /\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\nThis package is built with 💙 by **[LeanCode](https://leancode.co?utm_source=github.com\u0026utm_medium=referral\u0026utm_campaign=marionette-mcp)**.\nWe are **top-tier experts** focused on Flutter Enterprise solutions.\n\n### Why LeanCode?\n\n- **Creators of [Patrol](https://patrol.leancode.co/?utm_source=github.com\u0026utm_medium=referral\u0026utm_campaign=marionette-mcp)** – the next-gen testing framework for Flutter.\n\n- **Production-Ready** – We use this package in apps with millions of users.\n- **Full-Cycle Product Development** – We take your product from scratch to long-term maintenance.\n\n\u003cdiv align=\"center\"\u003e\n  \u003cbr /\u003e\n\n  **Need help with your Flutter project?**\n\n  [**👉 Hire our team**](https://leancode.co/get-estimate?utm_source=github.com\u0026utm_medium=referral\u0026utm_campaign=marionette-mcp)\n  \u0026nbsp;\u0026nbsp;•\u0026nbsp;\u0026nbsp;\n  [Check our other packages](https://pub.dev/packages?q=publisher%3Aleancode.co\u0026sort=downloads)\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleancodepl%2Fmarionette_mcp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleancodepl%2Fmarionette_mcp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleancodepl%2Fmarionette_mcp/lists"}