---
name: flutter-mcp
description: Use this skill whenever inspecting, interacting with, or live-editing a running Flutter app via the Flutter MCP toolkit server (`mcpServers` key **`flutter-mcp-toolkit`**, or legacy **`flutter-inspector`**). Covers preflight, snapshot/tap/enter/scroll loop, hot-reload validation, and error envelope parsing.
---

<!-- @FMT_MODE_PRELUDE -->

# Flutter MCP

Golden path for agents driving a live Flutter app via the **`flutter-mcp-toolkit`** MCP server entry. Older configs may use the legacy **`flutter-inspector`** `mcpServers` registry id for the same binary (that id is **not** the Claude subagent **`flutter-mcp-toolkit-runtime`**).

## When to use

- User references a running Flutter app (debug mode) and wants to inspect, screenshot, interact with, or hot-reload it.
- User pastes a VM service URI (`ws://127.0.0.1:8181/.../ws`) or mentions port 8181.
- You need runtime proof (before/after screenshots) that a code edit took effect.

## Preflight (always first)

Before any VM-dependent call:

1. Call `doctor` (or run `flutter-mcp-toolkit doctor --json`) — parses env, ports, app reachability.
2. Confirm required toolkit extensions exist on the target:
   - `ext.mcp.toolkit.app_errors`
   - `ext.mcp.toolkit.view_details`
   - `ext.mcp.toolkit.view_screenshots`
   - `ext.mcp.toolkit.inspect_widget_at_point`

If missing, stop and report the instrumentation gap — do **not** guess:

- Add `mcp_toolkit` to `pubspec.yaml`.
- Ensure `MCPToolkitBinding.instance..initialize()..initializeFlutterToolkit();` runs before `runApp`.
- Hot **restart** (not reload) — reload is often insufficient for extension registration.

## Tool naming (v3.0.0+)

All MCP tools surface under the `fmt_` capability prefix
(`fmt_tap_widget`, `fmt_hot_reload_and_capture`, etc.). The prefix is
mandatory in `tools/call`. Skill-local references below use the bare name
for readability — when invoking, prepend `fmt_`. Dynamic-registry host
tools (`fmt_list_client_tools_and_resources`, `fmt_client_tool`,
`fmt_client_resource`) use the same prefix for a single consistent surface.

## Interaction loop (Playwright-style)

1. `fmt_semantic_snapshot` → returns `s_0..s_N` refs + `snapshot_id`.
2. `fmt_tap_widget` / `fmt_enter_text` / `fmt_scroll` / `fmt_swipe` / `fmt_long_press` / `fmt_drag` — act on a ref. **Always pass `snapshotId`** — you get a structured `stale_snapshot` error if the tree moved, instead of a silent wrong tap.
3. `fmt_reveal_search` — for off-screen semantic targets, bounded snapshot/search/scroll that returns a fresh `ref` + `snapshotId`.
4. `fmt_evaluate_dart_expression` — read state directly (e.g. `AgentState.instance.counter`).
5. `fmt_hot_reload_and_capture` — after a code edit, returns reload status + screenshot + fresh snapshot + errors in one response. Prefer this over manual reload + separate capture.

## Error envelope contract

Errors are `{code, message, details, descriptor, recovery}`. Parse `error.descriptor` (not the top-level object) for the machine-readable shape. Strict schemas default to `additionalProperties: false` — unknown params reject.

Common codes and recovery:

- `connection_selection_required` — retry with `arguments.connection.targetId` or exact `arguments.connection.uri` from `app.debugPort.wsUri`.
- `target_not_found` — refresh targets, then prefer exact `arguments.connection.uri`.
- `stale_snapshot` — call `fmt_semantic_snapshot` again, then retry the action with the new `snapshotId` input.
- `tool_not_found` — confirm the prefixed name (`fmt_<tool>`); v3.0.0 dropped legacy unprefixed names.
- Empty screenshot output — verify the server was not started with `--no-images`.
- Missing view resource/tool — verify the server was not started with `--no-resources`.

## Visual QA

- Before/after screenshots are the proof artifact for any UI claim. Capture before with `fmt_capture_ui_snapshot` (or one of the `visual://localhost/...` resources), edit, `fmt_hot_reload_and_capture`, compare.
- For each reported visual issue, attach coordinate + `fmt_inspect_widget_at_point` output.
- Map defects to source via `fmt_get_app_errors` top stack frame (`file`, `line`, `column`) when available.
- Do **not** use `fmt_debug_dump_*` unless explicitly requested (server must be started with `--dumps`; high token cost).

## Permissions (macOS)

Screen Recording permission belongs to the process running `flutter-mcp-toolkit` (or the MCP server host). If visual capture is denied:

```bash
flutter-mcp-toolkit permissions status
flutter-mcp-toolkit permissions request
flutter-mcp-toolkit permissions open-settings
```

## Non-modifiable apps

If the target app cannot be instrumented (third-party binary, restricted env), report flutter-mcp as unavailable for that app. Do **not** claim screenshot/layout/error inspection success.

## Related

- For the dynamic-tools side (registering custom MCP tools from inside the Flutter app), see the `flutter-mcp-toolkit-custom-tools` skill.
- For routing across setup / inspect / control / debug skills, see `flutter-mcp-toolkit-guide`.
