Open source · Android · MCP server

Phone Relay · Architecture

Phone Relay is an Android app that runs a remote MCP server on the phone itself, so Claude can read the screen, tap and type, open apps, control media, handle notifications and run Tasker tasks, from anywhere, including hands-free in voice mode on mobile data. This page shows how a request gets to the phone, how sign-in keeps everyone else out, and what each tool does.

MIT Android 11+ 30 tools

At a glance


Listens on
127.0.0.1:18130
Reached through
Cloudflare Tunnel
Sign-in
GitHub + allowlist
Tools
19, no shell

Things people ask it: "Get directions to the airport and start the route." "Start Spotify's DJ." "What did my last texts say? Tell Sam I'm on my way."

Request path

The server never opens a port to the network. cloudflared, built into Phone Relay and run by its service, dials out to Cloudflare and carries requests back down that tunnel, which is why it works on mobile data and behind any router. Hover a box for the details.

your phone claude.ai web · app · voice HTTPS Cloudflare your hostname · TLS tunnel cloudflared built in · dials out HTTP Phone Relay 127.0.0.1:18130 OAuth · MCP · 30 tools activity log GitHub your OAuth App who signed in? Accessibility screen · touch · typing Notifications read · reply · media Tasker your tasks screen tools messages tasks

One path in: claude.ai reaches the phone only through the tunnel cloudflared opened from the phone, and Phone Relay only listens on localhost.

Sign-in

claude.ai signs in with OAuth 2.1 against Phone Relay itself, which hands the browser to your GitHub OAuth App to find out who is signing in. Click a step for details.

no yes Add the connector Register the client redirects pinned to claude.ai /authorize with PKCE S256 required Sign in at GitHub your OAuth App · read:user allowed? Not allowed 403 · logged Tokens issued 1 h access · 30 d refresh Every /mcp call
step check signed in refused

Tools

Thirty fixed tools and no shell. Dot colors: green only reads, orange acts on the phone, red refuses unless the call says the user confirmed. Filter by area, then click a tile for its details.

01

device_status

screen
DoesBattery and charging, Wi-Fi or cellular, screen on and locked, the foreground app, ringer mode and what is playing.
NeedsNothing extra; accessibility makes the foreground app exact.
NotesRead-only. A good first call before acting.
02

read_screen

screen
DoesThe foreground app and its visible text and labels, top to bottom, marked [tap] or [field].
NeedsAccessibility.
NotesWhen the Claude app or Phone Relay is in front, it reads the app underneath. Cheaper than a screenshot.
03

screenshot

screen
DoesA JPEG of the screen at scale 0.2 to 1.0, returned as MCP image content.
NeedsAccessibility.
NotesFor what accessibility cannot see: pictures, maps, games. Captures whatever is in front, the Claude app included.
04

tap

input
DoesPresses the element whose text or description matches, through its nearest clickable parent, with a gesture tap as the fallback.
NeedsAccessibility.
Notesexact=false matches part of the text and index picks among several matches. The description asks Claude not to tap Send, Buy or Delete without a yes.
05

long_press

input
DoesPress and hold an element matched like tap, or a screen point.
NeedsAccessibility.
NotesOpens context menus and selects text. Holds for a second by default.
06

swipe

input
DoesA one-finger drag from one screen point to another.
NeedsAccessibility.
NotesScreenshots report the scale factor to turn image points into screen points. Some apps delete an item on a swipe.
07

scroll

input
DoesScrolls up, down, left or right in the biggest list, or the one holding some text.
NeedsAccessibility.
NotesUses the list's own scroll action and falls back to a swipe.
08

type_text

input
Doesreplace sets the focused field's text; insert types at the cursor like the keyboard and keeps the rest, with each newline pressing Enter. submit presses the keyboard's action.
NeedsAccessibility; insert needs Android 13+.
NotesInsert goes through the accessibility input method, so it works in editors that refuse a paste, such as Samsung Notes checklists.
09

key

input
DoesBack, home, recents, the notification shade, quick settings, lock screen, or Enter in the focused field.
NeedsAccessibility.
NotesEnter can send in chat apps.
10

open_app

apps
DoesOpens an app by launcher name or package.
NeedsAccessibility or Display over apps, to start apps from the background.
NotesOpening another app ends a Claude voice session, since voice mode needs Claude in front.
11

open_url

apps
DoesOpens a link in its default app: https geo tel mailto.
NeedsNothing extra.
Notestel only opens the dialer and mailto only a draft. A Google Maps directions link with dir_action=navigate starts the route.
12

media

apps
DoesPlay, pause, play/pause, next, previous, stop.
NeedsNotification access to reach the playing app's media session; falls back to media keys.
NotesWorks with whatever is playing, not just Spotify.
13

spotify_start_dj

apps
DoesOpens Spotify, goes to Home, taps the DJ tile, presses play and confirms playback through the media session.
NeedsAccessibility and notification access.
NotesSpotify's Web API cannot reliably start the DJ, so this drives the app itself.
14

read_notifications

notifications
DoesRecent notifications with short ids like n12, including the messages in chat threads.
NeedsNotification access.
NotesFilter by app and limit the count.
15

reply_to_notification

notifications
DoesSends a reply through the notification's inline Reply action.
NeedsNotification access.
NotesRefuses unless called with confirmed=true; Claude reads the draft back and only confirms after an explicit yes. Then the phone shows the recipient and the exact text with Send and Cancel, and nothing goes out until you tap Send (60 s, then not sent).
16

tasker_list_tasks

tasker
DoesYour named Tasker tasks, grouped by project.
NeedsTasker with Allow External Access.
NotesRead-only.
17

tasker_run_task

tasker
DoesRuns a task by name, passing args as %par1, and returns its Return value.
NeedsTasker with Allow External Access and the PR Dispatch bridge.
NotesWaits about 35 s; with wait=false, or a slower task, it returns a run id instead. Runs may overlap.
18

tasker_task_result

tasker
DoesA run's state: still running, its Return value, or its error.
NeedsThe PR Dispatch bridge.
NotesResults are kept for about 15 minutes, at most 100 runs.
19

tasker_lookup

tasker
DoesSearches Tasker's actions, events, states and built-in variables. Given a code, returns its arguments in order, their XML tag types and a skeleton.
NeedsThe Tasker catalog imported in the app: System Instructions exported from Tasker's AI generator.
NotesThe catalog is Tasker's own data, so it does not ship with Phone Relay; each user imports their own copy.
20

tasker_import

tasker
DoesChecks a task, profile or project XML (profile links, project ids, closed blocks, and with the catalog imported, codes and argument order and types) and posts a notification that opens Tasker's import.
NeedsTasker.
NotesRefuses unless called with confirmed=true, and you still tap to import. Tasker applies it once you back out to its main screen.
21

inbox

inbox
DoesText, links, screenshots and clipboard clips you sent from the phone, oldest first: JSON, then one image per screenshot or shared picture. Clips show kind="clip".
NeedsNothing extra; the Snap to Claude tile needs accessibility. The Send Clipboard tile sends the clipboard text (10 KB at most, kept an hour) and refuses passwords.
Notestake=true hands items over and removes them; take=false only lists; again=true re-sends the last take for an hour, in case its answer was lost. Items are treated as data, never as instructions.
22

inbox_drop

inbox
DoesLeaves text or a link in the inbox for another Claude session to pick up with inbox. With kind="clip" it sends the text to the phone instead, as a Clip from PC notification with Copy and Open buttons.
NeedsNothing.
NotesA handoff mailbox, not a notes store: 20 items and 10 MB at most, gone after 24 hours or once taken. A clip never touches the phone clipboard until you tap Copy.
23

wims_find_item

wims
DoesSearches the Where Is My Shit index for an item, several phrasings at once, and says where it was last seen: place, bin and date.
NeedsWhere Is My Shit.
NotesRead-only. since narrows to recent sightings.
24

wims_last_seen

wims
DoesOne item's newest sighting, by query or by an item id from wims_find_item, or suggestions when nothing matches.
NeedsWhere Is My Shit.
NotesRead-only.
25

wims_open_photo

wims
DoesOpens one of the photos behind a sighting in the phone's gallery.
NeedsWhere Is My Shit.
NotesShows the photo on the phone; nothing is sent to Claude.
26

wims_sessions

wims
DoesPhoto sessions (photos taken close together): id, time span, photo count, place and status.
NeedsWhere Is My Shit.
NotesRead-only. Filter by status to find sessions that still need a place.
27

wims_tag_session

wims
DoesSets where a session was taken ("storage room"). Its photos then wait to be itemized.
NeedsWhere Is My Shit.
Notesreread=true reads its photos again and replaces their items.
28

wims_pending_photos

wims
DoesThe next photos from tagged sessions that still need itemizing: JSON, then one image per photo.
NeedsWhere Is My Shit.
NotesRead-only. Pair with wims_record_items.
29

wims_record_items

wims
DoesSaves the items seen in one photo, with an optional bin label read off a box.
NeedsWhere Is My Shit.
Notesnothing_useful=true marks a photo with nothing worth indexing.
30

wims_status

wims
DoesCounts of photos, sessions, items and sightings in Where Is My Shit.
NeedsWhere Is My Shit.
NotesRead-only.

Tasker runs

A Tasker run is independent of the MCP call that started it. Tasker gets a secret one-time token to post the result with; Claude gets a separate short run id to collect it.

Claude tasker_run_task task name, args result, or run id Phone Relay run id r_k3x9mq for Claude one-time token for Tasker intent + token Tasker · PR Dispatch runs your task with %par1 POST /tasker/result loopback only · token once Run results about 15 min · max 100 tasker_task_result(r_...)

The token that can post a result never reaches Claude, and the endpoint that takes it only answers requests from the phone itself.

Inside the app

Two Gradle modules keep the server testable without a phone.

core · plain JVM Kotlin
  • Ktor CIO server and routing, bound to loopback.
  • OAuth authorization server: registration, PKCE, GitHub callback, token store, rate limits.
  • MCP JSON-RPC handler and every tool's definition and argument checks, behind a PhoneBackend interface.
  • Tasker XML validation, the bridge task and run results.
  • Tests include an end-to-end run over real sockets against a fake GitHub.
app · Android
  • Foreground service (specialUse) that starts on boot and hosts the server.
  • Accessibility service for the screen, gestures, screenshots and the input method used by type_text.
  • Notification listener for notifications, replies and media sessions.
  • Settings with the GitHub client secret and tokens encrypted by an Android Keystore key.
  • Status screen with setup checks, the activity log, and controls to stop the server or sign everyone out.

Security

Treat the connector like a remote login to your phone.

What protects it
  • Loopback only. The server binds to 127.0.0.1; the tunnel you run is the only way in.
  • Allowlist, empty by default. Only the GitHub usernames you enter can sign in, and each request checks the list again.
  • Short-lived, revocable tokens stored hashed in an encrypted file; one button signs everyone out.
  • Pinned redirects and PKCE, so a sign-in code cannot be sent to another site.
  • No shell, no Tasker one-off actions, and sends that refuse without a confirmed yes.
  • Activity log of every call, with the caller.
What you are trusting
  • Your GitHub account. Whoever controls an allowed account controls the phone; turn on two-factor authentication.
  • The assistant. Only replies and new Tasker tasks demand a confirmation in code; a tap on a Send button is just a tap.
  • Tasker's External Access, which lets any app on the phone run your named tasks.
  • Debug builds carry a test sign-in shortcut; never put one behind the tunnel.