技術 約8分で読めます

Android MCPサーバーでエミュレータテストを自動化する

Flutterアプリをリリース前に、毎回手動でエミュレータを起動して画面タップしたり、スクリーンショットを撮ったりするのが面倒。 そこで便利なのがAndroid MCPサーバーだ。 Claude Codeに統合することで、テストの確認業務をAIに任せられる。

もともとADB操作でアプリのビルドから実機への流し込みまではClaude Codeにやらせていたので、同じ感覚でMCP経由の画面操作もできるなら、かなり楽になりそうだと思った。明日以降、実際に試してみる予定。

この記事では、android-mcp-serverのセットアップから、実装例まで、初心者向けに解説する。


Android MCPサーバーの概要

android-mcp-serverは、エミュレータやAndroid端末に接続して、以下の操作をClaude Code経由で実行できるツール。

主な機能:

  1. スクリーンショット取得 - 現在の画面をキャプチャ
  2. タップ操作 - 画面上の座標をタップ(自動でUI要素を認識)
  3. テキスト入力 - キーボード入力(英語のみ)
  4. アプリ操作 - 特定アプリの起動・停止
  5. UIレイアウト検査 - 画面上のクリック可能要素を自動解析

従来の手動確認と違い、「コーディング → 動作確認 → PR作成」の流れをすべてClaude Codeに任せることができる。


セットアップ手順

前提条件

  • Python 3.11以上
  • uvパッケージマネージャー
  • Android Studio(エミュレータを使う場合)
  • ADB環境

Windowsを想定した手順で進める。

1. リポジトリをクローン

git clone https://github.com/minhalvp/android-mcp-server.git
cd android-mcp-server

2. Python環境を構築

uv python install 3.11
uv sync

3. Claude Codeに登録

claude mcp add --transport stdio android-mcp \
  --scope user -- uv --directory "C:\path\to\android-mcp-server" run server.py

C:\path\to\android-mcp-serverは、クローンしたディレクトリのフルパスに置き換える。

4. Claude Codeを再起動

claude code

MCP登録後は必ず再起動し、MCPサーバーを読み込ませる。


AVD(エミュレータ)の準備

android-mcp-serverを使うには、Android Studioでエミュレータを起動しておく必要がある。

Android Studioをインストール

公式サイトからダウンロードしてインストール。

エミュレータを作成

  1. Android Studio を起動
  2. メニューから Tools > Device Manager を開く
  3. + Create Device をクリック
  4. Phone カテゴリから Pixel 4 など、スペック控えめなモデルを選択(軽いため推奨)
  5. System Image では、アプリ対応のAndroid OSを選択
    • Flutter アプリの android/app/build.gradleminSdkVersion に合わせるとよい
  6. NextFinish で作成

エミュレータを起動

Device Manager に作成したAVDが表示されるので、▶ボタンを押して起動。初回は1分程度かかるが、以降はスナップショットで高速化される。

ADB接続確認

adb devices

エミュレータが表示されれば成功。


ADBの基本操作

android-mcp-serverは内部でADBコマンドを使っている。どんなことができるか、基本的なADBコマンドを紹介する。

スクリーンショット取得

adb exec-out screencap -p > screenshot.png

画面をタップ

adb shell input tap 540 960

座標はスクリーンショット上の x, y 値。開発者オプションの「ポインタ位置」を有効にすると、画面に座標が表示される。

テキスト入力

adb shell input text "hello world"

日本語は対応していないため、英字のみ。

アプリ起動

adb shell am start -n com.example.app/.MainActivity

パッケージ名とActivity名を指定。

android-mcp-serverは、これらのコマンドをClaude CodeのUI経由で自動実行する。ユーザーが手作業で入力する必要はない。


Flutter×MCPの実装例

実装例として、Flutterアプリの起動確認やテストをMCP経由で行うシナリオを想定する。

シンプルな確認の流れ

以下のようなFlutterアプリを想定:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('MCP Test App')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () {},
                child: const Text('テストボタン'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Claude Code でテストを指示

Claude Codeに以下のように指示:

エミュレータのスクリーンショットを取得して、「テストボタン」が表示されているか確認してください。
表示されていたら、ボタンをタップしてください。

Claude Codeがmcp-android-serverを呼び出し、自動的に以下を実行:

  1. スクリーンショットを取得
  2. 画像を解析(UIレイアウト検査)
  3. 「テストボタン」の座標を特定
  4. その座標をタップ
  5. 操作結果をレポート

トークン消費量に注意

MCPを使ったテストは、スクリーンショットを画像として処理するため、通常の2倍以上のトークン消費が発生する。重要な動作確認のみに絞った方が効率的。

また、スクリーンショットを何枚も取得していると、Claudeが「画像が多すぎてトークンが長い」と言って処理を中断することがある。1回のセッションでスクリーンショットは3〜5枚程度に制限するのがおすすめ。

回避策として、スクリーンショットは取るだけ取っておいて、分析はサブエージェントに任せる方法が考えられる。サブエージェント内でスキルを参照させれば、メインのコンテキストを圧迫せずに済む。

日本語入力の制限

テキスト入力は英語のみ対応。日本語テストが必要な場合は、あらかじめアプリ側にテスト用テキストを埋め込むか、サーバーから日本語テキストを注入する工夫が必要。


Kotlin×コルーチンのベストプラクティス(参考)

Flutterから呼ばれるネイティブコード(Kotlin)を含む場合、テスト対象になることが多い。Kotlin側でよくある落とし穴を紹介する。

suspend関数の使い方

MCPテストで複数の非同期操作を組み合わせる場合、Kotlinのコルーチンがよく登場する。

// NG: 呼び出し側が dispatcher を指定させられる
fun fetchData(): Flow<String> {
  // ...
}

// OK: Main-Safe. 呼び出し側は dispatcher を気にしない
suspend fun fetchData(): String = withContext(Dispatchers.IO) {
  // ...
}

すべてのsuspend関数を「main-safe」設計にすると、テストコードが簡潔になる。

Room DatabaseとDispatchers

RoomのDAO(データアクセスオブジェクト)にsuspend関数を付けると、Roomが自動でDispatchers.IOで実行する。

@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    suspend fun getAllUsers(): List<User>  // Room が自動的に IO dispatcher で実行
}

withContext(Dispatchers.IO) を手動で書く必要はない。Roomの設計が洗練されている点。

バッチ操作の性能差

// ❌ NG: 1行ずつ挿入(遅い)
users.forEach { user ->
    userDao.insertUser(user)
}

// ✅ OK: トランザクション内で一括挿入
@Transaction
suspend fun insertUsersInBatch(users: List<User>) {
    users.forEach { userDao.insertUser(it) }
}

バッチ挿入は、1行ずつ挿入の100倍以上高速。MCPテストで多くのデータを扱う場合、バッチ操作は必須。

詳しくは、公式ドキュメントやコルーチン関連の技術記事を参照。


トラブルシューティング

MCPサーバーが起動しない

Error: Could not find python 3.11

uv python install 3.11 を再実行し、Pythonをインストール。

ADB接続が切れる

error: device not found

→ エミュレータを再起動。

adb kill-server
adb start-server
adb devices

タップ座標がズレる

UIレイアウト検査の精度が低い場合、手動で座標を確認してから指示する。開発者オプション > 「ポインタ位置」を有効にすると、タップ時に座標が表示される。

日本語テキストが入力できない

adb shell input text は英字のみ。日本語は以下の方法で代替:

  • 方法1: テストデータを事前にDBに挿入
  • 方法2: API経由で日本語を注入
  • 方法3: IME(入力方法エディタ)を経由する(高度)

実機を使う場合(参考)

エミュレータではなく実機を使うこともできる。PC負荷が低いのが最大のメリット。エミュレータはCPU・メモリを大量に消費するが、実機ならその負荷がゼロになる。

前提条件

  1. USBデバッグを有効化

    • 設定 > 端末情報 > ビルド番号を7回タップ → 開発者オプションが有効に
    • 設定 > 開発者向けオプション > USBデバッグをON
  2. USBケーブルでPCに接続

    • データ転送対応のケーブルを使用(充電専用ケーブルはNG)
  3. ADBで認識されていることを確認

adb devices
# 例: 1234567890ABCDEF   device

複数デバイス接続時の設定

実機とエミュレータを同時に接続している場合、config.yaml でデバイスシリアルを指定する。

# android-mcp-server/config.yaml
device_serial: "1234567890ABCDEF"

シリアル番号は adb devices で表示される文字列。

実機を使うメリット

項目エミュレータ実機
PC負荷高い(CPU・メモリ消費大)なし
起動速度初回は遅い即時
実際の動作仮想環境本番と同じ
カメラ・センサー制限ありフル対応

開発用の古いスマホがあれば、テスト専用機として使うのがおすすめ。


MCPの可能性と今後

android-mcp-serverは、モバイルアプリテストの自動化を大きく簡素化する。ただし現状は、以下の制限がある:

  • トークン消費量 - 画像処理の負荷が高い
  • 日本語対応 - 入力が英字のみ
  • UIレイアウト解析の精度 - 複雑な画面では認識がズレることも

だが、これらは改善されていくだろう。MCPが業界標準化していく中で、より多くのツール・SDKが対応し、AIアシスタントによるテスト自動化は当たり前になっていく可能性が高い。


参考リンク