From 5fb6948413b121ddeb779bae9424ada847ab710d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiktor=20Zaj=C4=85c?= Date: Wed, 23 Sep 2026 15:03:58 +0200 Subject: [PATCH] flutter-marionette: match Marionette MCP 0.6.0 LoggingLogCollector and LoggerLogCollector moved to the separate marionette_logging and marionette_logger packages in 0.4.0. Update the log collection docs, the tool list, the build-mode note (debug and profile) and the version-mismatch note for connect. Name the runtime actions in the usage skill description. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../.claude-plugin/plugin.json | 2 +- plugins/flutter-marionette/CHANGELOG.md | 13 +++++ plugins/flutter-marionette/README.md | 24 +++++++--- .../skills/flutter-marionette-usage/SKILL.md | 2 +- .../references/marionette.md | 47 ++++++++++++------- 5 files changed, 63 insertions(+), 25 deletions(-) create mode 100644 plugins/flutter-marionette/CHANGELOG.md diff --git a/plugins/flutter-marionette/.claude-plugin/plugin.json b/plugins/flutter-marionette/.claude-plugin/plugin.json index bd05639..1c6cd17 100644 --- a/plugins/flutter-marionette/.claude-plugin/plugin.json +++ b/plugins/flutter-marionette/.claude-plugin/plugin.json @@ -3,7 +3,7 @@ "name": "flutter-marionette", "displayName": "Flutter Marionette", "description": "LeanCode Marionette MCP setup and usage — runtime interaction with a live Flutter debug app for AI-driven exploration, smoke verification, and UI debugging.", - "version": "0.1.0", + "version": "0.1.1", "author": { "name": "LeanCode" }, diff --git a/plugins/flutter-marionette/CHANGELOG.md b/plugins/flutter-marionette/CHANGELOG.md new file mode 100644 index 0000000..dcaae7a --- /dev/null +++ b/plugins/flutter-marionette/CHANGELOG.md @@ -0,0 +1,13 @@ +# Changelog + +## 0.1.1 + +- Log collection: `LoggingLogCollector` and `LoggerLogCollector` come from the separate `marionette_logging` and `marionette_logger` packages, not from `marionette_flutter`. The README and `references/marionette.md` now say which package to add. +- `references/marionette.md`: the tool list matches Marionette MCP 0.6.0, with selectors for `tap` and `enter_text`, and the gesture, key, restart, device-config and custom-extension tools. +- Build mode: Marionette works in debug and profile builds, not only debug. +- `connect` fails when `marionette_mcp` and `marionette_flutter` versions differ; the README says how to align them. +- `flutter-marionette-usage` description names the runtime actions (tap, type, scroll, screenshots, smoke tests) so the skill loads for them. + +## 0.1.0 + +- Initial `flutter-marionette` plugin in the public marketplace. diff --git a/plugins/flutter-marionette/README.md b/plugins/flutter-marionette/README.md index 0a012f0..659b2c4 100644 --- a/plugins/flutter-marionette/README.md +++ b/plugins/flutter-marionette/README.md @@ -17,7 +17,7 @@ LeanCode Flutter plugin for [Marionette MCP](https://github.com/leancodepl/mario | Runs against | Live `flutter run` debug session | `patrol develop` / `patrol test` | | Test files | None; the agent drives the app | Dart test files in `patrol_test/` | | Best for | Iterating on a feature, smoke after refactor | Regression-proof suites in CI | -| Build mode | Debug only | Debug + release | +| Build mode | Debug and profile | Debug + release | Both plugins can coexist; they solve different parts of the AI-assisted testing workflow. @@ -106,17 +106,29 @@ flutter run Copy the VM service URI from the run output (format: `ws://127.0.0.1:PORT/ws`). In your AI agent, call the `connect` tool with that URI. +`connect` checks that `marionette_mcp` and the app's `marionette_flutter` are the same version and fails if they differ. After upgrading `marionette_flutter`, activate the matching server too: `dart pub global activate marionette_mcp `. + ## Log collection -`get_logs` requires a configured `LogCollector`. +`get_logs` needs a `LogCollector`, passed as `MarionetteConfiguration(logCollector: ...)`. The collectors for the two common logging packages ship as separate packages: + +| Your app logs with | Add to the app | Collector | +| --- | --- | --- | +| [`logging`](https://pub.dev/packages/logging) | `flutter pub add marionette_logging` | `LoggingLogCollector()` from `package:marionette_logging/marionette_logging.dart` | +| [`logger`](https://pub.dev/packages/logger) | `flutter pub add marionette_logger` | `LoggerLogCollector()` from `package:marionette_logger/marionette_logger.dart`. Also add it to your `Logger`'s outputs. | +| Anything else | Nothing, it ships in `marionette_flutter` | `PrintLogCollector()`. Call `collector.addLog(message)` wherever your logs flow. | + +```dart +MarionetteBinding.ensureInitialized( + MarionetteConfiguration(logCollector: LoggingLogCollector()), +); +``` -- Use `LoggingLogCollector()` for apps using the `logging` package. -- Use `LoggerLogCollector()` for apps using the `logger` package. -- Use `PrintLogCollector()` for custom logging setups. +`get_logs` returns the logs since app start or the last hot reload. Without a collector it explains how to enable one. Upstream docs: [Log Collection](https://github.com/leancodepl/marionette_mcp/blob/main/docs/logging.md). ## Build-mode constraint -Marionette relies on Flutter's VM Service and is intended for a live `flutter run` session. It does not work in release builds. Use Patrol for release-mode automation. +Marionette relies on Flutter's VM Service, so it works in debug and profile builds, not in release builds. The `kDebugMode` guard above enables it in debug only; use `!kReleaseMode` to include profile builds. Use Patrol for release-mode automation. ## Example usage diff --git a/plugins/flutter-marionette/skills/flutter-marionette-usage/SKILL.md b/plugins/flutter-marionette/skills/flutter-marionette-usage/SKILL.md index 246a09e..c47455e 100644 --- a/plugins/flutter-marionette/skills/flutter-marionette-usage/SKILL.md +++ b/plugins/flutter-marionette/skills/flutter-marionette-usage/SKILL.md @@ -1,6 +1,6 @@ --- name: flutter-marionette-usage -description: Explain what the `flutter-marionette` plugin does and how to use it. Use when the user invokes `/flutter-marionette-usage`, asks what this plugin covers, or needs help with Marionette MCP setup, runtime exploration, or custom widget configuration. +description: Explain what the `flutter-marionette` plugin does and how to drive a running Flutter app through Marionette MCP. Use when the user invokes `/flutter-marionette-usage`, asks what this plugin covers, wants Claude to tap, type, scroll, take screenshots or smoke-test a running Flutter app, or needs help with Marionette MCP setup, `get_logs`, or custom widget configuration. --- # Marionette Usage diff --git a/plugins/flutter-marionette/skills/flutter-marionette-usage/references/marionette.md b/plugins/flutter-marionette/skills/flutter-marionette-usage/references/marionette.md index a30e6aa..df71457 100644 --- a/plugins/flutter-marionette/skills/flutter-marionette-usage/references/marionette.md +++ b/plugins/flutter-marionette/skills/flutter-marionette-usage/references/marionette.md @@ -14,7 +14,7 @@ Both are LeanCode testing tools, but they solve different problems: | Runs against | A live `flutter run` debug session | `patrol develop` or `patrol test` runner | | Test files | None — agent drives the app interactively | Dart test files in `patrol_test/` | | Best for | "Does my new feature work?", smoke after refactor, debugging unresponsive UI | Regression-proof test suites in CI | -| Build mode | Debug only (requires VM Service) | Debug + release (via Patrol runner) | +| Build mode | Debug and profile, not release (requires VM Service) | Debug + release (via Patrol runner) | Rules of thumb: - Iterating on a new feature and want the agent to click around? **Marionette.** @@ -32,14 +32,21 @@ Prepare the Flutter app before trying to drive it: When the `marionette` MCP server is configured and connected, use these tools: -- `connect` / `disconnect` — manage the VM service connection. Call `connect` first, passing the `ws://127.0.0.1:PORT/ws` URI printed by `flutter run`. -- `get_interactive_elements` — retrieve the list of currently tappable / actionable UI components. **Always call this before `tap` / `enter_text`** to see what is available and to get stable identifiers. -- `tap` — simulate a button or element press. -- `enter_text` — input data into a text field. -- `scroll_to` — scroll an off-screen element into view. -- `take_screenshots` — capture the current app visuals as base64. Use to verify visual state after an action or to debug why something is not found. -- `get_logs` — retrieve application logs. Use when an action silently fails or state is unclear. -- `hot_reload` — apply code changes without losing app state. Useful when iterating on a feature and wanting to re-check it without restarting. +- `connect` / `disconnect`: manage the VM service connection. Call `connect` first, passing the `ws://127.0.0.1:PORT/ws` URI printed by `flutter run`. It fails when the `marionette_mcp` server and the app's `marionette_flutter` are different versions; align them. +- `get_interactive_elements`: list the visible interactive elements with their type, text, key and Semantics identifier. **Always call this before acting** to see what is available and to get stable selectors. +- `tap`, `double_tap`, `long_press`, `secondary_tap` (desktop only), `pinch_zoom`: gestures on one element, matched by exactly one of `key`, `identifier`, `text`, `type` or `coordinates`. Prefer `key` (a `ValueKey`), then `identifier` (the Semantics identifier). Tapping a text field focuses it. +- `swipe`: element-based (`key`, `identifier` or `text`, plus `direction`) or coordinate-based. Use it for `PageView`, `Dismissible`, `Drawer` and sliders. +- `scroll_to`: scroll until an element matching `key`, `identifier` or `text` is visible. +- `press_back_button`: the system back action (Android back, iOS swipe-back). +- `enter_text`: type into a field. Pass `input` and exactly one of `key`, `identifier`, or `focused_element: true` after tapping the field. There is no text matcher. +- `press_key`: a real key event on the focused element (`enter`, `tab`, `escape`, arrows, characters, optional `modifiers`). On iOS and Android, change a field's value with `enter_text` instead. +- `take_screenshots`: capture all active views as base64 PNGs. Use to verify visual state after an action or to debug why something is not found. +- `get_logs`: app logs since start or the last hot reload. Needs a `LogCollector` (see "Log collection"). Use when an action silently fails or state is unclear. +- `hot_reload`: apply code changes without losing app state. `hot_restart`: restart from `main()` and reset all state. +- `set_device_config`: override text scale, bold text and brightness. The app must mount `MarionetteDeviceConfig` first. +- `list_custom_extensions` / `call_custom_extension`: app-specific extensions registered with `registerMarionetteExtension`. + +Parameters for every tool are in the upstream [MCP Tools](https://github.com/leancodepl/marionette_mcp/blob/main/docs/mcp-tools.md) doc. ## Binding initialization @@ -66,22 +73,28 @@ For apps with a custom design system, pass a `MarionetteConfiguration` — see ` ## Log collection -`get_logs` works only when a `LogCollector` is configured. +`get_logs` works only when a `LogCollector` is passed as `MarionetteConfiguration(logCollector: ...)`. The `logging` and `logger` collectors are separate packages, not part of `marionette_flutter`: -- For apps using the `logging` package, use `LoggingLogCollector()`. -- For apps using the `logger` package, use `LoggerLogCollector()`. -- For other setups, use `PrintLogCollector()` and forward logs into it. -- If no collector is configured, `get_logs` should explain how to enable it. +- `logging` package: `flutter pub add marionette_logging`, then `LoggingLogCollector()` from `package:marionette_logging/marionette_logging.dart`. +- `logger` package: `flutter pub add marionette_logger`, then `LoggerLogCollector()` from `package:marionette_logger/marionette_logger.dart`. It is also a `LogOutput`, so add the same instance to the `Logger`'s outputs. +- Anything else: `PrintLogCollector()` from `marionette_flutter`, fed with `collector.addLog(message)`. +- If no collector is configured, `get_logs` explains how to enable it. + +```dart +MarionetteBinding.ensureInitialized( + MarionetteConfiguration(logCollector: LoggingLogCollector()), +); +``` ## Workflow: driving the app as an agent 1. Start the app: `flutter run` (take note of the VM service URI). 2. Call `connect` with the URI. 3. Call `get_interactive_elements` to see what is on screen. -4. Act: `tap`, `enter_text`, `scroll_to`. +4. Act: `tap`, `enter_text`, `scroll_to`, `swipe`, `press_back_button` and the other gestures. 5. Call `take_screenshots` to verify visual state (and when the element list looks wrong). 6. Use `get_logs` if an action has no visible effect. -7. Call `hot_reload` after a code change to re-test without losing state. +7. Call `hot_reload` after a code change to re-test without losing state, or `hot_restart` when the change needs a fresh start. 8. Call `disconnect` when finished. ## Best-effort caveat @@ -102,4 +115,4 @@ Marionette observes the UI but does not automatically know your product's flows, ## Build-mode constraint -Marionette relies on Flutter's VM Service and is designed to drive a live `flutter run` session. It does not work in release builds. For release-mode automation use Patrol instead. +Marionette relies on Flutter's VM Service, so it works in debug and profile builds, not in release builds. The `kDebugMode` guard above limits it to debug; `!kReleaseMode` also covers profile. For release-mode automation use Patrol instead.