---
title: The Automation Chain
description: >-
  Why flutter-mcp-toolkit exists, how its surfaces chain together (MCP, CLI,
  harness, oka), and the first loop for new users.
---
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.page/arenukvern/mcp_flutter/llms.txt
> Use this file to discover all available pages before exploring further.

# The Automation Chain

## Why this exists at all

An AI agent watching a Flutter app through screenshots is guessing: it
cannot read your widgets, cannot wait deterministically, and every fix
requires you to relay information by hand. `flutter-mcp-toolkit` closes that
loop — your debug-mode app registers `ext.mcp.toolkit.*` service extensions,
and an agent gets a semantic snapshot (every widget with a stable ref, label,
role, and bounds), acts through real semantics (tap, type, scroll, navigate),
hot-reloads, and re-reads state to verify its own work.

The bigger idea: that observe → act → verify loop should not be locked to
one transport. The toolkit is becoming a **chain of declarative automation
APIs** on top of the universal `AutomationDriver` contract
(`universal_automation_*` family, ADR-0038) — the same vocabulary drives
Flutter apps, browsers, and OS-native tiers, and different surfaces own
different parts of the lifecycle.

## The chain at a glance

```text
 your Flutter app (debug)  ── registers ──►  ext.mcp.toolkit.* extensions
                                                │
        ┌───────────────────┬───────────────────┼──────────────────────┐
        ▼                   ▼                   ▼                      ▼
  MCP fmt_* tools      CLI (fmtk)      flutter_mcp_harness       intentcall hints
  (chat, editor)      (scripts, CI)    (Dart scenarios, CI)      (declarative routing)
                                                 │
                                        build + lifecycle (optional):
                                        oka owns compile/install/process
```

Every surface observes and drives the same extensions; only **one** surface
may own the app's session (see the ownership law below).

## Pick your tier

| You want… | Use | Start here |
|---|---|---|
| Chat-driven debugging and verification | MCP `fmt_*` tools | [Get started](https://docs.page/arenukvern/mcp_flutter) |
| One-shot commands in scripts/CI | CLI (`flutter-mcp-toolkit` / `fmtk`) | [CLI vs MCP](/start_here/cli_vs_mcp) |
| Repeatable E2E scenarios as checked-in Dart | `flutter_mcp_harness` | [E2E Scenarios](/guides/e2e_scenarios) |
| Drive a NATIVE macOS app (any app, not just Flutter) | `MacosDriver` via `showcase/drivers/bin/drive_macos.dart` | this page, below |
| Drive a WEB page | `CdpDriver` via `showcase/drivers/bin/drive_web.dart` | this page, below |
| Declarative device builds + process lifecycle | **oka** (separate repo) | this page, below |
| Intent → automation routing without hardcoded transports | `IntentDriverRouter` | [IntentCall consumer guide](/intentcall/README) |

## Your first loop (five minutes)

```bash
# 1. Install the binary (ships flutter-mcp-toolkit + fmtk alias)
curl -fsSL https://raw.githubusercontent.com/Arenukvern/mcp_flutter/main/install.sh | bash

# 2. Wire your app (adds mcp_toolkit, emits the main.dart snippet)
cd my-flutter-app && flutter-mcp-toolkit codegen-init

# 3. Install skills + MCP registration for your agent
flutter-mcp-toolkit init claude-code   # or: cursor | codex | cline | all

# 4. Run the app
flutter run --debug
```

That is the whole setup. Ask your agent "what's on screen?" — it should
answer from a semantic snapshot, not a screenshot. From here the agent can
tap, fill forms, hot-reload, and prove its own changes; your app can even
register custom MCP tools at runtime (see [Dynamic Tools
Registration](https://docs.page/arenukvern/mcp_flutter)).

## Second loop: scenarios you can commit

When a loop is worth repeating (regression checks, pre-release sweeps),
graduate it from chat into a Dart scenario with the published
`flutter_mcp_harness` package — the harness builds the app, launches the
process, attaches to the VM service, then drives/asserts, with CI-friendly
exit codes:

```dart
final app = await MacosAppTarget(projectDir: '.', binaryPath: binary).launch();
final driver = ToolkitDriver(await app.vm());
// observe → act → verify; app.stop() in a finally
```

Full anatomy (targets, `Scenario`, retrying, two-app compositions): [E2E
Scenarios](/guides/e2e_scenarios) and the `flutter-mcp-e2e-harness` skill.

## The OS-native and web tiers: one contract, three surfaces

The `AutomationDriver` contract (`universal_automation_*` family) is the
universal adaptation: the SAME observe → act → verify vocabulary against
three backends —

| Tier | Driver | Transport | Observe | Act |
|---|---|---|---|---|
| Flutter (instrumented) | `ToolkitDriver` | VM service | semantic widget snapshot | tap/type/scroll/navigate |
| Web | `CdpDriver` | Chrome DevTools Protocol | accessibility tree | click/type/key/scroll |
| macOS (any app) | `MacosDriver` | AXUIElement + CGEvent | AX tree (per-app or focused) | AXPress/type/key/scroll |

`MacosDriver` is also the APP-MANAGEMENT rung — the part that makes the
macOS tier manage applications, not only the focused one:
`runningApps()` (discovery), `launch(bundleId)` (launch-or-activate),
`activate(pid)`, `terminate(pid)`, `frontmost()`, and `snapshotOfApp(pid)`
(background reads). The composition root proves the loop against a real
desktop app:

```bash
# discover → activate → observe → act → verify → screenshot (.showcase/)
dart run showcase/drivers/bin/drive_macos.dart --bundle com.example.vosgesDesktop
```

Non-claims: the macOS tier is CLI/composition-first today — the `fmt_*`
MCP surface and the CLI command catalog remain Flutter-tier (adding a
native tier to the catalog is contract work: schema parity, MCP
exposure, capabilities); the harness and oka do not yet compose
`MacosDriver`.

## Third loop: device builds via oka (optional)

oka is a separate, declarative build system (`dart pub global activate
oka`). It owns what an E2E harness should not: compiling, installing, and
the process lifecycle. Its `oka_harness` package consumes the published
`flutter_mcp_harness`, so device scenarios use the same driver contract as
desktop ones:

```bash
oka init          # project-owned composition root: tool/oka_pipeline.dart
oka build apk     # no-Gradle Android pipeline
oka dev           # owning dev session — agents: oka dev --watch --json
```

While `oka dev` owns the app, the toolkit's MCP tools attach as usual: the
server reads `.flutter_mcp/runner-session.json` (spec v2) and delegates
reload/restart to the runner. You do **not** need oka for the first two
loops — it earns its place when you want reproducible device builds and
owned sessions across Android/iOS.

## The ownership law (read before composing)

**At most one owning attach session per app.** The owner (a `flutter run`,
or `oka dev`) is the only compile-capable reload channel; a second owning
attach can kill the first (ADR-0014). MCP tools and harness drivers are
observers — they attach over the same VM service without taking ownership.
Details and the runner-session contract: [dev-session delegation
roadmap](/guides/dev-session-delegation-roadmap).

## FAQ

**Do I need oka?** No. The MCP and harness loops are fully self-contained;
oka is for declarative device builds and owned dev sessions.

**Can the agent use MCP while a scenario runs?** Observers can coexist; two
*owners* cannot. Run the scenario as the owner and use MCP tools read-only,
or attach the MCP server to a runner-owned session via spec v2.

**Where do YAML/declarative scenario files live?** Nowhere, for now — the external `flutter_harness` HS-DSL experiment (ADR-0012) is retired (see ADR-0017). Repeatable scenarios are checked-in Dart via `flutter_mcp_harness`; a declarative layer can be re-homed on the invoke tier when wanted.

## Related

- [Why This Repo Matters](/start_here/why_this_repo_matters) — the motivation
- [CLI vs MCP](/start_here/cli_vs_mcp) — surface choice in depth
- [Feature Map](/start_here/feature_map) — the 31 `fmt_*` tools
- Skills: `flutter-mcp-automation-chain` (chain wiring), `flutter-mcp` (interactive), `flutter-mcp-e2e-harness` (scenarios)
