flutter-mcp-toolkit-debug について
flutter-mcp-toolkit-debug は、Flutter アプリの障害調査、実行ログの確認、実行中の状態の読み取りに使うスキルです。エラーコード別の復旧手順に加え、複数アプリ接続時の操作順も説明します。
主な機能
- 01エラーコード別の切り分け
- 02アプリ実行ログの取得
- 03Dart 式による状態の評価
- 04複数のデバッグ対象への接続
活用シーン
- 01ツール呼び出し失敗の原因調査
- 02予期しない動作のログ確認
- 03実行中アプリの状態を変更せずに確認
MCP 対応
ログ、式の評価、動的ツール呼び出しなど、Flutter アプリのデバッグに使う flutter-mcp-toolkit の MCP ツールを案内します。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ arenukvern/ mcp_flutter --skill flutter-mcp-toolkit-debugインストール後、Claude Code で/flutter-mcp-toolkit-debugを入力して読み込まれたか確認します。
詳細
使用するタイミング
何かが壊れ、その原因を把握する必要がある場合にこのスキルを使用します。
- ツール呼び出しがエラーエンベロープ(
ok: false)を返した場合。 - アプリが予期しない動作をし、実行時ログの出力が必要な場合。
- ライブアプリの状態を変更せずに調べる必要がある場合(
AgentState.instance.valueを読み取ります)。 - 以前の
controlアクション(tap、hot_reload、navigate)がエラーなく完了したものの、結果が正しくない場合。
次の用途にはこのスキルを使用しないでください。
- 現在画面に表示されている内容の読み取り —
flutter-mcp-toolkit-inspectを使用してください。 - ツールキット自体への接続失敗 —
flutter-mcp-toolkit-setupを読み込んでください。
レジストリとバンドルされたツール
- バンドルされた
fmt_*ツール(screenshot、logs、evaluate など)は、ホスト上のAgentRegistryを介して MCP サーバー上で実行されます。 - アプリに登録されたサーフェスは、
MCPToolkitBinding.addEntries/AgentCallEntryの後に表示されます。fmt_list_client_tools_and_resourcesで検出し、次にfmt_client_tool/fmt_client_resourceを使用してください。
トリアージの流れ
- エラーエンベロープが返された場合:まず
error.codeを読み、次にerror.descriptor.retryableを確認します。以下のエラーエンベロープのプレイブックでコードを調べてください。 - 再試行可能なエラーの場合:
flutter-mcp-toolkit doctor --jsonを実行します。doctor が失敗した場合はflutter-mcp-toolkit-setupを読み込んでください。 - ログ出力が必要な場合:
count: 100とレベルフィルターを指定してget_recent_logsを呼び出します。失敗した時刻付近にあるスタックトレースやアサーションメッセージを探してください。 - ライブ状態が必要な場合:対象を絞った式(例:
MyBloc.instance.state.toString())を指定してevaluate_dart_expressionを呼び出します。ログを確認した後に実行し、ログの代わりにはしないでください。 - inspect と連携する場合:順序は
semantic_snapshot→evaluate_dart_expression→get_recent_logsです。スナップショットで式の評価前の現在のウィジェットツリーを確認できます。式の評価で状態は変更されず、ログからは後続の状況を把握できます。 - 複数のターゲットがある場合:
connection_selection_requiredが発生したら、discover_debug_appsを呼び出してtargetIdを選び、その後のすべての呼び出しにconnection: {targetId: "..."}を渡します。
ツールリファレンス
get_recent_logs
実行中のアプリのメインアイソレートから、最近の print() および debugPrint() の出力を取得します。
count• integer • 省略可能、デフォルト:50 — 返すログ行数です。connection• object • 省略可能 — 接続のオーバーライドです。複数のデバッグアプリが実行中の場合は必須です。
get_recent_logs(count: 100)
get_recent_logs(count: 50, connection: {targetId: "ws://127.0.0.1:8181/<token>/ws"})
戻り値:{"logs": ["[INFO] page loaded", "[ERROR] assertion failed: ..."], "count": 50}
読み取り専用で、コードは実行されません。アプリの起動またはホットリスタート以降にバッファーされた行のみを返します。失敗した場合は、プレイブックの getRecentLogsFailed を参照してください。
evaluate_dart_expression
実行中のアプリのメインアイソレートで Dart 式を評価し、その文字列表現を返します。
expression• string • 必須 — Dart 式(例:"MyClass.instance.counter")。connection• object • 省略可能 — 接続のオーバーライドです。
evaluate_dart_expression(expression: "Navigator.of(context).canPop()")
evaluate_dart_expression(expression: "AgentState.instance.value.toString()")
戻り値:{"result": "42", "kind": "Int", "classRef": "_Smi", "truncated": false} — result は常に文字列です。長い文字列は、最大32,768 UTF-16 コード単位まで全体が返されます(単位の length と String.length はこの単位で数えられるため、ASCII 以外のテキストはより多くのバイトにエンコードされる場合があります)。それを超えると truncated: true とともに length、returnedLength、残りを読み取る .substring(...) を示すヒントが返されます。List、Map、オブジェクトはヒント付きでクラス名のみ("_GrowableList")が返されます。値を読み取るには、式を .toString() または jsonEncode(...) で囲んでください。ライブアイソレート内で任意のコードを実行するため、副作用のある式は避けてください。デバッグモードでのみ使用できます。失敗した場合は、プレイブックの evaluateExpressionFailed を参照してください。
接続/マルチアプリのフロー
discover_debug_apps が複数のエントリーを返した場合(または、いずれかの呼び出しが connection_selection_required を返した場合):
discover_debug_apps()を呼び出し、実行中の各アプリについてtargets[*].targetIdを確認します。- ポートまたはホスト名でターゲットを特定します。
connect_debug_app(connection: {targetId: "ws://127.0.0.1:<port>/<token>/ws"})を呼び出して、セッションを固定します。- セッション中の後続のすべてのツール呼び出しに、同じ
connectionオブジェクトを渡します。
接続のオーバーライドパターン — すべての呼び出しで connection を渡します:
get_recent_logs(count: 50, connection: {targetId: "ws://127.0.0.1:8182/<token>/ws"})
evaluate_dart_expression(expression: "x.toString()", connection: {targetId: "ws://127.0.0.1:8182/<token>/ws"})
ターゲットが変わった場合(アプリの再起動、ポートの変更など)は、discover_debug_apps を再実行して新しい targetId を取得します。古い URI は connect_failed を返します。
エラーエンベロープの対処ガイド
エラーが発生すると、必ず {code, message, details, descriptor, recovery} が返されます。retryable と exitCode は、トップレベルのエンベロープではなく、必ず error.descriptor を確認してください。error.recovery.fix_command が指定されている場合は、そのコマンドを直接実行します。
unexpectedExecutorError(unexpected_executor_error)
意味: コマンド実行ツールで未処理の例外が発生しました。 原因: サーバーのバグ、予期しない nil、復旧不能な VM の状態。 復旧方法:
flutter-mcp-toolkit doctor --json- doctor が成功した場合は一度だけ再試行します。再発した場合は、
error.detailsを添えてバグを報告してください。
connectFailed(connect_failed)
意味: VM Service WebSocket への接続に失敗しました。 原因: ポートが誤っている、トークンが古い、アプリが実行されていない。 復旧方法:
flutter-mcp-toolkit exec --name get_vm --args '{"connection":{"uri":"ws://127.0.0.1:8181/<token>/ws"}}'- Flutter の出力にある
app.debugPort.wsUriから正確な URI を取得します。
vmNotConnected(vm_not_connected)
意味: 接続が確立する前に、VM に依存するコマンドが呼び出されました。 復旧方法:
flutter-mcp-toolkit exec --name status --args '{}'- 続けて
flutter-mcp-toolkit doctor --jsonを実行します。
connectionSelectionRequired(connection_selection_required)
意味: デバッグターゲットが複数存在するため、明示的なターゲットの指定が必要です。 原因: Flutter アプリがデバッグモードで同時に複数実行されています。 復旧方法:
flutter-mcp-toolkit exec --name discover_debug_apps --args '{}'details.availableTargetsから正しいtargetIdを選択します。connection: {targetId: "<chosen_id>"}を指定して、元の呼び出しを再試行します。
discoverDebugAppsFailed(discover_debug_apps_failed)
意味: ローカルの VM Service ポートの検出スキャンに失敗しました。
復旧方法: flutter-mcp-toolkit doctor --json
getVmFailed(get_vm_failed)
意味: VM Service への get_vm RPC 呼び出しに失敗しました。
復旧方法: flutter-mcp-toolkit doctor --json
getExtensionRpcsFailed(get_extension_rpcs_failed)
意味: 登録済みの拡張 RPC の一覧表示に失敗しました。
復旧方法: flutter-mcp-toolkit doctor --json
hotReloadFailed(hot_reload_failed)
意味: Dart コンパイラーまたは VM によってホットリロードが拒否されました。 原因: 変更したファイルにコンパイルエラーがある、アイソレートの状態が不正。 復旧方法:
flutter-mcp-toolkit exec --name get_app_errors --args '{}'- コンパイルエラーを修正してから、再試行します。
hotRestartFailed(hot_restart_failed)
意味: ホットリスタート全体に失敗しました。
復旧方法: flutter-mcp-toolkit doctor --json を実行します。VM に到達できない場合は、手動でアプリを再起動してください。
getActivePortsFailed(get_active_ports_failed)
意味: アクティブなデバッグポートのスキャンに失敗しました。
復旧方法: flutter-mcp-toolkit doctor --json
getAppErrorsFailed(get_app_errors_failed)
意味: ツールキットブリッジからのアプリエラーの取得に失敗しました。
復旧方法: flutter-mcp-toolkit doctor --json
getScreenshotsFailed (get_screenshots_failed)
意味: スクリーンショットのキャプチャに失敗しました(モードが正しくない、ホストウィンドウが利用できない、Simulator ウィンドウの競合状態など)。 復旧方法:
flutter-mcp-toolkit doctor --json—visual_capture_permission_deniedを個別に確認します。get_view_detailsまたはスクリーンショットのペイロードでcaptureHintsを確認します。強いシグナル(platformViewsDetected):UiKitView、AppKitView、AndroidView、HtmlElementView—desktop_windowまたはautoを使用します(macOS アプリ、iOS Simulator、または CDP を使用する任意のホスト上の Chrome/ウェブ)。ウェブの実キャプチャでは、まず macOS ScreenCaptureKit を使用し、次に Chrome CDP を使用します(captureBackend: cdp)。ポートを上書きするには--web-browser-debugging-portを指定します。弱いシグナル(weakSignalsDetected):Textureのみ — macOS ホストではdesktop_windowを優先しますが、autoは切り替わりません。プラットフォームビューのない WGPU/カスタムエンジンでは、アプリでMCPToolkitBinding.captureHintsContributorを設定します。画像のみを返すget_screenshotsツールも、meta内のルーティング JSON と先頭のテキストブロックを返します。ショーケースを実行するには、make showcase-stopの後にmake showcase(macOSAppKitView)またはflutter run -d chrome(ウェブHtmlElementView+ CDP キャプチャ)を実行します。focus_window(MCP:fmt_focus_window)を実行し、その後mode: desktop_windowを指定してget_screenshotsを再試行します。validate-runtimeでは、エグゼキューターがフォーカスとキャプチャを1回再試行します(desktopCaptureRetried)。desktop_windowが引き続き失敗する場合、プラットフォームビューが検出されていても、validate-runtime はflutter_layerで1回再試行します。data.summaryのcapturePlatformViewsDetected、captureFocusAttempted、captureFallbackUsedを確認します。
visualCapturePermissionDenied (visual_capture_permission_denied)
意味: macOS の画面収録権限が付与されていません。 復旧方法:
flutter-mcp-toolkit permissions request --kind visual_capture- または、システム設定 → プライバシーとセキュリティ → 画面収録を開きます。
visualCaptureUnsupported (visual_capture_unsupported)
意味: このプラットフォームまたはキャプチャモードでは、ビジュアルキャプチャがサポートされていません。再試行できません。
復旧方法: flutter-mcp-toolkit permissions status && flutter-mcp-toolkit doctor --json
getViewDetailsFailed (get_view_details_failed)
意味: FlutterView の寸法の取得に失敗しました。
復旧方法: flutter-mcp-toolkit doctor --json
debugDumpFailed (debug_dump_failed)
意味: VM のデバッグダンプ RPC(レンダーツリー、セマンティクス、レイヤー)に失敗しました。
復旧方法: flutter-mcp-toolkit doctor --json
dynamicRegistryDisabled (dynamic_registry_disabled)
意味: 動的ツール/リソースの呼び出しが行われましたが、動的レジストリが無効になっています。再試行できません。
復旧方法: flutter-mcp-toolkit --dynamics exec --name status --args '{}' — 有効にするには --dynamics フラグを渡します。
dynamicRegistryListFailed (dynamic_registry_list_failed)
意味: レジストリから動的ツール/リソースを一覧表示できませんでした。
復旧方法: flutter-mcp-toolkit doctor --json
missingToolName (missing_tool_name)
意味: ツール名を指定せずに動的ツールが呼び出されました。再試行できません。
復旧方法: 呼び出しに tool_name パラメーターを含めます。flutter-mcp-toolkit schema --name fmt_client_tool
dynamicToolFailed (dynamic_tool_failed)
意味: 動的ツールの呼び出しに失敗しました。
復旧方法: flutter-mcp-toolkit doctor --json
missingResourceUri (missing_resource_uri)
意味: URI を指定せずに動的リソースの読み取りが呼び出されました。再試行できません。
復旧方法: uri パラメーターを含めます。flutter-mcp-toolkit schema --name fmt_client_resource
dynamicResourceFailed (dynamic_resource_failed)
意味: 動的リソースの読み取りに失敗しました。
復旧方法: flutter-mcp-toolkit doctor --json
sessionManagerNotConfigured (session_manager_not_configured)
意味: セッションコマンドが呼び出されましたが、セッションマネージャーが接続されていません。再試行できません。
復旧方法: flutter-mcp-toolkit doctor --json — サーバー設定の問題です。flutter-mcp-toolkit-setup を再読み込みします。
sessionNotFound (session_not_found)
意味: 要求されたセッション ID が存在しません。再試行できません。 復旧方法: アクティブなセッションを一覧表示し、参照する前に新しいセッションを開始します。
invalidCommand (invalid_command)
意味: コマンド名または引数のスキーマが無効です。再試行できません。
復旧方法: flutter-mcp-toolkit schema --name <command_name>
stateStoreReadFailed (state_store_read_failed)
意味: 永続状態ストアからの読み取りに失敗しました。
復旧方法: flutter-mcp-toolkit doctor --json — 状態ディレクトリのファイルシステム権限を確認します。
stateStoreWriteFailed (state_store_write_failed)
意味: 永続状態ストアへの書き込みに失敗しました。
復旧方法: flutter-mcp-toolkit doctor --json — ディスク容量と権限を確認します。
stateLockTimeout (state_lock_timeout)
意味: 状態ロックの取得がタイムアウトしました(エージェント間の競合)。 原因: 別のエージェントまたは CLI 呼び出しがロックを保持しています。デッドロックが発生しています。 復旧方法: 待ってから再試行します。繰り返し発生する場合は、ロックを保持している他のエージェントプロセスを終了します。
stateLockConflict (state_lock_conflict)
意味: 競合する状態ロックが検出されました。 原因: 複数のエージェントが同時に書き込みを行っています。 復旧方法: 呼び出しを直列化し、競合する操作の完了後に再試行します。
diagnoseFailed (diagnose_failed)
意味: 複合 diagnose コマンドが失敗しました。
復旧方法: flutter-mcp-toolkit doctor --json
explainErrorsFailed (explain_errors_failed)
意味: エラー説明コマンドが失敗しました。
復旧方法: flutter-mcp-toolkit doctor --json
unsupportedSummaryProvider (unsupported_summary_provider)
意味: 認識されない要約プロバイダーが要求されました。再試行できません。
復旧方法: flutter-mcp-toolkit schema --name diagnose — 使用可能な provider の値を確認します。
snapshotNotFound (snapshot_not_found)
意味: 参照されたスナップショット ID が存在しません。再試行できません。
復旧方法: flutter-mcp-toolkit snapshot create --name <snapshot_id> --args '{}'
snapshotInvalid (snapshot_invalid)
意味: スナップショットのペイロードの形式が不正か、検証に失敗しました。再試行できません。 復旧方法: スナップショットを作り直します。破損したファイルは再利用しないでください。
staleSnapshot (stale_snapshot)
意味: 指定された snapshotId が現在のアプリの状態と一致しなくなっています。
原因: スナップショット取得後にホットリロードまたは操作が行われ、ウィジェットツリーが変更されました。
復旧方法:
evaluate_dart_expression(expression: "true")— アプリに到達できることを確認します。semantic_snapshot()— 新しいスナップショット ID を取得し、元の呼び出しを再試行します。
bundleBuildFailed (bundle_build_failed)
意味: バンドルの作成または公開に失敗しました。
復旧方法: flutter-mcp-toolkit doctor --json — ビルド成果物と出力パスを確認します。
writeBlocked (write_blocked)
意味: --no-overwrite が設定されていて、対象がすでに存在するため、書き込みがブロックされました。再試行できません。
復旧方法: --no-overwrite を付けずに再試行するか、別の --output/--name を指定します。
doctorCriticalFailed (doctor_critical_failed)
意味: 1 つ以上の重要な doctor チェックに失敗しました。
復旧方法: flutter-mcp-toolkit doctor --json — status: "fail" かつ critical: true の checks[*] を読み取り、flutter-mcp-toolkit-setup を読み込みます。
interactionFailed (interaction_failed)
意味: tap/scroll/swipe/drag/long_press/enter_text/reveal_search/focus_widget の呼び出しが拒否されました。
原因: ref が古い、ウィジェットが表示されていない・操作できない・無効になっている、対象が見つからない、またはツールキットブリッジが初期化されていません。focus_widget の場合:focus_not_exposed(その ref に対してフォーカス可能な要素が応答していません — FocusNode のないタップ専用ボタンなど)、または focus_refused(フォーカスが別の場所に移動しました。details.focusedNow にフォーカスを保持している要素が示されます)。
復旧方法:
error.details.hintを読み取ります — 拒否の原因と次の手順が示されており、error.recovery.summaryにも同じヒントが含まれます。- それ以外の場合は
semantic_snapshot()で新しいrefを取得してから、再試行します。
semanticSnapshotFailed (semantic_snapshot_failed)
意味: semantic_snapshot の実行に失敗しました。
対処方法: flutter-mcp-toolkit doctor --json — MCPToolkitBinding.initialize() が呼び出されていることを確認してください。
evaluateExpressionFailed (evaluate_expression_failed)
意味: evaluate_dart_expression の実行に失敗しました。
原因: 式の構文エラー、実行時に例外がスローされた、または isolate に到達できません。
対処方法: 式を簡潔にし、構文を確認してください。flutter-mcp-toolkit doctor --json も実行してください。
getRecentLogsFailed (get_recent_logs_failed)
意味: get_recent_logs の取得に失敗しました。
対処方法: flutter-mcp-toolkit doctor --json — ツールキットが初期化されていることを確認してください。
invalidPredicate (invalid_predicate)
意味: wait_for が受け取った述語では、要求された観測をタイムアウト時間内に完了できません。
原因: stableWindowMs が実効 timeoutMs 以上です。
対処方法: timeoutMs を stableWindowMs より大きく設定してください。timeoutMs を省略すると、デフォルトの 5000 ms が使用されます。
waitTimeout (wait_timeout)
意味: timeoutMs が経過する前に wait_for の述語が一致しませんでした。
原因: 述語の条件が真にならない、アプリの状態が変化しない、または timeoutMs が短すぎます。
対処方法:
semantic_snapshot()— 期待されるウィジェットの状態を確認してください。timeoutMsを延長するか、述語を調整してください。
waitForFailed (wait_for_failed)
意味: wait_for の実行に失敗しました(述語の形式が不正、またはツールキットエラー)。
対処方法: flutter-mcp-toolkit schema --name wait_for
pressKeyFailed (press_key_failed)
意味: press_key の実行に失敗しました。
対処方法: flutter-mcp-toolkit schema --name press_key
handleDialogFailed (handle_dialog_failed)
意味: handle_dialog(ダイアログの閉じる/承認)の実行に失敗しました。
原因: ダイアログが表示されていない、またはすでに閉じられています。
対処方法: 呼び出す前に semantic_snapshot() でダイアログが表示されていることを確認してください。
navigateFailed (navigate_failed)
意味: navigate の push/pop/popUntil に失敗しました。
対処方法: flutter-mcp-toolkit schema --name navigate
navigatorNotRegistered (navigator_not_registered)
意味: navigate が呼び出されましたが、アプリで GlobalKey<NavigatorState> が登録されていません。再試行できません。
原因: ホストアプリで MCPToolkitBinding.instance.navigatorKey が設定されていません。
対処方法: アプリの main.dart で MCPToolkitBinding.instance.navigatorKey = navigatorKey を代入し、ホットリスタートしてください。
fillFormFailed (fill_form_failed)
意味: fill_form のオーケストレーションに失敗しました(トランスポートまたはフィールドごとのエラー)。
対処方法: flutter-mcp-toolkit schema --name fill_form
hoverFailed (hover_failed)
意味: hover の実行に失敗しました。
対処方法: flutter-mcp-toolkit doctor --json
focusWidgetFailed (focus_widget_failed)
意味: focus_widget がアプリに到達できませんでした。アプリ自体が報告する拒否(focus_not_exposed、focus_refused)は、ツールキットのヒントを details に含む interaction_failed として返されます。
対処方法: flutter-mcp-toolkit doctor --json
unknown (unknown_error)
意味: 認識できないエラーコードに対するフォールバックです。
対処方法: flutter-mcp-toolkit doctor --json — 生の原因を確認するには error.details を調べてください。