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:
- Screenshot capture — capture the current screen
- Tap — tap a coordinate on screen (automatically identifies UI elements)
- Text input — keyboard input (English only)
- App control — launch or stop specific apps
- 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
- Launch Android Studio
- Open Tools > Device Manager
- Click + Create Device
- Under Phone, select a lightweight model like Pixel 4 (recommended for lower resource usage)
- For System Image, pick an Android OS version that matches your app’s
minSdkVersioninandroid/app/build.gradle - Click Next → Finish
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:
- Takes a screenshot
- Analyzes the image (UI layout inspection)
- Identifies the coordinates of “Test Button”
- Taps those coordinates
- 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
-
Enable USB debugging
- Settings → About phone → tap Build number 7 times → Developer options enabled
- Settings → Developer options → USB debugging ON
-
Connect via USB
- Use a data-capable cable (charge-only cables won’t work)
-
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
| Item | Emulator | Physical Device |
|---|---|---|
| PC load | High (CPU/RAM intensive) | None |
| Boot speed | Slow on first run | Instant |
| Actual behavior | Virtual environment | Same as production |
| Camera/sensors | Limited | Full 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.