Skip to content
Debugging
Skill

/flutter-mcp-e2e-harness

Use this skill when writing or running repeatable E2E scenarios for Flutter apps as checked-in Dart with the `flutter_mcp_harness` package (`packages/harness`) — build/launch the app, attach to its VM service, drive widgets (snapshot/tap/enter text) and assert without MCP; also

BOOST
From plugin
mcp-flutter
38922 skills1 agent
Install
$ npx -y skills add Arenukvern/mcp_flutter --skill flutter-mcp-e2e-harness --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/flutter-mcp-e2e-harness

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use this skill when writing or running repeatable E2E scenarios for Flutter apps as checked-in Dart with the `flutter_mcp_harness` package (`packages/harness`) — build/launch the app, attach to its VM service, drive widgets (snapshot/tap/enter text) and assert without MCP; also

SKILL.md

flutter-mcp-e2e-harness.SKILL.md
name: flutter-mcp-e2e-harness
description: Use this skill when writing or running repeatable E2E scenarios for Flutter apps as checked-in Dart with the `flutter_mcp_harness` package (`packages/harness`) — build/launch the app, attach to its VM service, drive widgets (snapshot/tap/enter text) and assert without MCP; also covers the repo showcase launcher (`make showcase`, `--web`, `--stop`) and multi-instance desktop compositions. For interactive assistant-driven debugging through MCP `fmt_*` tools, use `flutter-mcp` instead.

<!-- @FMT_MODE_PRELUDE -->

Flutter MCP E2E Harness

Golden path for **programmatic** E2E over the toolkit: `packages/harness` (`flutter_mcp_harness`) owns the whole lifecycle — build the app, launch the process, attach to its Dart VM service, and only then drive/assert. It speaks the same `ext.mcp.toolkit.*` service extensions as the MCP server, as plain Dart. ADR: `decisions/0015_flutter_mcp_harness_extraction.mdx`.

The driving layer speaks the universal `AutomationDriver` contract (ADR-0038, `universal_automation_interface`): `ToolkitDriver` exposes the observe/act/verify loop (`snapshot` → semantic `AxNode` tree with refs and bounds; `perform` click/type/key/named-route-navigate/evaluate; `screenshot` PNG), so the same agent vocabulary drives Flutter apps, browsers (`CdpDriver`), and OS-native targets. `IntentDriverRouter` additionally routes an intent's `automation` hint (transport + verb + locator) onto the bound driver. Frame pipelines are deliberately NOT a harness dependency — they are composition-level (`showcase/drivers`, which also holds the live end-to-end drives: `make drive-flutter` / `drive-flutter-chrome` / `drive-web`).

App-specific verbs use the invoke tier (ADR-0017): the app's intent registry IS the action catalog — `driver.actions()` lists it, and `InvokeAction(name, args)` dispatches to a registered entry with schema-validated arguments (custom `IntentAutomationAction.custom` hints route the same way). Web surfaces without a VM service (Jaspr, plain JS) compose identically over `CdpDriver` via the `window.__mcpActions` page registry.

| Need | Use | |---|---| | Repeatable scenario as code, CI-friendly exit codes | this skill (`flutter_mcp_harness`) | | One-off interactive debugging from chat/editor | `flutter-mcp` (MCP `fmt_*` tools) | | Browser/WebMCP dogfood of the web showcase | `flutter-mcp-toolkit-maintain-web` |

Evidence boundaries (what each tier can prove)

A green tier proves only what that tier touches. When you record a result — in a validation ledger, a gate, or a report — say which tier it came from, and never let one tier's pass stand in for another's:

| Tier | Proves | Cannot prove | |---|---|---| | Model / unit tests (app package) | the gesture/decision algorithm on synthetic inputs (direction → symbol → correction) | that the app's real signal chain reaches the algorithm | | MCP / driver UI scenarios (this harness) | controls exist, are reachable, and respond — presentation and wiring through the toolkit | anything whose input never crosses the UI (e.g. camera-delivered gestures) | | Sensor replay (deterministic frames → the REAL pipeline) | app integration: frames → tracker/recognizer → selector → draft / undo / redo / commit with the real filters and state machines, no person needed | real-hand comfort, camera noise, latency feel | | Live camera trial (a human hand) | the real sensor story: detection robustness, comfort, fatigue | repeatability — it is a manual trial, never a CI gate |

Worked example (vosges low-look, that repo's ADR-0008): model tests verified the codebook; the MCP scenario verified the Settings controls; `low_look_sensor_replay_test.dart` feeds a synthetic hand through the real tracker and selector and asserts the committed word; only a live trial says anything about a real hand. A scenario that only touches the UI must not claim the writing loop works.

The web/AOT tier (WebBuildTarget + FlutterWebCdp)

The VM-service toolkit tier exists only where a Dart VM service exists: JIT web (DDC, `flutter run`) and debug desktop/mobile. An **AOT web build (`flutter build web`) has no VM service — the CDP tier is not a fallback there, it is the only tier** (tier matrix: family ADR-0045; this repo ADR-0018):

final app = await WebBuildTarget(projectDir: '.', build: !skipBuild).launch();
final flutter = app.flutter;              // FlutterWebCdp over CdpDriver
await flutter.waitText('Settings');
await flutter.clickText('Sync between devices', expect: 'I have a pairing code');
await flutter.typeFocused('mesh-pair/v1 ...');   // bringToFront + insertText
await app.stop();

Measured rules encoded in `FlutterWebCdp` — do not re-learn them:

  • Semantic clicks through the driver's fallback ladder work on real

Chromium; the OLD dead path was three stacked bugs (`DOM.enable` missing, AX nodes resolving to TEXT nodes, `callFunctionOn` `this` rebinding) — fixed in `universal_browser_cdp` and guarded by the opt-in live fixture (`XS_TEST_CDP_LIVE=1` in the cdp package).

  • Locate by text/aria-label in the LIVE `flt-semantics` DOM;

effect-verify every click (`expect:`) and check the effect BEFORE re-clicking (a toggle re-clicks itself off).

  • The first click after load is swallowed — `warmUpClick()` burns it.
  • Typing needs `Page.bringToFront` + `Input.insertText` (background

windows have no OS focus; per-key events land nowhere).

  • Navigate in-app only (`popRoute` = `history.back()`); a reload drops

in-memory state. Headless Chrome never runs rAF — Flutter freezes; `WebBuildTarget` defaults to headed Chrome for this reason.

Failure evidence (EvidenceCollector)

Attach evidence while the scenario runs; `Scenario` dumps it under `$TMPDIR/<scenario>/` on the first failing step: `attachCdp` buffers the page's console + uncaught exceptions (an app-side throw renders as a canvas error screen — invisible in the DOM — and ONLY the console names it), `attachLines` tails app stdout, `a

Read more
Ships withmcp-flutter

Inspect and drive a running Flutter app from your AI assistant. 📖 Docs: docs.page/arenukvern/mcp_flutter 🤝 Contribute: guide · contributors · code of conduct · security flutter-mcp-toolkit is a Dart MCP server + Flutter package for AI agents (Codex, Zed,

Get the whole plugin

Other skills on mcp-flutter.