Skip to content

Releases: pironjulien/NexusAndroid

NexusAndroid 0.7.2

Choose a tag to compare

@pironjulien pironjulien released this 19 Oct 15:39

Added

  • capture tool, to show a problem to a support service: screenshot saves the current screen at full resolution
    (PNG, or JPEG when the name ends in .jpg) with security codes blacked out as on look images; record films the
    next seconds (default 30), start and stop film while gestures go on (Android's screenrecord, MP4, 720p,
    4 Mbit/s, 180 s at most, one recording at a time). The video is pulled to the PC then deleted from the phone; the
    response gives the path, the length and the size. Files go to ~\Downloads\Telephone by default, with the same
    folder rules as files pull, and never overwrite. Videos cannot be masked: no recording starts while a banking,
    payment or identity app is in the foreground, and the response says codes may appear in the video. A screenshot
    of such an app, or one without code masking (UI tree unreadable), needs confirm=true. Black images (FLAG_SECURE,
    screen off) are reported, and so is a video shorter than the recording time (Android adds frames only when the
    screen changes, none while it is off). A recording still running when the server stops is stopped and saved.
    Before any screenshot or video, a sleeping screen on an unlocked phone is turned on (KEYCODE_WAKEUP, counted as an
    agent gesture and kept on like after act); a locked phone, screen on or off, is refused without capturing, with a
    message to ask the user to unlock it (the agent never types a code): no more black screenshots or empty videos.
  • logs tool (read-only), to understand why an app crashed or misbehaves: kind="crash" groups the crash log one
    block per crash (time, package, Java exception and each Caused by, the top frame and the first app frames; native
    crashes with signal, cause and backtrace); kind="anr" lists "app not responding" events from Android's drop box
    (kept for days) and the event log, without duplicates, with their reason; kind="recent" gives the last lines
    log lines (200 by default, 2000 at most), for one app with app (resolved like launch; its running processes,
    those started earlier, and lines naming it; an uninstalled app by its full package name). since (15m, 2h,
    13:05, 2026-10-19 13:05, phone time) and query (case-insensitive, applied after masking) filter; newest
    last, about 30,000 characters. Any security code, tokens and secrets (known key prefixes, JWT, random hex or
    base64, Bearer, password=…), e-mail addresses and phone numbers are masked; stack frames are kept as is.
    Banking, payment and identity apps: recent is refused for them and their lines are left out of the general
    view; their crashes and ANRs show only exception types and app frames, no message.
  • logs kind="crash": identical crashes (same process, type and text once object hashes, pids, bracketed numbers
    and times are ignored) are shown as one block, ×N (first …, last …, pids …), placed at the newest one; the
    exception Android re-prints after "Couldn't report crash" (processes without a package) is dropped, and the
    "Error reporting crash" cause is one short line. 21 crashes, 19 of them identical, went from 22,500 to 2,100 characters.

Fixed

  • When another automation tool holds the phone's single accessibility connection (UiAutomation), such as the
    mobile-mcp server com.mobilenext.mobilecli.DeviceServer that stays alive 30 minutes, Appium UiAutomator2,
    uiautomator2/atx-agent or an instrumentation run, look and the gestures aimed by text now say so at once, with
    the tool and its pid: "Another automation tool is using the phone's accessibility connection (…, pid N): close it
    or stop it, then retry." One ps call, no more 3 to 4 seconds of uiautomator dump retries ending in a misleading
    "screen moving or protected". The other tool's process is never stopped automatically.

NexusAndroid 0.7.1

Choose a tag to compare

@pironjulien pironjulien released this 19 Oct 14:48

Added

  • act(then="changes"): after the gestures, only what changed since the last element list (+ added, ~ changed
    text, state or position beyond 10 px, - removed; unchanged elements counted, not repeated). Kept elements keep
    their [N], new ones get fresh numbers, so the next act aims on the current screen. Full list, said in one line,
    when there is no previous list, the foreground app changed or more than 60 % of the elements differ. Codes stay
    masked (compared masked). About 97 % fewer characters on a 90-element settings screen after one toggle.

Fixed

  • Phone numbers (10 digits or more, or after +, such as 06 11 22 33 44) are no longer masked as security codes
    (seen on a real helpline button); codes of 4 to 9 digits still are.
  • Screenshot marks are larger (18 px on the 1280 px side, was 14) and easier to read.

NexusAndroid 0.7.0

Choose a tag to compare

@pironjulien pironjulien released this 19 Oct 14:16

Security

  • Payment pages behind plain https links need confirm=true even when they open in the browser: revolut.me,
    paypal.me, paypal.com/paypalme (and /checkoutnow, /donate), cash.app, lydia-app.com, lydia.me, buy/checkout/donate
    .stripe.com, venmo.com, wise.com/pay, SumUp links, bunq.me, monzo.me (host and subdomains, any case).
  • Shutdown, reboot and reset intents (ACTION_REQUEST_SHUTDOWN, REBOOT, MASTER_CLEAR, FACTORY_RESET and vendor
    variants with SHUTDOWN, REBOOT or WIPE in the action) need confirm=true.
  • A link that Android would send to one app is still checked against every app that could take it: a banking or
    identity app among them is refused as before. Any component whose class name contains "Chooser" or "Resolver" is
    treated as a chooser, not a final target (One UI answers android/com.android.internal.app.ResolverActivity).
  • Clipboard: fewer false alarms and fewer misses. ., - and _ no longer count as password symbols, so file
    names, versions, model numbers, plates and host names are shown; known key prefixes (sk-, ghp_, github_pat_,
    AKIA, xox…-, AIza, glpat-…), JWTs, PEM blocks, long random hex or base64 tokens and short words mixing
    letters with password punctuation (!@#$%^&*+=?~…) or three kinds of characters are hidden.
  • Forged messages: an SMS whose text contained \nRow: 1 _id=999, type=2, body=… could add a fake (even outgoing)
    message to a conversation. The phone helper no longer returns the text output of content: it queries the
    providers itself (same ActivityManager path as content) and serializes the cursor rows with length-prefixed
    values (protocol NXQ2), decoded as such on the PC. On the adb shell fallback, a read is refused, and reported,
    when its Row: N numbers do not run strictly from 0, exceed the requested LIMIT, or differ from the row count of
    the same read reduced to _id (no free text). Columns are split along the requested projection, so a contact named
    "Dupont, Jean" is no longer cut to "Dupont".
  • Predictable helper socket: each start draws a random 128-bit token, passed to app_process, part of the abstract
    socket name and sent back in every answer. The PC refuses a server that does not return its token; a name already
    taken is no longer considered normal (the PC relaunches with a new token, then falls back to adb shell). A
    forward left by another process is adopted with its token instead of piling up.
  • Shared safeguards policy updated from NexusHarness/shared: post, reply, tweet, subscribe, free trial, book,
    reserve, donate, answer and accept a call, dial, transfer and price buttons (4,99 €, $12) need confirm=true;
    plain_label spells out €, $, £ and ¥ as eur, usd, gbp and jpy like the shared plain.
  • Security codes are now also masked in English, Spanish, German and Italian messages ("Use 482913 to sign in",
    "Tu código: 482-913", "Ihr Bestätigungscode lautet 482913", "Il tuo codice è 482913", "Token: 482913", "log in",
    "clave"), without masking dates (12/10/2026, 2026-10-12) or amounts followed by a currency (1 250 €).
  • A short swipe whose start and end fall inside the same clickable, non-scrollable element is judged as a tap, with
    the strict safeguard (confirmation word, unnamed icon in a messaging app, nothing readable).
  • files on the PC side: keys, secrets and papers (.ssh, .gnupg, .aws, .azure, .nexus, NexusSecure,
    Administratif, .env files) and the Windows Startup folders are never read or written, even with
    confirm=true; outside Downloads, Documents, Desktop, Pictures and Videos (OneDrive included), confirm=true is
    required. The real path (symlinks, ..) is resolved before the check.
  • User back on the phone during a batch: before every gesture after the first, a light activity read
    (dumpsys power filtered on the phone) stops the batch at once if the user touched the screen since the agent's last
    gesture, and the report lists the steps already done. A one-gesture batch costs nothing more.
  • Screen kept on: the raised screen_off_timeout is now 5 minutes instead of 30 (Android counts it again from every
    injected gesture; always longer than the idle delay). A server killed outright leaves the phone unlocked 5 minutes
    at most. The 30-minute value set by older versions is still recognised and restored. During a long look-only
    phase, more than 3 minutes after the last gesture or poke, a screen that is on gets KEYCODE_WAKEUP (no visible
    effect, counted by Android as activity), recorded as an agent gesture, never as the user's touch.

Added

  • look(marks=...): with show=both, the image carries small [N] badges (the element numbers of the list) at the
    top-left of each element, with a thin outline of its bounds. auto (default) numbers every element when there are
    21 or fewer, otherwise only the unnamed ones (icons without text or description); all; none. Drawn after the
    security codes are blacked out (a badge only covers, never reveals), never on an image that is refused, and not
    with show=image (no list for the numbers to refer to). About 4 ms for 80 elements on a Fold screenshot.

  • clipboard(action="read"|"write", text): reads the phone's clipboard (security codes masked like messages; a lone
    code, or a single word that looks like a password or a token, is never shown; says when it is empty or not text)
    or sets it without pasting, through the same scrcpy server used to type accents (started for the call, stopped
    after). Typing accents still gives the user their previous clipboard back.

  • launch(action, data, extras, confirm): opens a precise screen (android.settings.WIFI_SETTINGS, a deep link,
    extras as string, integer or boolean; app then limits the target to that app). The target is resolved first
    (cmd package resolve-activity), started by its exact component, and reported with the foreground read back.
    Calling, sending or paying directly (CALL, RESPOND_VIA_MESSAGE, uninstall and reset actions, payment actions
    such as org.chromium.intent.action.PAY, upi:/bitcoin:/payto:… links, auto-send extras) needs
    confirm=true; a banking, payment or identity app, as the resolved target or as one of the candidates of
    Android's chooser, is refused even with confirm=true. act open_url refuses payment links without confirm=true
    too.

  • Tests that tool descriptions, titles and schema descriptions contain no French, that offline errors and refusals
    (phone absent, unknown target, step needing confirm=true, missing or invalid arguments, locked phone) are in
    English, and that the instructions and descriptions never compare the server with other tools.

  • Typing accents and emoji without install.ps1: when the pinned scrcpy server (v5.0.1) is missing or altered, the
    server downloads it once from the official Genymobile/scrcpy GitHub release into ~/.nexus/nexus-android/tools,
    checks it against the SHA-256 written in the code and writes it atomically. If the download fails or the digest
    differs, the step fails with a message giving the URL, the expected digest and the folder; nothing is typed, and
    the text is never transliterated.

  • Windows installer for the public repository (install.ps1): uv environment under ~\.nexus\nexus-android\venv
    (package copied, not editable), pinned scrcpy server, stdio registration with Codex (~\.codex\config.toml,
    previous nexus-android table replaced, file backed up first) and Antigravity (~\.gemini\config\mcp_config.json,
    backed up first) when they are installed, the claude mcp add command printed for Claude Code (no Claude CLI call),
    then test.ps1. No OneDrive, daemon supervisor or author-specific path; -NoRegister, -SkipTests, -Live.

  • Python 3.12 and 3.13 support: requires-python = ">=3.12" (3.11 lacks the type statement); the CI runs
    test.ps1 -Python 3.12|3.13|3.14 on Windows. test.ps1 -Python X.Y keeps a separate test environment per version.

  • server.json lists NEXUS_ANDROID_AWAKE_IDLE.

  • messages(before=...): reads older messages (or older conversations) from the ISO date given at the end of the
    previous answer. Answers of messages, notifications and files list are capped at about 60,000 characters, cut
    between two messages or lines, and say how to read the rest.

  • A call cancelled by the client (MCP notifications/cancelled, or the client disconnecting) stops the batch before
    its next step, stops a wait step, and stops waiting for the phone.

Changed

  • Private installer: Antigravity now gets nexus-android as a plugin (~\.gemini\config\plugins\nexus-android, with
    the skill and its own mcp_config.json pointing at the shared daemon with the token, written on the PC only), like
    the other Nexus tools. The old entry in the global mcp_config.json is removed after a backup; the plugin is
    enabled in config.json only while Antigravity is closed.
  • look in about 0.17 s instead of 3 s over Wi-Fi: the phone helper keeps one accessibility connection open and
    returns the UI tree, the screenshot (JPEG made on the phone, already at the size sent to the agent) and the screen
    state with the fold posture in one exchange, instead of uiautomator dump, screencap and several dumpsys. The
    tree is read like uiautomator dump does (active window, visible nodes, bounds clipped to the display, same
    attributes), so the element list, numbering, masking and refusals do not change; the target check before each
    gesture uses it too. As with uiautomator dump, the tree waits for one second without accessibility events, but
    only after a change: a still screen is read at once, and a screen that never settles (video) is read after 3 s
    instead of failing. Read-only (no gesture, no global action); the user's accessibility services are not suspended.
    The connection is released before any uiautomator dump fallback, after 45 s with...
Read more

NexusAndroid 0.6.2

Choose a tag to compare

@pironjulien pironjulien released this 19 Oct 11:12

Changed

  • Server instructions sent to every MCP client are now in English and neutral ("the user"): no phone model, no
    mention of a shared policy between named agents. Same rules and flow as before. The startup test checks that the
    instructions stay under the 2048-character client limit and that nothing personal reaches initialize or
    tools/list.

NexusAndroid 0.6.1

Choose a tag to compare

@pironjulien pironjulien released this 19 Oct 09:53

Added

  • Cable-free use without a shared Wi-Fi network: when the phone runs a hotspot (One UI ap_br_swlan0 / swlan0)
    and the PC is connected to it, the address recorded at each USB connection is the hotspot address (wlan0 still
    comes first). After adb tcpip 5555, the server finds the phone again over the hotspot once the cable is removed.
    README section for this setup.

NexusAndroid 0.6.0

Choose a tag to compare

@pironjulien pironjulien released this 19 Oct 09:39

Performance

  • Message, contact, call log and calendar reads no longer start a Java process on the phone for each content query (about 1 s per wave of queries). At the first read the server pushes a 7 KB helper
    (phone/NexusQuery.java, built into src/nexus_android/nexus-query.dex by phone/build.ps1) to
    /data/local/tmp and starts it once with app_process under the shell identity, reached through adb forward
    on a local abstract socket: the scrcpy server's method (mobile-mcp's device server works the same way; DroidMind
    stays on plain adb shell). No app to install. It runs each query with the code of Android's own content
    command (/system/framework/content.jar), so the output is identical to the character. Measured on a Galaxy
    Fold over USB: conversation list 3.1 s → 0.07 s (0.14 s from a fresh process, 0.73 s when the helper must first
    be pushed and started), one conversation 2.1 s → 0.03 s.
  • One port forward per phone, reused by every process and after a daemon restart.

Security

  • The helper is read-only (only query is served; insert, update, delete and call are refused), answers only
    connections from the shell user or root (adbd; kernel-checked peer credentials, which no app on the phone can
    have), and stops by itself after 10 minutes without a request. Each dex version has its own socket name, so an
    old helper is never taken for the current one; older ones are stopped and removed.
  • If the helper cannot be started or answers badly, reads fall back to the 0.5 method (adb shell), with the same
    output and the same masking of security codes, and the helper is retried a minute later.

Changed (Codex plugin, 2026-10-19)

  • install.ps1 no longer writes [mcp_servers.nexus-android] to Codex's config.toml: Codex gets the server from a
    personal Codex plugin.

NexusAndroid 0.5.1

Choose a tag to compare

@pironjulien pironjulien released this 19 Oct 09:33

Security

  • Shared policy (garde-fous.json) synced with NexusHarness: every "Réinitialiser…" / reset button needs
    confirm=true again, including the bare "Réinitialiser" confirmation button of One UI and Windows and
    "Réinitialiser les paramètres réseau". Only harmless resets stay free (filters, zoom, view, sort, search, layout,
    form, fields, text, selection, colours). This reverts the 0.5.0 narrowing, which let the final One UI reset button
    through without confirmation.

[0.5.0] - 2026-10-19

Performance

  • messages() is about 5 times faster. Every content query starts a Java process on the phone (about 1 s), and
    the tool used to run them one after another: 34 for the conversation list, 7 to 11 per conversation. Queries now
    run in parallel on the phone inside a single adb shell (30 cost about as much as one), the phone returns only the
    last N rows (LIMIT), MMS parts and addresses are chained on the phone instead of going back to the PC, and
    contact names are cached for 10 minutes, shared by all sessions of the daemon. Measured on a Galaxy Fold over USB:
    conversation list 35 s → 4.6 s (3.1 s with names cached), one conversation 7-11 s → 2.1 s, with 4 images
    11.6 s → 3.3 s, by contact name 11.4 s → 4.3 s. Output is byte-for-byte identical, security codes still masked.
  • info(kind="calls") reads only the last N calls and resolves their names in one exchange.

Security

  • Shared policy (garde-fous.json) synced with NexusHarness: "reset" needs confirm=true only for the whole device
    ("Réinitialiser le téléphone", "Réinitialisation d'usine"); other "Réinitialiser…" buttons inside an app are free.

Changed (Claude Code plugin, 2026-10-19)

  • install.ps1 no longer registers the server with Claude Code and never calls a Claude CLI: Claude Code gets
    nexus-android from a Claude Code plugin (shared daemon URL, token read from an environment variable).

Fixed

  • install.ps1: without claude on the PATH, Claude Code was skipped on a new PC. Falls back to the claude.exe
    shipped with the Claude desktop app, like NexusClaude and NexusBrowser.

Changed

  • Repository renamed to pironjulien/NexusAndroid; GitHub redirects the old address.
  • Published on PyPI (nexus-android) and in the official MCP registry (io.github.pironjulien/nexus-android);
    the quick start installs with uvx --python 3.14 nexus-android.

Added

  • Shared daemon mode: nexus-android --http [port] serves MCP Streamable HTTP on 127.0.0.1 (default port 16180),
    so Claude Code, Codex and Antigravity share one process instead of one per session. A bearer token is required
    (NEXUS_MCP_TOKEN or NEXUS_MCP_TOKEN_FILE, 32 characters or more); each MCP session keeps its own [N]
    numbering and conversation list, while the ADB link and the phone lock stay shared. Clients reconnect on their
    own after a daemon restart.
  • install.ps1 declares the daemon to the Nexus daemon supervisor and registers nexus-android by URL for Claude Code,
    Codex and Antigravity (instead of a stdio process per session).
  • README: animated demo recorded from the real tool on a real phone (docs/assets/demo.webp).

NexusAndroid 0.4.1

Choose a tag to compare

@pironjulien pironjulien released this 18 Oct 14:10

Project page: https://julienpiron.fr/nexusandroid/

Security

  • Wiping, resetting or formatting the whole device now needs confirm=true: "Reset phone", "Reset device",
    "Reset tablet", "Reset PC", "Formater le téléphone", "Formater l'appareil", "Format phone" (shared policy). "Tout
    effacer", "Effacer tout" and "Tout supprimer" need it on a reset screen (factory reset, erase all data) and stay free
    elsewhere (calculator, notifications). One test per label.

NexusAndroid 0.4.0

Choose a tag to compare

@pironjulien pironjulien released this 18 Oct 13:51

Project page: https://julienpiron.fr/nexusandroid/

Fixes from the 18/10 audit, aligned on the safeguard agreement between Claude and Antigravity.

Security

  • Confirmation labels now come from the policy file shared with the desktop tools (garde-fous.json, embedded copy;
    a test checks it is identical to the shared one in the author's workspace). Every listed form is caught, accents
    and case ignored: "Envoyez", "Payez", "Paiement", "Achetez", "Commandez", "Publish", "Purchase", "Checkout",
    "Virer" no longer pass without confirm=true. One test per pattern.
  • A clickable icon with no name at all in a messaging, mail, social or phone app (often the send or call button,
    whose resource id does not always say so) needs confirm=true, x,y taps included.
  • look no longer sends the image when the UI tree cannot be read (security codes could not be blacked out), unless
    confirm=true.
  • open_url refuses a link that Android would open in a protected app (banking, payment, identity), or that offers
    one in its app chooser.
  • The lock state and the foreground app are read in the same round trip as the UI tree, just before each targeted
    gesture: an app opened by the previous step of a batch is seen before the next one.

Changed

  • Aligned on the shared policy: deleting (to the trash), accepting, sharing, validating and allowing no longer need
    confirmation; formatting, cancelling a subscription and making a repository public do. Calling stays under
    confirmation (a real call on a phone; added to the shared policy). A generic
    "OK"/"Validate" needs it on a screen about a payment or a format. An explicit order from the user for a gesture
    counts as confirmation (tool descriptions and refusals say so).
  • Protected apps are readable: scroll and wait are free in them; taps, typing and swipes need confirm=true
    (navigation on the user's order); a money or send button, or an unnamed icon, is refused there even with
    confirm=true: the user does it.
  • Faster batches: phone state read once per act batch, in one adb round trip instead of three, then together with
    each UI read. Measured on a Galaxy Z Fold over Wi-Fi (6 steps): 28 → 8 adb round trips, 11.0 s → 9.9 s.

Added

  • Structured output (outputSchema and structuredContent) for phone, info and settings, next to the text.

NexusAndroid 0.3.1 — first public release

Choose a tag to compare

@pironjulien pironjulien released this 17 Oct 19:20

Changed

  • Ready for anyone's phone: messages, docs and tool descriptions speak of "the user" instead of the author.
  • test.ps1 works from any copy of the repository: each checkout gets its own test environment (created on first
    run) and never touches the environment of the installed server.
  • Docs and changelog in English; MIT license.

Security

  • Test fixtures anonymized (phone numbers, serial number, display identifier, Wi-Fi addresses, location, contact and
    computer names).
  • Protected apps: the apps of the main French retail banks added (Crédit Agricole, BNP Paribas, Société Générale,
    Caisse d'Epargne, Banque Populaire, Crédit Mutuel, CIC, LCL), package names checked on Google Play.