flutter-bridge-mcp
Provides tools for interacting with Android devices and emulators, including reading logcat, finding crashes, capturing screenshots, tapping and swiping, launching or stopping apps, and retrieving device information.
Provides tools for debugging live Flutter apps, including attaching to the Dart VM Service, inspecting the widget tree with source locations, locating widgets on screen, triggering hot reload, and diffing screenshots to verify changes.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@flutter-bridge-mcp Locate the widget behind the odd 'Total Patients' label, then I'll hot reload and diff"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
flutter-bridge-mcp
Your Android tools see a Flutter app as one opaque rectangle. This makes that rectangle readable. Point at wrong-looking text on screen and get back the file and line that built it, edit it, hot reload, then diff the pixels to prove only that changed.
It also does the Android basics — logcat, crashes, taps, screenshots — filtered and truncated server-side so a response can never blow up the model's context.
Tools
Reading logs
Tool | Purpose |
| Connected devices/emulators |
| Recent logcat, filtered by package / tag / level / substring |
| Whole stack traces from the crash buffer |
| Wipe buffers before reproducing a bug |
| Background ring-buffer capture |
| De-obfuscate an R8/ProGuard trace with |
Driving the device
Tool | Purpose |
| See the screen (downscaled before sending) |
| List on-screen elements with tap coordinates |
| Tap by element text, or by coordinate |
| Scroll or drag, by direction or coordinates |
| Type into the focused field, optionally clearing or submitting |
| back, home, recents, enter, dpad, volume, power… |
| Cold starts, and |
| Screen size, density, Android version, foreground activity |
Flutter ↔ Android bridge
Android tooling sees a Flutter app as one opaque SurfaceView; Dart tooling sees
widgets but knows nothing about logcat, ANRs or native crashes. These join the two.
Tool | Purpose |
| Attach to the running app's Dart VM Service |
| Live widget tree, each widget tagged with its source file and line |
| The bridge — tap coordinates and the code that built the widget |
| Dart exceptions correlated with the native errors around them |
| Attach the Flutter tool so hot reload works |
| Apply Dart edits to the running app |
| End the attach session, leaving the app running |
The loop
The point of the bridge is this cycle:
screenshot see the bug
flutter_locate -> "Total Patients" at (410,269), built by
lib/presentation/dashboard/widgets/dashboard_grid.dart:76
ui_checkpoint remember the screen
<edit that line>
flutter_hot_reload apply it
ui_diff confirm exactly what changed, and nothing elseui_diff reports the percentage of pixels that changed, clusters them into regions,
and returns the screen with those regions outlined — so "did my fix land, and did it
disturb anything else?" is answered rather than eyeballed.
Tool | Purpose |
| Remember the current screen |
| Compare now against a checkpoint, outlining what moved |
Requires a debug or profile build. The VM Service URI is printed to logcat only at
launch, so if it has scrolled away use flutter_connect(package=..., relaunch=True).
Prefer ui_dump and tap(text=...) over screenshots and raw coordinates: it is far
cheaper, and it does not break when the layout or resolution changes. Screenshots are
downscaled, so coordinates read off one are not device coordinates.
There is deliberately no arbitrary adb shell tool.
For how the join works, how the VM Service is discovered without adding a package to your app, and how this compares to the other Flutter and Android MCP servers, see design.md.
Related MCP server: adb_mcp_server
Install
It needs Python 3.10+ and adb — from Android Studio's SDK, or platform-tools.
pipx install flutter-bridge-mcppipx puts the server in its own virtualenv and the
flutter-bridge-mcp executable on your PATH, which is what the MCP configs below
expect. On Debian/Ubuntu, sudo apt install pipx && pipx ensurepath first.
A plain pip install works too, but on Debian, Ubuntu and recent Fedora it fails
with error: externally-managed-environment — those distros forbid pip from writing
into the system Python (PEP 668). Use pipx, or a
virtualenv of your own:
python3 -m venv ~/.venvs/flutter-bridge
~/.venvs/flutter-bridge/bin/pip install flutter-bridge-mcpThen point your MCP config at ~/.venvs/flutter-bridge/bin/flutter-bridge-mcp
rather than the bare command. Do not reach for --break-system-packages; it writes
into the Python your package manager owns.
From a clone, to hack on it:
python3 -m venv .venv
.venv/bin/pip install -e .The executable is then .venv/bin/flutter-bridge-mcp.
Tests
The unit suite covers log parsing, error grouping and widget-tree flattening, and needs no device:
python3 -m venv .venv
.venv/bin/pip install -e ".[test]"
.venv/bin/python -m pytestselftest.py is the other half — it exercises the real thing against an attached
device:
.venv/bin/python selftest.py --package com.example.app --project ~/StudioProjects/appWithout --package it checks the Android side only; --project adds hot reload.
Use in Claude Code (including the Claude plugin in Android Studio)
The Studio plugin reads the Claude Code CLI config, so registering it once covers both:
claude mcp add flutter-bridge flutter-bridge-mcp \
-e ADB_PATH=/abs/path/to/Android/Sdk/platform-tools/adbIf you installed into a virtualenv rather than with pipx, use that venv's absolute
path in place of the bare flutter-bridge-mcp.
Use with Gemini in Android Studio
Create mcp.json in the Studio config directory
(~/.config/Google/AndroidStudio<version>/mcp.json on Linux,
~/Library/Application Support/Google/AndroidStudio<version>/ on macOS):
{
"mcpServers": {
"flutter-bridge": {
"command": "/abs/path/to/flutter-bridge-mcp",
"args": [],
"env": { "ADB_PATH": "/abs/path/to/Android/Sdk/platform-tools/adb" }
}
}
}Restart Studio, then in Gemini → Agent mode check the tools menu for flutter-bridge.
Configuration
Env var | Default | Meaning |
|
| adb binary |
| — | Default device when several are attached |
| 200 | Hard cap on returned lines |
| 400 | Per-message truncation |
| 8000 | Lines pulled from logcat before filtering |
| 40000 | Ring-buffer size for |
| 30 | Seconds to wait on a Dart VM Service call |
|
| flutter binary, for |
| — | Path to |
License
MIT — see LICENSE.
Available Tools
26 toolscapture_readB
Query the background capture buffer with the same filters as read_logs.
Args: stop: also end the capture after reading.
Inputs: tag, stop, limit, package, +2 more. Returns: result
capture_startA
Begin recording logcat into a background ring buffer.
Use this when the user is about to reproduce a bug: start the capture, let them reproduce it, then query with capture_read. Unlike read_logs this survives log rotation and captures everything, including verbose output.
Inputs: label, serial. Returns: result
capture_stopA
Stop the background logcat capture and release the adb process.
Inputs: no named parameters. Returns: result
clear_logsB
Clear all logcat buffers. Do this before reproducing a bug so logs stay clean.
Inputs: serial. Returns: result
device_infoB
Report screen size, density, Android version and the foreground activity.
Inputs: serial. Returns: result
find_crashesA
Extract recent crashes and ANRs as whole stack traces from the crash buffer.
Args: package: restrict to traces mentioning this package name. limit: how many of the most recent traces to return. serial: device serial when several are attached.
Inputs: limit, serial, package. Returns: result
flutter_attachA
Attach the Flutter tool to the running app so hot reload becomes possible.
Needed once per session before flutter_hot_reload: the VM Service on its own has no Dart compiler, so source edits cannot be applied without this.
Args: project_dir: the Flutter project root, the directory holding pubspec.yaml. serial: device serial when several are attached.
Inputs: project_dir (required), serial. Returns: result
flutter_connectA
Connect to a running Flutter app's Dart VM Service. Call this first.
The VM Service URI is printed to logcat only at launch, and only by debug and profile builds. If it has already scrolled out of the buffer, pass relaunch=True to restart the app and capture it.
Args: package: application id, needed for relaunch. relaunch: force-stop and restart the app to capture a fresh URI. serial: device serial when several are attached.
Inputs: serial, package, relaunch. Returns: result
flutter_detachA
End the attach session, leaving the app running on the device.
Inputs: no named parameters. Returns: result
flutter_diagnoseA
Correlate Dart-side errors with the native Android log around them.
A Flutter failure usually leaves two unrelated-looking traces: a Dart exception and, for anything crossing a platform channel, a Java/Kotlin one in logcat. This pulls both and puts them next to each other in time order.
Args: package: app to restrict native logs to, e.g. com.example.app. limit: how many Dart errors to report. serial: device serial when several are attached.
Inputs: limit, serial, package. Returns: result
flutter_hot_reloadA
Apply Dart source edits to the running app.
Requires flutter_attach first. Use full_restart after changing initState, global state or main(), which a plain reload cannot pick up.
Args: full_restart: hot restart instead of hot reload. package, serial: as elsewhere.
Inputs: serial, package, full_restart. Returns: result
flutter_locateA
Find something on screen and report both how to tap it and where it lives in code.
This is the bridge: tap coordinates come from Android's semantics tree, while the widget type and source location come from the Dart VM Service. Use it to go from "this looks wrong on screen" to the exact line that built it.
Args: text: visible text, semantics label or widget type to look for. package, serial: as elsewhere.
Inputs: text (required), serial, package. Returns: result
flutter_widget_treeA
Show the live widget tree with the source location of each widget.
Only widgets from your own project are shown by default — the framework's own wrappers are noise. Each line ends with the file and line that built it.
Args: contains: case-insensitive filter on widget type or description. max_depth: 0 for no limit, otherwise prune deeper than this. limit: max widgets returned. include_framework: also show widgets from Flutter and third-party packages. package, serial: as elsewhere.
Inputs: limit, serial, package, contains, +2 more. Returns: result
input_textA
Type into the focused field. Tap the field first so it has focus.
Args: text: the text to type. ASCII only — adb cannot type emoji or most non-Latin text. submit: press Enter afterwards. clear: delete the field's existing contents first. serial: device serial when several are attached.
Inputs: text (required), clear, serial, submit. Returns: result
launch_appA
Launch an app, optionally from a clean state.
Args: package: application id, e.g. com.example.app. activity: fully qualified activity to start instead of the launcher entry point. clear_data: wipe the app's data first, for a true first-run test. serial: device serial when several are attached.
Inputs: package (required), serial, activity, clear_data. Returns: result
list_devicesA
List connected Android devices and emulators with their state and model.
Inputs: no named parameters. Returns: result
press_keyB
Press a hardware or navigation key.
Args: key: back, home, recents, enter, tab, delete, escape, search, menu, power, wake, sleep, volume_up, volume_down, camera, or dpad_up/down/left/right/center. A raw KEYCODE_* name or a numeric keycode also works. serial: device serial when several are attached.
Inputs: key (required), serial. Returns: result
read_logsA
Read recent logcat output, filtered.
Args: package: only show logs from this app's process (e.g. com.example.app). tag: regex matched against the log tag. min_level: one of V, D, I, W, E, F. Defaults to I. contains: case-insensitive substring that must appear in tag or message. limit: max lines returned (hard-capped by FLUTTER_BRIDGE_MAX_LINES). buffer: main, system, crash, events, radio, or all. serial: device serial when several are attached.
Inputs: tag, limit, buffer, serial, +3 more. Returns: result
retraceA
De-obfuscate an R8/ProGuard stack trace using a mapping.txt file.
Args: stacktrace: the obfuscated trace, copied from find_crashes output. mapping_path: path to mapping.txt, usually under app/build/outputs/mapping//mapping.txt
Inputs: stacktrace (required), mapping_path (required). Returns: result
screenshotA
Capture what is currently on screen. Use this to see the app before acting on it.
The image is downscaled before sending, so coordinates read off it are NOT the device's own. Use ui_dump, or tap(text=...), to get real tap targets.
Args: max_width: longest edge of the returned image in pixels. serial: device serial when several are attached.
Inputs: serial, max_width.
stop_appA
Force-stop an app. Pair with launch_app to test a cold start.
Args: package: application id, e.g. com.example.app. serial: device serial when several are attached.
Inputs: package (required), serial. Returns: result
swipeA
Swipe or scroll. Give a direction for a centred swipe, or explicit coordinates.
Args: direction: up, down, left or right. "up" scrolls the content up (reveals what is below), matching how a finger moves. x1, y1, x2, y2: start and end points, used when direction is omitted. duration_ms: swipe duration; raise it for a slow drag, lower it to fling. serial: device serial when several are attached.
Inputs: x1, x2, y1, y2, +3 more. Returns: result
tapA
Tap the screen, either at a coordinate or on the element matching text.
Prefer text: it survives layout and resolution differences. Exact matches win
over partial ones; if several elements match, none is tapped and they are listed
so you can pick a coordinate instead.
Args:
x, y: device coordinates. Ignored when text is given.
text: text, content description or resource id of the element to tap.
serial: device serial when several are attached.
Inputs: x, y, text, serial. Returns: result
ui_checkpointA
Remember the current screen so a later ui_diff can show what changed.
Take one before editing code, then call ui_diff after hot reloading.
Args: label: name for this checkpoint. serial: device serial when several are attached.
Inputs: label, serial. Returns: result
ui_diffA
Compare the screen now against a checkpoint and highlight what moved.
Returns a summary plus the current screen with changed regions outlined, so you can confirm an edit did what you meant and did not disturb anything else.
Args: label: which checkpoint to compare against. threshold: per-channel difference (0-255) that counts as a real change. Raise it to ignore animations and anti-aliasing. max_width: longest edge of the returned image. serial: device serial when several are attached.
Inputs: label, serial, max_width, threshold.
ui_dumpA
List on-screen UI elements with their tap coordinates.
Cheaper and more precise than a screenshot when you only need to find a control.
Args: contains: case-insensitive filter on text, content description or id. clickable_only: only return elements that accept taps. limit: max elements returned. serial: device serial when several are attached.
Inputs: limit, serial, contains, clickable_only. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 26 tools
Most tools target a distinct action and resource, but read_logs and capture_read share the same filter semantics, and ui_dump and flutter_locate both find on-screen elements. The descriptions are detailed enough to disambiguate, so the overlap is manageable.
All tool names follow a consistent snake_case verb_noun style: list_devices, read_logs, clear_logs, launch_app, flutter_connect, ui_diff. The flutter_* and ui_* prefixes further group related operations, making the naming predictable and scannable.
At 26 tools, the set exceeds the well-scoped range and feels heavy for an MCP server. The tools are organized into clear categories, but the capture_* trio, multiple UI-inspection tools, and the attach/reload/detach sequence add bulk that could have been consolidated.
The surface covers the full Flutter debugging loop: log capture, crash extraction, retracing, UI inspection, interaction, VM Service connection, widget tree analysis, hot reload, and visual verification via ui_diff. There are no obvious dead ends or missing core operations for the stated purpose.
Maintenance
Related MCP Connectors
remote debug iOS/Android/Unity/Godot/Flutter/RN/Web on real-device.ui-tree/screenshots/taps,tests.
Flutter in-app bug tracking, visual pin annotations, and device logs directly into your AI IDE.
Drive real devices from your AI Coding tool. Embed a client SDK (Unity, Godot, Flutter, iOS/macOS, Android, React Native, Web) in your app, then capture screenshots, traverse the UI tree, inject taps and key events, and run automated test tasks on the physical device over a secure relay.
Give your AI agent a real cloud Android phone: push APKs, drive it, get screenshots and logs.
Related MCP Servers
- AlicenseCqualityDmaintenanceEnables comprehensive control of Android devices via ADB for Flutter development, UI testing, and visual QA workflows. Provides 60+ tools for device management, UI inspection, app testing, performance profiling, and debugging through natural language.7727 PyPIMIT
- AlicenseNot gradedqualityDmaintenanceEnables remote control of Android devices via ADB, including screen operations, input, app management, file transfer, and advanced tools like UI testing and performance analysis.25MIT
- AlicenseAqualityDmaintenanceEnables an agent to inspect and interact with Android emulators or physical devices via ADB, capturing UI snapshots, tapping nodes, typing text, and reading app logs.10MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI assistants and IDEs to drive real Android devices and emulators for cross-app UI automation, testing, and diagnostics, including collecting Logcat output and screenshots.Apache 2.0