Connect an AI agent to your phone
Pony MCP server 0.3 · Last updated September 30, 2026
The Pony MCP server gives any MCP-capable agent (Claude Desktop, Claude Code, Cursor, and others) hands on a real Android phone running the Pony Companion app. The agent and the phone meet at an encrypted relay, and you pair them once per session with a QR code or a link.
Before you start
- Node.js 22 or newer on the computer that runs your agent.
- The Pony source folder (
pony-companion). Unpack it anywhere and runnpm installin it once. The examples below use/path/to/pony-companion. Replace that with your real absolute path. On Windows, use thetsx.cmdfile innode_modules\.bin. - The Pony Companion app on your Android phone (Android 10+), with the consent screens done and Pony control turned on under Accessibility.
- A relay. Use ours,
https://relay.pony.karlmagendavid.com, or run your own withnpm run relay(port 8787) and use a URL your phone can reach.localhostwon't work, because on the phone it means the phone itself.
Launch the server with tsx directly, as shown below. Don't wrap the stdio command in npm run, because npm prints a banner to stdout and the MCP client will fail to parse it.
Claude Desktop
Open claude_desktop_config.json (Settings → Developer → Edit Config; on macOS it's in ~/Library/Application Support/Claude/, on Windows in %APPDATA%\Claude\) and add:
{
"mcpServers": {
"pony": {
"command": "/path/to/pony-companion/node_modules/.bin/tsx",
"args": ["/path/to/pony-companion/packages/mcp/src/cli.ts"],
"env": {
"PONY_RELAY": "https://relay.pony.karlmagendavid.com"
}
}
}
}
Quit and reopen Claude Desktop. "pony" should appear in the tools menu.
Claude Code
claude mcp add pony --scope user \
-e PONY_RELAY=https://relay.pony.karlmagendavid.com \
-- /path/to/pony-companion/node_modules/.bin/tsx /path/to/pony-companion/packages/mcp/src/cli.ts
Check it with claude mcp list, or /mcp inside a session. Leave out --scope user to add it to the current project only.
Cursor
Put the same block in ~/.cursor/mcp.json (all projects) or .cursor/mcp.json (one project), then enable "pony" under Settings → MCP:
{
"mcpServers": {
"pony": {
"command": "/path/to/pony-companion/node_modules/.bin/tsx",
"args": ["/path/to/pony-companion/packages/mcp/src/cli.ts"],
"env": {
"PONY_RELAY": "https://relay.pony.karlmagendavid.com"
}
}
}
}
Any other MCP client
Stdio (default)
Command: /path/to/pony-companion/node_modules/.bin/tsx
Args: /path/to/pony-companion/packages/mcp/src/cli.ts (optionally --relay <url>)
Env: PONY_RELAY=https://relay.pony.karlmagendavid.com
Streamable HTTP (localhost)
From the pony-companion folder:
npm run mcp -- --http 43123 --relay https://relay.pony.karlmagendavid.com
The server listens on http://127.0.0.1:43123/mcp and accepts only localhost Host and Origin headers. It has no auth token, so don't expose it to a network. Client config:
{
"mcpServers": {
"pony": { "url": "http://127.0.0.1:43123/mcp" }
}
}
One server process controls one phone. The relay can also be set for a single session with the relay argument of pair.
How pairing works
- Ask your agent to "pair my phone". It calls
pair, which asks the relay for a one-time token and returns a QR code image plus apony://pair?…link. The token expires after 5 minutes and works once. - In the Pony app, scan the QR. If the QR is on the phone's own screen, open the link or paste it (or the QR text) into the pair screen.
- Accept Android's screen-capture prompt. It's asked every session on purpose.
- Your phone and the agent's computer exchange public keys (X25519) through the relay and derive session keys. From here on, every frame is encrypted with ChaCha20-Poly1305, and the relay only forwards ciphertext.
- Compare the 6-digit safety code. The app shows it, and the agent reads it from
status. If the codes don't match, disconnect. - The session lasts up to 30 minutes, then the phone disconnects itself. Pair again for more time.
Tools the server exposes
| Tool | What it does |
|---|---|
pair | Start a pairing session. Returns the QR (PNG and data URL), the pony://pair link, the token, and the QR JSON. Optional waitMs (0–60000) and relay. |
status | Whether a phone is connected, the safety code, time left in the 30-minute session, and the action log (character counts, not text). |
screenshot | Capture the current screen as a JPEG image. |
ui_tree | Read the accessibility tree as text. Password fields appear as [password] with no contents. |
tap | Tap x, y in screen pixels from the top left. |
swipe | Swipe from x1,y1 to x2,y2, optional durationMs. |
type | Insert text at the focused field (Pony keyboard first, then set-text; clipboard paste only if the owner enabled it). Refused for password fields. |
key | Press back, home, or recents. |
open_app | Open an installed app by package name, such as com.android.settings. |
wait_for_request | Long-poll (up to 55 s) for the next thing the owner said or typed to Pony. {"empty": true} means call again. |
speak | Say a sentence out loud on the phone. |
ask_user | Ask a question out loud and return the owner's spoken answer. |
confirm | Ask a yes-or-no question. Returns {"accepted": true|false}. Ambiguous speech is not a yes. |
disconnect | End the session and drop the phone connection. |
Errors the phone can return include password_field, not_confirmed (the owner declined a risky tap), locked (Pony won't unlock the phone), stopped (the owner said stop), ime_disabled / ime_required (Pony keyboard not enabled or not chosen), and screenshot_failed (for example, a secure app). If type reports method: "key_events", check the field before continuing.
Safety model
- Consent first. Nothing works until the owner reads the explanation screens, ticks consent, turns on Pony control, and accepts screen capture for that session.
- End-to-end encryption. X25519 + HKDF-SHA256 + ChaCha20-Poly1305 between the phone and the MCP server, and a 6-digit safety code on both sides. The relay only matches a one-time token and forwards opaque frames. It stores nothing.
- Short sessions. Pairing tokens last 5 minutes and work once. Sessions end after 30 minutes. The owner can Disconnect from the app or the notification at any time.
- Explicit yes for risky actions. A tap or app open labelled send, pay, buy, delete, call, transfer, or a security change is spoken back to the owner, and it runs only after an explicit yes.
- Passwords are off-limits. They are hidden in the UI tree, and typing into them is refused.
- No unlocking. Pony returns
lockedand asks the owner to unlock. - Keys stay home. AI provider keys saved in the app are encrypted with Android Keystore and never cross the relay.
- Logs without content. The phone and the MCP server keep an action log. For typed text and what the owner says, it holds character counts, not the words.
- Localhost only. The HTTP transport binds to 127.0.0.1 and checks Host and Origin.
More detail is on the privacy page. Questions: hello@orbitbuild.com.