Repository avatar
Developer Tools
v1.0.0
active

appdyno

io.github.fatihcimen/appdyno

Test, drive and measure iOS and Android apps on simulators, emulators and real phones, from a Mac.

Documentation

appdyno

Let an AI agent drive, test and measure your mobile app.
An MCP server, a command line and a Mac app for iOS and Android,
on simulators, emulators and real phones.

English · Türkçe

npm CI License: MIT

A real run: a terminal of MCP tool calls drives an iPhone simulator in the appdyno hub, which turns the phone to dark mode and back and scrolls Settings, while the Performance pane reads frames, CPU and memory

A real run, not a mock-up: an MCP client makes the calls in the terminal, with their real times, while the hub shows the phone and reads it.

An agent reads the screen as elements, taps, types and swipes. It sends a push, opens a deep link, and measures how the app starts, scrolls and holds its memory. Every action answers with the screen once it has settled, so the agent sees what it did in the same call. You can watch and touch the same device in a live window. It all runs on your Mac, with no cloud and no telemetry.

Works with Claude Code, Claude Desktop, Codex, ChatGPT, Cursor, VS Code, Devin Desktop (formerly Windsurf), Antigravity, Gemini CLI, Zed, opencode, Cline and any other MCP client.

Quick start

On a Mac with Node 22 or newer:

npx -y appdyno install     # adds appdyno to the MCP clients on this Mac, asking which

Restart the client, start a simulator or an emulator or connect a phone, and ask your agent:

Open my app, go through checkout, and tell me what breaks.

Each step answers with what the device showed. When a tap takes the app down, that same answer says so, with the crash and the app's last log lines. A real one, from this repository's test app on an iPhone simulator:

APP_NOT_RUNNING: tapped @9p29 "Crash"; the app died: EXC_BREAKPOINT SIGTRAP Trace/BPT trap: 5 (~/Library/Logs/DiagnosticReports/Fixture-….ips)
its last log lines:
2026-11-12 22:46:57.485 Df Fixture[80069:c58417] (libswiftCore.dylib) Fixture/ListScreen.swift:20: Fatal error: fixture crash
  launch_app starts it again, and crashes says why it died

Ask it as well to send your app a push, to open each link your app opens, or to measure its cold launch and keep it as a baseline. npx -y appdyno doctor says what this Mac is missing, and Install covers a phone, the Mac app and any other client.

One call, one answer

An AI agent asks to tap Dark. appdyno finds the element on a fresh read and taps it on the device, waits until the screen is still for 200 ms, at most 3 s, and answers with the settled screen and what changed

There is no second call to look, and no fixed sleep: an answer says settled when the screen came to rest, and changed when it differs from before. A refusal has a code and a hint for the next step.

What an agent can do

what an agent can doits tools
See and act. Read the screen as elements and act on one by text, id or ref: tap, type, swipe, scroll to it, long press, drag, pinch, press a key. Up to 30 steps in one call.screen find tap type_text swipe gesture steps wait_for
Push notifications. Send a push of each type the device takes, from a title and a body or from the platform's own JSON.push
Links. Open a deep link, a universal link or an App Link. List the links an installed app opens.open_link
Conditions. Dark mode, text size, language, a slow network, airplane mode and biometrics, each put back when the session ends. A location, and the way the screen is turned.device_setting set_location set_orientation
Measure. Launch time, frames, memory, CPU, network bytes and energy, read from the platform's own tools and kept with their raw output. A gate fails a change that made the app slower.perf_launch perf_scroll perf_snapshot perf_compare and more
Evidence. Video, a report of each step, logs, crashes, network traffic, accessibility audits and visual diffs.record report logs crashes network audit visual
Flows. What an agent did once runs again with no model, on another device, and in CI.flow
A person, too. A live window you can touch while the agent drives. When only a person can go on, such as at Face ID, the agent asks one and waits until they say done.mirror hand_off

All 48 tools are in Tools. Each is also a command, and a call in the library.

Push notifications and links

A real run: an MCP client lists the links a test app opens on an iPhone simulator in the hub, opens its deep link, sends it a push its own code shows, then another one onto the home screen, with each call's real time and first line of answer below

A real run on a simulator, through MCP, with the times it took on a busy Mac.

push sends a push by type, and answers with the screen after it. Asked with no type, it says which types this device takes, how each one is delivered, and the payload it would send.

  • On a simulator an alert goes by simctl and reaches the app's own code, from fields or from APNs' own payload with a badge, a sound and a category.
  • On Android a push always goes to the app itself. A debuggable build that includes Firebase Messaging takes alert, rich, background and Live Activity pushes as real FCM messages, handed straight to its Firebase SDK, with no Firebase project or key. tapped opens the app as a tapped notification does, with the push's data in the intent that launches it. Any other build takes tapped only, and the answer says what the rest need.
  • With APNs set up by a person, with appdyno push setup apns, a simulator also takes rich, background, Live Activity, VoIP and widget pushes by APNs' sandbox, and a real iPhone on its cable takes those and an alert. Behind strict controls: the sandbox by default, a token only from the app's own log or typed by a person in a window's Push pane, 4 KB at most (5 for VoIP), 10 pushes a minute, and no key or token in any answer or log.
  • With FCM set up, with appdyno push setup fcm, an Android device takes alert, rich, background and Live Activity pushes by FCM, behind the same kind of controls.

Until APNs is set up, a real iPhone takes no push, and the answer says so. FCM's own message was run on an API 34 emulator, and its data pushes on a Samsung phone too. The requests to Apple and Google are tested against recorded ones, and the sending to Apple against a local stand-in server: no push has yet gone through Apple's or Google's own servers in testing.

open_link opens a deep link, a universal link or an App Link and answers with the screen it led to. Given an app's id, it lists the links that app opens: on a simulator its URL schemes and the domains its signature names, on Android each filter as scheme://host/path and each App Link domain with the state Android verified it in.

Push notifications has every type on every device, and what was run on what.

The live window and the hub

The hub in its dark look: an iPhone simulator live in Settings while an AI client scrolls it, and the Performance pane reading the picture at 84 frames a second, CPU and memory

appdyno hub opens every device on this Mac in one window, driven by mouse and keyboard while an agent drives it too. Its Performance pane reads frames, CPU and memory as they happen. AI Clients shows each MCP client on this Mac as it truly stands, and adds appdyno to it. A phone joins by cable, or by Wi-Fi once paired with appdyno pair. See The window and the hub.

A simulator's own window in its light look, with a test app's push on the home screen A simulator's own window in its dark look, with its Push pane open: the app's id, the type Alert and what it sends, and a title, a body and data filled in

A device's own window, light and dark. Its Push pane sends by push, as an agent does, with the types this device takes.

A person approves what loses data

An AI agent asks to clear an app's data. appdyno stops, as this loses data, and asks the person in the client's own prompt, naming the app, the device and what is lost. The person says yes, and only then does appdyno clear the data and answer the agent. On a no, or no answer, nothing is done

Nothing the agent sends counts as a yes, and no tool erases or resets a device. What asks the person first lists each action, and how a person allows them ahead of time.

On each device

What works on each device: an iOS simulator, an iPhone, an Android emulator and an Android phone, row by row, from reading the screen to biometrics and the network
devicewhat it needs
iOS simulatorXcode. No account and no signing
iPhoneXcode, Developer Mode, and a development signing identity with a profile for it
Android emulatoradb, from Android Studio or Google's platform tools
Android phoneadb, and USB debugging. Wi-Fi once paired

What works where has every measure on every device, and how each was proved. An iPhone's live picture is experimental, and rests on undocumented Apple services that may change.

Install

1. What you need

foryou need
appdynoa Mac, and Node 22 or newer. The Mac app carries its own Node; from npm, the hub's window needs Xcode or its Command Line Tools
an iOS simulator or an iPhoneXcode, and iOS 17 or newer
an Android emulator or phoneadb, from Android Studio or Google's platform tools, and Android 8.0 (API 26) or newer

2. Get appdyno

npm install -g appdyno     # installs the appdyno command

Or take the Mac app, which needs no terminal: download Appdyno-<version>-arm64.dmg from the releases and drag it to Applications. It runs on Apple silicon with macOS 13.5 or newer, and Add to MCP Clients in its menu bar does step 3. See The Mac app. To build appdyno from a checkout instead, see CONTRIBUTING.md.

3. Add it to your MCP client

appdyno install --dry-run  # shows the change for each client it finds, and writes nothing
appdyno install            # asks which clients; --yes takes all of them

It changes only appdyno's own entry, keeps a backup beside the file, and appdyno install --remove takes it out. Restart the client afterwards. For any other client, add this to its server list:

{
  "mcpServers": {
    "appdyno": { "command": "npx", "args": ["-y", "appdyno", "mcp"] }
  }
}

A client opened from the Dock may not find npx: if it says spawn npx ENOENT, see An app cannot find npx or adb.

4. Get a device ready

  • iOS simulator: nothing more. Start one in Xcode's Simulator, or ask your agent to. appdyno ios build builds appdyno's helpers for a simulator and an iPhone ahead of time, which takes a few minutes; otherwise the first action builds the one it needs. It also deletes builds of older versions, except one that a running process still uses.
  • Android emulator: create one in Android Studio's Device Manager and start it.
  • Android phone: turn on Developer options (Settings, About phone, tap Build number seven times), then USB debugging in them. Connect the cable and answer Allow on the phone. For Wi-Fi, pair once under Wireless debugging, Pair device with pairing code: appdyno pair <address> <code>.
  • iPhone: connect the cable and answer Trust, turn on Developer Mode, and give it a development signing identity with a profile: An iPhone or a simulator takes you through it. For Wi-Fi, pair once on the cable: appdyno pair --device <udid>.

5. Check

appdyno doctor             # what is here, what is missing, and how to fix it
appdyno devices            # what is connected
appdyno screen             # what is on the screen
appdyno hub                # every device in one window you can touch
appdyno perf survey --app com.example.app   # every measure on this device, then a page of them

Connecting a client takes each client step by step, and gets a phone ready from nothing, signing included.

Speed

Medians of 5 runs, through MCP over stdio, on the fixture app, on an M5 Pro, on 2026-11-10 (the phones on 2026-11-09). The iPhone is a 13 Pro Max, and the Android phone a Samsung SM-A525F:

calliOS simulatorAndroid emulatoriPhoneAndroid phone
read the screen18 ms11 ms308 ms49 ms
tap by coordinate890 ms469 ms970 ms711 ms
tap by selector1,084 ms696 ms918 ms989 ms
type into a field1,137 ms648 ms1,934 ms930 ms
screenshot82 ms28 ms184 ms166 ms

A tap waits until the screen has been still for 200 ms and answers with what changed, so most of its time is the app's own. In a checkout, after pnpm build and with the fixture app installed on the device, node scripts/speed.mjs <device id> runs the same calls on your Mac. Speed has every call, where the time goes, and what each one proves.

Documentation

readto
Connecting a clientjoin appdyno to your MCP client and get a phone ready
Flowsrecord what an agent did, replay it with no model, run it in CI
Evidenceleave a video, a report, a network log or a visual diff behind
Measuring performancemeasure an app, keep a baseline, and fail a change that makes it slower
Toolsevery tool, and what each platform can and cannot do
How it is builthow the parts fit, for someone about to change them

The documentation has the rest of the reference.

Security

Local only: no network listener by default, no telemetry, and you decide which devices, apps and links it may touch. Use a test device and test accounts. See SECURITY.md.

Contributing

See CONTRIBUTING.md and the Code of Conduct. CHANGELOG.md lists the releases.

License

MIT. See LICENSE.

NPM
appdyno
Install Command
npm install appdyno
Runtime: npx