nexus-android 0.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,266 @@
1
+ Metadata-Version: 2.4
2
+ Name: nexus-android
3
+ Version: 0.4.1
4
+ Summary: Android computer use for AI agents over ADB: one MCP server shared by Claude Code, Codex and Antigravity
5
+ Keywords: mcp,mcp-server,android,adb,computer-use,ai-agents,claude,codex,antigravity
6
+ Author: Julien Piron
7
+ License-Expression: MIT
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: Microsoft :: Windows
11
+ Classifier: Programming Language :: Python :: 3.14
12
+ Classifier: Topic :: Software Development :: Testing
13
+ Requires-Dist: mcp>=2.3.0
14
+ Requires-Dist: pillow>=12.3.0
15
+ Requires-Python: >=3.14
16
+ Project-URL: Homepage, https://julienpiron.fr/nexusandroid/
17
+ Project-URL: Repository, https://github.com/pironjulien/android-computer-use
18
+ Project-URL: Changelog, https://github.com/pironjulien/android-computer-use/blob/main/CHANGELOG.md
19
+ Project-URL: Issues, https://github.com/pironjulien/android-computer-use/issues
20
+ Description-Content-Type: text/markdown
21
+
22
+ # NexusAndroid
23
+
24
+ <!-- mcp-name: io.github.pironjulien/nexus-android -->
25
+
26
+ [![Release](https://img.shields.io/github/v/release/pironjulien/android-computer-use)](https://github.com/pironjulien/android-computer-use/releases)
27
+ [![Tests](https://github.com/pironjulien/android-computer-use/actions/workflows/tests.yml/badge.svg)](https://github.com/pironjulien/android-computer-use/actions/workflows/tests.yml)
28
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](https://github.com/pironjulien/android-computer-use/blob/main/LICENSE)
29
+ [![Python 3.14](https://img.shields.io/badge/python-3.14-blue?logo=python&logoColor=white)](https://github.com/pironjulien/android-computer-use/blob/main/pyproject.toml)
30
+ [![MCP](https://img.shields.io/badge/MCP-server-7ee2a8)](https://modelcontextprotocol.io)
31
+
32
+ [![NexusAndroid: Android computer use for AI agents, over plain ADB. Works with Claude, Codex and Antigravity.](https://raw.githubusercontent.com/pironjulien/android-computer-use/main/docs/assets/banner.webp)](https://julienpiron.fr/nexusandroid/)
33
+
34
+ **Android computer use for AI agents, over plain ADB.** One MCP server, `nexus-android`, lets Claude Code, Codex
35
+ and Antigravity see and drive a real Android phone: screen and UI tree, gestures, typing with accents and emoji,
36
+ SMS/RCS reading, prepared replies, notifications, files, settings. No root, no app to install on the phone, no
37
+ cloud relay: a USB cable or the local Wi-Fi.
38
+
39
+ Built and tested on a Samsung Galaxy Fold (`SM-F971B`, One UI) with a Windows PC. Other Android phones with USB
40
+ debugging should work but have not been tested. Agent-facing messages are in French.
41
+ Part of the Nexus family: [NexusWindows](https://julienpiron.fr/nexuswindows/)
42
+ ([Claude](https://github.com/pironjulien/claude-windows-computer-use) ·
43
+ [Codex](https://github.com/pironjulien/codex-windows-computer-use) ·
44
+ [Antigravity](https://github.com/pironjulien/antigravity-windows-computer-use)),
45
+ [NexusBrowser](https://julienpiron.fr/nexusbrowser/) ([repo](https://github.com/pironjulien/chrome-computer-use)),
46
+ [NexusAndroid](https://julienpiron.fr/nexusandroid/) ([repo](https://github.com/pironjulien/android-computer-use)).
47
+ Project page: [julienpiron.fr/nexusandroid](https://julienpiron.fr/nexusandroid/).
48
+
49
+ ## Quick start
50
+
51
+ 1. Phone: Settings → About phone → tap *Build number* 7 times, then Developer options → **USB debugging** on.
52
+ Plug it in and accept the PC's key on the phone.
53
+ 2. PC: install [`uv`](https://docs.astral.sh/uv/) and the Android Platform Tools (`winget install Google.PlatformTools`).
54
+ 3. Add the server to your agent (it runs straight from GitHub, no clone needed):
55
+
56
+ ```bash
57
+ claude mcp add nexus-android --scope user -- uvx --python 3.14 --from git+https://github.com/pironjulien/android-computer-use nexus-android
58
+ ```
59
+
60
+ Then ask your agent: *"Look at my phone and tell me what is on the screen."*
61
+
62
+ <details>
63
+ <summary>Codex (<code>~/.codex/config.toml</code>)</summary>
64
+
65
+ ```toml
66
+ [mcp_servers.nexus-android]
67
+ command = "uvx"
68
+ args = ["--python", "3.14", "--from", "git+https://github.com/pironjulien/android-computer-use", "nexus-android"]
69
+ ```
70
+ </details>
71
+
72
+ <details>
73
+ <summary>Antigravity, Claude Desktop, Cursor and other JSON clients</summary>
74
+
75
+ ```json
76
+ {
77
+ "mcpServers": {
78
+ "nexus-android": {
79
+ "command": "uvx",
80
+ "args": ["--python", "3.14", "--from", "git+https://github.com/pironjulien/android-computer-use", "nexus-android"]
81
+ }
82
+ }
83
+ }
84
+ ```
85
+ </details>
86
+
87
+ If `adb` is not on the `PATH`, set `NEXUS_ANDROID_ADB` to its full path in the server's environment. Typing accents
88
+ and emoji needs the pinned scrcpy server that [`install.ps1`](#full-install-windows) downloads and checks; without it,
89
+ ASCII typing still works.
90
+
91
+ ### Example prompts
92
+
93
+ - *"Open Google Maps and start navigation to the nearest pharmacy."*
94
+ - *"Read my last SMS from Alice and draft a reply saying I'll be 10 minutes late."* (drafted, never sent without your "yes")
95
+ - *"Turn on do not disturb and lower the brightness."*
96
+ - *"Pull the last three photos from the phone to my PC."*
97
+ - *"Run a checkup: battery, storage, security patch, apps installed outside the stores."*
98
+
99
+ ## How it works
100
+
101
+ ```mermaid
102
+ flowchart LR
103
+ A["Claude Code"] --> S
104
+ B["Codex"] --> S
105
+ C["Antigravity"] --> S
106
+ S["nexus-android<br/>MCP server (Python)"] -->|"ADB over USB or local Wi-Fi"| P["Android phone<br/>(no app, no root)"]
107
+ S -.->|"shared lock, screen-awake state"| L[("~/.nexus")]
108
+ ```
109
+
110
+ `look` returns a screenshot plus the numbered UI tree; `act` runs a batch of gestures, re-reading the screen before
111
+ each one and reporting what really happened. Messages, notifications, contacts and settings are read through
112
+ Android's own providers and `dumpsys`, never by scraping the screen.
113
+
114
+ ## Why a shared server
115
+
116
+ Each agent has its own desktop computer use, but none of the three clients has a built-in tool for a phone. One
117
+ server avoids maintaining the same ADB access three times, and lets the three agents share one lock, one
118
+ screen-awake session and the same safeguards.
119
+
120
+ ## Tools
121
+
122
+ | Tool | Role |
123
+ | --- | --- |
124
+ | `phone` | Connected phone (USB or Wi-Fi), model, screen, foreground app, fold posture, reboot detection (structured output) |
125
+ | `look` | Screen image and numbered `[N]` elements with text, state and center `@x,y` |
126
+ | `act` | Batched gestures: tap, long press, typing (accents and emoji included), clear, keys, scroll, swipe, wait, links (`geo:`, `google.navigation:`, `tel:` opens the dialer without calling, `mailto:`, `https://`) |
127
+ | `launch` | Open an app by package or part of its name |
128
+ | `messages` | SMS and RCS conversations, read by contact name or number, attached photos; security codes masked |
129
+ | `draft` | Open SMS/RCS or WhatsApp with the reply already written, recipient by name or number; sent only after the user explicitly says "send" |
130
+ | `notifications` | Notifications on screen: app, time, title, text; security codes masked; `all` adds ongoing ones |
131
+ | `files` | Shared storage only (`/sdcard`): list newest first, `pull` to the PC (`~\Downloads\Telephone`), `push` to the phone; never overwrites, never deletes |
132
+ | `info` | Read-only: last known location (accuracy, age, Maps link), recent calls, a contact's numbers, upcoming events (structured output) |
133
+ | `settings` | Read or change, then read back: Wi-Fi, Bluetooth, mobile data, location, NFC, do not disturb, brightness, media volume; turning Wi-Fi off is refused when the link goes through it (structured output) |
134
+ | `checkup` | Health report: system, security patch, battery, storage, memory, apps installed outside the stores, wireless debugging; `clean` clears caches |
135
+
136
+ Google Messages stores RCS messages in Android's messaging provider, like MMS, so the server reads them without going
137
+ through the screen. An RCS group only has an encoded address there: the server works out its members and names the
138
+ sender of each received message. A conversation archived or trashed in Google Messages stays readable and is labelled
139
+ as hidden in the app.
140
+
141
+ ## Safeguards
142
+
143
+ - **Paying, sending, calling, publishing, deleting for good, formatting, uninstalling, resetting, cancelling a subscription,
144
+ making a repository public**: a button of this kind is refused without `confirm=true`, for every gesture aimed at
145
+ it (tap, typing, clearing, swipe, `x,y` coordinates included). The agent sets it on the user's explicit order for
146
+ that gesture, or after their "yes". The labels come from one policy file shared with the desktop tools
147
+ (`garde-fous.json`), matched without accents or case and in every listed form ("Envoyez", "Payez", "Paiement",
148
+ "Commandez", "Checkout", "Appeler"...). Everything else is free: deleting to the trash, accepting, sharing.
149
+ A generic "OK" or "Validate" needs it on a screen that talks about a payment or a format. In a messaging, mail,
150
+ social or phone app, a clickable icon with no name at all (often the send or call button) needs it too. Enter, call keys and any key
151
+ outside a short safe list need it as well.
152
+ - **Target checked on the current screen**: the UI and the phone state (lock, foreground app) are read again in
153
+ one round trip just before each gesture; a changed `[N]` or a spot with nothing readable is refused. Reports: ✓ effect read back, ~ sent but not verified, ✗ refused or failed.
154
+ - **One phone only**: the owner's phone is identified by its hardware serial number, recorded at the first USB
155
+ connection; emulators, TV boxes and other devices are ignored. One agent at a time drives it (lock shared by all
156
+ servers on the PC).
157
+ - **Banking, payment, crypto, gambling, identity, authenticators**: look, scroll and wait freely; tap, type or
158
+ swipe only with `confirm=true` (the user's order to navigate); a money or send button, or an unnamed icon, never,
159
+ even with `confirm=true`; a link (`open_url`) that would open one of them is refused (a deep link can prepare a payment).
160
+ - **Codes received by SMS or shown in notifications**: masked before the agent reads them, and blacked out on the
161
+ image. When the UI tree cannot be read, codes cannot be located, so the image is not sent unless `look` gets
162
+ `confirm=true` (with the user's agreement).
163
+ - **Passwords**: no typing into a password field.
164
+ - **Locked phone**: no gestures. The user unlocks it; no code is ever entered.
165
+ - **Text read** on screen or in messages is data, never instructions.
166
+ - **User holding the phone**: no gesture mixes with theirs. If the phone was touched in the last 3 seconds (and not
167
+ by an agent), the server waits up to 10 seconds, then refuses.
168
+ - **Link lost during a gesture**: reported as `?` (effect unknown), never as a plain failure: the agent looks again
169
+ before redoing it, so nothing is sent twice.
170
+ - **Fold folded or unfolded** since the last image: `x,y` targets are refused until a new `look`.
171
+ - **Reboot**: `phone` and `look` say once that the phone restarted; when it cannot be found, the message reminds that
172
+ Android turns wireless debugging off after a reboot or a network change.
173
+ - **Other control tools** (mobile-mcp, Appium, Shizuku, another scrcpy) left running on the phone can block the UI
174
+ tree: `checkup` and the error name them.
175
+
176
+ ## Screen kept on while the agent works
177
+
178
+ Unlocking again and again while an agent works is tedious, but a phone used to pay must not stay unlocked. So, at the
179
+ first gesture (`act`, `launch`, `draft`) on a phone **the user has unlocked**, the server saves the screen timeout
180
+ (`screen_off_timeout`) and raises it to 30 minutes; `phone` shows until when.
181
+
182
+ About 2 minutes after the last gesture of any agent, the original timeout is restored and the screen is turned off,
183
+ which locks the phone again. If the user is holding the phone at that moment, or changed the timeout themselves, the
184
+ screen is left alone. The original value is saved once (`~\.nexus\nexus-android\awake.json`) and shared by all
185
+ servers under the common lock: one save, one restore, never the raised value taken for the original. After a crash,
186
+ the next server to start restores it. The server never unlocks the phone.
187
+ `NEXUS_ANDROID_AWAKE_IDLE` (seconds, default 120) changes the delay; `NEXUS_ANDROID_KEEP_AWAKE=0` turns it off.
188
+
189
+ ## Known limitations
190
+
191
+ - Gestures go through the real screen, on purpose: the user sees the agent working. No invisible virtual display.
192
+ - Accented text and emoji go through the clipboard (Android's `input text` only takes ASCII): the scrcpy server
193
+ (Genymobile, Apache 2.0, version and SHA-256 pinned, downloaded by `install.ps1`) runs only during the paste, then
194
+ the previous clipboard is put back. Samsung's clipboard history may keep the pasted text.
195
+ - Notifications: Android only keeps the app name of dismissed ones, not their text.
196
+ - An app protected by `FLAG_SECURE` produces a black screenshot; the server says so and the `[N]` elements stay
197
+ readable.
198
+ - The link is USB or local Wi-Fi through wireless debugging, paired with each PC. ADB does not work over Bluetooth.
199
+ - Windows only for now: the installer, the tests and the default paths target Windows.
200
+
201
+ ## Rejected options
202
+
203
+ - **Companion app on the phone**: notifications, typing with accents, files and settings all come from ADB itself; an
204
+ app would only add control away from USB and home Wi-Fi, which is rarely needed.
205
+ - **Invisible screen**: seeing the agent work on the real screen is a feature.
206
+ - **Internet relay** (tunnels, VPN): USB or local Wi-Fi only.
207
+
208
+ ## Full install (Windows)
209
+
210
+ Recommended for daily use: one local environment, registration in every client found on the PC, the scrcpy
211
+ server for accents, and the shared state between agents.
212
+
213
+ Prerequisites: Windows, PowerShell 7, [`uv`](https://docs.astral.sh/uv/), Python 3.14 and the Android Platform Tools
214
+ (`winget install Google.PlatformTools`). On the phone: Developer options → USB debugging.
215
+
216
+ ```powershell
217
+ pwsh -NoProfile -File install.ps1 # environment, Claude Code/Codex/Antigravity registration, tests
218
+ pwsh -NoProfile -File install.ps1 -Live # plus a run on the connected phone
219
+ ```
220
+
221
+ The script registers the server in each client present on the PC:
222
+
223
+ - Claude Code: `claude mcp add`, user scope.
224
+ - Codex: `[mcp_servers.nexus-android]` table in `~\.codex\config.toml`, with a backup made first.
225
+ - Antigravity: `nexus-android` entry in `~\.gemini\config\mcp_config.json`.
226
+
227
+ The virtual environment, caches and backups stay under `~\.nexus\nexus-android`, outside the repository.
228
+
229
+ ### Cable-free Wi-Fi, once per PC
230
+
231
+ 1. Phone: Settings → Developer options → Wireless debugging → Pair device with pairing code.
232
+ 2. PC: `adb pair <ip>:<port>`, then enter the code shown on the phone.
233
+
234
+ From then on, `nexus-android` finds the phone on the local Wi-Fi. Since One UI does not announce the phone over mDNS,
235
+ the server records its Wi-Fi address and the wireless debugging port every time it is plugged in over USB. This port
236
+ changes after a reboot or a network change: plugging in over USB once is then enough. The cable takes priority when
237
+ it is plugged in. `NEXUS_ANDROID_SERIAL` forces a specific device, by serial number or `ip:port` address.
238
+
239
+ ## Privacy
240
+
241
+ NexusAndroid runs entirely on your PC and talks to your phone over ADB (USB cable or local Wi-Fi). It has no
242
+ account, no telemetry, no analytics and no cloud relay; the server itself makes no internet requests.
243
+
244
+ - **What it reads**: the screen and UI tree, SMS/RCS messages, notifications, files in shared storage, location,
245
+ recent calls, contacts and calendar, only when the agent calls the matching tool. Security codes are masked before
246
+ the agent sees them.
247
+ - **Where it goes**: tool results are returned to the AI client you connected (Claude, Codex, Antigravity…). That
248
+ client may send them to its model provider under its own privacy policy; NexusAndroid sends nothing anywhere else.
249
+ - **What it stores on the PC**, under `~/.nexus/`: the phone's Wi-Fi address and wireless debugging port, the saved
250
+ screen timeout, a lock file, the pinned scrcpy server, and backups of the client configs that `install.ps1` edits.
251
+ Files you `pull` go to `~/Downloads/Telephone`. The server writes no messages, screenshots or logs to disk.
252
+ - **Removing it**: delete `~/.nexus/nexus-android/` and `~/.nexus/android.lock` and revoke the PC's key on the phone (Developer
253
+ options → Revoke USB debugging authorizations).
254
+
255
+ Questions or concerns: open an issue, or see [SECURITY.md](https://github.com/pironjulien/android-computer-use/blob/main/SECURITY.md)
256
+ for private reports.
257
+
258
+ ## Development
259
+
260
+ ```powershell
261
+ pwsh -NoProfile -File test.ps1 # ruff, pyright and the unit tests, offline, no phone needed
262
+ ```
263
+
264
+ ## License
265
+
266
+ [MIT](https://github.com/pironjulien/android-computer-use/blob/main/LICENSE). Not affiliated with Google, Samsung, Anthropic, OpenAI or Genymobile.
@@ -0,0 +1,245 @@
1
+ # NexusAndroid
2
+
3
+ <!-- mcp-name: io.github.pironjulien/nexus-android -->
4
+
5
+ [![Release](https://img.shields.io/github/v/release/pironjulien/android-computer-use)](https://github.com/pironjulien/android-computer-use/releases)
6
+ [![Tests](https://github.com/pironjulien/android-computer-use/actions/workflows/tests.yml/badge.svg)](https://github.com/pironjulien/android-computer-use/actions/workflows/tests.yml)
7
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](https://github.com/pironjulien/android-computer-use/blob/main/LICENSE)
8
+ [![Python 3.14](https://img.shields.io/badge/python-3.14-blue?logo=python&logoColor=white)](https://github.com/pironjulien/android-computer-use/blob/main/pyproject.toml)
9
+ [![MCP](https://img.shields.io/badge/MCP-server-7ee2a8)](https://modelcontextprotocol.io)
10
+
11
+ [![NexusAndroid: Android computer use for AI agents, over plain ADB. Works with Claude, Codex and Antigravity.](https://raw.githubusercontent.com/pironjulien/android-computer-use/main/docs/assets/banner.webp)](https://julienpiron.fr/nexusandroid/)
12
+
13
+ **Android computer use for AI agents, over plain ADB.** One MCP server, `nexus-android`, lets Claude Code, Codex
14
+ and Antigravity see and drive a real Android phone: screen and UI tree, gestures, typing with accents and emoji,
15
+ SMS/RCS reading, prepared replies, notifications, files, settings. No root, no app to install on the phone, no
16
+ cloud relay: a USB cable or the local Wi-Fi.
17
+
18
+ Built and tested on a Samsung Galaxy Fold (`SM-F971B`, One UI) with a Windows PC. Other Android phones with USB
19
+ debugging should work but have not been tested. Agent-facing messages are in French.
20
+ Part of the Nexus family: [NexusWindows](https://julienpiron.fr/nexuswindows/)
21
+ ([Claude](https://github.com/pironjulien/claude-windows-computer-use) ·
22
+ [Codex](https://github.com/pironjulien/codex-windows-computer-use) ·
23
+ [Antigravity](https://github.com/pironjulien/antigravity-windows-computer-use)),
24
+ [NexusBrowser](https://julienpiron.fr/nexusbrowser/) ([repo](https://github.com/pironjulien/chrome-computer-use)),
25
+ [NexusAndroid](https://julienpiron.fr/nexusandroid/) ([repo](https://github.com/pironjulien/android-computer-use)).
26
+ Project page: [julienpiron.fr/nexusandroid](https://julienpiron.fr/nexusandroid/).
27
+
28
+ ## Quick start
29
+
30
+ 1. Phone: Settings → About phone → tap *Build number* 7 times, then Developer options → **USB debugging** on.
31
+ Plug it in and accept the PC's key on the phone.
32
+ 2. PC: install [`uv`](https://docs.astral.sh/uv/) and the Android Platform Tools (`winget install Google.PlatformTools`).
33
+ 3. Add the server to your agent (it runs straight from GitHub, no clone needed):
34
+
35
+ ```bash
36
+ claude mcp add nexus-android --scope user -- uvx --python 3.14 --from git+https://github.com/pironjulien/android-computer-use nexus-android
37
+ ```
38
+
39
+ Then ask your agent: *"Look at my phone and tell me what is on the screen."*
40
+
41
+ <details>
42
+ <summary>Codex (<code>~/.codex/config.toml</code>)</summary>
43
+
44
+ ```toml
45
+ [mcp_servers.nexus-android]
46
+ command = "uvx"
47
+ args = ["--python", "3.14", "--from", "git+https://github.com/pironjulien/android-computer-use", "nexus-android"]
48
+ ```
49
+ </details>
50
+
51
+ <details>
52
+ <summary>Antigravity, Claude Desktop, Cursor and other JSON clients</summary>
53
+
54
+ ```json
55
+ {
56
+ "mcpServers": {
57
+ "nexus-android": {
58
+ "command": "uvx",
59
+ "args": ["--python", "3.14", "--from", "git+https://github.com/pironjulien/android-computer-use", "nexus-android"]
60
+ }
61
+ }
62
+ }
63
+ ```
64
+ </details>
65
+
66
+ If `adb` is not on the `PATH`, set `NEXUS_ANDROID_ADB` to its full path in the server's environment. Typing accents
67
+ and emoji needs the pinned scrcpy server that [`install.ps1`](#full-install-windows) downloads and checks; without it,
68
+ ASCII typing still works.
69
+
70
+ ### Example prompts
71
+
72
+ - *"Open Google Maps and start navigation to the nearest pharmacy."*
73
+ - *"Read my last SMS from Alice and draft a reply saying I'll be 10 minutes late."* (drafted, never sent without your "yes")
74
+ - *"Turn on do not disturb and lower the brightness."*
75
+ - *"Pull the last three photos from the phone to my PC."*
76
+ - *"Run a checkup: battery, storage, security patch, apps installed outside the stores."*
77
+
78
+ ## How it works
79
+
80
+ ```mermaid
81
+ flowchart LR
82
+ A["Claude Code"] --> S
83
+ B["Codex"] --> S
84
+ C["Antigravity"] --> S
85
+ S["nexus-android<br/>MCP server (Python)"] -->|"ADB over USB or local Wi-Fi"| P["Android phone<br/>(no app, no root)"]
86
+ S -.->|"shared lock, screen-awake state"| L[("~/.nexus")]
87
+ ```
88
+
89
+ `look` returns a screenshot plus the numbered UI tree; `act` runs a batch of gestures, re-reading the screen before
90
+ each one and reporting what really happened. Messages, notifications, contacts and settings are read through
91
+ Android's own providers and `dumpsys`, never by scraping the screen.
92
+
93
+ ## Why a shared server
94
+
95
+ Each agent has its own desktop computer use, but none of the three clients has a built-in tool for a phone. One
96
+ server avoids maintaining the same ADB access three times, and lets the three agents share one lock, one
97
+ screen-awake session and the same safeguards.
98
+
99
+ ## Tools
100
+
101
+ | Tool | Role |
102
+ | --- | --- |
103
+ | `phone` | Connected phone (USB or Wi-Fi), model, screen, foreground app, fold posture, reboot detection (structured output) |
104
+ | `look` | Screen image and numbered `[N]` elements with text, state and center `@x,y` |
105
+ | `act` | Batched gestures: tap, long press, typing (accents and emoji included), clear, keys, scroll, swipe, wait, links (`geo:`, `google.navigation:`, `tel:` opens the dialer without calling, `mailto:`, `https://`) |
106
+ | `launch` | Open an app by package or part of its name |
107
+ | `messages` | SMS and RCS conversations, read by contact name or number, attached photos; security codes masked |
108
+ | `draft` | Open SMS/RCS or WhatsApp with the reply already written, recipient by name or number; sent only after the user explicitly says "send" |
109
+ | `notifications` | Notifications on screen: app, time, title, text; security codes masked; `all` adds ongoing ones |
110
+ | `files` | Shared storage only (`/sdcard`): list newest first, `pull` to the PC (`~\Downloads\Telephone`), `push` to the phone; never overwrites, never deletes |
111
+ | `info` | Read-only: last known location (accuracy, age, Maps link), recent calls, a contact's numbers, upcoming events (structured output) |
112
+ | `settings` | Read or change, then read back: Wi-Fi, Bluetooth, mobile data, location, NFC, do not disturb, brightness, media volume; turning Wi-Fi off is refused when the link goes through it (structured output) |
113
+ | `checkup` | Health report: system, security patch, battery, storage, memory, apps installed outside the stores, wireless debugging; `clean` clears caches |
114
+
115
+ Google Messages stores RCS messages in Android's messaging provider, like MMS, so the server reads them without going
116
+ through the screen. An RCS group only has an encoded address there: the server works out its members and names the
117
+ sender of each received message. A conversation archived or trashed in Google Messages stays readable and is labelled
118
+ as hidden in the app.
119
+
120
+ ## Safeguards
121
+
122
+ - **Paying, sending, calling, publishing, deleting for good, formatting, uninstalling, resetting, cancelling a subscription,
123
+ making a repository public**: a button of this kind is refused without `confirm=true`, for every gesture aimed at
124
+ it (tap, typing, clearing, swipe, `x,y` coordinates included). The agent sets it on the user's explicit order for
125
+ that gesture, or after their "yes". The labels come from one policy file shared with the desktop tools
126
+ (`garde-fous.json`), matched without accents or case and in every listed form ("Envoyez", "Payez", "Paiement",
127
+ "Commandez", "Checkout", "Appeler"...). Everything else is free: deleting to the trash, accepting, sharing.
128
+ A generic "OK" or "Validate" needs it on a screen that talks about a payment or a format. In a messaging, mail,
129
+ social or phone app, a clickable icon with no name at all (often the send or call button) needs it too. Enter, call keys and any key
130
+ outside a short safe list need it as well.
131
+ - **Target checked on the current screen**: the UI and the phone state (lock, foreground app) are read again in
132
+ one round trip just before each gesture; a changed `[N]` or a spot with nothing readable is refused. Reports: ✓ effect read back, ~ sent but not verified, ✗ refused or failed.
133
+ - **One phone only**: the owner's phone is identified by its hardware serial number, recorded at the first USB
134
+ connection; emulators, TV boxes and other devices are ignored. One agent at a time drives it (lock shared by all
135
+ servers on the PC).
136
+ - **Banking, payment, crypto, gambling, identity, authenticators**: look, scroll and wait freely; tap, type or
137
+ swipe only with `confirm=true` (the user's order to navigate); a money or send button, or an unnamed icon, never,
138
+ even with `confirm=true`; a link (`open_url`) that would open one of them is refused (a deep link can prepare a payment).
139
+ - **Codes received by SMS or shown in notifications**: masked before the agent reads them, and blacked out on the
140
+ image. When the UI tree cannot be read, codes cannot be located, so the image is not sent unless `look` gets
141
+ `confirm=true` (with the user's agreement).
142
+ - **Passwords**: no typing into a password field.
143
+ - **Locked phone**: no gestures. The user unlocks it; no code is ever entered.
144
+ - **Text read** on screen or in messages is data, never instructions.
145
+ - **User holding the phone**: no gesture mixes with theirs. If the phone was touched in the last 3 seconds (and not
146
+ by an agent), the server waits up to 10 seconds, then refuses.
147
+ - **Link lost during a gesture**: reported as `?` (effect unknown), never as a plain failure: the agent looks again
148
+ before redoing it, so nothing is sent twice.
149
+ - **Fold folded or unfolded** since the last image: `x,y` targets are refused until a new `look`.
150
+ - **Reboot**: `phone` and `look` say once that the phone restarted; when it cannot be found, the message reminds that
151
+ Android turns wireless debugging off after a reboot or a network change.
152
+ - **Other control tools** (mobile-mcp, Appium, Shizuku, another scrcpy) left running on the phone can block the UI
153
+ tree: `checkup` and the error name them.
154
+
155
+ ## Screen kept on while the agent works
156
+
157
+ Unlocking again and again while an agent works is tedious, but a phone used to pay must not stay unlocked. So, at the
158
+ first gesture (`act`, `launch`, `draft`) on a phone **the user has unlocked**, the server saves the screen timeout
159
+ (`screen_off_timeout`) and raises it to 30 minutes; `phone` shows until when.
160
+
161
+ About 2 minutes after the last gesture of any agent, the original timeout is restored and the screen is turned off,
162
+ which locks the phone again. If the user is holding the phone at that moment, or changed the timeout themselves, the
163
+ screen is left alone. The original value is saved once (`~\.nexus\nexus-android\awake.json`) and shared by all
164
+ servers under the common lock: one save, one restore, never the raised value taken for the original. After a crash,
165
+ the next server to start restores it. The server never unlocks the phone.
166
+ `NEXUS_ANDROID_AWAKE_IDLE` (seconds, default 120) changes the delay; `NEXUS_ANDROID_KEEP_AWAKE=0` turns it off.
167
+
168
+ ## Known limitations
169
+
170
+ - Gestures go through the real screen, on purpose: the user sees the agent working. No invisible virtual display.
171
+ - Accented text and emoji go through the clipboard (Android's `input text` only takes ASCII): the scrcpy server
172
+ (Genymobile, Apache 2.0, version and SHA-256 pinned, downloaded by `install.ps1`) runs only during the paste, then
173
+ the previous clipboard is put back. Samsung's clipboard history may keep the pasted text.
174
+ - Notifications: Android only keeps the app name of dismissed ones, not their text.
175
+ - An app protected by `FLAG_SECURE` produces a black screenshot; the server says so and the `[N]` elements stay
176
+ readable.
177
+ - The link is USB or local Wi-Fi through wireless debugging, paired with each PC. ADB does not work over Bluetooth.
178
+ - Windows only for now: the installer, the tests and the default paths target Windows.
179
+
180
+ ## Rejected options
181
+
182
+ - **Companion app on the phone**: notifications, typing with accents, files and settings all come from ADB itself; an
183
+ app would only add control away from USB and home Wi-Fi, which is rarely needed.
184
+ - **Invisible screen**: seeing the agent work on the real screen is a feature.
185
+ - **Internet relay** (tunnels, VPN): USB or local Wi-Fi only.
186
+
187
+ ## Full install (Windows)
188
+
189
+ Recommended for daily use: one local environment, registration in every client found on the PC, the scrcpy
190
+ server for accents, and the shared state between agents.
191
+
192
+ Prerequisites: Windows, PowerShell 7, [`uv`](https://docs.astral.sh/uv/), Python 3.14 and the Android Platform Tools
193
+ (`winget install Google.PlatformTools`). On the phone: Developer options → USB debugging.
194
+
195
+ ```powershell
196
+ pwsh -NoProfile -File install.ps1 # environment, Claude Code/Codex/Antigravity registration, tests
197
+ pwsh -NoProfile -File install.ps1 -Live # plus a run on the connected phone
198
+ ```
199
+
200
+ The script registers the server in each client present on the PC:
201
+
202
+ - Claude Code: `claude mcp add`, user scope.
203
+ - Codex: `[mcp_servers.nexus-android]` table in `~\.codex\config.toml`, with a backup made first.
204
+ - Antigravity: `nexus-android` entry in `~\.gemini\config\mcp_config.json`.
205
+
206
+ The virtual environment, caches and backups stay under `~\.nexus\nexus-android`, outside the repository.
207
+
208
+ ### Cable-free Wi-Fi, once per PC
209
+
210
+ 1. Phone: Settings → Developer options → Wireless debugging → Pair device with pairing code.
211
+ 2. PC: `adb pair <ip>:<port>`, then enter the code shown on the phone.
212
+
213
+ From then on, `nexus-android` finds the phone on the local Wi-Fi. Since One UI does not announce the phone over mDNS,
214
+ the server records its Wi-Fi address and the wireless debugging port every time it is plugged in over USB. This port
215
+ changes after a reboot or a network change: plugging in over USB once is then enough. The cable takes priority when
216
+ it is plugged in. `NEXUS_ANDROID_SERIAL` forces a specific device, by serial number or `ip:port` address.
217
+
218
+ ## Privacy
219
+
220
+ NexusAndroid runs entirely on your PC and talks to your phone over ADB (USB cable or local Wi-Fi). It has no
221
+ account, no telemetry, no analytics and no cloud relay; the server itself makes no internet requests.
222
+
223
+ - **What it reads**: the screen and UI tree, SMS/RCS messages, notifications, files in shared storage, location,
224
+ recent calls, contacts and calendar, only when the agent calls the matching tool. Security codes are masked before
225
+ the agent sees them.
226
+ - **Where it goes**: tool results are returned to the AI client you connected (Claude, Codex, Antigravity…). That
227
+ client may send them to its model provider under its own privacy policy; NexusAndroid sends nothing anywhere else.
228
+ - **What it stores on the PC**, under `~/.nexus/`: the phone's Wi-Fi address and wireless debugging port, the saved
229
+ screen timeout, a lock file, the pinned scrcpy server, and backups of the client configs that `install.ps1` edits.
230
+ Files you `pull` go to `~/Downloads/Telephone`. The server writes no messages, screenshots or logs to disk.
231
+ - **Removing it**: delete `~/.nexus/nexus-android/` and `~/.nexus/android.lock` and revoke the PC's key on the phone (Developer
232
+ options → Revoke USB debugging authorizations).
233
+
234
+ Questions or concerns: open an issue, or see [SECURITY.md](https://github.com/pironjulien/android-computer-use/blob/main/SECURITY.md)
235
+ for private reports.
236
+
237
+ ## Development
238
+
239
+ ```powershell
240
+ pwsh -NoProfile -File test.ps1 # ruff, pyright and the unit tests, offline, no phone needed
241
+ ```
242
+
243
+ ## License
244
+
245
+ [MIT](https://github.com/pironjulien/android-computer-use/blob/main/LICENSE). Not affiliated with Google, Samsung, Anthropic, OpenAI or Genymobile.
@@ -0,0 +1,83 @@
1
+ [project]
2
+ name = "nexus-android"
3
+ version = "0.4.1"
4
+ description = "Android computer use for AI agents over ADB: one MCP server shared by Claude Code, Codex and Antigravity"
5
+ license = "MIT"
6
+ readme = "README.md"
7
+ requires-python = ">=3.14"
8
+ keywords = [
9
+ "mcp",
10
+ "mcp-server",
11
+ "android",
12
+ "adb",
13
+ "computer-use",
14
+ "ai-agents",
15
+ "claude",
16
+ "codex",
17
+ "antigravity",
18
+ ]
19
+ classifiers = [
20
+ "Development Status :: 4 - Beta",
21
+ "License :: OSI Approved :: MIT License",
22
+ "Operating System :: Microsoft :: Windows",
23
+ "Programming Language :: Python :: 3.14",
24
+ "Topic :: Software Development :: Testing",
25
+ ]
26
+ dependencies = [
27
+ "mcp>=2.3.0",
28
+ "pillow>=12.3.0",
29
+ ]
30
+
31
+ [[project.authors]]
32
+ name = "Julien Piron"
33
+
34
+ [project.urls]
35
+ Homepage = "https://julienpiron.fr/nexusandroid/"
36
+ Repository = "https://github.com/pironjulien/android-computer-use"
37
+ Changelog = "https://github.com/pironjulien/android-computer-use/blob/main/CHANGELOG.md"
38
+ Issues = "https://github.com/pironjulien/android-computer-use/issues"
39
+
40
+ [project.scripts]
41
+ nexus-android = "nexus_android.server:main"
42
+
43
+ [build-system]
44
+ requires = ["uv_build>=0.12.5,<0.13.0"]
45
+ build-backend = "uv_build"
46
+
47
+ [dependency-groups]
48
+ dev = [
49
+ "pyright==1.1.407",
50
+ "pytest>=9.1.1",
51
+ "ruff>=0.17.0",
52
+ ]
53
+
54
+ [tool.ruff]
55
+ line-length = 120
56
+ target-version = "py314"
57
+ cache-dir = "~/.nexus/nexus-android/cache/ruff"
58
+
59
+ [tool.ruff.lint]
60
+ select = [
61
+ "E",
62
+ "F",
63
+ "W",
64
+ "I",
65
+ "B",
66
+ "UP",
67
+ ]
68
+ ignore = [
69
+ "E501",
70
+ "E731",
71
+ ]
72
+
73
+ [tool.pyright]
74
+ include = [
75
+ "src",
76
+ "tests",
77
+ ]
78
+ pythonVersion = "3.14"
79
+
80
+ [tool.pytest.ini_options]
81
+ testpaths = ["tests"]
82
+ markers = ["live: tests sur le vrai téléphone"]
83
+ addopts = "-m 'not live' -p no:cacheprovider"
@@ -0,0 +1,58 @@
1
+ [project]
2
+ name = "nexus-android"
3
+ version = "0.4.1"
4
+ description = "Android computer use for AI agents over ADB: one MCP server shared by Claude Code, Codex and Antigravity"
5
+ authors = [{ name = "Julien Piron" }]
6
+ license = "MIT"
7
+ readme = "README.md"
8
+ requires-python = ">=3.14"
9
+ keywords = ["mcp", "mcp-server", "android", "adb", "computer-use", "ai-agents", "claude", "codex", "antigravity"]
10
+ classifiers = [
11
+ "Development Status :: 4 - Beta",
12
+ "License :: OSI Approved :: MIT License",
13
+ "Operating System :: Microsoft :: Windows",
14
+ "Programming Language :: Python :: 3.14",
15
+ "Topic :: Software Development :: Testing",
16
+ ]
17
+ dependencies = [
18
+ "mcp>=2.3.0",
19
+ "pillow>=12.3.0",
20
+ ]
21
+
22
+ [project.urls]
23
+ Homepage = "https://julienpiron.fr/nexusandroid/"
24
+ Repository = "https://github.com/pironjulien/android-computer-use"
25
+ Changelog = "https://github.com/pironjulien/android-computer-use/blob/main/CHANGELOG.md"
26
+ Issues = "https://github.com/pironjulien/android-computer-use/issues"
27
+
28
+ [project.scripts]
29
+ nexus-android = "nexus_android.server:main"
30
+
31
+ [build-system]
32
+ requires = ["uv_build>=0.12.5,<0.13.0"]
33
+ build-backend = "uv_build"
34
+
35
+ [dependency-groups]
36
+ dev = [
37
+ "pyright==1.1.407",
38
+ "pytest>=9.1.1",
39
+ "ruff>=0.17.0",
40
+ ]
41
+
42
+ [tool.ruff]
43
+ line-length = 120
44
+ target-version = "py314"
45
+ cache-dir = "~/.nexus/nexus-android/cache/ruff"
46
+
47
+ [tool.ruff.lint]
48
+ select = ["E", "F", "W", "I", "B", "UP"]
49
+ ignore = ["E501", "E731"]
50
+
51
+ [tool.pyright]
52
+ include = ["src", "tests"]
53
+ pythonVersion = "3.14"
54
+
55
+ [tool.pytest.ini_options]
56
+ testpaths = ["tests"]
57
+ markers = ["live: tests sur le vrai téléphone"]
58
+ addopts = "-m 'not live' -p no:cacheprovider"
@@ -0,0 +1,3 @@
1
+ """nexus-android : computer use du téléphone Android de l'utilisateur, partagé par ses agents."""
2
+
3
+ __version__ = "0.4.1"