Tech 7 min read

Automating Emulator Testing with Android MCP Server

Before releasing a Flutter app, manually launching the emulator, tapping through screens, and taking screenshots every time gets tedious. That’s where the Android MCP Server comes in. By integrating it with Claude Code, you can hand off UI verification work to AI.

I was already using Claude Code via ADB commands to build and push the app to a device, so if I could do screen interaction through MCP in the same way, it would be a huge time saver. Planning to actually test this out soon.

This post covers android-mcp-server from setup through implementation examples, aimed at beginners.


What Is the Android MCP Server?

android-mcp-server is a tool that connects to an emulator or Android device and lets Claude Code perform the following operations:

Main features:

  1. Screenshot capture — capture the current screen
  2. Tap — tap a coordinate on screen (automatically identifies UI elements)
  3. Text input — keyboard input (English only)
  4. App control — launch or stop specific apps
  5. UI layout inspection — automatically analyze tappable elements on screen

Unlike manual verification, the entire flow of “code → verify behavior → create PR” can be delegated to Claude Code.


Setup

Prerequisites

  • Python 3.11 or later
  • uv package manager
  • Android Studio (if using an emulator)
  • ADB environment

Steps below assume Windows.

1. Clone the repository

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

2. Set up the Python environment

uv python install 3.11
uv sync

3. Register with Claude Code

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

Replace C:\path\to\android-mcp-server with the full path to the cloned directory.

4. Restart Claude Code

claude code

After registering the MCP, always restart to load the MCP server.


Setting Up an AVD (Emulator)

android-mcp-server requires an emulator running in Android Studio.

Install Android Studio

Download and install from the official site.

Create an emulator

  1. Launch Android Studio
  2. Open Tools > Device Manager
  3. Click + Create Device
  4. Under Phone, select a lightweight model like Pixel 4 (recommended for lower resource usage)
  5. For System Image, pick an Android OS version that matches your app’s minSdkVersion in android/app/build.gradle
  6. Click NextFinish

Start the emulator

The created AVD appears in Device Manager. Click the play button to launch it. The first boot takes about a minute, but subsequent starts are faster thanks to snapshots.

Verify ADB connection

adb devices

If the emulator appears in the list, you’re good to go.


Basic ADB Commands

android-mcp-server uses ADB commands internally. Here are the basics so you know what’s possible.

Capture a screenshot

adb exec-out screencap -p > screenshot.png

Tap the screen

adb shell input tap 540 960

Coordinates are x, y values on the screenshot. Enable “Pointer location” in Developer Options to see coordinates displayed on screen.

Enter text

adb shell input text "hello world"

English characters only — Japanese isn’t supported.

Launch an app

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

Specify the package name and Activity name.

android-mcp-server runs these commands automatically through Claude Code’s UI. No manual input needed.


Flutter × MCP Implementation Example

Here’s a scenario for launching and verifying a Flutter app through MCP.

Simple verification flow

Assume this Flutter app:

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('Test Button'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Give Claude Code a test instruction

Take a screenshot of the emulator and check if "Test Button" is visible.
If it is, tap it.

Claude Code calls mcp-android-server and automatically:

  1. Takes a screenshot
  2. Analyzes the image (UI layout inspection)
  3. Identifies the coordinates of “Test Button”
  4. Taps those coordinates
  5. Reports the result

Watch token consumption

MCP-based testing processes screenshots as images, which uses more than twice the tokens of a typical interaction. Keep it to important verifications only.

Also, taking many screenshots in one session can cause Claude to say the context is too long and stop mid-task. Limit screenshots to about 3–5 per session.

A workaround: take screenshots but delegate the analysis to a sub-agent. Having the sub-agent reference the skill keeps it from bloating the main context.

Japanese text input limitation

Text input only supports English. For Japanese testing, either embed test strings in the app in advance or inject Japanese text from the server side.


Kotlin Coroutines Best Practices (Reference)

If your Flutter app calls native Kotlin code, that code often becomes a testing target. Here are some common pitfalls on the Kotlin side.

Suspend function design

When combining multiple async operations in MCP tests, Kotlin coroutines come up frequently.

// Bad: caller has to specify the dispatcher
fun fetchData(): Flow<String> {
  // ...
}

// Good: Main-Safe. Caller doesn't need to think about dispatchers
suspend fun fetchData(): String = withContext(Dispatchers.IO) {
  // ...
}

Designing all suspend functions as “main-safe” keeps test code clean.

Room Database and Dispatchers

Attaching suspend to a Room DAO function lets Room automatically run it on Dispatchers.IO:

@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    suspend fun getAllUsers(): List<User>  // Room runs this on IO dispatcher automatically
}

No need to write withContext(Dispatchers.IO) manually — Room handles it.

Batch operation performance

// Bad: insert one row at a time (slow)
users.forEach { user ->
    userDao.insertUser(user)
}

// Good: bulk insert inside a transaction
@Transaction
suspend fun insertUsersInBatch(users: List<User>) {
    users.forEach { userDao.insertUser(it) }
}

Batch inserts can be over 100x faster than one-at-a-time. When MCP tests handle large amounts of data, batch operations are essential.


Troubleshooting

MCP server won’t start

Error: Could not find python 3.11

→ Run uv python install 3.11 again to install Python.

ADB connection drops

error: device not found

→ Restart the emulator:

adb kill-server
adb start-server
adb devices

Tap coordinates are off

When UI layout inspection isn’t accurate enough, check coordinates manually before instructing. Enable Developer Options > “Pointer location” to see coordinates on tap.

Japanese text can’t be entered

adb shell input text only supports ASCII. Alternatives for Japanese:

  • Option 1: Pre-insert test data into the database
  • Option 2: Inject Japanese text via API
  • Option 3: Route through an IME (advanced)

Using a Physical Device (Reference)

You can use a real device instead of an emulator. The biggest advantage is no PC load — emulators are CPU/memory-hungry, but a physical device adds zero overhead.

Prerequisites

  1. Enable USB debugging

    • Settings → About phone → tap Build number 7 times → Developer options enabled
    • Settings → Developer options → USB debugging ON
  2. Connect via USB

    • Use a data-capable cable (charge-only cables won’t work)
  3. Verify ADB recognizes the device

adb devices
# Example: 1234567890ABCDEF   device

Multiple device setup

When both a device and an emulator are connected, specify the device serial in config.yaml:

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

The serial number is the string shown by adb devices.

Physical device vs. emulator

ItemEmulatorPhysical Device
PC loadHigh (CPU/RAM intensive)None
Boot speedSlow on first runInstant
Actual behaviorVirtual environmentSame as production
Camera/sensorsLimitedFull support

If you have an old phone sitting around, it makes a great dedicated test device.


Potential and Future of MCP

android-mcp-server significantly simplifies mobile app test automation. Current limitations include:

  • Token consumption — image processing is expensive
  • Japanese support — text input is ASCII only
  • UI layout analysis accuracy — complex screens can misidentify elements

These will improve over time. As MCP becomes an industry standard, more tools and SDKs will support it, and AI-assisted test automation is likely to become the norm.


References