mcp (0.1.1)
Installation
dart pub add mcp:0.1.1 --hosted-url=About this package
mcp
Lets an AI agent see and drive a running Flutter app, like Playwright does for a browser. Debug builds only: in profile and release builds, and on the web, start() does nothing.
Every running app registers itself in one shared file. One gateway, registered once in your MCP client, serves all of them, so there is nothing to configure per app or per port.
Add it to an app
dependencies:
mcp: ^0.1.0
import 'package:mcp/mcp.dart';
final mcp = Mcp(
name: 'My App',
version: '1.0.0',
state: () => {'user': session.userName},
tools: [
McpTool(
name: 'set_theme',
description: 'Switch between the light and dark theme.',
properties: {'dark': {'type': 'boolean'}},
requiredArguments: ['dark'],
handler: (arguments) async {
themeNotifier.value = arguments['dark'] == true ? ThemeMode.dark : ThemeMode.light;
return McpResult.text('ok');
},
),
],
);
Future<void> main() async {
await mcp.start();
runApp(const MyApp());
}
Optional: add McpNavigatorObserver() to navigatorObservers so state lists the open pages, and call McpLog.add(line) from your own logger to feed the log tool (debugPrint is captured already).
Register the gateway once
dart compile exe bin/gateway.dart -o mcp_gateway.exe
claude mcp add --scope user flutter -- /path/to/mcp_gateway.exe
Or run it from source: claude mcp add --scope user flutter -- dart run mcp:gateway.
Flags: --name (the server name clients see, default flutter) and --registry (path of the registry file).
Built-in tools
| Tool | What it does |
|---|---|
state |
App name, version, open pages and whatever your state callback returns |
snapshot |
Every on-screen element as text: role, name, value, states, actions, position, and a ref |
click |
Tap by ref, by text (exact or partial, case blind, nth for repeats) or by widget key |
type |
Set the text of a field |
scroll |
Scroll the first or a chosen scrollable up, down, left or right |
adjust |
Step a slider up or down |
wait_for |
Wait until some text or key is on screen |
screenshot |
PNG of the window, including embedded web views |
log |
Recent log lines |
With more than one app running, pass instance (an id, a pid, a platform or part of the app name). The instances tool lists them.
How it works
The snapshot and the actions use the app's semantics tree, so anything a screen reader can reach is reachable, and a widget you add later is covered with no extra code. Give icon-only buttons a tooltip or Semantics label so they get a name; a widget with only a ValueKey can still be clicked with key.
Each app serves loopback HTTP on a random port with a random bearer token, refuses browser origins and foreign hosts, and keeps itself in %LOCALAPPDATA%\Mcp\instances.json (~/.local/state/Mcp/instances.json elsewhere; MCP_REGISTRY overrides it) with a 5 second heartbeat. Entries not refreshed for 20 seconds are dropped. Apps started by flutter test do not register.