# Argent (npm · @swmansion/argent)

Drive iOS Simulators, Android emulators, TVs and Electron/web apps from your coding agent

- Trust score: 77/100 (medium)
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-08-13

## Components

- npm · `@swmansion/argent`: 77/100 (this document), [markdown](https://verifymcp.io/servers/software-mansion-argent/swmansion-argent.md), [page](https://verifymcp.io/servers/software-mansion-argent/swmansion-argent)

## Channel facts

- Registry: `npm`
- Package: `@swmansion/argent`
- Version: `0.19.0`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-08-13.

- **Supply Chain Security**: 98/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - No install/post-install scripts declared.
  - 62 of 180 dependencies flagged as unhealthy (1 deprecated).
- **Provenance & Transparency**: 97/100
  - Source repository is publicly reachable at the declared URL.
  - Cryptographically verified build provenance (signed, bound to software-mansion/argent).
  - Clear OSI-approved license (Apache-2.0).
  - Actively maintained (last published 1 days ago).
  - Disclosure check failed: no security disclosure policy was found in the source repository.
- **Schema Quality & AI Usability**: 64/100
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 25021 tokens (~342/item across 73 items; 73 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 0/100
  - Stability not yet verified: not enough scan history yet (needs a 30-day window).
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

**Unverified: 1 category.** A category scored 0 because we could not verify it: a data source with nothing on this package, evidence we could not reach, or a check we could not run. We only credit what we can confirm.

## Install

### Claude

```bash
claude mcp add software-mansion-argent -- npx -y @swmansion/argent
```

### Codex

```bash
codex mcp add software-mansion-argent -- npx -y @swmansion/argent
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "software-mansion-argent": {
      "type": "local",
      "command": [
        "npx",
        "-y",
        "@swmansion/argent"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add software-mansion-argent --command npx --arg -y --arg @swmansion/argent
```

### Hermes

```yaml
mcp_servers:
  software-mansion-argent:
    command: "npx"
    args: ["-y", "@swmansion/argent"]
```

### Other

```json
{
  "mcpServers": {
    "software-mansion-argent": {
      "command": "npx",
      "args": [
        "-y",
        "@swmansion/argent"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-08-12 (score 77, 0)

- [functional regression] Security disclosure: unverified → fail

### 2026-08-11 (score 77, +2)

- [functional] We updated how we score, so this day's move reflects our rubric, not a change to the server

### 2026-08-10 (score 75)

First indexed and scored.

## MCP tools (73)

### `list-devices` (~509 tokens)

List iOS simulators, Android emulators, connected physical Android devices, running Chromium apps, and Vega (Fire TV) devices in one place.
Use at the start of a session to pick a target id ('udid' for iOS entries, 'serial' for Android/Vega entries, 'id' for Chromium) to pass to interaction tools, and to see which targets are already running.
Returns { devices, avds } where each device carries a 'platform' discriminator ('ios', 'android', 'chromium', or 'vega'); 'avds' lists Android AVDs bootable via boot-device. A Vega VVD is listed under 'devices' whether running or stopped (state 'running'/'stopped'); start a stopped one with boot-device using its 'vvdImage'.
Android entries also carry a 'kind' ('emulator' for a local AVD, 'device' for a physical phone connected over USB / wireless adb) — physical phones are detected from `adb devices` (any serial that is not an `emulator-*` one) and are driven through the same interaction tools as emulators; they do not need boot-device (just connect the phone with USB debugging authorised).
TV targets are tagged with runtimeKind 'tv' (Apple TV simulators on iOS, Android TV / leanback devices on Android) — these are focus-driven, not touch-driven: use `describe` to read focus, `tv-remote` for remote presses (up/down/left/right/select/back/menu/home), and `keyboard` to type, rather than the coordinate/gesture tools.
iOS simulators from an additional CoreSimulator device set (the 'ios.additionalDeviceSets' configuration — e.g. devices created by Radon IDE) are listed alongside default-set ones, tagged with their owning 'deviceSet' path; they are driven through the same tools by udid, but run headless (no Simulator.app window attaches to them).
Chromium apps are discovered by probing CDP debugging ports (default 9222; extend via the ARGENT_CHROMIUM_PORTS=<comma-separated-ports> env var). They must already be running with --remote-debugging-port=<port> — use boot-device with electronAppPath to launch one.
Booted/ready devices are l…

### `boot-device` (~798 tokens)

Start an iOS simulator, launch an Android emulator, start a Vega (Fire TV) Virtual Device, or spawn an Electron app and wait until it is ready to accept interactions.
Pick the platform by which argument you pass: 'udid' for an iOS simulator from list-devices, 'avdName' for an Android AVD (a serial is assigned automatically), 'vvdImage' for a Vega VVD (the 'vvdImage' of a vega device from list-devices, e.g. 'tv'), or 'electronAppPath' for an Electron app (a CDP remote-debugging port is picked automatically, or pass 'electronPort' to fix one).
Use at the start of a session once you have picked a target.
Returns a tagged payload: { platform: 'ios', udid, booted } or { platform: 'android', serial, avdName, booted } or { platform: 'vega', serial, vvdImage, booted } or { platform: 'chromium', id, port, pid, booted } (an Electron app boots as a Chromium/CDP device).
Android boots take 2–10 minutes depending on machine and cold/warm state; the tool transparently hot-boots from the AVD's default_boot snapshot when usable and falls back to cold boot otherwise. Vega starts the single SDK-managed VVD via the vega CLI (~10s) and returns once it reports running. If an Android/Electron boot stage fails, the tool terminates the device it spawned so the next retry starts clean.

Input parameters:

- `avdName` (string): Android: AVD name to launch a new emulator from (from `list-devices` → `avds[].name`). Provide exactly one of `udid`, `avdName`, `vvdImage`, or `electronAppPath`.
- `bootTimeoutMs` (integer): Android/Vega: overall budget for the boot sequence. Default 480000 (8 min) on Android, 120000 (2 min) on Vega. Clamped to [30s, 15min]. Ignored on iOS.
- `electronAppPath` (string): Electron: path to the Electron app to launch. Either a packaged .app bundle / executable, or a project directory whose package.json points the Electron binary at the entry script. Mutually exclusive…
- `electronArgs` (array): Electron-only: extra CLI arguments forwarded to the Electron binary after the app path.
- `electronPort` (integer): Electron-only: CDP remote-debugging port to expose. Defaults to a free port; the resulting device id is `chromium-cdp-<port>`.
- `force` (boolean): Shut down and re-boot the device even if already running.
- `headless` (boolean): iOS only: boot the simulator core WITHOUT opening the Simulator.app GUI window. The device still streams via simulator-server; used by Argent Lens. Set the `ARGENT_SIMULATOR_NO_WINDOW` env var (1/tru…
- `udid` (string): iOS: simulator UDID to boot (from `list-devices`). Provide exactly one of `udid`, `avdName`, `vvdImage`, or `electronAppPath`.
- `vvdImage` (string): Vega (Fire TV): VVD image to boot — the `vvdImage` of a Vega device from `list-devices` (e.g. `tv`). Starts the single SDK-managed Vega Virtual Device. Provide exactly one of `udid`, `avdName`, `vvdI…

### `launch-app` (~429 tokens)

Open an app by its bundle id (iOS) or package name (Android), or confirm the running renderer (Chromium).
Use when starting any app — prefer this over tapping home-screen / launcher icons. Also prepares the native-devtools injection before the app starts (the iOS slice on iOS, the tvOS slice on Apple TV); on tvOS, interaction is focus-driven — use the tv-* tools rather than coordinate taps.
Returns { launched, bundleId }. Fails if the app is not installed on the target device (iOS / Android).
For Chromium, the app is already running behind a CDP port; this call simply refreshes the cached viewport and acknowledges the bundleId tag. To change the visible route, use `open-url`.
On Vega (Fire TV), pass the interactive component app id from manifest.toml (e.g. com.example.app.main) as bundleId.

Common iOS bundle ids: com.apple.MobileSMS, com.apple.mobilesafari, com.apple.Preferences, com.apple.Maps, com.apple.camera, com.apple.Photos, com.apple.mobilemail, com.apple.mobilenotes, com.apple.MobileAddressBook
Common Android packages: com.android.settings, com.android.chrome, com.google.android.apps.maps, com.google.android.gm, com.android.vending, com.google.android.dialer, com.google.android.apps.messaging

Input parameters:

- `activity` (string): Android-only: fully-qualified Activity name (e.g. `.MainActivity` or `com.example/com.example.MainActivity`). If omitted on Android, the app's default launcher activity is used. Ignored on iOS / Chro…
- `bundleId` (string, required): App identifier. iOS: bundle id (e.g. com.apple.MobileSMS). Android: package name from build.gradle `applicationId` (e.g. com.android.settings). Chromium: arbitrary tag; the call is a no-op since the…
- `udid` (string, required): Target device id from `list-devices` (iOS UDID, Android serial, or Chromium id).

### `restart-app` (~197 tokens)

Terminate then relaunch an app by bundle id / package name.
Use when you need a clean in-memory state without a full reinstall. Also refreshes the native-devtools injection before the relaunch (the iOS slice on iOS, the tvOS slice on Apple TV); on tvOS, interaction is focus-driven — use the tv-* tools rather than coordinate taps.
Returns { restarted, bundleId }. Fails if the app is not installed.

Input parameters:

- `activity` (string): Android-only: relaunch a non-launcher Activity (e.g. `.SettingsActivity` or `com.example/com.example.SettingsActivity`). If omitted, the app's default launcher activity is used. Ignored on iOS.
- `bundleId` (string, required): App identifier. iOS: bundle id. Android: package name.
- `udid` (string, required): Target device id from `list-devices` (iOS UDID or Android serial).

### `reinstall-app` (~262 tokens)

Install or reinstall an app on the device. The previous installation (if any) is uninstalled first so app data and runtime permissions are cleared.
Use for a full reinstall after rebuilding, or to start from a clean app state.
Returns { reinstalled, bundleId }. Fails if the app path does not exist or the package does not match the platform (.app for iOS, .apk for Android, .vpkg for Vega).

Input parameters:

- `appPath` (string, required): Path to the app bundle. iOS: `.app` directory (e.g. ./build/.../MyApp.app). Android: `.apk` file (e.g. android/app/build/outputs/apk/debug/app-debug.apk). Vega: `.vpkg` file. Relative paths are resol…
- `bundleId` (string, required): App identifier that matches the bundle at `appPath`. iOS: bundle id (used to uninstall first). Android: package name (used to uninstall first; the install itself identifies the app from the APK). Veg…
- `udid` (string, required): Target device id from `list-devices` (iOS UDID or Android serial).

### `settings-permissions` (~577 tokens)

Grant, deny, or reset a runtime permission for an app without navigating the system Settings UI. Use during test setup to pre-authorize (or explicitly deny) a service before the app asks, or `reset` so the permission dialog appears again on next use. Always per-app: bundleId is required.
Permissions: camera, microphone, photos, contacts, notifications, calendar, location, location-always, media-library, motion, reminders.
iOS simulator: edits the simulator's TCC store, always per-app. `notifications` is not supported (no iOS equivalent). `reset` is per-app — a device-wide reset is a no-op for existing grants on recent iOS, so it is not offered. `grant location`/`location-always` needs the app already installed (location auth isn't stored in TCC and isn't applied to a bundle id until the app exists) — enforced on local simulators; a remote simulator can't be probed for install state, so ensure the app is installed there first. Other services can be granted before install.
Android: changes the mapped `android.permission.*` runtime permissions (reset also best-effort clears the user-set permission flags). The app must be installed and declare them in its manifest; `reminders` has no Android equivalent.
Some permission changes terminate the app if it is running (system behavior on both platforms) — set permissions before launching, or relaunch after.
Returns { action, permission, bundleId, applied, skipped? }: `applied` lists the platform-level services/permissions actually changed; `skipped` (Android) lists mapped permissions the package manager rejected, e.g. ones the manifest doesn't declare. Fails if nothing could be applied.

Input parameters:

- `action` (string, required): `grant` pre-authorizes the permission, `deny` refuses it, `reset` returns it to the not-yet-asked state so the app prompts on next use.
- `bundleId` (string, required): App to change the permission for — required for every action. iOS: bundle id (e.g. com.example.app). Android: package name. `reset` is per-app too: simctl's device-wide reset (no bundleId) silently l…
- `permission` (string, required): The permission to change. `notifications` is Android-only (iOS has no simctl service for it); `reminders` is iOS-only; `camera` works on Android and on iOS only when the target simulator's runtime mo…
- `udid` (string, required): Target device id from `list-devices` (iOS simulator UDID or Android serial).

### `open-url` (~299 tokens)

Open a URL or URL scheme on the device.
Use to navigate to a web page or deep-link into an app. On Chromium, this navigates the primary renderer to the given URL.
Cross-platform schemes: https://, tel:, mailto:. iOS also: messages://, settings://, maps://. Android also: geo:, plus any app-specific deep link.
Deep-linking caveat: an https:// link opens the native app only when an installed app is verified for the link's domain (iOS Universal Links / Android App Links) — otherwise it opens in the browser, and on iOS simulators it may open in Safari even when the owning app is installed. To reliably open an installed app, use its custom scheme (scheme://path) or launch-app with its bundle id.
Returns { opened, url, note? }. note carries the deep-linking caveat when a web URL was opened on a native device. Fails if no app is registered to handle the URI (iOS/Android) or the renderer rejects the navigation (Chromium).

Input parameters:

- `udid` (string, required): Target device id from `list-devices` (iOS UDID, Android serial, or Chromium id).
- `url` (string, required): URL or scheme to open (e.g. https://example.com, messages://, tel:555, geo:37.0,-122.0). For Chromium this navigates the renderer.

### `screenshot` (~325 tokens)

Capture a screenshot of the device screen (iOS simulator, Android emulator, Apple TV simulator, Vega, or Chromium app). Returns { image }; the MCP adapter renders it as a visible image unless the caller passed includeImageInContext: false.
Use when you need a baseline image before an interaction or to inspect the current screen state after a delay.
Fails if the simulator-server / emulator backend / Chromium CDP is not reachable for the given device.

Input parameters:

- `downscaler` (string): Downscaling algorithm when scale<1 on Chromium. Defaults to lanczos3 (highest quality). Mirrors sim-server's wire enum.
- `includeImageInContext` (boolean): Default true. Set false only when capturing a full-resolution PNG (scale: 1.0) to save as a baseline/current for screenshot-diff — the file is still written, but the image bytes are not attached to t…
- `rotation` (string): Orientation override for the screenshot (rotates the captured image after Page.captureScreenshot on Chromium).
- `scale` (number): Scale factor (0.01-1.0). Defaults to ARGENT_SCREENSHOT_SCALE env var, or 0.3 if unset for iOS/Android. On Chromium the default is 1.0 (no downscale); pass <1 to opt in. Downscaling on Chromium requir…
- `udid` (string, required): Target device id from `list-devices` (iOS UDID, Android serial, Apple TV UDID, Vega serial, or Chromium id).

### `screenshot-diff` (~385 tokens)

Compare two PNG screenshots and return a compact visual-diff summary.
Accepts saved baseline/current PNG paths, or one saved PNG plus one live full-resolution capture from a device. Always provide udid so the simulator-server dependency can be resolved.
Use when stable before/after screenshots exist and the expected result is pixel-visible: layout, spacing, color, typography, image/icon rendering, clipping, overflow, or text rendering.
For live captures, set exactly one of captureBaseline or captureCurrent; use baselinePath + captureCurrent for the common visual-regression flow.
Returns { summary, diffPath, contextDiffPath }. The summary uses normalized [0,1] screen locations matching describe coordinates; diffPath is the full-size diff image and contextDiffPath is a downscaled image for MCP/agent display.
Ignores the fixed top status-bar band for both pixel and OCR text comparisons.
Fails if the input sources are invalid, PNG files cannot be read, outputDir cannot be written, or the simulator-server / emulator backend is not reachable.

Input parameters:

- `baselinePath` (string): Path to the baseline PNG file. Required unless captureBaseline is true.
- `captureBaseline` (boolean): Capture the baseline screenshot live at full resolution before diffing. Cannot be combined with captureCurrent.
- `captureCurrent` (boolean): Capture the current screenshot live at full resolution before diffing. Cannot be combined with captureBaseline.
- `currentPath` (string): Path to the current PNG file. Required unless captureCurrent is true.
- `outputDir` (string): Directory where diff artifacts should be written. Optional — defaults to a temp directory; the diff images are returned in the result either way.
- `rotation` (string): Orientation override for live baseline/current captures.
- `udid` (string, required): Target device id from `list-devices` (iOS UDID or Android serial).

### `screen-recording-start` (~490 tokens)

Start recording the device screen to a video file (h264 mp4, 30fps at the device's native resolution).
By default stretches where the screen does not change are trimmed out (see trimStatic), so a long session with only brief activity comes back as a short clip instead of minutes of dead air.
By default every tap, swipe, drag, pinch and rotate is drawn into the video as an on-screen touch marker (see showTouches), so the recording shows where each interaction landed.
The recording keeps running across other tool calls (every result carries a reminder) until `screen-recording-stop` is called or timeLimitSeconds elapses — immediately after starting, set yourself a reminder/wakeup for the expected end of the recording so it is never left running.
Use when the user wants a video of an interaction, animation, or app behavior — for a single still frame use `screenshot` instead.
Returns { status: "recording", timeLimitSeconds, outputFile } — the video is retrieved later by `screen-recording-stop`, not by reading outputFile directly.
Fails if a recording is already running on the device, the device is not booted, ffmpeg is not installed, or the platform cannot be recorded (tvOS, Chromium, Vega and remote simulators are unsupported).

Input parameters:

- `showTouches` (boolean): Default true. Draw simulator-server's touch visualizer into the recording: a pulse marks each tap, a comet trail follows swipes and drags, and paired markers show two-finger pinch/rotate, so the vide…
- `timeLimitSeconds` (integer): Auto-stop cap in seconds (default 180, max 600). Set it to slightly more than the interaction you plan to capture.
- `trimStatic` (boolean): Default true. Collapse stretches where the screen does not change: the first second of each still stretch is kept, then unchanged frames are dropped until something moves again, so a long recording w…
- `udid` (string, required): Target device id from `list-devices` (iOS Simulator UDID or Android serial).

### `screen-recording-stop` (~199 tokens)

Stop the screen recording started by `screen-recording-start` and retrieve the video: frame capture ends and ffmpeg finalizes the mp4.
Also retrieves the video when the recording already ended on its own (time limit reached, capture process died) — call it even after the cap fired.
Use when the interaction being captured is finished, or a tool-result note reminds you a recording is still running.
Returns { video, durationMs, wallClockMs?, trimmedMs?, warning? }; video is a downloadable artifact materialized to a local path. When static-frame trimming removed dead air, durationMs is the trimmed video length and wallClockMs/trimmedMs report the real duration and how much was cut.
Fails if no recording (running or finished-but-unretrieved) exists for the given udid.

Input parameters:

- `udid` (string, required): Target device id from `list-devices` (iOS Simulator UDID or Android serial).

### `gesture-tap` (~390 tokens)

Press the device screen (iOS simulator, Android emulator, or Chromium app) at normalized coordinates: x and y are fractions of screen width and height in 0.0–1.0 (not pixels).
Sends a Down event followed by an Up event at the same point. For Chromium, this dispatches a CDP mouse-press/release on the renderer.
Set clickCount: 2 for a double-tap / double-click — the taps are dispatched as one gesture with proper click counting, which two separate tap calls cannot guarantee.
Use when you need to tap a button, link, or any tappable element on the screen.
Returns { tapped: true, timestampMs }. Fails if the simulator-server / emulator backend / Chromium CDP is not reachable for the given device.
Before tapping, determine the correct coordinates by using discovery tools — pick by platform: iOS / Android use `describe`, `native-describe-screen`, or `debugger-component-tree`; Chromium uses `describe` (the DOM walker), since the native and RN-specific discovery tools don't apply. More information in `argent-device-interact` skill

Input parameters:

- `clickCount` (integer): Number of taps/clicks dispatched as ONE multi-tap gesture (2 = double-tap / double-click). The taps land inside the OS double-tap window; on Chromium each click carries an escalating CDP clickCount s…
- `udid` (string, required): Target device id from `list-devices` (iOS UDID, Android serial, or Chromium id).
- `x` (number, required): Normalized horizontal position 0.0–1.0 (left=0, right=1), not pixels
- `y` (number, required): Normalized vertical position 0.0–1.0 (top=0, bottom=1), not pixels

### `chromium-tabs` (~427 tokens)

List and switch the tabs / windows of a Chromium (CDP) app (an Electron app's BrowserWindows or a Chromium browser's tabs), and open or close them.
\- action="list": enumerate page targets with stable ids (`t1`, `t2`, …), title, url, and which is active.
\- action="select" (tab=<tabId|label>): make that tab the active one. The active tab is what describe / gesture-tap / screenshot / debugger-evaluate / open-url all operate on, so switch before driving a different tab.
\- action="new" (url?, label?): open a new tab/page and activate it.
\- action="close" (tab?=<tabId|label>): close a tab (defaults to the active one); if the active tab is closed, another live tab becomes active.
Use when an app exposes multiple windows or tabs and you need to inspect or drive one other than the current page, or to open/close a page during a flow. tabIds are stable for the session and never reused.
Returns { tabs: [{ tabId, targetId, title, url, active, label? }] }. Fails if the device is not a Chromium (CDP) device, or the requested tabId/label no longer matches a live tab. Chromium-only.

Input parameters:

- `action` (string, required): list: enumerate tabs/windows. select: make a tab active (every other tool then acts on it). new: open a tab. close: close a tab.
- `label` (string): `new` only: a memorable label usable interchangeably with the tabId.
- `tab` (string): Target tab for `select` / `close`: a tabId like `t2` or a label. `close` defaults to the active tab.
- `udid` (string, required): Chromium device id from `list-devices` (e.g. `chromium-cdp-9222`).
- `url` (string): `new` only: URL to open (defaults to about:blank).

### `chromium-cookies` (~403 tokens)

Read and write cookies of a Chromium (CDP) app (via the Network domain, so HttpOnly cookies are included).
\- action="get" (url?): list cookies, optionally restricted to given URLs (defaults to the active page).
\- action="set" (name, value, + url OR domain, optional path/secure/httpOnly/sameSite/expires): create or update a cookie.
\- action="delete" (name, + url/domain/path): remove a matching cookie.
\- action="clear": remove ALL browser cookies.
Use when seeding an authenticated session before a flow (set the session cookie, then navigate) or asserting cookie state after one.
Returns { cookies, count } for get, or a small status object ({ set } / { deleted } / { cleared }) otherwise. Fails if the device is not a Chromium (CDP) device, or set is missing name/value. Chromium-only.

Input parameters:

- `action` (string, required): get: read cookies. set: create/update a cookie. delete: remove a named cookie. clear: remove all browser cookies.
- `domain` (string): set/delete: scope the cookie by domain (alt to url).
- `expires` (number): set: expiry as a Unix timestamp (seconds). Omit for a session cookie.
- `httpOnly` (boolean): set: mark HttpOnly.
- `name` (string): set/delete: cookie name.
- `path` (string): set/delete: cookie path (default /).
- `sameSite` (string): set: SameSite policy.
- `secure` (boolean): set: mark Secure.
- `udid` (string, required): Chromium device id from `list-devices` (e.g. `chromium-cdp-9222`).
- `url` (string): get: restrict to these URLs (defaults to the active page). set/delete: scope the cookie by URL.
- `value` (string): set: cookie value.

### `chromium-storage` (~318 tokens)

Read and write localStorage / sessionStorage of a Chromium (CDP) app's active page.
\- action="get": with `key`, returns that value; without `key`, returns all entries.
\- action="set" (key, value): write an entry.
\- action="remove" (key): delete an entry.
\- action="clear": empty the store.
Set `store` to "local" or "session". Storage is per-origin, so it reflects the active tab's document.
Use when seeding feature flags / auth tokens before a flow or asserting persisted app state after one.
Returns { value } for a single key, { entries, count } for all, or a status object ({ set } / { removed } / { cleared }) otherwise. Fails if the device is not a Chromium (CDP) device, or set is missing key/value. Chromium-only.

Input parameters:

- `action` (string, required): get: read one key (with `key`) or all entries. set: write `key`=`value`. remove: delete `key`. clear: empty the store.
- `key` (string): get (optional) / set / remove: the storage key.
- `store` (string, required): Which Web Storage area: `local` (localStorage) or `session` (sessionStorage).
- `udid` (string, required): Chromium device id from `list-devices` (e.g. `chromium-cdp-9222`).
- `value` (string): set: the value to store.

### `gesture-swipe` (~380 tokens)

Execute a smooth swipe / drag touch gesture between two points on the device (iOS simulator or Android emulator). All from/to positions are normalized 0.0–1.0 (fractions of screen width/height, not pixels), same as gesture-tap.
Generates interpolated Move events for a natural feel (~60fps).
Swipe up (fromY > toY) to scroll content down.
Use when you need to scroll a list, dismiss a modal, drag an element, or navigate between pages. Not supported on Chromium — use gesture-scroll there instead.
Pass settle:true for a momentum-free swipe that lands exactly where the finger lifts (no fling), when you need a deterministic scroll distance. Returns { swiped: true, timestampMs }. Fails if the simulator-server / emulator backend is not reachable for the given device.

Input parameters:

- `durationMs` (number): Total gesture duration in milliseconds (default 300)
- `fromX` (number, required): Start x: normalized 0.0–1.0 (not pixels; same as tap)
- `fromY` (number, required): Start y: normalized 0.0–1.0 (not pixels; same as tap)
- `settle` (boolean): Momentum-free swipe: decelerate into the end point (ease-out) so the OS reads ~0 release velocity and applies little to no fling. Use for scroll-to-element loops; default false (a natural flinging sw…
- `toX` (number, required): End x: normalized 0.0–1.0 (not pixels; same as tap)
- `toY` (number, required): End y: normalized 0.0–1.0 (not pixels; same as tap)
- `udid` (string, required): Target device id from `list-devices` (iOS UDID or Android serial).

### `gesture-scroll` (~367 tokens)

Scroll content in a Chromium app by dispatching mouse-wheel events at a point. Anchor x/y are normalized 0.0–1.0 (fractions of the window, not pixels), same coordinate space as gesture-tap and describe. Deltas are fractions of the window too: deltaY 0.5 scrolls down half a window; negative scrolls back up.
Use when content is below/above the fold (describe shows off-screen elements with zero height) or a list needs scrolling. Chromium only — on iOS/Android use gesture-swipe.
Returns { scrolled: true, timestampMs }. Fails if the Chromium CDP session is not reachable for the given device.

Input parameters:

- `deltaX` (number): Horizontal scroll distance as a fraction of the window width (e.g. 0.5 = half a window). Positive scrolls content right (reveals content to the right).
- `deltaY` (number): Vertical scroll distance as a fraction of the window height (e.g. 0.5 = half a window). Positive scrolls content down (reveals content below), like rolling a mouse wheel toward you.
- `durationMs` (number): Spread the scroll over this many milliseconds in wheel-event steps (default 300) so scroll handlers fire progressively.
- `udid` (string, required): Target Chromium device id from `list-devices` (chromium-cdp-<port>).
- `x` (number, required): Anchor x: normalized 0.0–1.0 (fraction of window width, not pixels). The wheel events land here — put it over the element you want to scroll.
- `y` (number, required): Anchor y: normalized 0.0–1.0 (fraction of window height, not pixels).

### `gesture-drag` (~322 tokens)

Press the left mouse button at a start point, move to an end point, and release — a desktop mouse drag in a Chromium app. All positions are normalized 0.0–1.0 (fractions of the window, not pixels), same coordinate space as gesture-tap and describe. Interpolates mouse-move events at ~60fps over durationMs for a natural drag.
Use for slider thumbs, drag-and-drop, text selection, or draggable UI elements. Dragging never scrolls content on desktop — use gesture-scroll for lists/pages. Chromium only — on iOS/Android use gesture-swipe.
Returns { dragged: true, timestampMs }. Fails if the Chromium CDP session is not reachable for the given device.

Input parameters:

- `durationMs` (number): Total drag duration in milliseconds (default 300), interpolated at ~60fps.
- `fromX` (number, required): Press x: normalized 0.0–1.0 (fraction of window width, not pixels).
- `fromY` (number, required): Press y: normalized 0.0–1.0 (fraction of window height, not pixels).
- `toX` (number, required): Release x: normalized 0.0–1.0 (not pixels; same space as tap).
- `toY` (number, required): Release y: normalized 0.0–1.0 (not pixels; same space as tap).
- `udid` (string, required): Target Chromium device id from `list-devices` (chromium-cdp-<port>).

### `gesture-custom` (~505 tokens)

Send a sequence of touch events for complex gestures.
Use for: long press, drag-and-drop, custom scroll, pinch (second touch point).
For simple taps use the gesture-tap tool. For straight-line scrolling use the gesture-swipe tool.
For pinch gestures use gesture-pinch. For rotation gestures use gesture-rotate.
All x/y values are normalized 0.0–1.0 (screen fractions, not pixels). delayMs controls the delay before each event (default 16ms ≈ 60fps).
Set interpolate to auto-generate smooth intermediate Move events between your keyframes.
Returns { events: number } with the total count of events dispatched. Fails if the target device is not booted or an event type is invalid.

Example long-press at center:
  [{"type":"Down","x":0.5,"y":0.5},{"type":"Up","x":0.5,"y":0.5,"delayMs":800}]

Example smooth scroll down:
  [{"type":"Down","x":0.5,"y":0.7},
   {"type":"Move","x":0.5,"y":0.6},{"type":"Move","x":0.5,"y":0.5},{"type":"Move","x":0.5,"y":0.4},
   {"type":"Up","x":0.5,"y":0.3}]

Example pinch-to-zoom (with interpolate:10 for smoothness):
  events: [{"type":"Down","x":0.4,"y":0.5,"x2":0.6,"y2":0.5},
           {"type":"Up","x":0.2,"y":0.5,"x2":0.8,"y2":0.5}]
  interpolate: 10

Input parameters:

- `events` (array, required): Sequence of touch events; x/y (and optional second touch) are normalized 0.0–1.0, not pixels
- `interpolate` (number): Number of intermediate Move events to auto-insert between each pair of consecutive events. Smooths out gestures by linearly interpolating both primary (x,y) and secondary (x2,y2) coordinates. The del…
- `udid` (string, required): Target device id from `list-devices` (iOS UDID or Android serial).

### `gesture-pinch` (~587 tokens)

Execute a pinch-to-zoom gesture by moving two fingers toward or away from a center point to change the scale of on-screen content. All positions and distances are normalized 0.0–1.0 (fractions of screen width/height, not pixels)—same coordinate space as gesture-tap and gesture-swipe.
startDistance > endDistance = pinch in (zoom out). startDistance < endDistance = pinch out (zoom in).
Typical values: startDistance 0.2, endDistance 0.6 for a zoom-in pinch at screen center.
Auto-generates interpolated frames at ~60fps. The angle parameter controls the axis (0 = horizontal, 90 = vertical). Optional endCenterX/endCenterY drift the centroid linearly over the gesture (omitted = fixed center).
Use when you need to zoom in or out on a map, image, or zoomable view. Returns { pinched: true, timestampMs }. Fails if the simulator-server / emulator backend is not reachable for the given device.

Input parameters:

- `angle` (number): Axis angle in degrees along which the fingers are placed (default 0 = horizontal).
- `centerX` (number, required): Center of pinch, horizontal: normalized 0.0–1.0 (fraction of screen width, not pixels)
- `centerY` (number, required): Center of pinch, vertical: normalized 0.0–1.0 (fraction of screen height, not pixels)
- `durationMs` (number): Total gesture duration in milliseconds (default 300)
- `endCenterX` (number): Final horizontal center of the pinch: normalized 0.0–1.0. When set, the centroid drifts linearly from centerX to endCenterX over the gesture (e.g. to keep expanding fingers on-screen near an edge). O…
- `endCenterY` (number): Final vertical center of the pinch: normalized 0.0–1.0. When set, the centroid drifts linearly from centerY to endCenterY over the gesture. Omit for a fixed center.
- `endDistance` (number, required): Final distance between the two fingers: normalized 0.0–1.0 (fraction of screen, not pixels). E.g. 0.6 = fingers 60% of screen apart. Use a larger endDistance than startDistance to pinch out (zoom in).
- `startDistance` (number, required): Initial distance between the two fingers: normalized 0.0–1.0 (fraction of screen, not pixels). E.g. 0.2 = fingers 20% of screen apart. Use a larger startDistance than endDistance to pinch in (zoom ou…
- `udid` (string, required): Target device id from `list-devices` (iOS UDID or Android serial).

### `gesture-rotate` (~605 tokens)

Send a two-finger circular arc gesture to rotate on-screen content by a specified angle. Two fingers are placed opposite each other at a fixed radius from the center, then swept from startAngle to endAngle degrees. All positions and radii are normalized 0.0–1.0 (fractions of screen width/height, not pixels)—same coordinate space as gesture-tap and gesture-swipe.
endAngle > startAngle = clockwise rotation. Typical values: radius 0.15, startAngle 0, endAngle 90 for a 90° clockwise turn. A single radius applies to both axes, so on a non-square screen it traces a physical ellipse (finger separation varies through the turn); pass radiusX+radiusY (fractions of width/height with radiusX·width = radiusY·height) for a physically circular orbit instead.
Auto-generates interpolated frames at ~60fps.
Unlike gesture-pinch which moves fingers linearly to zoom, this orbits fingers in an arc to change orientation.
Use when you need to rotate a map, image picker, or any rotateable UI element. Returns { rotated: true, timestampMs }. Fails if the simulator-server / emulator backend is not reachable for the given device.

Input parameters:

- `centerX` (number, required): Center of rotation, horizontal: normalized 0.0–1.0 (fraction of screen width, not pixels)
- `centerY` (number, required): Center of rotation, vertical: normalized 0.0–1.0 (fraction of screen height, not pixels)
- `durationMs` (number): Total gesture duration in milliseconds (default 300)
- `endAngle` (number, required): Ending angle in degrees. endAngle > startAngle = clockwise.
- `radius` (number): Distance from center to each finger: normalized 0.0–1.0 (fraction of screen, not pixels). E.g. 0.15 = fingers placed 15% of screen away from center. One value for both axes, so on a non-square screen…
- `radiusX` (number): Per-axis finger distance, horizontal: normalized 0.0–1.0 fraction of screen WIDTH. Give both radiusX and radiusY (they override radius) with radiusX·screenWidth = radiusY·screenHeight for a physicall…
- `radiusY` (number): Per-axis finger distance, vertical: normalized 0.0–1.0 fraction of screen HEIGHT. Always paired with radiusX — see radiusX.
- `startAngle` (number, required): Starting angle in degrees (0 = right, 90 = down)
- `udid` (string, required): Target device id from `list-devices` (iOS UDID or Android serial).

### `button` (~190 tokens)

Press a device hardware button (iOS simulator, Android emulator or device). iOS sends a Down then Up event automatically; Android injects a single `adb` key event.
Supported buttons depend on the platform: home, back, power, volumeUp, volumeDown, appSwitch, actionButton — buttons not present on the target platform (e.g. 'back' on iOS, 'actionButton' on Android) are rejected with a clear error.
Use when you need to trigger hardware button events.
Returns { pressed: buttonName }.
Fails if the device backend is not reachable — the simulator-server for iOS, or `adb` for Android (Android presses are injected with `adb shell input keyevent`).

Input parameters:

- `button` (string, required): Hardware button to press
- `udid` (string, required): Target device id from `list-devices` (iOS UDID or Android serial).

### `keyboard` (~794 tokens)

Type text or press special keys on the device (iOS simulator, Android emulator or device, Chromium app, Vega Virtual Device, or Apple TV / Android TV) using keyboard events.
Use when you need to enter text or trigger a named key such as enter, escape, or arrow keys. On Vega and Apple TV / Android TV, prefer the remote tools for D-pad navigation; use keyboard to type into a focused text field (e.g. a search or login box).
Returns { typed: string, keys: number }. Fails if an unsupported key name is provided or the device's input backend is not reachable.
\- text: types a string (supports uppercase, digits, common punctuation). To type a credential, use `{{secret:<NAME>}}` — resolved server-side from the `ARGENT_SECRET_<NAME>` env var or an argent secrets file (`.argent/secrets.env` in the project, `~/.argent/secrets.env`, or an `ARGENT_SECRET_`-prefixed key in the project's `.env`/`.env.local`), so the plaintext never enters agent context; the result echoes the placeholder, not the value, and the after-typing auto-screenshot is skipped.
\- key: presses a single named key (enter, escape, backspace, tab, arrow-up/down/left/right, f1–f12) — NOT supported on TV targets; move focus with `tv-remote` instead.
On a TV target (runtimeKind 'tv') only `text` applies — focus a text field first (with `tv-remote`), then type into it (injected HID keyboard on Apple TV, `adb input text` on Android TV).
Provide text, key, or both — when both are given, the text is typed first and the key is pressed after it (text + key:"enter" types and submits).

Input parameters:

- `delayMs` (number): Delay in ms between key presses (default 50). Ignored on Android phones/tablets (typed via `adb input text`, which has no per-key cadence), on Vega (text/keys injected in a single shot), and on TV ta…
- `key` (string): Named key to press: enter, escape, backspace, tab, space, arrow-up, arrow-down, arrow-left, arrow-right, f1–f12. When combined with `text`, the key is pressed AFTER the text is typed (so text + enter…
- `text` (string): Text to type character by character. Handles uppercase and common punctuation. To type a credential without its plaintext ever entering your context, use a secret placeholder: `{{secret:<NAME>}}` — e…
- `udid` (string, required): Target device id from `list-devices` (iOS UDID, Android serial, Vega serial, or Chromium id).

### `rotate` (~98 tokens)

Set the device orientation to Portrait, LandscapeLeft, LandscapeRight, or PortraitUpsideDown.
Use to test layout in a different orientation. Re-run `describe` afterwards — frame coordinates change with the orientation.
Returns { orientation }. Fails if the target device is not booted.

Input parameters:

- `orientation` (string, required): Target orientation
- `udid` (string, required): Target device id from `list-devices` (iOS UDID or Android serial).

### `tv-remote` (~464 tokens)

Press a TV remote / D-pad button (or a whole path of them) on a TV device — Apple TV (tvOS), Android TV (leanback), or Vega (Fire TV).
A TV is navigated with a directional remote, not touch — use this instead of gesture-tap/swipe (which do not apply on a TV). Move focus with up/down/left/right, confirm with select, go back with back/menu, exit with home, and use playPause/rewind/fastForward/next/previous/volumeUp/volumeDown/mute for the corresponding remote keys. (On the Apple TV simulator the media-transport and volume keys are rejected — its HID stack ignores them; they work on Android TV and Vega.)
Single press: { button: "down" }. Repeat the same button: { button: "down", repeat: 3 }.
Multi-step navigation: pass a path as { button: ["up","right","right","select"] } — it runs in one tool call, far cheaper than separate presses.
Read the screen with `describe` before and after to see where focus landed.
Returns { pressed, count }.

Input parameters:

- `button`: A single TV-remote button, or a path of them run in one call. Buttons: up/down/left/right (D-pad), select (OK), back, home, menu, playPause, rewind, fastForward, next, previous, volumeUp, volumeDown,…
- `repeat` (integer): Repeat the whole `button` value this many times (default 1). Compact for long same-button runs, e.g. { button: "down", repeat: 12 }.
- `udid` (string, required): Target TV device id from `list-devices` (Apple TV, Android TV, or Vega).

### `run-sequence` (~1312 tokens)

Execute multiple device interaction steps in a single call (iOS simulator, Android emulator, Apple TV / Android TV, or Chromium app).
Use when you need sequential actions and do NOT need to observe the screen between them
(e.g. scrolling multiple times, typing then pressing enter, rotating back and forth).
Returns { completed, total, steps } with per-step results. Fails if an unrecognised tool name is used in a step (error returned at that step, execution stops).
No screenshot is captured automatically — call screenshot separately after the sequence if needed.

ONLY use this when every step is known in advance. If any step depends on the
result of a previous one (e.g. tapping a menu item that only appears after
a prior tap), use individual tool calls instead.

Allowed tools and their args (udid is auto-injected, do NOT include it in args):

  gesture-tap:    { x: number, y: number, clickCount?: number }                                                        [ios/android/chromium]
  gesture-swipe:  { fromX: number, fromY: number, toX: number, toY: number, durationMs?: number }                       [ios/android]
  gesture-scroll: { x: number, y: number, deltaX?: number, deltaY?: number, durationMs?: number }                       [chromium only]
  gesture-drag:   { fromX: number, fromY: number, toX: number, toY: number, durationMs?: number }                       [chromium only]
  gesture-custom: { events: [{ type: "Down"|"Move"|"Up", x: number, y: number, x2?: number, y2?: number, delayMs?: number }], interpolate?: number }  [ios/android]
  gesture-pinch:  { centerX: number, centerY: number, startDistance: number, endDistance: number, endCenterX?: number, endCenterY?: number, angle?: number, durationMs?: number }  [ios/android]
  gesture-rotate: { centerX: number, centerY: number, radius?: number, radiusX?: number, radiusY?: number, startAngle: number, endAngle: number, durationMs?: number }  [ios/android]
  button:         { button: "home"|"back"|"power"|"volumeUp…

Input parameters:

- `steps` (array, required): Ordered list of interaction steps to execute sequentially
- `udid` (string, required): Target device id from `list-devices` (iOS UDID, Android serial, Vega serial, or Chromium id) — shared across all steps.

### `debugger-connect` (~267 tokens)

Connect to a JS runtime CDP debugger.
iOS / Android / Vega: connects to Metro's CDP endpoint on the given port. Chromium: re-uses the page CDP session opened by boot-device — port is ignored.
Returns connection info including port, projectRoot (empty on Chromium and on legacy Metro, e.g. Vega), deviceName, appName, logicalDeviceId (absent on Vega), and isNewDebugger. If already connected, returns the existing connection.
Use when starting a debug session or before calling other debugger-* tools. Fails if the runtime is unreachable (Metro down, or Chromium CDP terminated).

Input parameters:

- `device_id` (string, required): Device id from list-devices: iOS simulator UDID, Android serial, Vega serial (amazon-...), or Chromium device id (chromium-cdp-<port>). Pass this SAME id as device_id to every subsequent debugger-* c…
- `port` (number): Metro server port (ignored for Chromium — its CDP port is encoded in device_id)

### `debugger-status` (~185 tokens)

Get JS runtime debugger connection status and diagnostic info.
Use when you need to verify connectivity before using other debugger tools. Returns port, projectRoot (empty on Chromium and on legacy Metro, e.g. Vega), deviceName, appName, logicalDeviceId (absent on Vega), isNewDebugger (false on the legacy inspector), connected flag, loadedScripts count, and sourceMapReady (always true — waits for pending source maps before returning; no-op on Chromium). Fails if the runtime is unreachable.

Input parameters:

- `device_id` (string, required): Device id from list-devices — the SAME id you passed to debugger-connect (iOS simulator UDID, Android serial, Vega serial, or Chromium device id). The logicalDeviceId debugger-connect returns also re…
- `port` (number): Metro server port (ignored for Chromium)

### `debugger-evaluate` (~223 tokens)

Execute arbitrary JavaScript in the app's JS runtime via CDP — Hermes on iOS / Android / Vega, V8 on Chromium.
Returns the evaluation result as a JSON-serializable value, along with deviceName, appName, and logicalDeviceId for context. Use when you need to read app state, call app functions, or test logic at runtime. The result is serialized by value, so cyclic objects (many RN runtime values — fiber nodes, navigation refs, global — are cyclic) fail with a serialization error rather than returning silently. Fails if the expression throws or the runtime is not connected.

Input parameters:

- `device_id` (string, required): Device id from list-devices — the SAME id you passed to debugger-connect (iOS simulator UDID, Android serial, Vega serial, or Chromium device id). The logicalDeviceId debugger-connect returns also re…
- `expression` (string, required): JavaScript expression to evaluate in the app runtime
- `port` (number): Metro server port (ignored for Chromium)

### `debugger-reload-metro` (~127 tokens)

Restart the Metro JS bundle in the connected React Native app without restarting the native process.
Use when you want to apply code changes or reset JS state. Returns { reloaded, port, method, deviceName, appName, logicalDeviceId } indicating which reload path was used and which device/app was targeted. Fails if Metro is not running on the given port.

Input parameters:

- `device_id` (string, required): Device id from list-devices — the SAME id you passed to debugger-connect (iOS simulator UDID or Android serial).
- `port` (number): Metro server port

### `debugger-component-tree` (~412 tokens)

Fetch the current screen of a running React Native app as a compact component text tree.
Only shows on-screen components with unique positions — off-screen (scrolled) content,
full-screen transparent wrappers, and implementation-detail components are pruned.

Each visible component is listed with its name, text content, and normalized
tap coordinates in [0,1] space (fractions of the screen, not pixels — same space as tap/swipe/gesture).

This is the preferred element discovery tool for React Native apps. More information in argent-react-native-app-workflow skill.

Workflow:
  1\. Call this tool to get the component tree.
  2\. Find the desired element by name, text, testID, or accessibilityLabel.
  3\. Use the (tap: x,y) coordinates directly with the tap tool.

Call again after navigation or state changes since positions may shift.
Set includeSkipped=true to see a summary of all filtered components.
Use when you need tap coordinates for a React Native UI element. Returns a compact text tree with (tap: x,y) coords. Fails if Metro debugger is not connected.

Input parameters:

- `device_id` (string, required): Device id from list-devices — the SAME id you passed to debugger-connect (iOS simulator UDID or Android serial).
- `includeSkipped` (boolean): When true, appends a summary of all filtered components: total fiber count, JS-side skip counts by name, and TS-side filter pass removals. Useful for understanding what was pruned from the tree.
- `maxNodes` (number): Maximum total nodes to include. When exceeded, intermediate single-child wrapper chains are collapsed to preserve both root structure and leaf elements. Default: no limit.
- `onScreenOnly` (boolean): When true (default), only components visible on screen are returned. Set to false to include all mounted components including those scrolled off-screen. Useful when you need to understand the full pa…
- `port` (number): Metro server port

### `debugger-inspect-element` (~431 tokens)

Inspect the React component hierarchy at a screen coordinate (x, y).
Returns components from the tapped element upward through its parent hierarchy,
each with its source file:line and a code fragment.

The first items (lowest indices) are the most specific — the exact component under
the tap point and its direct parents. Higher indices are broader context (page, navigator).
Default shows 35 items which covers all app-specific code; use maxItems=70+ to also
see the navigation/screen structure.

Uses getInspectorDataForViewAtPoint + _debugStack + Metro /symbolicate.
Set resolveSourceMaps to false to skip symbolication and get raw bundled locations instead.
Set includeSkipped=true to see filtered items annotated with skip reasons.
Use when you need the source file and line for a component at a tap coordinate. Fails if the app is not connected or the coordinate is outside the screen.

Input parameters:

- `contextLines` (number): Lines of source context to include around the component definition
- `device_id` (string, required): Device id from list-devices — the SAME id you passed to debugger-connect (iOS simulator UDID or Android serial).
- `includeSkipped` (boolean): When true, items that would normally be filtered are kept in the response with skipped=true and a skipReason. Useful for understanding what was pruned.
- `maxItems` (number): Maximum number of hierarchy items to return, counted from the bottom (most specific component first). The hierarchy walks from the tapped element up to the root — the first items are the most relevan…
- `port` (number): Metro server port
- `resolveSourceMaps` (boolean): When true, resolves bundled frame locations to original source files via Metro symbolication and includes a code fragment. When false, returns the raw bundled frame info (file, line, column) without…
- `x` (number, required): Logical X coordinate on device screen
- `y` (number, required): Logical Y coordinate on device screen

### `debugger-log-registry` (~175 tokens)

Get a summary of all console logs captured from the app's JS runtime.
Returns the log file path, entry counts by level, and message clusters (grouped by similarity). Works against Hermes (iOS / Android / Vega) and V8 (Chromium).
Use when investigating warnings, errors, or unexpected output — call this first for an overview, then read the returned file for details. Returns empty stats if no log data has been captured yet.

Input parameters:

- `device_id` (string, required): Device id from list-devices — the SAME id you passed to debugger-connect (iOS simulator UDID, Android serial, Vega serial, or Chromium device id). The logicalDeviceId debugger-connect returns also re…
- `port` (number): Metro server port (ignored for Chromium)

### `view-network-logs` (~223 tokens)

Retrieve captured network (HTTP) requests from the running app.
Returns a paginated list of requests with method, URL, status, resource type, size, and duration.
Each entry includes a requestId that can be passed to view-network-request-details for full details.
On React Native (iOS / Android / Vega) interception is injected into the JS runtime — it captures fetch() calls. On Chromium it reads the browser's native CDP Network domain (the active tab; all request types).
Use when inspecting outbound HTTP traffic or debugging API calls in the running app.
Fails if the app is not connected (RN) or the device is not reachable (Chromium).

Input parameters:

- `device_id` (string, required): Device id from list-devices (iOS simulator UDID or Android serial) — the same id used with debugger-connect.
- `pageIndex`: Page index (0-based) or "latest" for the most recent page. Each page contains up to 50 entries.
- `port` (number): Metro server port (RN only; ignored on Chromium)

### `view-network-request-details` (~184 tokens)

Get full details of a specific network request by its requestId (from view-network-logs).
Returns request/response headers (sensitive headers redacted), status, timing, and optionally the response body.
Large response bodies are truncated. Use when you need headers, body, or timing for a specific request after listing logs.
Returns an error message string if the requestId is not found — use view-network-logs to get valid requestId values.

Input parameters:

- `device_id` (string, required): Device id from list-devices (iOS simulator UDID or Android serial) — the same id used with debugger-connect.
- `includeBody` (boolean): Whether to include the response body (if captured). Defaults to true.
- `port` (number): Metro server port
- `requestId` (string, required): The requestId from view-network-logs to get full details for

### `describe` (~596 tokens)

Get the accessibility / DOM element tree for the current screen.
On iOS, uses the AXRuntime accessibility service to inspect whatever is currently visible — including
system dialogs, permission prompts, and any foreground app content. On Android, runs `uiautomator dump`.
On Chromium, walks the renderer's DOM via Chrome DevTools Protocol — every visible element with its ARIA
role, accessible name, and bounding rect (normalized to 0–1).
On Vega (Fire TV), reads the on-device automation toolkit (`getPageSource`); each element carries
\`[focused]`/`[selected]` so you can see where the D-pad cursor is, then move it with the `tv-remote` tool
(Vega is remote-driven, not touch). If describe returns an empty tree on Vega, relaunch the foreground
app (the toolkit attaches at launch) and try again.

When a system dialog is visible, describe returns the dialog's interactive elements (buttons, text)
with tap coordinates. When no dialog is present, it returns the foreground app's accessible elements.

Returns `{ description, source }` where `description` is a text rendering of the UI tree — one
line per element with its role, label/value/id, interactivity flags, and frame. Frame coordinates
are normalized [0,1] fractions of the screen / window width/height (not pixels) — the same space as
gesture-tap / gesture-swipe / gesture-pinch.

To tap an element use the centre of its frame: `tap_x = frame.x + frame.width / 2`,
\`tap_y = frame.y + frame.height / 2`. The same formula appears in the response header so it
can be applied to a line in isolation.

For app-scoped inspection with full UIKit properties (accessibilityIdentifier, viewClassName),
use native-describe-screen with an explicit bundleId instead (iOS only).
For React Native apps, debugger-component-tree returns React component names with tap coordinates.

On a TV target (Apple TV / Android TV — a `list-devices` entry with runtimeKind 'tv') this returns
the focus-driven view instead: the currently FOCUSED element and the list o…

Input parameters:

- `bundleId` (string): Optional app bundle ID. Used as a target hint on iOS when the AX-service returns no elements and the describe tool falls back to native-devtools inspection. If omitted, the fallback auto-detects the…
- `udid` (string, required): Target device id from `list-devices` (iOS UDID, Android serial, Vega serial, or Chromium id).

### `await-ui-element` (~685 tokens)

Block until a UI element reaches an expected state or a timeout elapses, so you don't have to poll screenshot/describe yourself.

Conditions:
  exists   — the selector matches an element anywhere in the tree.
  visible  — the selector matches an element with a non-zero on-screen frame.
  hidden   — the selector matches nothing, or only a zero-area element (e.g. a spinner that disappeared).
  text     — the first VISIBLE match in reading order (topmost, then leftmost; falling back to the first match
             overall if none is visible) contains expectedText (case-insensitive substring), or exactly matches
             it when textMatch is `equals`. A loose selector can match several elements; only that one is
             inspected, so if a different match is the one holding the text the wait still reports failure —
             narrow the selector to target it.

The selector is { text?, identifier?, role? }; every provided field must match. text and role match as
case-insensitive substrings of the element's label/value and role; identifier matches exactly (case-insensitive),
also accepting the unqualified Android resource-id name ('submit' matches 'com.example.app:id/submit').
It polls the same accessibility / DOM tree as `describe`
(iOS AXRuntime, Android uiautomator, Chromium CDP, Vega automation toolkit) every pollIntervalMs
(default 400ms) until timeoutMs (default 5000ms).

Returns { success: boolean, elapsed: number } — success=false means the condition never held before the
timeout (a `note` then explains what was seen). Use this after a tap/navigation to wait for the next screen,
or before tapping an element that appears asynchronously.

Input parameters:

- `bundleId` (string): Optional iOS app bundle id, passed to the describe fallback (see `describe`). Ignored on Android / Chromium.
- `condition` (string, required): What to wait for. `exists`: selector is anywhere in the tree. `visible`: selector is present with a non-zero on-screen frame. `hidden`: selector is absent or zero-area. `text`: the first visible matc…
- `expectedText` (string): For condition `text`: the string the first visible matched element (topmost in reading order; the first match overall if none is visible) must contain (default) or equal — see `textMatch`. Case-insen…
- `pollIntervalMs` (integer): How often to re-check the tree (default 400).
- `selector` (object, required): Element to match (text / identifier / role).
- `textMatch` (string): For condition `text`: how expectedText is compared. `contains` (default) is a case-insensitive substring; `equals` is a case-insensitive full-string match.
- `timeoutMs` (integer): Max time to wait for the condition before giving up (default 5000).
- `udid` (string, required): Target device id from `list-devices` (iOS UDID, Android serial, or Chromium id).

### `await-screen-idle` (~226 tokens)

Block until the screen has rendered content and stopped changing, or a timeout elapses.

Polls the same accessibility / DOM tree as `describe` every pollIntervalMs (default 200ms) until it
has content and that content holds identical for minStableMs (default 250ms), or timeoutMs (default
3000ms) is reached. Returns { settled, waitedMs, polls } — settled=false means the screen never went
still before the timeout. Use after a launch/navigation to wait for the UI to render before screenshotting or tapping.

Input parameters:

- `minStableMs` (integer): The screen must hold the same content for at least this long to count as settled (default 250).
- `pollIntervalMs` (integer): How often to re-read the tree (default 200).
- `timeoutMs` (integer): Max time to wait for the screen to settle before giving up (default 3000).
- `udid` (string, required): Target device id from `list-devices` (iOS UDID, Android serial, or Chromium id).

### `react-profiler-start` (~304 tokens)

Start CPU profiling + React commit capture on the connected Hermes runtime.
Delegates React commit capture to the in-app React DevTools backend (ri.startProfiling).
If another tool-server already owns the session, returns { already_running: true, owner, stale, how_to_reclaim } without clobbering their data. Pass { force: true } to reclaim a fresh owner's session, but BEFORE OVERTAKING - ask the user for approval first, see relevant skill for guidance.
Before calling this, ask the user if they also want native profiling (native-profiler-start) — recommend running both in parallel for a complete picture.
After starting, ask the user to perform the interaction to profile, then call react-profiler-stop.
Returns { started_at, startedAtEpochMs, hermes_version, detected_architecture } on success, or the already_running payload described above.
Fails if the Hermes runtime is not reachable or the Metro CDP connection cannot be established.

Input parameters:

- `device_id` (string, required): Device id from list-devices — the SAME id you passed to debugger-connect (iOS simulator UDID or Android serial).
- `force` (boolean): Take over an active profiling session even when it is owned by another tool-server and still fresh. Set to true only when you know the prior owner is gone.
- `port` (number): Metro server port
- `sample_interval_us` (integer): CPU sampling interval in microseconds (default 100)

### `react-profiler-stop` (~240 tokens)

Stop CPU profiling and collect the cpuProfile + React commit tree.
Reads commit data from the in-app React DevTools backend.
Stores results in the ReactProfilerSession for later use by react-profiler-analyze or react-profiler-cpu-summary.
Call react-profiler-start first, then exercise the app, then call this.
Returns { duration_ms, sample_count, fiber_renders_captured, total_react_commits, hot_commit_indices } summarizing the session.
When any commit had fibers whose display name could not be resolved at stop time (typically transient components like modals/tooltips/animations that unmounted before stop), the response also includes { unattributed_ms, unattributed_fiber_count, unattributed_commit_count } — these quantify how much work is not accounted for in the per-component breakdown (the per-commit duration itself remains correct).
Fails if no active profiling session exists or the CDP connection was lost during recording.

Input parameters:

- `device_id` (string, required): Device id from list-devices — the SAME id you passed to debugger-connect (iOS simulator UDID or Android serial).
- `port` (number): Metro server port

### `react-profiler-status` (~160 tokens)

Check the state of the React profiler session without side effects. Use after an interruption (debugger disconnect, unexpected error, agent pause) to decide whether to continue with react-profiler-stop, start a new session, or reconnect the debugger. Ownership is verified server-side against this tool-server's in-memory session — no token-threading is required. Returns { session_status, is_running, current_owner, … }. If this tool-server process restarted after react-profiler-start, status will report 'taken_over'; use react-profiler-start { force: true } to reclaim.

Input parameters:

- `device_id` (string, required): Device logicalDeviceId from debugger-connect (iOS simulator UDID or Android logicalDeviceId).
- `port` (number): Metro server port

### `react-profiler-analyze` (~388 tokens)

Analyze stored profiling data and return a markdown performance report.
Returns { report, reportFile, hotCommitsTotal, hotCommitsShown, sessionFiles }.
The report is structured around hot React commits (≥16ms absolute floor) with per-commit
render cascades, root cause identification, and a top components table.
Raw profiling data is saved to disk with a unique session timestamp for later reload via profiler-load.
After presenting the report, ask the user whether to investigate further (drill-down with
profiler-cpu-query / profiler-commit-query) or implement fixes and re-profile for comparison.
Requires react-profiler-stop to have been called first.
Optional annotations param: provide Array<{offsetMs, label}> to annotate commits with
the user action that preceded them. Compute offsetMs = tapTimestampMs - startedAtEpochMs
where tapTimestampMs is the timestampMs returned by the tap/swipe tool and startedAtEpochMs
is returned by react-profiler-start.
Use when the profiling session is complete and you need to interpret the collected data.
Fails if react-profiler-stop has not been called or no profiling data is stored.

Input parameters:

- `annotations` (array): Optional list of user actions with their time offset from profiling start. Compute offsetMs = tapTimestampMs - startedAtEpochMs, where tapTimestampMs comes from the tap/swipe tool return value and st…
- `device_id` (string, required): Device logicalDeviceId from debugger-connect (iOS simulator UDID or Android logicalDeviceId).
- `platform` (string): Target platform
- `port` (number): Metro server port
- `project_root` (string, required): Absolute path to the RN project root for session context detection
- `rn_version` (string): React Native version (e.g. "0.73.4")

### `react-profiler-component-source` (~191 tokens)

Find a React component's source via tree-sitter AST lookup: returns file path, line number, memoization status (isMemoized, hasUseCallback, hasUseMemo), and 50 lines of source for a named React component.
Call this per-finding after react-profiler-analyze to inspect source before proposing a fix.
Returns found: false if the component is not found in user-owned code (e.g. lives in node_modules).
When several files define a component with the same name (e.g. platform variants like List.tsx and List.web.tsx), returns the primary match and lists the rest under otherMatches[] (file/line/col) — check it before assuming the returned file is the one you meant.

Input parameters:

- `component_name` (string, required): Name of the React component to look up
- `project_root` (string, required): Absolute path to the RN project root

### `react-profiler-cpu-summary` (~225 tokens)

Return a raw Hermes CPU flamegraph summary (top hotspot functions by self-time).
FOR DEDICATED CPU INVESTIGATION ONLY — do NOT call this as part of a normal profiling session.
Use react-profiler-analyze instead; it covers all React rendering performance analysis.
Use when you specifically need to investigate JS CPU hotspots that are NOT tied to React rendering (e.g. regex slowness, cryptography, heavy computations).
Call react-profiler-stop first. Reads directly from the stored cpuProfile.
Returns a markdown table of the top hotspot functions with self-time, total-time, and location.
Fails if react-profiler-stop has not been called or no CPU profile is stored.

Input parameters:

- `device_id` (string, required): Device logicalDeviceId from debugger-connect (iOS simulator UDID or Android logicalDeviceId).
- `port` (number): Metro server port
- `react_only` (boolean): If true, only show React component functions (PascalCase names)
- `top_n` (integer): Number of top hotspot functions to return (default 20)

### `react-profiler-renders` (~142 tokens)

Scan the live React fiber tree to collect component render counts and durations.
Returns a markdown table of the top re-rendering components. No profiling session required — works on a live connected app.
Use when you want a quick snapshot of render counts without a full profiling session.
Fails if the React DevTools hook is not present in the runtime or the app is not connected.

Input parameters:

- `device_id` (string, required): Device logicalDeviceId from debugger-connect (iOS simulator UDID or Android logicalDeviceId).
- `port` (number): Metro server port
- `top_n` (integer): Number of top re-rendering components to return (default 20)

### `react-profiler-fiber-tree` (~161 tokens)

Inspect the React fiber tree and return a JSON representation of the component hierarchy.
Use when tracing ancestry of a library component or checking for useMemoCache hook (confirms React Compiler is active on a component).
Returns a nested JSON tree of fiber nodes with name, tag, actualDuration, selfBaseDuration, and children.
Fails if the React DevTools hook is not present or no fiber roots have been committed yet.

Input parameters:

- `device_id` (string, required): Device logicalDeviceId from debugger-connect (iOS simulator UDID or Android logicalDeviceId).
- `filter` (string): Regex string to filter component names
- `max_depth` (integer): Maximum tree depth to traverse (default 10)
- `port` (number): Metro server port

### `native-profiler-start` (~469 tokens)

Start native profiling on a booted device. iOS: Instruments via xctrace (CPU, hangs, memory). Android: Perfetto (CPU, jank, RSS-growth weak signal).
Auto-detects the running app process unless app_process is explicitly provided.
After starting, let the user interact with the app, then call native-profiler-stop.
Use when you want to capture native CPU, hang, and memory data for a running app.
Returns { status, pid, traceFile } confirming the recording has started.
Fails if no app is running on the device, or the profiler cannot attach to the process.

Input parameters:

- `app_process` (string): iOS: the CFBundleExecutable or display name of the app to profile. Android: the app's package name. If omitted, auto-detects the currently running foreground app. Only provide this if auto-detection…
- `device_id` (string, required): Target device id from `list-devices` (iOS UDID or Android serial).
- `malloc_stack_logging` (boolean): iOS-only. When true, cold-launches the app under the profiler with Malloc Stack Logging enabled so memory leaks carry an allocation backtrace (responsible frame + library). Without it, leaks are stil…
- `template_path` (string): iOS-only: path to an Instruments .tracetemplate file (defaults to bundled Argent template). Ignored on Android.

### `native-profiler-stop` (~170 tokens)

Stop native profiling and export trace data.
iOS: sends SIGINT to xctrace, waits for packaging, then exports CPU, hangs, and leaks XML.
Android: sends SIGTERM to the perfetto daemon, polls /proc/<pid>, then `adb pull`s the .pftrace.
Call native-profiler-start first.
Use when the user has finished the interaction to profile and you need to export the trace.
Returns { traceFile, exportedFiles, exportDiagnostics? }; traceFile is the raw trace bundle and exportedFiles the exports, all downloadable artifacts materialized to local paths.
Fails if no active native-profiler-start session exists for the given device_id.

Input parameters:

- `device_id` (string, required): Target device id from `list-devices` (iOS UDID or Android serial).

### `native-profiler-analyze` (~210 tokens)

Analyze exported native trace data and return an LLM-optimized markdown report.
iOS: parses CPU time profile, UI hangs, and memory leaks from the exported XML files.
Android: queries the Perfetto .pftrace via the in-process Perfetto trace-processor engine for CPU hotspots, UI hangs with jank reason + main-thread state breakdown, GC annotation, and an RSS-growth weak signal.
Returns a structured markdown report with severity indicators, tables, and actionable suggestions.
After presenting the report, ask the user whether to investigate further (drill-down with
profiler-stack-query for hang stacks, CPU context, leak details) or implement fixes and re-profile.
Call native-profiler-stop first to export the trace data.
Use when you need to interpret a completed native profiling recording.
Fails if native-profiler-stop has not been called first to export trace data.

Input parameters:

- `device_id` (string, required): Target device id from `list-devices` (iOS UDID or Android serial).

### `profiler-cpu-query` (~337 tokens)

Query Hermes CPU profile data with targeted modes for iterative investigation.
Requires react-profiler-stop (and ideally react-profiler-analyze) to have been called first.
Modes:
\- top_functions: Global CPU hotspots ranked by self-time. Optional time_window_ms to filter.
\- time_window: CPU breakdown for a specific time range (e.g. during a slow commit or hang).
\- call_tree: For a given function_name, show its callees and optionally callers.
\- component_cpu: For a given component_name, aggregate CPU activity across all its commits.
Use when investigating JS CPU hotspots or correlating CPU cost with specific components.
Returns a markdown table of CPU hotspots, call tree, or per-component CPU breakdown.
Fails if no CPU profile is stored — run react-profiler-stop first.

Input parameters:

- `component_name` (string): Component name for component_cpu mode
- `device_id` (string, required): Device logicalDeviceId from debugger-connect (iOS simulator UDID or Android logicalDeviceId).
- `function_name` (string): Function name for call_tree mode
- `include_callers` (boolean): For call_tree mode: also show callers of the function
- `mode` (string, required): Query mode: top_functions (global hotspots), time_window (CPU in a time range), call_tree (callers/callees of a function), component_cpu (CPU during a component's commits)
- `port` (number): Metro server port
- `time_window_ms` (object): Time window filter for time_window mode (ms, performance.now clock)
- `top_n` (integer): Number of results to return (default 15)

### `profiler-commit-query` (~295 tokens)

Query React commit data for iterative investigation of render performance.
Requires react-profiler-stop to have been called first.
Modes:
\- by_component: All commits where a specific component rendered, with causes and durations.
\- by_time_range: What happened in a specific time window.
\- by_index: Full detail dump of a single commit (all components, props changed, parent cascade).
\- cascade_tree: Parent-child cascade tree for a commit showing who triggered whom.
Use when drilling into specific components or time windows after react-profiler-analyze.
Returns a markdown table or tree of commit data matching the requested mode.
Fails if react-profiler-stop has not been called or no commit data is stored.

Input parameters:

- `commit_index` (integer): Commit index for by_index and cascade_tree modes
- `component_name` (string): Component name for by_component mode
- `device_id` (string, required): Device logicalDeviceId from debugger-connect (iOS simulator UDID or Android logicalDeviceId).
- `mode` (string, required): Query mode: by_component (commits for a component), by_time_range (commits in a window), by_index (full detail for one commit), cascade_tree (parent-child cascade for a commit)
- `port` (number): Metro server port
- `time_range_ms` (object): Time range filter for by_time_range mode
- `top_n` (integer): Max results to return (default 20)

### `profiler-stack-query` (~375 tokens)

Query native profiler trace data for iterative investigation of native performance.
Requires native-profiler-stop → native-profiler-analyze to have been called first.
Modes:
\- hang_stacks: Full CPU context during a specific hang (by hang_index).
\- function_callers: Who calls a specific native function and what it calls.
\- thread_breakdown: CPU time split by thread, optionally filtered.
\- leak_stacks: Memory leak details (iOS only), optionally filtered by object_type.
Use when drilling into native hang stacks, thread CPU breakdown, or memory leaks after native-profiler-analyze.
Returns a markdown report with native call stacks, thread weights, or leak details for the selected mode.
Fails if native-profiler-analyze has not been run or no parsed trace data is in memory.

Input parameters:

- `device_id` (string, required): iOS Simulator UDID or Android serial.
- `function_name` (string): Function name for function_callers mode
- `hang_index` (integer): 0-based index into the hang list for hang_stacks mode
- `mode` (string, required): Query mode: hang_stacks (full CPU context during a hang), function_callers (who calls a native function), thread_breakdown (CPU split by thread), leak_stacks (leak details by object type)
- `object_type` (string): Object type filter for leak_stacks mode
- `thread` (string): Thread filter. thread_breakdown: case-insensitive substring match. function_callers: exact raw thread name (e.g. ".blueskyweb.app"), or "main" for the UI thread; omit to search ALL threads (each resu…
- `top_n` (integer): Max results to return (default 15)

### `profiler-combined-report` (~137 tokens)

Generate a cross-correlated report combining React Profiler and native profiler data.
Maps native hangs to React commits using wall-clock time alignment.
Requires both react-profiler-analyze and native-profiler-analyze to have been called first.
Call this tool when both profilers were run in parallel on the same session.
Returns a markdown report correlating hangs with React commits, memory leaks, and investigation hints.
Fails if either react-profiler-analyze or native-profiler-analyze has not been called first.

Input parameters:

- `device_id` (string, required): iOS Simulator/device UDID or Android serial
- `port` (number): Metro server port

### `profiler-load` (~397 tokens)

Fetch and restore a previously captured profiling session from disk into memory so query tools can operate on it.
This is the disk-restore counterpart to react-profiler-stop/native-profiler-stop, which write data, and to the query tools (profiler-cpu-query, profiler-commit-query, profiler-stack-query), which read it.
Use when you need to revisit past session data without capturing a new recording.
Modes:
\- list: Show all available profiling sessions in the project's debug directory.
\- load_react: Load a React profiler session (CPU profile + commit tree) into memory. Requires session_id.
\- load_native: Re-parse native profiler XML files into memory. Requires session_id and device_id.
  For Android .pftrace restores, pass app_process for older sessions that do not have a metadata sidecar.
Returns a summary of the loaded session or a session list for the list mode.
Fails if the session_id is not found or required XML files are missing from disk.

Input parameters:

- `app_process` (string): Android package name to use when restoring older load_native .pftrace sessions that do not have a metadata sidecar.
- `device_id` (string, required): Target device id from `list-devices`. Used to cache the loaded React session under the correct port+device key, and required to resolve the native profiler session for load_native.
- `mode` (string, required): list: show available sessions on disk. load_react: load a React profiler session into memory for query tools. load_native: re-parse native profiler XML files (xctrace on iOS) into memory for query to…
- `port` (number): Metro port — the loaded React data is cached under this port for query tools (default 8081)
- `session_id` (string): Timestamp-based session identifier (e.g. '20250313-143022') from the list output. Required for load_react and load_native modes.

### `gather-workspace-data` (~314 tokens)

Fetch a structured snapshot of a mobile app project's workspace.

Returns package.json contents, metro/babel config text, app.json, eas.json, tsconfig,
platform directory presence (ios/, android/), presence of android/gradlew (android_has_gradle),
iOS .xcworkspace name and Podfile presence, lockfile type, .env file keys (no values),
installed CLI tool versions, scripts/ directory listing, husky hooks, CI config type,
Makefile targets, lint-staged config, and a list of detected config files.

DO NOT RUN THIS TOOL IF YOU ARE THE MAIN AGENT AND THIS TASK CAN BE DELEGATED TO A SUBAGENT.

If you are a subagent tasked with exploring the project environment, run this as the first step. The snapshot
provides the raw data needed to determine the project type (React Native, Expo,
Flutter, native iOS/Android, or other), build commands, startup scripts, platform
support, package manager, and QA tooling. Follow up with Read/Glob/Grep for deeper
exploration of anything the snapshot surfaces.
Use when you need to inspect project configuration without manually reading multiple files.
Returns partial data if workspacePath does not exist or is not readable; missing items are represented as null or empty collections.
Fails if the workspacePath is not an absolute path or the directory cannot be accessed.

Input parameters:

- `workspacePath` (string, required): Absolute path to the project root directory to inspect (e.g. /Users/dev/MyApp)

### `native-devtools-status` (~485 tokens)

Check whether native devtools are connected to a specific app and whether the next launch is prepared for injection.
Use when you need to verify native devtools readiness before calling native-full-hierarchy, native-describe-screen, or native-network-logs.

Returns { envSetup, appRunning, connected, requiresRestart, nextLaunchWillBeInjected, injectable }:
\- envSetup: DYLD_INSERT_LIBRARIES is configured in the simulator's launchd environment
\- appRunning: the target bundle currently has a running UIKit process on the simulator
\- connected: the dylib is active in the current running process for this bundleId
\- requiresRestart: the app is already running but its current process does not have native devtools injected (always false for a non-injectable app)
\- nextLaunchWillBeInjected: if you launch this bundle now, native devtools env setup is already in place (always false for a non-injectable app)
\- injectable: whether native devtools can ever be injected into this app. Apple system apps (bundle ids under com.apple.) are platform binaries with library validation, so the dylib can never load into them.

Call this before using app-scoped native hierarchy tools or native-network-logs.
If injectable is false: this is a TERMINAL state — the app can never be injected. Do NOT restart/retry. Use the standard `describe` tool (its accessibility path reads the screen without injection) or `screenshot` (then interact by coordinate). Do not fall back to the native-devtools feature tools (native-describe-screen, native-find-views, native-full-hierarchy, native-network-logs, native-view-at-point, native-user-interactable-view-at-point) — they run the same injection precheck and fail with the same non-injectable error.
If appRunning is false and nextLaunchWillBeInjected is true: use launch-app normally.
If requiresRestart is true: call restart-app, then proceed with the native feature.
Returns { status: "init_failed", message, attempts } instead when the simulator's native-devtools e…

Input parameters:

- `bundleId` (string, required): Bundle ID of the app to check (e.g. com.example.MyApp)
- `udid` (string, required): Simulator UDID

### `native-network-logs` (~202 tokens)

Retrieve network requests captured at the native NSURLProtocol level. 
Unlike the JS-level network inspector (view-network-logs), this captures ALL network traffic from the app including native modules, Swift/Objective-C networking, and background transfers that bypass JS fetch. 
Use when you need to inspect native-level HTTP traffic that is invisible to JS fetch interception. 
Returns { status, count, events } where each event contains URL, method, status code, headers, and timing. Returns { status: "restart_required" } if the dylib is not injected - if this happens, call "restart-app" then retry. 
Fails if native devtools are not connected or the app is not running.

Input parameters:

- `bundleId` (string, required): Bundle ID of the app
- `clear` (boolean): Clear the log after reading
- `limit` (number): Maximum number of events to return (most recent first)
- `udid` (string, required): Simulator UDID

### `native-find-views` (~339 tokens)

Search for specific UIViews in the running app by class name, accessibility identifier, label, tag, or React Native nativeID.
Use when you need to locate a specific view by its properties without dumping the entire hierarchy.
Returns { status: "ok", matches } with matching views including their frames, properties, optional ancestors, and optional children. Much more targeted than native-full-hierarchy.
At least one of className, identifier, label, tag, or nativeID must be provided.
Fails if native devtools are not connected, the app is not running, or status is restart_required (call restart-app then retry).

Input parameters:

- `bundleId` (string, required): Bundle ID of the app
- `className` (string): UIView class name to match (exact, e.g. UIButton)
- `fields` (array): View fields to include. Defaults: className, frame, hidden, alpha, identifier, label, nativeID, userInteractionEnabled, depth. Additional: pointer, tag, windowFrame, bounds, center, opaque, clipsToBo…
- `identifier` (string): Accessibility identifier to match (exact)
- `includeAncestors` (boolean): Include ancestor chain for each matched view (default true)
- `includeChildren` (boolean): Include child views for each matched view (default true)
- `label` (string): Accessibility label to match (exact)
- `nativeID` (string): React Native nativeID prop to match (exact)
- `tag` (integer): UIView tag integer to match
- `udid` (string, required): Simulator UDID

### `native-full-hierarchy` (~384 tokens)

Get the complete UIKit view tree for the running app.
WARNING: Output can be extremely large (100KB–500KB+) for complex apps, especially those built with SwiftUI. Prefer native-find-views for targeted queries.
Use skipClasses / skipClassPrefixes to prune SwiftUI internal subtrees and reduce output size. Use the fields param to request only the properties you need.
Use when you need deep layout debugging, finding views with no accessibility labels, or verifying view structure not exposed through the accessibility tree.
Returns { status: "ok", windows } with the full view hierarchy, or { status: "restart_required" } if the dylib is not injected.
Fails if native devtools are not connected or the app is not running.

Input parameters:

- `bundleId` (string, required): Bundle ID of the app
- `fields` (array): View fields to include. Use EXACT names: className, frame, hidden, alpha, identifier, label, nativeID, userInteractionEnabled, depth, pointer, tag, windowFrame, bounds, center, opaque, clipsToBounds,…
- `maxDepth` (integer): Maximum recursion depth (default 8). Increase for deeper inspection, decrease to reduce output size.
- `skipClassPrefixes` (array): Class name prefixes to prune entire subtrees. For SwiftUI apps use ["_TtGC7SwiftUI"] to drop mangled SwiftUI generic type subtrees while keeping _UIHostingView and UIKit bridges. Avoid broad prefixes…
- `skipClasses` (array): Exact UIView class names whose entire subtree should be pruned (e.g. ["UIImageView"] to drop image leaf nodes)
- `udid` (string, required): Simulator UDID

### `native-describe-screen` (~239 tokens)

Read the running app's native accessibility screen description via injected native devtools.

Returns a flat list of accessibility leaf elements with:
\- raw native point-space frame and tapPoint
\- normalizedFrame and normalizedTapPoint relative to the app's main screen bounds
\- top-level screenFrame metadata
\- traits and optional labels/identifiers

This is a low-level native inspection tool. The normalized fields are intended to help
with backend migration work, but the public describe contract is still separate.

Useful for evaluating or debugging the lower-level native data that powers the public describe tool.

If status is restart_required: call restart-app then retry.

Input parameters:

- `bundleId` (string, required): Bundle ID of the app
- `skipClassPrefixes` (array): Class name prefixes to prune entire subtrees. For SwiftUI apps use ["_TtGC7SwiftUI"] to drop mangled SwiftUI generic type subtrees while keeping UIKit bridges.
- `skipClasses` (array): Exact UIView class names whose entire subtree should be pruned (e.g. ["UIImageView"] to drop image-heavy branches)
- `udid` (string, required): Simulator UDID

### `native-view-at-point` (~335 tokens)

Inspect the deepest visible UIView at a raw native window point.

Unlike native-user-interactable-view-at-point, this ignores userInteractionEnabled,
so it answers "what is visually here?" rather than "what would receive the touch?".

IMPORTANT: x and y are raw iOS window coordinates in points, NOT normalized [0,1]
simulator tap coordinates.

If status is restart_required: call restart-app then retry.

Input parameters:

- `bundleId` (string, required): Bundle ID of the app
- `fields` (array): View fields to include. Defaults: pointer, className, tag, frame, windowFrame, bounds, hidden, alpha, opaque, clipsToBounds, userInteractionEnabled, depth, identifier, label, layerName, nativeID. Add…
- `includeAncestors` (boolean): Include ancestor chain for the matched view (default true)
- `includeChildren` (boolean): Include child views for the matched view (default false)
- `maxDepth` (integer): Maximum depth for returned child/ancestor serialization (default 150)
- `skipClassPrefixes` (array): Class name prefixes to prune entire subtrees
- `skipClasses` (array): Exact UIView class names whose entire subtree should be pruned
- `udid` (string, required): Simulator UDID
- `x` (number, required): Raw X coordinate in the app window's native point space. NOT normalized [0,1] tap space.
- `y` (number, required): Raw Y coordinate in the app window's native point space. NOT normalized [0,1] tap space.

### `native-user-interactable-view-at-point` (~330 tokens)

Inspect the deepest UIView at a raw native window point that would actually receive touch input.

Unlike native-view-at-point, this respects userInteractionEnabled and is closer to
UIKit hit-testing semantics.

IMPORTANT: x and y are raw iOS window coordinates in points, NOT normalized [0,1]
simulator tap coordinates.

If status is restart_required: call restart-app then retry.

Input parameters:

- `bundleId` (string, required): Bundle ID of the app
- `fields` (array): View fields to include. Defaults: pointer, className, tag, frame, windowFrame, bounds, hidden, alpha, opaque, clipsToBounds, userInteractionEnabled, depth, identifier, label, layerName, nativeID. Add…
- `includeAncestors` (boolean): Include ancestor chain for the matched view (default true)
- `includeChildren` (boolean): Include child views for the matched view (default false)
- `maxDepth` (integer): Maximum depth for returned child/ancestor serialization (default 150)
- `skipClassPrefixes` (array): Class name prefixes to prune entire subtrees
- `skipClasses` (array): Exact UIView class names whose entire subtree should be pruned
- `udid` (string, required): Simulator UDID
- `x` (number, required): Raw X coordinate in the app window's native point space. NOT normalized [0,1] tap space.
- `y` (number, required): Raw Y coordinate in the app window's native point space. NOT normalized [0,1] tap space.

### `stop-simulator-server` (~105 tokens)

Stop the transport session for a specific device (iOS / Android: simulator-server process; Chromium: CDP WebSocket) and free its resources. Use when you are done interacting with one device but want to keep others running. Returns { stopped, udid }. Fails silently if no session is open for the given id.

Input parameters:

- `udid` (string, required): Target device id (iOS UDID, Android serial, or Chromium id) whose transport session to stop

### `stop-all-simulator-servers` (~77 tokens)

Stop all running simulator-server processes (iOS + Android), native devtools services, and Chromium CDP sessions, freeing their resources. Call this when your session ends or the user says they are done. Returns { stopped } — an array of URNs that were shut down. Fails silently if no servers are running.

### `stop-metro` (~111 tokens)

Stop the Metro bundler process listening on a given port (default 8081). Use when ending a React Native session or when Metro must be restarted. Returns { stopped, port, pids }; stopped=false if no process is found on the port. Fails if the port lookup command times out or the process cannot be killed. This is DESTRUCTIVE — always ask the user for confirmation before calling this tool.

Input parameters:

- `port` (integer): TCP port Metro is listening on (default 8081)

### `flow-start-recording` (~346 tokens)

Start recording a new flow. Creates a .yaml file in the .argent/flows/ directory.
Use when you want to capture a reusable sequence of device interactions for later replay.
Returns { message, flowFile, savedTo } and optionally { previousFlow } if a prior recording was abandoned.
Fails if the .argent/flows/ directory cannot be created or the flow file cannot be written.

After starting, use flow-add-step to append tool calls — each step is executed
LIVE so you can verify it works before it gets recorded. For a self-contained
e2e flow, record a restart-app of the app under test as the FIRST step (captured
as the flow's `launch` step); for a reusable fragment, skip that and pass
executionPrerequisite instead. Use flow-add-echo to add labels. Call
flow-finish-recording when done.

If a recorded step turns out to be wrong, you can edit the .yaml file directly
to remove or reorder steps.

Input parameters:

- `executionPrerequisite` (string): Fragments only: the app/device state assumed on entry (e.g. "Settings app open on General page"). For a self-contained e2e flow, omit this and record a `restart-app` as the first step instead — it is…
- `name` (string, required): Name for this flow (e.g. "settings-explore")
- `project_root` (string, required): Absolute path to the project root directory (the directory that contains or should contain `.argent/flows/`). The flow file is created at `<project_root>/.argent/flows/<name>.yaml`.

### `flow-add-step` (~243 tokens)

Execute a tool call and record it as a step in the active flow. Use when recording a flow with flow-start-recording and you want to run and capture each action. A coordinate `gesture-tap` is recorded as a portable `tap: { selector }` step when the tapped element has stable text/identifier (otherwise coordinates are kept with a warning); a `restart-app` is recorded as a `launch` step (record one FIRST to make the flow a self-contained e2e flow). Returns { message, toolResult, flowFile } on success. If it fails an error is returned and nothing is recorded.
If a step was recorded by mistake, edit the .yaml file directly to remove it.

Input parameters:

- `args` (string): Tool arguments as a JSON string, e.g. '{"udid": "ABC", "x": 0.5, "y": 0.3}'. Omit for tools with no arguments.
- `command` (string, required): MCP tool name (e.g. "tap", "screenshot", "launch-app")
- `delayMs` (integer): Milliseconds to sleep before executing this step during replay.

### `flow-add-echo` (~90 tokens)

Record an echo step in the active flow. Echo steps print a message when the flow is replayed — useful as labels between tool calls.
Use when you want to annotate a recorded flow with a human-readable label or checkpoint message.
Returns { message, flowFile }. Fails if no active flow recording is in progress.

Input parameters:

- `message` (string, required): Message to echo when the flow is replayed

### `flow-finish-recording` (~73 tokens)

Finish recording the active flow. Returns a summary of all recorded steps and the final YAML content. Use when you have added all desired steps and want to finalize the flow file. Fails if no active flow recording is in progress.
You can still edit the .yaml file directly afterwards to remove or reorder steps.

### `flow-read-prerequisite` (~337 tokens)

Read the execution prerequisite of a flow without running it — a saved flow from the .argent/flows/ directory, or an explicit boundary-managed flow_path.
Returns the prerequisite description so you can verify the required state is met before calling flow-execute.
Use when you need to check what app/simulator state is required before executing a flow; pass the same flow
source (name or flow_path) you will pass to flow-execute, so the prerequisite you read is the contract of
the flow that will actually run.
Fails if the flow file does not exist.

Input parameters:

- `flow_file` (string): Path to the flow .yaml as readable by the tool-server. Internal — the argent client derives it from project_root and name automatically; leave unset.
- `flow_path` (string): Absolute path to a co-located flow .yaml on the client and tool server's shared filesystem. This must be supplied through the file-input boundary. Pass the same flow source here as to flow-execute, s…
- `name` (string): Name of a saved flow to inspect from `.argent/flows` (e.g. "settings-explore"). Omit when flow_path is set.
- `project_root` (string, required): Absolute path to the calling agent's project root — the cwd it is working in. With name, the saved flow is read from `.argent/flows/<name>.yaml` under this root; with flow_path, the prerequisite is r…

### `flow-execute` (~1113 tokens)

Run a saved flow from the .argent/flows/ directory, or an explicit boundary-managed flow_path.
Steps run in order: `launch` starts an app from scratch (terminate + relaunch) and waits until it is
ready; `tool` calls dispatch through the registry; `tap`/`long-press`/`type` resolve a selector to an
element and act on it (`tap: { on, times: 2 }` double-taps; `long-press: { on, duration }` presses and
holds; `tap`/`long-press` alternatively take a raw normalized point — bare `{ x, y }` or `on: { x, y }`;
any selector may scope its matches geometrically, the CSS combinators read off frames: `within: <selector>`
(descendant — inside that container's frame), `after: <selector>` (CSS `~` — following it in reading
order), `next: <selector>` (CSS `+` — the nearest such follower, which unlike CSS reaches past a
non-matching neighbour rather than failing), plus `any: true` (CSS `*` — legal only WITH a scope and
never beside text/id/role). Scopes nest to disambiguate — `within: { id: card, within: { id: list } }`
reads "inside card inside list", each container's frame inside the next);
\`scroll-to` scrolls (momentum-free) until a target is visible; `pinch` zooms
(`pinch: { on?, scale }` — scale > 1 in, < 1 out; screen center when `on` is omitted); `rotate` is the
two-finger rotation gesture (`rotate: { on?, by }` — degrees, + clockwise, within ±3000°; screen center
when `on` is omitted; distinct from the `rotate` tool, which changes device orientation); `await` waits
for a UI condition; `wait` pauses for a fixed number of milliseconds; `assert` checks one now; `snapshot`
diffs a screenshot — or, with `cropOn: <selector>`, one element's cropped region — against a stored
baseline (a missing baseline fails the step — set updateBaselines to adopt the current screen; a
cropped element whose size drifted fails on dimensions); `echo` annotates; `run` executes another flow
inline — a YAML path resolved against the directory of the flow file that references it (co-located
runs only).
A `…

Input parameters:

- `device` (string): Device id to run against (iOS UDID, Android/Vega serial, Chromium id). Auto-detected when omitted.
- `flow_file` (string): Path to the flow .yaml as readable by the tool-server. Internal — the argent client derives it from project_root and name automatically; leave unset.
- `flow_path` (string): Absolute path to a co-located flow .yaml on the client and tool server's shared filesystem. This must be supplied through the file-input boundary. For remote execution, pass name + project_root inste…
- `name` (string): Name of a saved flow to run from `.argent/flows` (e.g. "settings-explore"). Omit when flow_path is set.
- `platform` (string): Restrict auto-detection to this platform when several devices are booted.
- `prerequisiteAcknowledged` (boolean): Set to true to confirm the execution prerequisite has been met. Required (LLM path) when a fragment defines an executionPrerequisite.
- `project_root` (string, required): Absolute path to the calling agent's project root — the cwd it is working in. With name, the saved flow is read from `.argent/flows/<name>.yaml` under this root; with flow_path, the flow, its run: si…
- `updateBaselines` (boolean): Write/refresh screenshot baselines for `snapshot` steps instead of diffing against them.

### `update-argent` (~175 tokens)

Apply a pending Argent update. Only call this tool when the user has explicitly consented to updating Argent in this conversation. Use when an update notification indicates a new version is available and the user agrees to update. By default updates the install serving this session; pass `target` to choose global/local/both. Returns { message } with the update status and version info. The tool server will restart automatically after the update. Fails if no update is available or an update is already in progress.

Input parameters:

- `target` (string): Which install to update. 'auto' (default) updates the install serving this session — the global PATH install or this project's local devDependency, whichever this server runs from. Pass 'global' / 'l…

### `dismiss-update` (~72 tokens)

Clear the Argent update notification for the given number of hours. Use when the user asks to postpone or silence update reminders. Returns { message } confirming the suppression duration. Fails if the hours value is negative or the suppression state cannot be persisted.

Input parameters:

- `hours` (number, required): Number of hours to suppress the update notification

## Diagnostics

Captured diagnostic sections: Provenance, Dependencies. The full working is on the page: https://verifymcp.io/servers/software-mansion-argent/swmansion-argent#diagnostics

## Score history

- 2026-08-13: 77
- 2026-08-12: 77
- 2026-08-11: 77
- 2026-08-10: 75

## Links

- npm package: https://www.npmjs.com/package/@swmansion/argent
- Socket report: https://socket.dev/npm/package/@swmansion/argent
- Repository: https://github.com/software-mansion/argent
- Website: https://argent.swmansion.com/
- Changelog RSS feed: https://verifymcp.io/servers/software-mansion-argent/swmansion-argent.xml
- Changelog JSON feed: https://verifymcp.io/servers/software-mansion-argent/swmansion-argent.json
- HTML version of this page: https://verifymcp.io/servers/software-mansion-argent/swmansion-argent
