Pony is the hands, you pick the brain. Let Claude, Grok, Gemini or any MCP agent use your Android phone.
Kotlin
2
9 commits
updated Oct 2, 2026
Pony is the hands. You pick the brain.
Pony is a harness for your Android phone. Claude, Grok, Gemini, OpenAI, or any MCP agent can see the screen and tap and type through apps that have no API — Uber, Postmates, Resy, phone Settings.
The agent never needs a vendor SDK. Pony shares the screen, reads the UI tree, and performs the gestures. You choose which model — or which MCP host — is in charge.
c64f123b24b8969474ebc117664dbbc4b0420eaa139030fd7b468c5660b697a6). latest.json always lists the current version and its SHA-256.Your phone opens an outbound WebSocket to a relay. Your assistant does the same. A one-time code pairs them. Screenshots and commands are encrypted on the two ends, so the relay cannot read them.
Pony can:
On the phone you can also run a key-based brain (Claude, Gemini, OpenAI, xAI Grok, OpenRouter, or any OpenAI-compatible endpoint). Keys stay on the device, sealed by the Android Keystore. Assistants never see them.
Pick whichever fits. The phone side is the same app in every case.
| Brain | What you need | Computer? |
|---|---|---|
| Your own API key, on the phone: Claude, OpenAI, Gemini, or xAI Grok (OpenRouter and any OpenAI-compatible endpoint under Advanced) | A key from that provider | No. The agent loop runs on the phone. |
| Any MCP host: Claude Desktop, Claude Code, Cursor, and others | The Pony MCP server (below) | Yes, Node.js 20+ |
| Grok Bot, through the Pony template | A Grok Bot account. No API key. | No |
The default relay is Pony Cloud at https://relay.pony.karlmagendavid.com. A private relay — including Tailscale or your own machine — is a first-class choice. End-to-end encryption stays on in every mode.
pony-phone CLI yourself. A key on the phone or the Grok Bot template needs no computer.Distribution is a sideloaded APK, not Google Play. See PLAY_POLICY.md.
A key on the phone needs no pairing. For an MCP host or Grok Bot, the quickest way to pair is a one-tap https://download.pony.karlmagendavid.com/pair… link: open it on the phone and it hands the pairing to Pony. The QR is the fallback when the link is on another screen: scan it (Scan code on Pony's pairing screen), or paste the link (Paste link). Either way, compare the six-digit safety code on both sides, tap It matches, and accept screen sharing (Share entire screen). A pairing token lasts 15 minutes.
Your own key (no computer)
Any MCP host
pair, which returns the one-tap link (pairPageLink), a pony://pair link, and a QR image.In listen mode the server remembers the pairing (~/.pony/mcp.json) and rejoins on start.
Grok Bot
npm install
npm run -s mcp -- --listen --http 43123
Point the MCP host at http://127.0.0.1:43123/mcp. For a stdio host, launch the server directly — npm prints a banner on stdout, which breaks stdio:
{
"mcpServers": {
"pony": {
"command": "/absolute/path/to/pony/node_modules/.bin/tsx",
"args": ["/absolute/path/to/pony/packages/mcp/src/cli.ts", "--listen"],
"env": { "PONY_RELAY": "https://relay.pony.karlmagendavid.com" }
}
}
}
Copy .env.example if you want a documented list of overrides (PONY_RELAY, PONY_CLIENT_NAME, PONY_STATE, PONY_HOME, PONY_LISTEN). The server and CLI read the process environment; they do not load .env themselves.
pair · status · screenshot · ui_tree · wait_idle · tap · swipe · long_press · drag · pinch · type · key · open_app · open_settings · wait_for_request · done · speak · ask_user · confirm · disconnect
Screen tools take optional background and display (main or background). Omit both to follow the phone. Password fields are shown as [password] and refused for typing. The action log stores character counts, never the text.
In listen mode a standing wait_for_request stays open while the assistant is active, so asks typed or spoken on the phone are not lost between tool calls. done reports a one-sentence result.
Pony is allowed to drive the phone. It is not allowed to be reckless.
[password]. Typing into one is refused. A tap on one asks you first.http/ws is allowed only for localhost, Tailscale (100.64.0.0/10), and private LAN ranges. Everything else must be https/wss.App-by-app detail: docs/hidden-display-apps.md.
Official APKs are signed with a private release key that is not in this repository. A build from this tree uses your debug or release key. Android will not update an official install with a self-signed APK, or the other way around — install them side by side (the debug build is app.pony.companion.debug) or uninstall first.
npm install
npm test # relay, bot client, listener, MCP server, crypto
npm run demo # a scripted session against a mock phone
The Android app needs JDK 17 and Android SDK 36 (platforms;android-36, build-tools;36.0.0). Point ANDROID_HOME at the SDK.
cd android
./gradlew :app:assembleDebug # app/build/outputs/apk/debug/app-debug.apk
./gradlew :app:testDebugUnitTest # JVM tests and screenshot tests
./gradlew :app:assembleRelease # unsigned release APK
Sign a release build with a keystore you control. Never commit the keystore or its password.
$ANDROID_HOME/build-tools/36.0.0/apksigner sign --ks /path/to/your-release.jks \
--out pony-0.6.2.apk app/build/outputs/apk/release/app-release-unsigned.apk
Settings → Check for updates reads https://download.pony.karlmagendavid.com/latest.json and only installs an APK signed with the same certificate as the app already on the phone. A self-built install will not accept the official update stream.
shared/ protocol + E2E crypto (TypeScript)
relay/ WebSocket pairing relay (Node, Docker)
client/ pony-phone CLI (pair, listen, unpair), bot session, mock phone
packages/mcp/ MCP server (@pony/mcp)
web/ pairing hand-off page (pair.html)
android/ Pony Companion app (Kotlin, Jetpack Compose)
docker compose up --build # relay on :8787
npm run listen # print each ask from the phone
npm run pair -- --relay https://relay.pony.karlmagendavid.com
Relay environment is documented in relay/.env.example. What changed in each release is in CHANGELOG.md.
MIT. Copyright (c) 2026 Karl Magen David.
Kotlin
82.8%
TypeScript
15.8%
Pony is the hands, you pick the brain. Let Claude, Grok, Gemini or any MCP agent use your Android phone.
Kotlin
2
9 commits
updated Oct 2, 2026
Pony is the hands. You pick the brain.
Pony is a harness for your Android phone. Claude, Grok, Gemini, OpenAI, or any MCP agent can see the screen and tap and type through apps that have no API — Uber, Postmates, Resy, phone Settings.
The agent never needs a vendor SDK. Pony shares the screen, reads the UI tree, and performs the gestures. You choose which model — or which MCP host — is in charge.
c64f123b24b8969474ebc117664dbbc4b0420eaa139030fd7b468c5660b697a6). latest.json always lists the current version and its SHA-256.Your phone opens an outbound WebSocket to a relay. Your assistant does the same. A one-time code pairs them. Screenshots and commands are encrypted on the two ends, so the relay cannot read them.
Pony can:
On the phone you can also run a key-based brain (Claude, Gemini, OpenAI, xAI Grok, OpenRouter, or any OpenAI-compatible endpoint). Keys stay on the device, sealed by the Android Keystore. Assistants never see them.
Pick whichever fits. The phone side is the same app in every case.
| Brain | What you need | Computer? |
|---|---|---|
| Your own API key, on the phone: Claude, OpenAI, Gemini, or xAI Grok (OpenRouter and any OpenAI-compatible endpoint under Advanced) | A key from that provider | No. The agent loop runs on the phone. |
| Any MCP host: Claude Desktop, Claude Code, Cursor, and others | The Pony MCP server (below) | Yes, Node.js 20+ |
| Grok Bot, through the Pony template | A Grok Bot account. No API key. | No |
The default relay is Pony Cloud at https://relay.pony.karlmagendavid.com. A private relay — including Tailscale or your own machine — is a first-class choice. End-to-end encryption stays on in every mode.
pony-phone CLI yourself. A key on the phone or the Grok Bot template needs no computer.Distribution is a sideloaded APK, not Google Play. See PLAY_POLICY.md.
A key on the phone needs no pairing. For an MCP host or Grok Bot, the quickest way to pair is a one-tap https://download.pony.karlmagendavid.com/pair… link: open it on the phone and it hands the pairing to Pony. The QR is the fallback when the link is on another screen: scan it (Scan code on Pony's pairing screen), or paste the link (Paste link). Either way, compare the six-digit safety code on both sides, tap It matches, and accept screen sharing (Share entire screen). A pairing token lasts 15 minutes.
Your own key (no computer)
Any MCP host
pair, which returns the one-tap link (pairPageLink), a pony://pair link, and a QR image.In listen mode the server remembers the pairing (~/.pony/mcp.json) and rejoins on start.
Grok Bot
npm install
npm run -s mcp -- --listen --http 43123
Point the MCP host at http://127.0.0.1:43123/mcp. For a stdio host, launch the server directly — npm prints a banner on stdout, which breaks stdio:
{
"mcpServers": {
"pony": {
"command": "/absolute/path/to/pony/node_modules/.bin/tsx",
"args": ["/absolute/path/to/pony/packages/mcp/src/cli.ts", "--listen"],
"env": { "PONY_RELAY": "https://relay.pony.karlmagendavid.com" }
}
}
}
Copy .env.example if you want a documented list of overrides (PONY_RELAY, PONY_CLIENT_NAME, PONY_STATE, PONY_HOME, PONY_LISTEN). The server and CLI read the process environment; they do not load .env themselves.
pair · status · screenshot · ui_tree · wait_idle · tap · swipe · long_press · drag · pinch · type · key · open_app · open_settings · wait_for_request · done · speak · ask_user · confirm · disconnect
Screen tools take optional background and display (main or background). Omit both to follow the phone. Password fields are shown as [password] and refused for typing. The action log stores character counts, never the text.
In listen mode a standing wait_for_request stays open while the assistant is active, so asks typed or spoken on the phone are not lost between tool calls. done reports a one-sentence result.
Pony is allowed to drive the phone. It is not allowed to be reckless.
[password]. Typing into one is refused. A tap on one asks you first.http/ws is allowed only for localhost, Tailscale (100.64.0.0/10), and private LAN ranges. Everything else must be https/wss.App-by-app detail: docs/hidden-display-apps.md.
Official APKs are signed with a private release key that is not in this repository. A build from this tree uses your debug or release key. Android will not update an official install with a self-signed APK, or the other way around — install them side by side (the debug build is app.pony.companion.debug) or uninstall first.
npm install
npm test # relay, bot client, listener, MCP server, crypto
npm run demo # a scripted session against a mock phone
The Android app needs JDK 17 and Android SDK 36 (platforms;android-36, build-tools;36.0.0). Point ANDROID_HOME at the SDK.
cd android
./gradlew :app:assembleDebug # app/build/outputs/apk/debug/app-debug.apk
./gradlew :app:testDebugUnitTest # JVM tests and screenshot tests
./gradlew :app:assembleRelease # unsigned release APK
Sign a release build with a keystore you control. Never commit the keystore or its password.
$ANDROID_HOME/build-tools/36.0.0/apksigner sign --ks /path/to/your-release.jks \
--out pony-0.6.2.apk app/build/outputs/apk/release/app-release-unsigned.apk
Settings → Check for updates reads https://download.pony.karlmagendavid.com/latest.json and only installs an APK signed with the same certificate as the app already on the phone. A self-built install will not accept the official update stream.
shared/ protocol + E2E crypto (TypeScript)
relay/ WebSocket pairing relay (Node, Docker)
client/ pony-phone CLI (pair, listen, unpair), bot session, mock phone
packages/mcp/ MCP server (@pony/mcp)
web/ pairing hand-off page (pair.html)
android/ Pony Companion app (Kotlin, Jetpack Compose)
docker compose up --build # relay on :8787
npm run listen # print each ask from the phone
npm run pair -- --relay https://relay.pony.karlmagendavid.com
Relay environment is documented in relay/.env.example. What changed in each release is in CHANGELOG.md.
MIT. Copyright (c) 2026 Karl Magen David.
Kotlin
82.8%
TypeScript
15.8%