agent-desktop으로 Claude Code가 스크린샷 없이 맥 앱 단추를 누르게 하는 법
계산기 단추를 ref로 눌러 답을 읽는다
agent-desktop은 맥 앱의 단추마다 ref라는 이름표를 붙이고, 그 ref로 단추를 누르는 CLI입니다. 이 CLI와 스킬을 넣은 Claude Code에 계산기 곱셈을 맡기면, 계산기를 띄우고 단추 8개를 ref로 누른 뒤 표시창 값을 읽어 답합니다. 네 번 맡기면 네 번 모두 표시창의 6,096을 읽어 옵니다.
이 세션은 agent-desktop 명령만 허락한 묻지 않는 모드라, 변수를 쓴 for 루프 하나는 거절되고 Claude Code가 단추마다 명령을 따로 보냈습니다.
npm install -g agent-desktopnpx skills add https://github.com/vercel-labs/agent-desktop --skill agent-desktop -a claude-code -yagent-desktop permissions둘째 줄은 지금 폴더의 .claude/skills/agent-desktop에 스킬을 넣습니다. 셋째 줄의 accessibility가 granted가 아니면 agent-desktop permissions --guide를 칩니다. 시스템 설정의 손쉬운 사용 화면이 열리고, 넣을 앱으로 agent-desktop을 띄운 터미널 앱을 보여 줍니다.
이 권한은 그 터미널에서 띄운 모든 프로그램에 붙고, agent-desktop에는 앱을 고르는 허용 목록이 없어 메일·메신저 창도 똑같이 읽고 누릅니다. snapshot으로 읽은 창의 글자는 Claude Code 대화에 그대로 들어갑니다. 다 쓴 뒤에는 같은 목록에서 터미널 앱 스위치를 끕니다.
그 폴더에서 Claude Code를 열고 맡깁니다.
계산기에서 127×48 을 계산해 결과를 알려 줘기본 모드에서는 명령마다 실행할지 묻습니다.
npm이 install script를 막는다면
npm 12처럼 install script를 막는 npm에서는 명령은 되지만 호출마다 Node 런처를 거쳐 125ms쯤 늦어집니다. 설치 안내대로 스크립트를 허락해 다시 깔거나 Homebrew로 깝니다.
npm install -g agent-desktop --allow-scripts=agent-desktopbrew install agent-desktopclaude -p나 묻지 않는 모드로 돌린다면
허락 규칙에 Bash(agent-desktop:*)를 넣으면 agent-desktop 명령을 묻지 않고 실행합니다. 이 규칙은 계산기 밖 모든 앱에 쓰는 명령과, 창을 앞으로 가져와 실제 마우스·키 입력을 보내는 --headed까지 허락합니다. --headed를 막는 것은 "사용자가 허락할 때만 --headed"라고 적은 스킬 문장뿐입니다. 변수를 쓴 for 루프는 이 규칙으로도 묻는 대상이라 묻지 않는 모드에서는 거절됩니다. 네 번 모두 Claude Code가 루프를 풀어 단추마다 명령을 다시 보냈습니다.
claude -p "계산기에서 127×48 을 계산해 결과를 알려 줘" --allowedTools "Bash(agent-desktop:*)"단추 목록은 접근성 트리에서 온다
agent-desktop은 VoiceOver 같은 화면 읽기 프로그램이 읽는 단추·글자 목록인 접근성 트리를 JSON으로 읽고, 누를 수 있는 요소마다 ref를 붙입니다. ref는 @스냅샷id:e번호 꼴 이름표입니다. 앞서 계산한 적이 있는 계산기를 읽으면 최근 표현식 칸까지 단추와 칸 27개가 이렇게 나옵니다.
이름표가 그 스냅샷의 id를 달고 다녀서, id 없이 @e15만 넘기면 거절됩니다.
기본 방식인 headless는 마우스 포인터와 포커스를 건드리지 않고 접근성 동작 AXPress로 단추를 누릅니다. 실제 마우스 클릭은 --headed를 붙였을 때만 보냅니다. AXPress가 안 듣는 요소면 다른 접근성 동작까지만 해 보고, 마우스로 넘어가지 않고 ACTION_FAILED로 멈춥니다.
Claude Code는 ref 27개를 받아 AXPress로 누르고 다시 읽는다
누를 때마다 응답은 delivered_unverified입니다. 눌렸는지 응답만으로는 알 수 없어, 같은 창을 다시 읽어 표시창 값을 봅니다.
응답의 retry가 unsafe라 같은 ref를 또 누르면 두 번 눌릴 수 있습니다.
권한과 빈 트리에서 멈춘다
agent-desktop은 막히면 오류 코드나 빈 ref 목록을 돌려줍니다.
| 막히는 곳 | 응답 | 할 일 |
|---|---|---|
| 터미널 앱에 손쉬운 사용 권한 없음 | PERM_DENIED | agent-desktop permissions --guide |
| 트리를 거의 안 내는 앱(게임 등) | ref가 없거나 몇 개뿐 | -i 없이 다시 읽기, 안 되면 screenshot(화면 기록 권한)으로 보고 --headed 좌표 클릭 |
| 누른 뒤 다시 그려진 칸의 옛 ref | STALE_REF | 새 snapshot의 ref로 |
| 더블클릭·끌기·hover | POLICY_DENIED | 사용자가 허락할 때만 --headed |
계산기에서도 누른 뒤 최근 표현식 칸이 다시 그려졌습니다. claude -p 세 번 중 두 번은 그 칸의 옛 ref로 읽다가 STALE_REF를 받았고, 표시창을 먼저 읽어 두어 답은 같았습니다.
권한 안내가 화면 기록까지 묻거나 tmux·SSH에서 띄운다면
--guide는 손쉬운 사용 다음에 화면 기록 권한도 안내합니다. 화면 기록은 screenshot에만 쓰여, 쓰지 않으면 건너뜁니다.
권한은 agent-desktop을 띄운 앱에 붙습니다. tmux 서버나 SSH 세션 아래에서는 띄운 앱이 없어서 --guide가 agent-desktop 바이너리를 내놓습니다. 이때는 tmux를 시작한 터미널 앱을 시스템 설정 > 개인정보 보호 및 보안 > 손쉬운 사용에 손으로 넣습니다. 목록에 agent-desktop 바이너리가 따로 보이면 그것도 켭니다. 권한을 준 뒤 터미널을 다시 시작하지 않아도 다음 agent-desktop 명령부터 권한이 듣습니다. macOS 27부터는 이 목록 이름이 Device Control and Data Access입니다.