waydroid-mcp
by YeeDochi
README.md
# waydroid-mcp
에이전트가 안드로이드 화면을 **그림이 아니라 좌표 붙은 텍스트로** 읽고 조작하게 해주는
MCP 서버 겸 CLI. 조작을 녹화해 매크로로 만들고, 매크로가 어긋날 때만 에이전트를 부른다.
## 왜
에이전트로 안드로이드를 조작할 때 병목은 adb가 아니다. 실측하면 adb 왕복은 0.01초다.
진짜 비용은 에이전트가 큰 스크린샷을 읽고 "버튼이 저기쯤"이라고 추정하는 시간이다.
UI 계층을 탭가능 목록으로 압축하면 홈 화면 한 장이 **191바이트**가 된다.
같은 화면의 이미지가 69KB이니 1/350이고, 좌표는 추정이 아니라 조회 결과다.
```
이전: [1920x1056 이미지] → 눈으로 훑고 좌표 추정
이후: [1] 설정 (960,1260) ... → 즉시 선택
```
## 무엇을 하나
| 툴 | 하는 일 |
|---|---|
| `screen` | 탭가능 목록. 트리를 못 읽으면 축소 이미지로 폴백 |
| `tap` | 라벨 또는 좌표로 탭하고 **바뀐 화면을 함께 반환** |
| `swipe` / `send` | 스와이프, 문자·키 입력 |
| `record` | `start`~`stop` 사이의 조작을 매크로 스크립트로 생성 |
| `device` | 세션 제어와 진단 |
`tap`이 결과 화면을 함께 주므로 왕복이 절반이 된다. 헛짚은 탭도 즉시 드러난다.
MCP와 CLI가 **같은 코어**를 쓴다. 매크로 셸은 CLI를 부르므로 녹화한 대로 재생된다.
구현을 나누면 "녹화한 대로 재생되지 않는" 버그가 나는데, 그건 원인 찾기가 최악이다.
## 설치
```bash
python3 -m venv .venv && .venv/bin/pip install -e .
.venv/bin/waydroid-mcp bootstrap # adb 키 상태 확인과 필요한 명령 출력
.venv/bin/waydroid-mcp session start # gamescope 작은 창으로 Waydroid 기동
.venv/bin/waydroid-mcp screen
```
adb로 붙으므로 Waydroid 전용이 아니다. 실기기나 다른 에뮬레이터에도 `screen`/`tap`은 그대로 쓸 수 있다.
Waydroid에 한정되는 것은 세션 제어와 adb 키 부트스트랩뿐이다.
## Waydroid를 쓸 때 알아둘 것
- **창이 없으면 컨테이너가 동결되고 adb 호출이 에러 없이 멈춘다.** 세션만 띄우지 말고 창까지 띄운다.
이 경우 툴이 `frozen`으로 사유를 알려준다
- Waydroid에는 adb 승인 팝업이 없다. 호스트 공개키를 컨테이너에 직접 넣어야 하고,
**sudo가 필요한 것은 그 한 번뿐**이다
- gamescope를 중첩해 안드로이드는 1280x720으로 렌더하고 창만 작게 띄운다.
화면 인식 도구는 내부 해상도를 보므로 창을 줄여도 인식이 깨지지 않는다
## 매크로
조작을 녹화하면 이런 스크립트가 나온다. **기대 화면 검증문이 공짜로 따라온다** —
실제로 그 화면에서 눌러 성공했다는 사실이 근거다.
```bash
step 1 expect "카메라"
wm-tap '카메라'
step 2
wm-key HOME
finish
```
```bash
export WM_BIN=/path/to/waydroid-mcp MACRO_REPORT=$PWD/report.txt
bash rec.sh; echo $?
```
종료 코드로 사유가 구분된다.
| 코드 | 뜻 | 대응 |
|---|---|---|
| 0 | 정상 | |
| 2 | 타임아웃 | 기다리던 화면이 안 나옴 |
| 3 | 기대 화면 불일치 | 팝업 등 다른 화면 — 처리하고 복귀 |
| 4 | 디바이스 이상 | 동결·연결 끊김 |
| 5 | 진행 없음 | 눌러도 반응 없음 — **반복해도 소용없다** |
3과 5를 나누는 것이 중요하다. 3은 다른 화면을 처리하고 돌아오면 되지만,
5는 눌러도 반응이 없으니 같은 조작을 반복해봐야 소용없다.
실패하면 보고 파일에 단계·기대·실제 화면이 남는다.
에이전트는 이 파일만 읽고 판단하므로 화면을 다시 뜨는 왕복이 필요 없다.
```
단계: 1
사유: 기대 화면 불일치 (코드 3)
기대: 카메라
--- 실제 화면 ---
[1] 옵션 (1035,41)
[2] 셔터 (1178,333)
```
이렇게 해서 에이전트는 조작의 주체가 아니라 **예외 처리자**가 된다.
정상 구간에서는 토큰을 쓰지 않는다.
## 캔버스 앱 검증 — 화면 지문
게임처럼 캔버스로 그리는 앱은 UI 트리가 비어서 라벨로 검증할 수 없다.
그렇다고 좌표만 나열하면 한 번 어긋난 뒤 끝까지 엉뚱한 곳을 누른다.
그래서 화면을 성긴 격자(8x5)로 줄여 칸별 평균 밝기를 지문으로 쓴다.
```bash
waydroid-mcp fingerprint # 지금 화면의 지문
waydroid-mcp expect-screen <지문> # 그 화면인가? 아니면 종료 3
```
녹화 시 라벨이 없는 단계에는 이 지문이 자동으로 붙는다.
```bash
step 3 expect-screen 6a97cacaad909093...
wm-tap '640,400'
```
정확한 해시가 아니라 **평균 + 허용오차**인 것이 요점이다. 애니메이션과
안티에일리어싱 때문에 같은 화면도 픽셀은 매번 다르다. 실측에서 같은 홈 화면은
40/40칸 일치, 카메라 화면은 0/40으로 갈렸다.
조절 손잡이:
| 인자 | 뜻 |
|---|---|
| `--tol` | 칸별 밝기 허용오차(기본 14). 키우면 관대해진다 |
| `--min-match` | 일치해야 할 칸 비율(기본 0.85) |
부분만 바뀌는 화면(팝업 하나 뜬 정도)은 `--min-match`를 낮춰야 통과한다.
반대로 비슷한 화면을 구분해야 하면 올린다. **환경마다 다르니 실제로 재보고 잡는다.**
## 개발
```bash
python3 -m pytest tests/ -q # 전부 가짜 adb로 돈다. 실기기 불필요
```
실기기 확인은 [docs/manual-verification.md](docs/manual-verification.md).
## 한계
- `input tap` 한 번에 0.6초, `uiautomator dump`는 1.6초가 든다.
전자는 매번 JVM을 띄우는 비용이고, monkey 네트워크 모드(포트 바인딩 실패)와
`/dev/input` 직접 쓰기(evdev가 아님)로는 우회할 수 없었다.
더 줄이려면 안드로이드 안에 상주 프로세스가 필요하다
- 캔버스로 그리는 앱(게임, 일부 Flutter)은 UI 트리가 비어서 이미지로 폴백한다.
구조적 한계라 없앨 수 없다
- `uiautomator dump`는 같은 앱에서도 성공과 실패를 오간다. **일시적 실패라 재시도로 넘기고**,
끝까지 안 되면 이미지 폴백으로 떨어진다. 조용히 옛 화면을 주는 일은 없다
## 다른 접근성 서비스와 같이 쓰기 (중요)
**`uiautomator dump`는 다른 접근성 서비스의 바인딩을 약 0.8초 끊는다.** 실측했다 —
덤프 도중 상대 서비스의 바인딩이 0으로 떨어지고 곧 다시 붙는다. 상대 앱은
"접근성 서비스가 중지되었습니다" 같은 알림을 띄우고, 스크립트가 돌고 있었다면 깨질 수 있다.
프로세스 자체는 죽지 않고 설정도 유지되지만, **끊겼다 붙는 것만으로 자동화 도구에는 치명적이다.**
### 해결: 트리 조회를 끈다
```bash
waydroid-mcp --no-tree screen # 또는 WM_NO_TREE=1
```
`--no-tree`면 `uiautomator`를 아예 부르지 않는다. 실측에서 상대 바인딩이
25회 관측 내내 유지됐다.
잃는 것이 거의 없다. **접근성 자동화가 붙는 대상은 대개 게임 같은 캔버스 앱이고,
그런 앱은 트리가 애초에 비어 있다.** 덤프해서 얻는 것 없이 상대만 끊고 있었던 셈이다.
이 모드에서는:
| | 동작 |
|---|---|
| `screen` | 축소 이미지 |
| `tap "x,y"` | 그대로 동작 (`input` 명령은 접근성과 무관) |
| `tap "라벨"` | 좌표로 지정하라는 오류 |
정리하면 **트리가 필요한 화면과 접근성 도구가 도는 화면은 대개 겹치지 않는다.**
겹칠 때는 트리를 포기하는 쪽이 맞다.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues