はじめに
AI駆動開発により、開発はAIエージェントにほとんど任せられるようになってきました。しかし、生成されたコードが実際のアプリ上で正しく動くかどうかは、目視で確認する必要がありました。
Marionette MCPは、この確認作業をAIエージェントに任せるためのMCPツールです。実行中のFlutterアプリに接続し、ボタンのタップやテキスト入力、スクリーンショットの取得までをAIエージェントが自動でしてくれます!
この記事では、Marionette MCPのセットアップからサンプルアプリでの操作まで、一連の流れを紹介します。
Marionette MCPの仕組み
アーキテクチャ
Marionette MCPは、FlutterのVM Serviceを活用してアプリとAIエージェントを接続します。全体の流れは4ステップです。
- Flutterアプリが
MarionetteBindingを初期化し、VM Serviceにカスタム拡張(ext.flutter.marionette.*)を登録する - MCPサーバーがアプリのVM Service URLに接続する
- AIエージェントがMCPサーバーのツール(
tap、enter_textなど)を呼び出す - MCPサーバーがVM Service経由でアプリにアクションを実行させ、結果を返す
AIエージェントはMCPサーバーを介してアプリを操作するため、アプリ側の変更はMarionetteBindingの初期化のみで済みます。
公式Flutter MCPサーバーとの違い
Flutterには公式のMCPサーバーも存在します。公式MCPサーバーはpub.devのパッケージ検索、依存関係の管理、コード解析といった開発時のタスクに焦点を当てています。実行中のアプリのウィジェットツリーの確認や操作にも対応していますが、開発支援全般をカバーする位置づけです。
Marionette MCPは実行時のUI操作に特化しています。ボタンのタップ、テキスト入力、スクロール、スクリーンショット取得といったユーザー操作の再現を、最小限のコード変更で実現します!
提供するツール
Marionette MCPは以下のツールをAIエージェントに提供します。
| ツール | 機能 |
|---|---|
connect |
VM Service URIを指定してアプリに接続する |
disconnect |
接続を切断する |
get_interactive_elements |
画面上の操作可能な要素を一覧取得する |
tap |
キーまたはテキストで要素を指定してタップする |
enter_text |
テキストフィールドに文字列を入力する |
scroll_to |
指定した要素が表示されるまでスクロールする |
get_logs |
アプリケーションログを取得する |
take_screenshots |
画面のスクリーンショットを撮影する |
hot_reload |
コード変更後にホットリロードを実行する |
セットアップ手順
詳しい手順は公式のQuick Startも参照してください。
1. MCPサーバーのインストール
marionette_mcpをグローバルツールとしてインストールします。
dart pub global activate marionette_mcp
2. Flutterパッケージの追加
アプリのプロジェクトにmarionette_flutterを追加します。
flutter pub add marionette_flutter
3. MarionetteBindingの初期化
main.dartでMarionetteBinding.ensureInitialized()を呼び出します。デバッグモードでのみ初期化するため、リリースビルドには影響しません。
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:marionette_flutter/marionette_flutter.dart';
void main() {
if (kDebugMode) {
MarionetteBinding.ensureInitialized();
} else {
WidgetsFlutterBinding.ensureInitialized();
}
runApp(const MyApp());
}
4. AIツールへのMCPサーバー登録
使用するAIツールにMarionette MCPサーバーを登録します。
Claude Code
claude mcp add --transport stdio marionette -- marionette_mcp
Cursor
プロジェクトの.cursor/mcp.jsonまたはグローバルの~/.cursor/mcp.jsonに以下を追加します。
{
"mcpServers": {
"marionette": {
"command": "marionette_mcp",
"args": []
}
}
}
Copilot(VS Code)
mcp.jsonに以下を追加します。
{
"servers": {
"marionette": {
"command": "marionette_mcp",
"args": []
}
}
}
Gemini CLI
~/.gemini/settings.jsonに以下を追加します。
{
"mcpServers": {
"marionette": {
"command": "marionette_mcp",
"args": []
}
}
}
Google Antigravity
MCP storeを開き、「Manage MCP Servers」→「View raw config」からmcp_config.jsonに以下を追加します。
{
"mcpServers": {
"marionette": {
"command": "marionette_mcp",
"args": []
}
}
}
5. アプリの起動と接続
Flutterアプリをデバッグモードで起動します。
flutter run
コンソールに出力されるVM Service URIをコピーし、AIエージェントに接続を依頼します。URIはws://127.0.0.1:XXXXX/YYYYY=/wsのような形式です。
サンプルアプリで試す
loggingパッケージの設定
Marionette MCPのget_logsツールは、Dartのloggingで出力されたログを取得します。アプリ側で以下の設定が必要です。
flutter pub add logging
import 'package:logging/logging.dart';
final _logger = Logger('MyApp');
void main() {
Logger.root.level = Level.ALL;
// ...
}
Logger.root.level = Level.ALLを設定することで、すべてのログレベル(INFO、WARNING、SEVEREなど)がMarionette MCP経由で取得可能になります。あとはアプリ内の任意の箇所で_logger.info()や_logger.severe()を呼び出すだけです。
ValueKeyの付与
Marionette MCPがUI要素を特定するには、ウィジェットにValueKeyを設定する必要があります。キーが設定されていない要素はテキストやウィジェットの型で検索できますが、キーによる指定が最も確実です。
FilledButton(
key: const ValueKey('submit_button'),
onPressed: _submit,
child: const Text('送信'),
),
キーの命名は、要素の役割がわかる名前を付けます。button_1のような連番より、submit_buttonやclear_buttonのように意味を持たせた方が、AIエージェントが文脈を把握しやすくなります。
操作の実例
サンプルアプリを生成してみました。
テスト内容
- 名前とメールアドレスを入力する
- 送信ボタンをタップする
- 送信結果が正しく表示されることを確認する
- ログを取得し、エラーや警告が出ていないことを確認する
AIエージェントへの指示
ws://127.0.0.1:XXXXX/YYYYY=/ws に接続し、以下の操作を実施してください。
- name_fieldに「山田太郎」を入力
- email_fieldに「taro@example.com」を入力
- submit_buttonをタップして送信
- 送信結果が表示されることを確認
- ログを取得し、エラーや警告が出ていないことを確認
各操作後にスクリーンショットを取得してください。
結果
手動で実施していたフォームの入力確認を、自然言語の指示だけで実施できました。各画面のスクリーンショットもエビデンスとして取得でき、送信結果が正しく表示されていることも確認できています。
さらに、送信処理に擬似的なエラーを仕込んだところ、AIエージェントは画面上の「送信に失敗しました」というメッセージだけでなく、get_logsで取得したアプリケーションログからもSEVEREレベルのエラーを検出し、問題を報告してくれました。UIの見た目だけでは気づきにくい内部エラーも、ログ取得まで含めて確認できるのは大きな利点です。
カスタムウィジェットへの対応
独自のデザインシステムを使用しているプロジェクトでは、標準のFlutterウィジェット以外の独自ウィジェットをMarionette MCPに認識させる設定が必要です。MarionetteConfigurationのisInteractiveWidgetとextractTextで対応します。
MarionetteBinding.ensureInitialized(
MarionetteConfiguration(
isInteractiveWidget: (type) =>
type == MyPrimaryButton ||
type == MyTextField ||
type == MyCheckbox,
extractText: (widget) {
if (widget is MyText) return widget.data;
if (widget is MyTextField) return widget.controller?.text;
return null;
},
),
);
isInteractiveWidgetは、get_interactive_elementsでカスタムウィジェットを検出対象に含めます。extractTextは、tap(text: "ラベル")のようにテキスト指定で要素を検索する際に、カスタムウィジェットからテキストを抽出します。
まとめ
AI駆動開発では、開発者が毎ステップでAIの出力を確認・判定する必要があり、そこに時間が取られてしまいます。Marionette MCPは、AIエージェントに自分で動かして確認する能力を与えます。スキルで実装→動作確認→修正のループをAIが自律的に回せるようになれば、開発者は最終レビューをするだけになります。
まだプロダクションレベルの運用には至っていませんが、動作確認まで含めて任せられる環境は、今後の開発体制を大きく変えてくれそうです。引き続き活用しながら、最適な使い方を探っていきたいと思います!
お知らせ (採用情報)
AppTime では一緒に働くメンバーを募集しております。
詳しくは採用情報ページをご確認ください。
みなさまからのご応募をお待ちしております!




