Flutter Lamp
Live eyes for your AI on a running Flutter app.
- Role
- Author & maintainer
- Period
- Aug 2026 to present
- Status
- Actively developed
- License
- MIT
- Latest
- v0.21.0 on npm
Source on GitHub (opens in a new tab)npm package (opens in a new tab)
Overview
An MCP server that connects Claude Code, Cursor, Codex and Gemini to a running Flutter app over the Dart VM Service. It streams exceptions, logs, network calls, frame timings and memory as structured data, runs an evidence-first root-cause diagnosis, and serves a live dashboard.
- 22
- MCP tools, each annotated read-only or mutating
- 8
- golden incidents replayed from real device sessions
- 70%
- confidence floor. Below it, the answer is “Unknown”
- 0
- dependencies in the live dashboard
Problem
Debugging Flutter with an AI assistant means copy-pasting stack traces, flutter run output and DevTools screenshots. The assistant is blind between messages and rebuilds what happened from whatever text it was handed.
The failure worth preventing is a confident wrong conclusion. An honest “unknown” is the better outcome.
What I built
- Collectors for exceptions with reconstructed stack traces, console and
dart:developerlogs,dart:ioHTTP, frame timings with jank detection, navigation, widget rebuilds, Riverpod, Provider and Bloc activity, memory and the VM timeline. - One normalised, capped event store. The MCP tools and the browser dashboard are two readers of the same store in the same process, with no bridge between them that could drift or disagree.
diagnose_runtimecorrelates evidence into a summary, root cause, cited evidence ids, confidence, alternatives and fixes, including which categories nothing could observe.- A live dashboard at
127.0.0.1:7373over native HTTP + WebSocket: logs, network, exceptions, timeline, performance charts, and every tool call the agent made with its latency and response size. - An evaluation suite that replays recorded device sessions and scores the diagnosis against apps that actually misbehaved.
Architecture
Collectors read the Dart VM Service into one capped event store, redacting secrets before anything is stored. The MCP tools, the diagnosis engine and the live dashboard all read that store in the same process.
RuntimeStore
one capped event stream
Every event carries a timestamp, source, severity and category. Each category keeps a fixed window (3,000 logs … 500 navigation); runtime_status reports what was evicted so a truncated window is never presented as a whole session.
Flutter app
debug or profile build
Runs on a device or emulator over USB or TCP. Release builds are out of scope: the VM Service, Inspector and dart:io profiling don’t exist there.
Dart VM Service
JSON-RPC over WebSocket
Official protocol only. DevTools is never scraped. A cable-free Android path (ensure_tcp_device) keeps the tunnel alive when the USB cable moves.
Collectors
logs, exceptions, frames, network
Each runtime source implements one Collector interface and registers itself. Adding a source changes no other layer.
Redaction
at capture, before storage
Authorization, cookies, token-shaped headers, sensitive query params and JWT/Bearer strings are removed before they are stored. The VM Service credential, which authorises arbitrary Dart execution, is scrubbed unconditionally.
RuntimeStore
one capped event stream
Every event carries a timestamp, source, severity and category. Each category keeps a fixed window (3,000 logs … 500 navigation); runtime_status reports what was evicted so a truncated window is never presented as a whole session.
Diagnosis engine
evidence to cause
Correlates evidence into a stable cause label with cited evidence ids, alternatives and coverage. Below 70% confidence it says “Unknown” instead of guessing.
MCP tools
stdio → Claude Code, Cursor, Codex
22 tools, each carrying an MCP readOnlyHint. The recommended agent flow is runtime_health → what_changed → a targeted get_* → diagnose_runtime.
Dashboard
HTTP + WebSocket on loopback
Streams the same store to a browser. The WebSocket handshake needs a per-process token and a loopback Origin, because browsers exempt WebSocket from the same-origin policy.
Eval replay
8 golden incidents
Recorded sessions are hydrated back into a store with their ids intact, the diagnosers re-run, and the output is scored for false confidence.
Decisions
Score false confidence, not accuracy
Each golden incident is a real exported session plus the right answer, argued in prose from the events. The loader rejects a short justification: a golden pasted from today’s output would be a test that can never fail.
Redact at capture, not at display
Secrets never enter the event store, so no consumer (the AI model or the dashboard) can leak what was never stored. Withheld header names are listed so a partial picture is visible as partial.
Loopback is not a security boundary
Any page open in the browser could connect to ws://127.0.0.1:7373 without a check. The handshake requires a per-process token inlined into the served page; /health returns liveness only, never the VM Service URI.
Assumptions carry their provenance
The 16.67 ms frame budget is one value that reports where it came from. Deriving the refresh rate from batched event arrival was measured and rejected, so the rate is reported as unknown, and a developer can supply it.
One process, two readers
Because the dashboard and the MCP tools read the same store in the same process, the dashboard can show exactly what the agent asked for and what it cost, and guarantee the human and the agent see identical evidence.
Result
Published to npm and run with npx -y flutter-lamp, with a bundled Claude Code skill that runs connect → gather → diagnose without asking for pasted logs.
Versions 0.1.0 to 0.20.0 shipped between 22 and 28 August 2026, each documented in a Keep a Changelog history, including a fix for a tool-accounting bug that was silently reporting zero errors.
Known limitations
- Debug and profile builds only.
- Network capture is pull-on-demand: Dart exposes no push stream for dart:io HTTP.
- Dart-side HTTP only. Calls from Kotlin, Swift or a WebView don’t appear.
- CPU is not observable: the VM Service does not sample it.
Stack
TypeScript, Node.js, MCP, Dart VM Service, WebSocket, Claude Code, GitHub Actions