
Crewdroid
io.github.namyunsu由 GitHub 账号 namyunsu 发布v0.2.7更新于 Nov 7, 2026
Light Android emulators for AI agents: text screen outline, per-device network, battery, location.
概览
Crewdroid 运行轻量 Android 模拟器,让 AI 助手通过 MCP 启动、操作并检查设备,用于构建和测试应用。
- 可以做什么
- 启动、列出和关闭无窗口模拟器集群,支持混合屏幕、折叠机型和多个 Android 版本
- 以文本大纲读取屏幕,并按名称点击、输入、滑动、按键和截图
- 设置单设备条件:网络故障、Wi-Fi 或蜂窝链路、电量、GPS、旋转、深色模式、字体、语言、短信和来电
- 控制应用:权限、深链接、清除数据、备份与恢复、Doze 和存储压力
- 对 APK 运行多设备流程,并校验服务端数据中的重复、缺失和丢失更新
- 诊断崩溃、ANR、性能、APK 内容以及不同屏幕和字体下的布局
- 适用场景
- 适合让助手在无人点击窗口的情况下反复构建并检查 Android 应用,或复现重试、离线队列、并发编辑等多用户缺陷。它面向开发测试,不适用于推送通知、Play 服务、Google 登录或相机流程。
- 运行要求
- 通过 npx 作为本地进程运行,需要 Node 22 或更高版本,以及 Android SDK 和 36.4 或更高版本的模拟器(ANDROID_SDK_ROOT 或 ANDROID_HOME,或 crewdroid 的 SDK 目录);setup 命令可安装 SDK、JDK 17 和 google_atd 镜像。需要硬件加速(WHPX、Hypervisor.framework 或 KVM),支持 Windows x64、Apple Silicon macOS 或 Linux x64/arm64。
安装
在 SourceWeft 中
- 打开 控制台中的 Crewdroid,将其添加到工作区。
- 为需要使用其工具的对话启用该服务。
Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。
其他 MCP 客户端
参照 仓库 中的启动说明。
README
crewdroid
[ci] [npm] [MCP Registry] [license]
An Android emulator your AI agent drives on under half the RAM of Android Studio's, with the phone's conditions set per device: network, battery, location, screen, font, Android version.
crewdroid is for the build-and-check loop while you develop an app; a real phone is still where you test push notifications, Play services, Google sign-in and the camera app (Limits).
- Faster to a tested app. In one run per side, an AI agent built and 3-device tested the same app in 13 min 42 s on crewdroid and 40 min 30 s on Android Studio's default AVD, with the same result (details).
- Light. 1472 MB of host RAM and 2.09 GiB of disk per device, against 3388 MB and 7.29 GiB for Android Studio's default AVD; a screen read through the CLI takes 370.7 ms against 2074.1 ms (table).
- Made for an agent.
screenreturns a text outline ([3] btn "Send" #send @540,1210) in about a third of a screenshot's tokens (1882 vs 6183 per task),tap --text Sendtaps by name on any screen size, and every command is both a CLI with--jsonand an MCP tool. - Conditions on demand. Per device:
net airplane|nodata|slow|flaky,envbattery / location / dark mode / font size / locale / rotation, Galaxy S24 · Fold · Flip screens with fold / unfold, Android 12 / 14 / 16, a used-phoneload.
Also in the box: judging your server's data for duplicate, missing and silently overwritten records after a multi-device run, a layout sweep across screens and fonts, crash / ANR triage.
One app, built by an agent on both
We gave an AI agent the same empty project, spec and buggy server twice: build a designated-driver app, check every feature on screen, fix what breaks, then test 3 drivers against the server. Same model, no human. One run per side, 2026-11-02 (method and limits, files to repeat it).
Same result, about a third of the time. Boot explains nearly half of the gap: without those 759 s the Studio side takes about 28 minutes, and an agent reusing a running emulator would not pay all of it. This is one run per side, not a benchmark.
The run command then took about 5 minutes for 3 devices x 5 rounds with network faults. It failed the buggy server 2 of 2 times and passed the fixed server 2 of 2 times.
One device, against Android Studio's default emulator
If you found crewdroid because an AI agent on the stock emulator is slow, start here. One device, medians of 3 runs, measured on the same PC (raw runs in docs/measurements/single-vs-studio.json):
Bold = crewdroid clearly ahead. Cold boot and snapshot boot are a tie, and idle CPU varies too much between runs to call. The CLI rows time the installed crewdroid command (node bin/crewdroid.mjs, a new node process per call; it starts in 79 ms, --version); the MCP rows time calls to one crewdroid mcp server kept running. npx crewdroid adds about 0.4 s to every call (cli-speed.json), so install it once (npm i -g crewdroid) or use the MCP server. Per-phase numbers in single-vs-studio.md.
An AI agent driving one device (Field Notes demo app, "add a note and confirm it", 5 runs per path, API 34, s24ultra; raw in docs/measurements/agent-bench.json): path A reads crewdroid's text outline and taps by text, path B reads a screenshot after every action and taps by x,y.
What crewdroid adds over mobile-mcp + the stock emulator: a lighter device (table above, single-vs-studio.json), per-device network faults (net), layout checks across screens and fonts (layout.md) and judging the server's data (run-app.json). What it does not add: tap / type speed, which is the same (agent-bench.json; mobile-mcp itself was not benchmarked, path B stands in for screenshot-driven agents).
Setup: Android 16 (API 36); the Studio side is a pixel_9 AVD from google_apis_playstore with default 2 GB RAM and 4 cores, window on, -gpu auto, quickboot; the crewdroid side is up with no flags (headless, 2 cores, google_atd image); Ryzen 7 5700X, 64 GB, Windows 11, emulator 37.1.11.0. The CLI rows include starting node.
What only crewdroid does
- Judges the server, not the screen. After a run,
crewdroid judgereads your API's data and checks rules:unique(no duplicates),count(nothing missing),noLostUpdate(no silent overwrite). A bug comes back asVIOLATIONlines and exit code 1, not as a screenshot to interpret. No export endpoint?crewdroid proxy start --target http://127.0.0.1:8080records every request the emulators send (through the Android system proxy, no app or server change), andcrewdroid judge --rules judge/proxy.rules.json --log proxy.jsonlchecksdupSuccess(one write applied twice),lostUpdate(two edits on the same version both accepted),lostWrite(a failed write never retried) andidlefrom that log. Plain HTTP only for now (ADR 0008). - Breaks one device at a time, mid-flow. Network
airplane/nodata/slow/flakyper device on its own schedule, fold / unfold during an upload, Doze, trim-memory, full storage, a low-end CPU / RAM spec. Every setter reads the state back. - Runs a mixed fleet from one command. Android 12 / 14 / 16 and S24 · Fold · Flip or any
WxH@dpi, headless, all clones of one base image: 3 devices in 3.93 GB of host RAM and 6.99 GiB of disk (allocated).
Also in the box: crash / ANR triage, cold start and jank, APK inspection. Every command has --json and an exit code, and every command is an MCP tool.
New to it? Try the bundled sample first (sample app + sample server with a race condition): npx crewdroid run scenarios/photo-upload.json --seed 1. Your own app step by step: docs/guide-your-app.md.
Why not just the Android Emulator?
The stock emulator is one phone with a window, driven by hand. crewdroid is a fleet driven by a program.
Resources for 3 devices (3 API levels each; stock = avdmanager defaults, headless, fresh):
"Allocated" = bytes really on disk (sparse holes and NTFS compression excluded), the only disk number comparable to crewdroid's slimmed clones; the google_apis figure was taken as file length, so it overstates that column. Disk and RAM are lighter than both stock images, but idle CPU is not: crewdroid's ~600 % is higher than 3 stock google_atd AVDs (~248-352 % over 6 runs; one 60 s sample is noisy), so the CPU gap against google_apis comes from the image, not from crewdroid. Measured on one Windows 11 PC (Ryzen 7 5700X, 64 GB, WHPX), 3 devices. Raw JSON and method in docs/measurements/ (baseline.md, baseline-atd.md).
No barrier, by design. Real concurrency bugs come from retries, offline queues and late edits overlapping — not from pressing a button at the exact same millisecond. crewdroid gives each device its own delays and network schedule and lets the overlap happen, then reads the truth from the server (ADR 0002).
60-second tour
Boot a mixed fleet (headless, no emulator windows; on Windows a tray icon shows what runs):
Cut one device's data mid-upload, let another go flaky:
Run your own app (a release APK is fine): run --apk app.apk --flow flows/add-item.yaml --server http://127.0.0.1:8080 repeats your Maestro flow on every device under network faults, puts the recording proxy in front of your backend and judges the request log (docs/guide-your-app.md; a scenario file adds intents / adb actions per iteration). Worked example on an app that is not ours: DAVx5 (F-Droid) + a local Radicale CardDAV server, scenarios/davx5-contacts.json, 3 devices with airplane / nodata / slow / flaky, then again with 30 % of the answers cut: 0 violations, 12 of 12 contacts stored, none twice, so no bug found in DAVx5 (docs/measurements/run-app.json).
Or try the bundled sample: the multi-user scenario against a server with a race condition (BUGGY=1 in the bundled sample server), and read the verdict:
Same scenario, server fixed → OK: 3 rules, 0 violations, exit 0. The judge rules are a JSON file, so the same check runs against your own API's export.
Read the screen as text (what to tap, by name; one line per element, --json for the full tree):
Agent interface
What an agent gets is a contract, not speed claims: JSON in, JSON out, an exit code, and the state read back after every change.
- Every command is machine-readable:
--json, non-zero exit on failure, read-back after every setter, clear error before any adb call on bad input. - MCP server:
crewdroid mcpexposesdevices,up,down,net,env,app,fold,triage,screen,perf,apk,judge,hammer,runover stdio — the same functions the CLI calls, no second implementation. - Skills in
.agents/skills/(read by Claude Code, Cursor, Gemini CLI, Copilot, Codex):crewdroid,android-triage,android-screen,android-permissions-intents,android-apk,android-perf,android-offline-concurrency. Each is a short recipe of exact commands whose output the agent can act on. AGENTS.mdwith the rules an agent must keep (adb only againstemulator-<port>, one run at a time, never a barrier) andCLAUDE.mdfor Claude Code.- Safe on a shared machine: a USB phone plugged into the same PC is never touched; clones are read-only;
downkills onlycrew-*.
Cursor: install the MCP server · Codex / Gemini CLI / Copilot: the skills and AGENTS.md are picked up from the repo.
What it finds
On your app: docs/guide-your-app.md (run --apk --flow --server, proxy log rules dupSuccess / lostUpdate / lostWrite). On DAVx5 with 30 % of the answers cut, each committed-but-unanswered PUT was resent with If-None-Match: *, got 412 and made no second copy: 0 violations, 12 of 12 contacts (docs/measurements/run-app.json).
The bundled sample app (Kotlin, offline outbox, probe-app/) and sample server (sample/server/, BUGGY=1 switch) make every row above reproducible on a laptop.
Limits: when to use a real phone
crewdroid runs Google's google_atd images (chosen for lightness). Measured on fresh devices, API 31 / 34 / 36 (docs/measurements/atd-limits.md, re-check: node scripts/check-atd-limits.mjs):
Requirements
- Android SDK with command-line tools and emulator >= 36.4 (
ANDROID_SDK_ROOTorANDROID_HOMEmust point at it, or~/.crewdroid/android-sdkmust exist). No SDK yet:npx crewdroid setup --yesinstalls JDK 17 (for sdkmanager only), command-line tools, platform-tools, emulator and thegoogle_atdimage (--images 34,31,36,--dry-runlists sizes first); crewdroid downloads thegoogle_atdimages it needs for the host CPU (x86_64, or arm64-v8a on Apple Silicon / ARM Linux; API 31/34/36, ~0.9 GB each) and shrinks them withslim - Node >= 22
- From a git checkout instead of npm: run
npm ci && npm run prepackonce. It builds the on-device helper APK (JDK 17) that the npm package already ships; without itscreen/tapfall back touiautomatorandtypetakes ASCII only - Windows 10/11 x64, macOS on Apple Silicon, or Linux x64/arm64
- Hardware acceleration: WHPX (Windows), Hypervisor.framework (macOS) or KVM (Linux,
/dev/kvm);setupreportsemulator -accel-check - Image limits (
google_atd, chosen for lightness): see Limits; also no Messages or Dialer (SMS and calls change telephony state only) and no GPU (frame numbers compare runs, not phones). Supported despite the image: any Unicode text input incl. Korean (on-device helper), and the API 31 black-screen-after-relaunch is detected and recovered (blackRelaunch) - macOS/Linux:
slimskips the NTFS sparse/compress steps (snapshot zero-fill and stale-overlay cleanup still run); RAM figures are process RSS (shared pages not split)
Commands
Every command takes --json and exits non-zero on failure.
Screenshots and input
tap, type, swipe and key then print the new screen outline (as screen), read once the tree is unchanged for 150 ms (1 s at most), so an agent needs no separate observe call; --no-screen (MCP screen:false) skips it, --json (MCP format:"json") returns the result object with the outline as screen.
screen, tap, type, swipe and key go through a small on-device helper that up starts on every device (instrumentation APK dev.crewdroid.agent, reached over adb forward on 127.0.0.1; docs/adr/0007-agent-helper.md): a tree read takes tens of milliseconds instead of a uiautomator dump. Each result says via: "helper" or via: "fallback" (helper not answering: the old uiautomator / input path, then the helper is restarted). Points outside the screen exit 1 before anything is sent. Without the helper, non-ASCII text (e.g. Korean) is not typed: type exits 1 with the reason. input text only types characters in the device key map, ATD has no shell command that sets the clipboard for a paste (cmd clipboard has no implementation), and crewdroid does not install a keyboard app; pass such text through your app (intent extra, test hook) or install an IME such as ADBKeyboard yourself. The probe app has a form for these: --es check form (field id name, button Send -> CrewProbe FORM <text>, a 3-screen scroll whose bottom END logs CrewProbe SCROLL END). node scripts/check-input.mjs runs all of it on an API 34 and an API 36 device.
Diagnostics
Read-only commands (nothing stays running on the device). Each takes --json; the same four are MCP tools.
The probe app has targets for these: adb -s emulator-5580 shell am start -n dev.crewdroid.probe/.ProbeActivity --es check crash|anr|jank (anr: tap the button, then tap again within 10 s). node scripts/check-triage.mjs runs all four on one API 34 device.
Environment
crewdroid env <serial> <what> [args] [--json]. Every setter reads the state back and exits 1 with a message when it differs; bad values (battery 150, rotate 9, a serial that is not emulator-<port>) exit 1 before any adb call. Also an MCP tool (env).
rotate also runs wm fixed-to-user-rotation enabled: without it, on the API 36 image WindowManager restored the previous rotation (revertOverride) at the next app launch. rotate and show read the lock back from settings and WindowManager. gsm signal is confirmed only by the console's OK (no read-back exists). Negative numbers work (env emulator-5580 geo -122.084 37.422). ATD has no Messages or Dialer app: SMS and calls change telephony state and broadcasts only. The probe app logs what it sees: am start -n dev.crewdroid.probe/.ProbeActivity --es check env -> CrewProbe ENV dark=<bool> font=<scale> rot=<n> battery=<pct> loc=<lat>,<lon>|none (loc needs the location permission: pm grant). node scripts/check-env.mjs runs all of it on 2 devices.
App control
crewdroid app <serial> <pkg> <what> [args] [--json]. Setters read the state back and exit 1 with a message when it differs; bad names, buckets or sizes exit 1 before any adb call. Also an MCP tool (app).
The probe app has the deep links crewdroid://probe/* and https://probe.crewdroid.example/* (autoVerify) and logs CrewProbe LINK <uri>. node scripts/check-app.mjs runs all of it on 2 devices.
Used-phone load
crewdroid load <serial> off|low|mid|high [--keep-apps] [--json], or crewdroid up --load <level> (applied after boot). Also an MCP tool (load, and a load input on up). Off by default: a plain up installs nothing of this.
A real user's phone has many apps installed, several alive in the background, little free RAM and some CPU work going on. Each level is a fixed recipe: installed filler apps (30 / 80 / 150 tiny APKs, generated once by scripts/gen-filler-apks.mjs; every 3rd wakes up and schedules a periodic job), background processes of the load app dev.crewdroid.load (5 / 15 / 30), memory ballast until MemAvailable is at 50 / 30 / 15 % of its value with load off, CPU workers at 10 / 25 / 50 % of every guest core, and AlarmManager / JobScheduler wakeups. off force-stops the load app (processes, ballast, alarms, jobs) and uninstalls the fillers and the load app unless --keep-apps. The result is the measured guest state: MemAvailable (MB, %), swap, load average, CPU busy %, running processes, installed packages.
The default 896 MB guest has only ~130 MB free at idle, so lmkd kills apps from low on; use --ram 2048 or more to tell the levels apart. What each level costs (cold start, jank, low-memory kills of the app under test, host CPU/RAM) on API 34 and 36: docs/measurements/load.md (node scripts/check-load.mjs).
Add a device profile
A profile is a screen (width x height, density) applied with wm size / wm density after boot, not a separate AVD. Add an entry to profiles/galaxy.json:
Then crewdroid up --count 1 --profiles s25. minApi picks which Android image the device lands on.
Any size without editing that file: crewdroid up --count 2 --screen 1920x720@160,3840x2160@320 (density optional, derived for a ~360dp short side), or --profiles-file my.json (same schema; see profiles/custom.example.json). Sizes beyond the host monitor work — up to 7680x2160 tested; see docs/measurements/size.md.
Safety rules
- Every adb call goes through a guard that only accepts
emulator-<port>serials, so a USB phone on the same machine is never touched. - Emulators run as read-only clones of one base AVD: nothing is saved back, and
downremoves onlycrew-*instances. - One crewdroid run at a time per machine (
up/downtake a lock). - The SDK path comes only from
ANDROID_SDK_ROOT/ANDROID_HOME; nothing is hard-coded.
How it stays light
Clones of one base AVD (-read-only), system images rewritten sparse + NTFS-compressed, quickboot RAM image zero-filled before the snapshot, -lowram with 896 MB guest RAM, host Vulkan off, no audio, no window. Every knob was measured one at a time: docs/measurements/slim.md (disk), ram.md (RAM), features.md (each runtime feature's idle cost).
Hidden emulators leave nothing in the taskbar, so on Windows up adds one tray icon: a badge with the device count, green when ready. Click it for each device and the last command (command and serial only, never typed text). It holds 51 MB and 0.17 % of one core while devices run, and exits on down or when the emulators die, so nothing stays behind. CREWDROID_NO_TRAY=1 turns it off.
Devices an agent forgot are stopped for you: up leaves a small watcher (70 MB, 0.02 % of one core) that runs down after 60 minutes without a crewdroid command, or as soon as the MCP server that ran up exits. A long command such as run or layout counts as use while it runs. up --idle-minutes 120 changes the time (MCP idleMinutes, env CREWDROID_IDLE_MINUTES); 0 keeps the devices until down.
Korean docs: README.ko.md · License: MIT
Keywords: android emulator, headless, multi-device testing, concurrency testing, race condition, idempotency, lost update, offline-first sync, flaky network, Galaxy Fold Flip profiles, MCP server, AI agent, Claude Code plugin, agent skills, mobile QA automation.
来源:README.md,提交 c63f132
工具
0版本历史
5- v0.2.7最新Nov 7, 2026
- v0.2.6Nov 3, 2026
- v0.2.3Nov 3, 2026
- v0.2.0Oct 30, 2026
- v0.1.0Oct 26, 2026
评分
已满足适用检查项的 50%
该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。
- 近 30 天内可连通25 分
请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。
- 已声明认证方式10 分
请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。
- 已列出工具15 分
请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。
- 描述必需15 分
- 图标10 分
- 文档15 分
- 许可证10 分