MCP Studio Mobile
A thin remote-control client for MCP Studio. Stream your desktop's chat panel to your phone, send prompts back, switch tabs and projects — all over your own tunnel. No cloud infrastructure.
Architecture in one sentence. Phone holds a device token + a WebSocket. Desktop runs the MCPs, the agent loop, and the credential vault. The tunnel (Tailscale / Cloudflare / ngrok / ZeroTier) provides the network path; Studio operates no servers.
Install
Android · APK
- Open the Releases page and download the latest
.apk. - Tap the file in your downloads to install. Allow "install from unknown sources" if Android asks.
- Open MCP Studio Mobile. You should land on the empty pair screen.
iOS · TestFlight (coming)
iOS distribution via TestFlight is on the roadmap and requires an active Apple Developer enrollment. Until then, run via Expo Go for dev / personal use: open the Expo dev server URL from your developer's Mac in the Expo Go app.
Pair with desktop
Pairing is one-time per device. The desktop mints a 5-minute one-time token; you scan or paste it on the phone and the desktop exchanges it for a long-lived device token stored in the OS Secure Enclave / Keystore via Expo SecureStore.
- On desktop: open
Settings → Mobile. Paste your tunnel URL in the field (or click Detect Tailscale URL). Click Enable. - Click Generate pair code. A QR +
mcpstudio://pair?u=…&t=…link appears (5-minute TTL). - On the phone: open MCP Studio Mobile. Tap Scan QR / camera (or paste the link).
- Give the device a name (e.g. "Rohan's iPhone") → Pair.
The chat screen opens. You should see the connection dot turn green within a second, the desktop's active-tab transcript stream in, and a green pill in the desktop's Paired devices list with your device name.
The desktop persists the WebSocket port across enable / disable cycles in ~/.mcp-widget/mobile-control.json — your phone reconnects after a desktop restart without re-pairing.
Tunnel setup
The mobile app reaches the desktop at whatever URL your tunnel terminates at. Studio supports any. Pick what fits your threat model.
Tailscale (recommended)
Install Tailscale on both the Mac + the phone, log into the same tailnet. Then:
# On the Mac, in any terminal:
tailscale serve --bg http://localhost:<PORT>
# Replace <PORT> with whatever Studio's Settings → Mobile shows.
Your tunnel URL becomes https://<hostname>.<tailnet>.ts.net. Paste that in Studio's Tunnel URL field. The phone reaches it over the encrypted tailnet — no port forwarding, no public exposure.
Cloudflare Tunnel
cloudflared tunnel --url http://localhost:<PORT>
Returns a https://<random>.trycloudflare.com URL. Free, HTTPS-terminated, no account needed.
ngrok
ngrok http <PORT>
Returns https://<random>.ngrok.app. Free tier resets the URL on each session; ngrok config add-authtoken persists a custom URL.
ZeroTier
Same idea as Tailscale — both devices on the same ZeroTier network. Use the desktop's ZeroTier IP + the bound port: http://<zt-ip>:<PORT>.
Chat + transcript
The main screen mirrors your active desktop tab. Bubbles render markdown — code blocks, lists, tables, inline code — the same way desktop responses do.
- Connection dot: green = open, amber = connecting, red = revoked, grey = offline.
- ⋯ menu: theme picker (System / Light / Dark) + Forget this desktop.
- Header title: shows the active project name if any, otherwise MCP Studio.
Slash commands
Typing / in the input opens an autocomplete strip with the built-in commands. Tap one to fill the input, then send.
| Command | What it does |
|---|---|
/help | List every built-in command |
/mcp-servers | List installed MCPs (and tool counts) |
/tab | List / new / switch / close / rename tabs |
/project | List / new / switch / delete / end projects |
/build-mcp | Tell the desktop to open the MCP Builder |
/clear | Clear the current tab's transcript (both ends) |
Custom quick_commands declared by installed MCPs aren't surfaced in the mobile autocomplete yet — coming in a future update (currently they still work if you type them manually).
Tabs + projects
The desktop's tab list mirrors to the phone. Switch tabs from the desktop and the mobile transcript updates automatically. From the phone, the slash-command path (/tab switch …) drives the desktop the same way typing in the panel would.
Switching to a project (/project switch foo) changes the tab's project pointer and rebinds the transcript / agent context to that project's history. Projects persist server-side; the mobile is read-only on per-project storage.
Theme
Tap ⋯ in the chat header → Theme → System / Light / Dark. System follows your OS appearance. The choice is persisted in SecureStore so it survives a relaunch.
Auto-updates
Production builds ship via Expo's OTA update channel. Every cold start, the app silently checks for a new JS bundle on its release branch (preview or production). If an update is ready, it downloads in the background and applies on the next launch — no app-store re-publish needed for JS-only changes.
Native package upgrades still require a fresh APK install (the release tag bumps signal those).
Troubleshooting
"Network request failed" or "Couldn't reach the desktop"
The phone can't reach the URL. Check, in order:
- The desktop's
Settings → Mobileserver is Enabled and reports a bound address. - Open
<your-tunnel-url>/healthin the phone's browser. It should respond withok. If it doesn't, the tunnel itself isn't reaching the desktop — fix that first. - Tailscale users: confirm the phone is on the same tailnet (open the Tailscale app, see Connected).
- Try the other scheme:
http://↔https://. The mobile's pair flow auto-flips, but a stale URL stored in SecureStore stays put — Forget this desktop + re-pair clears it.
"500" error when scanning a QR
Server side: the desktop's pairings::add failed (rare — usually a corrupted ~/.mcp-widget/mobile-pairings.json). Quit Studio, delete that file, relaunch, generate a fresh pair code.
QR doesn't include the tunnel URL
Fill in the Tunnel URL field on the desktop before clicking Generate pair code. The URL is embedded in the QR via the u= query param. If it's empty, the mobile app prompts for it manually.
Build version mismatch
The mobile validates the server's schema_version in its hello snapshot. If the server is newer, you'll see a screen prompting you to update the app from the Releases page.
Privacy + security
- No third-party servers. Tunnel TLS is the user's responsibility; the desktop never phones home.
- Device tokens are 32 random bytes generated server-side at pair time. The desktop stores only
SHA-256(token)— the plaintext lives only in your phone's keychain. - Pair tokens (one-time, 5-min TTL) are minted by the desktop UI only. Single-use; replays return
UNAUTHORIZED. - Connection cap: 1 active WebSocket per
device_id. Reconnecting a paired phone kicks any stale connection withCloseFrame(4409, "superseded"). - Revoke any device anytime from the desktop's
Settings → Mobile → Paired devices → Revoke. The revoked phone's WebSocket force-closes withCloseFrame(4401, "revoked").
Roadmap
What's shipped today, what's in progress, what's planned. Listed here before scope shifts during a sprint — adjust expectations accordingly.
✓ Shipped
- QR pair + paste-link fallbackURL embedded in QR, scheme auto-flip
- Live transcript streamingWebSocket with reconnect + heartbeat
- Slash command autocompletebuilt-in commands
- Markdown renderingcode, lists, tables, links via react-native-markdown-display
- Tab + project switchingfrom the phone, drives desktop
- Dark modeSystem / Light / Dark, persisted
- OTA updatesvia Expo Updates
○ Planned
- Push notificationsagent-done pings via Expo Push
- Voice inputon-device ASR → prompt:submit
- iOS TestFlightrequires Apple Developer cert
- Custom quick command autocompleteprotocol extension: surface MCP-declared commands
- Multi-desktop pickerSecureStore already stores an array
- File / image attachmentssend screenshots / docs as prompt context
- MCP install + settings from phonedrive Settings → MCP Servers remotely
- Offline queuesend prompts while disconnected, flush on reconnect
- App iconcurrently using Expo default