inkpal_bridge 1.4.1
inkpal_bridge: ^1.4.1 copied to clipboard
Flutter MCP bridge for AI agents — runtime error capture, HTTP monitor, widget tree inspection, tap/type/screenshot. 33+ VM service extensions, zero deps.
inkpal_bridge #
Flutter MCP bridge for AI agents. Runtime error capture, HTTP monitor, widget tree inspection, tap/type/screenshot — over WebSocket and 33+ VM service extensions. Drop-in replacement for
runApp().
In-app debug bridge plugin for InkPal. Gives AI coding agents (Claude Code, Cursor, Windsurf) direct control over your running Flutter app:
- Runtime error capture — three-layer catch (FlutterError + PlatformDispatcher + zone) with 2s deduplication
- HTTP request monitor — ring buffer with sensitive header redaction
- Widget & semantics tree inspection — find any element by label, role, or coordinate
- Interaction control — tap, scroll, text input, navigate, screenshot
- State time-travel — snapshot, diff, rewind app state
- Recording & replay — capture flows, export as JSON or Dart tests
- Network mocking — intercept HTTP, simulate offline, inject latency
Zero third-party dependencies. Debug-only. Zero overhead in release builds.
Features #
| Feature | Free | Pro | Studio |
|---|---|---|---|
| UI inspection (semantics tree) | ✅ | ✅ | ✅ |
| Route tracking & navigation | ✅ | ✅ | ✅ |
| Screenshot capture | ✅ | ✅ | ✅ |
| Tap, scroll, text input, gestures | ✅ | ✅ | |
| Real-time error telemetry | ✅ | ✅ | |
| Error context & healing | ✅ | ✅ | |
| State time-travel debugging | ✅ | ✅ | |
| Interaction recording & replay | ✅ | ✅ | |
| Layout diffing | ✅ | ✅ | |
| Performance monitoring (FPS, jank) | ✅ | ✅ | |
| App manifest for LLM context | ✅ | ✅ | |
| VM Service extensions (33) | ✅ | ✅ | |
| Network mocking & offline mode | ✅ |
Quick Start #
1. Add dependency #
dependencies:
inkpal_bridge: ^1.4.0
2. Initialize in main() #
import 'package:inkpal_bridge/inkpal_bridge.dart';
void main() {
InkPalBridge.init(
serverUrl: 'ws://localhost:8765',
appRunner: () => runApp(const MyApp()),
licenseKey: const String.fromEnvironment('INKPAL_LICENSE_KEY'),
knownRoutes: ['/home', '/settings', '/profile'],
routeDescriptions: {
'/home': 'Main dashboard with stats and actions',
'/settings': 'App preferences and account settings',
'/profile': 'User profile with edit capability',
},
);
}
3. Wire the navigator observer and screenshot boundary #
MaterialApp(
navigatorObservers: [
if (InkPalBridge.instance != null)
InkPalBridge.instance!.navigatorObserver,
],
home: RepaintBoundary(
key: InkPalBridge.instance?.repaintBoundaryKey,
child: const HomeScreen(),
),
)
4. Run with license key #
flutter run --dart-define=INKPAL_LICENSE_KEY=ink_your_key_here
How It Works #
AI Tool (Claude/Cursor)
|
v
MCP Server (InkPal intelligence — patterns, fabricate, safety)
|
v WebSocket (primary)
inkpal_bridge (in-app)
|
v VM Service extensions (fallback)
ext.flutter.inkpal.*
Commands flow in (tap, inspect, navigate). Telemetry flows out (logs, errors, performance). The WebSocket channel is primary; VM Service extensions serve as fallback for local development without a WebSocket server.
Configuration #
InkPalBridge.init(
// Required
serverUrl: 'ws://localhost:8765', // WebSocket URL
appRunner: () => runApp(const MyApp()), // App entry point
// License
licenseKey: 'ink_...', // License key (optional, free tier if omitted)
apiUrl: 'https://...', // Custom API URL (optional)
// Navigation
navigatorKey: navigatorKey, // GlobalKey<NavigatorState> (optional)
knownRoutes: ['/home', '/settings'], // Known route names for resolution
routeDescriptions: {'/home': '...'}, // Route descriptions for AI context
onNavigateToRoute: (r) async => ..., // Custom nav callback (GetX, go_router)
// Advanced
manifest: AiAppManifest(...), // Rich app structure for LLM
globalStateProvider: () async => {...}, // App state callback
screenshotWidth: 720, // Screenshot target width (px)
);
Router Support #
Works with all major Flutter routing packages:
Standard Navigator #
InkPalBridge.init(
serverUrl: 'ws://localhost:8765',
appRunner: () => runApp(const MyApp()),
);
go_router #
InkPalBridge.init(
serverUrl: 'ws://localhost:8765',
appRunner: () => runApp(const MyApp()),
onNavigateToRoute: (route) async => router.go(route),
);
GetX #
InkPalBridge.init(
serverUrl: 'ws://localhost:8765',
appRunner: () => runApp(const MyApp()),
navigatorKey: Get.key,
onNavigateToRoute: (route) async => Get.toNamed(route),
);
Beamer #
InkPalBridge.init(
serverUrl: 'ws://localhost:8765',
appRunner: () => runApp(const MyApp()),
onNavigateToRoute: (route) async =>
beamerDelegate.beamToNamed(route),
);
Commands (37+) #
Inspection (Free) #
| Command | Description |
|---|---|
get_screen_content |
Read all UI elements via semantics tree |
get_widget_tree |
Get element list as JSON |
find_widget |
Search by label or text |
get_current_route |
Current route + navigation stack |
get_routes |
All discovered routes this session |
get_app_state |
Screen + state + routes snapshot |
take_screenshot |
On-demand PNG capture |
ping |
Health check with license status |
Interaction (Pro) #
| Command | Description |
|---|---|
tap_element |
Tap by label, key, or semantics |
set_text |
Fill text fields |
scroll |
Scroll in direction |
navigate_to_route |
Navigate to named route |
go_back |
Pop current route |
long_press |
Long press element |
increase_value |
Increment slider/stepper |
decrease_value |
Decrement slider/stepper |
Error Healing (Pro) #
| Command | Description |
|---|---|
heal_watch_start |
Start watching for errors |
heal_watch_stop |
Stop error watcher |
heal_get_errors |
Get captured errors |
heal_get_error_context |
Full error context (widget tree, state, logs) |
heal_verify_no_error |
Verify a fix worked |
State Time-Travel (Pro) #
| Command | Description |
|---|---|
state_capture |
Snapshot current state |
state_list |
List all snapshots |
state_get |
Retrieve snapshot by ID |
state_diff |
Diff two snapshots |
stream_state |
Observe state changes over time |
Recording (Pro) #
| Command | Description |
|---|---|
recording_start |
Start recording interactions |
recording_stop |
Stop recording |
recording_status |
Check recording state |
recording_export |
Export as JSON or integration test code |
Telemetry & Performance (Pro) #
| Command | Description |
|---|---|
get_log_history |
Past log entries |
get_error_history |
Past errors |
get_recent_logs |
Logs since timestamp |
tap_with_context |
Tap + return correlated logs |
get_performance |
FPS and jank metrics |
Layout & Manifest (Pro) #
| Command | Description |
|---|---|
screen_snapshot |
Save layout snapshot |
screen_diff |
Compare layout changes |
get_app_map |
Full app structure |
get_screen_manifest |
Screen detail manifest |
Network Control (Studio) #
| Command | Description |
|---|---|
mock_network |
Add per-URL mock rules |
clear_network_mocks |
Remove all mock rules |
go_offline |
Block all HTTP requests |
go_online |
Restore network |
network_conditions |
Set latency + packet loss |
VM Service Extensions #
33 extensions registered under ext.flutter.inkpal.* as fallback when WebSocket isn't available. These mirror the WebSocket commands and are accessible via Flutter DevTools or flutter attach.
Release Mode #
In release builds (kReleaseMode), InkPalBridge.init() calls appRunner() directly with zero overhead:
- No WebSocket connection
- No semantics walking
- No VM extensions registered
- No memory allocation
- Bridge instance is
null
Logging #
Use the built-in structured logger:
InkPalBridge.instance?.logger.log('User tapped checkout');
InkPalBridge.instance?.logger.debug('Cart items: $count');
InkPalBridge.instance?.logger.warning('Payment timeout');
InkPalBridge.instance?.logger.error('Failed to load', error: e);
Logs are batched (500ms) and streamed to the MCP server via WebSocket. Errors are sent immediately.
State Provider #
Provide app-level state for AI context:
InkPalBridge.init(
// ...
globalStateProvider: () async => {
'user': {'name': currentUser.name, 'plan': currentUser.plan},
'cart': {'items': cart.length, 'total': cart.total},
'theme': isDarkMode ? 'dark' : 'light',
},
);
Touch Visualization #
Visual feedback overlay for AI-driven interactions:
// Wrap your app
TouchVisualizerOverlay(
controller: InkPalBridge.instance!.touchVisualizer,
child: const MyApp(),
)
Requirements #
- Flutter >= 3.10.0
- Dart >= 3.0.0
- Debug mode only (zero overhead in release)
License #
MIT - see LICENSE.