Phone Harness MCP
Connect AI agents to real iPhones and Androids.
The official Model Context Protocol server for Phone Harness. The agent sees the screen, taps, types, swipes, and opens apps on a real phone in Phone Harness Cloud, all over HTTPS. Nothing to pair, no adb, no Mac required, any OS.
Tool names follow mobile-mcp
(phone_take_screenshot, phone_click_on_screen_at_coordinates, …), so an
agent that has driven a phone before already knows how.
Use it two ways:
- Run it yourself with
uvx phone-harness-mcpover stdio. It signs in as you: an API key, or the sign-inphone-harness cloud loginleft on the machine. - Hosted at
https://mcp.phone-harness.com/mcp. Nothing to install; your client sends your API key as a bearer token.
Requirements
- A Phone Harness Cloud account, and an API key from the
dashboard (or the
phone-harness CLI signed in with
phone-harness cloud login). - Python 3.10+ with uv for the local server. The hosted server needs nothing.
Getting started
Most clients take this standard config:
{
"mcpServers": {
"phone-harness": {
"command": "uvx",
"args": ["phone-harness-mcp"],
"env": { "PHONE_HARNESS_API_KEY": "pck_…" }
}
}
}
Leave out env if the machine is already signed in with phone-harness cloud login.
Claude Code
Local:
claude mcp add phone-harness -e PHONE_HARNESS_API_KEY=pck_… -- uvx phone-harness-mcp
Hosted:
claude mcp add --transport http phone-harness https://mcp.phone-harness.com/mcp \
--header "Authorization: Bearer pck_…"
Claude Desktop
Settings → Connectors → Add custom connector, URL https://mcp.phone-harness.com/mcp.
Or add the standard config to claude_desktop_config.json to run it locally.
Codex
codex mcp add phone-harness --env PHONE_HARNESS_API_KEY=pck_… -- uvx phone-harness-mcp
Or in ~/.codex/config.toml, hosted:
[mcp_servers.phone-harness]
url = "https://mcp.phone-harness.com/mcp"
bearer_token_env_var = "PHONE_HARNESS_API_KEY"
Cursor
Click the Cursor badge above, or Settings → MCP → Add new MCP Server and paste
the standard config into ~/.cursor/mcp.json. For the hosted server:
{
"mcpServers": {
"phone-harness": {
"url": "https://mcp.phone-harness.com/mcp",
"headers": { "Authorization": "Bearer pck_…" }
}
}
}
VS Code
Click a VS Code badge above, or:
code --add-mcp '{"name":"phone-harness","command":"uvx","args":["phone-harness-mcp"],"env":{"PHONE_HARNESS_API_KEY":"pck_…"}}'
Gemini CLI
Add the standard config under mcpServers in ~/.gemini/settings.json.
Copilot CLI
Run /mcp add, or put this in ~/.copilot/mcp-config.json:
{
"mcpServers": {
"phone-harness": {
"type": "local",
"command": "uvx",
"args": ["phone-harness-mcp"],
"env": { "PHONE_HARNESS_API_KEY": "pck_…" },
"tools": ["*"]
}
}
}
Windsurf
Add the standard config to ~/.codeium/windsurf/mcp_config.json.
Goose
Advanced settings → Extensions → Add custom extension, type STDIO, command
uvx phone-harness-mcp, environment variable PHONE_HARNESS_API_KEY.
Kiro
Add the standard config to .kiro/settings/mcp.json.
LM Studio
Program → Install → Edit mcp.json, paste the standard config.
opencode
{
"mcp": {
"phone-harness": {
"type": "local",
"command": ["uvx", "phone-harness-mcp"],
"environment": { "PHONE_HARNESS_API_KEY": "pck_…" },
"enabled": true
}
}
}
Warp
Settings → AI → Manage MCP Servers → + Add, paste the standard config.
Any other client
Local: command uvx, arguments phone-harness-mcp, environment PHONE_HARNESS_API_KEY.
Hosted: streamable HTTP at https://mcp.phone-harness.com/mcp with header
Authorization: Bearer <API key>.
What the agent does with it
● phone_list_available_devices → no phone running; an iPhone and an Android are available
● phone_start_device → s_7f3a: Android emulator, 390x844, 30 min
● phone_launch_app name="Weather"
● phone_list_elements_on_screen → "San Francisco" at (195, 212) …
● phone_take_screenshot → forecast is up
● phone_stop_device
Tools
Every tool but the first two takes device, an id from phone_list_available_devices.
| Tool | Does |
|---|---|
phone_list_available_devices |
Running phones and their ids, plus the phones the account can start. |
phone_start_device |
Start a phone and wait until it is ready. platform + kind (android/emulator, ios/device), device_id for a particular iPhone, minutes (default 30). Rented phones bill by the minute. |
phone_stop_device |
End the session; billing stops. |
phone_get_screen_size |
The coordinate space taps use. |
phone_take_screenshot |
JPEG, long side ≤ max_size (1024) or scale, preceded by a line that converts image points to screen coordinates. |
phone_save_screenshot |
Write a PNG or JPEG to a path. Local server only. |
phone_list_elements_on_screen |
Visible text with tap-ready centers (text or json). Accessibility tree on Android, text recognition on iPhone. |
phone_click_on_screen_at_coordinates |
Tap x, y. |
phone_long_press_on_screen_at_coordinates |
Hold for duration ms (800). |
phone_swipe_on_screen |
direction is the finger's motion; from x, y or the center, distance px or 40% of the screen. |
phone_type_keys |
Type into the focused field; submit presses Enter. |
phone_press_button |
HOME, BACK (Android), APP_SWITCHER, ENTER. |
phone_list_apps |
Installed apps (Android). |
phone_launch_app |
Open an app by name or package id. |
phone_get_foreground_app |
Which app is in front. |
Not yet: opening a URL (url.open is not served by the session route today),
installing apps, clipboard, screen recording.
Configuration
phone-harness-mcp [OPTIONS]
--api-key KEY API key. Default $PHONE_HARNESS_API_KEY, else the CLI's sign-in. stdio only.
--api-url URL Default $PHONE_HARNESS_CLOUD_API or https://api.phone-harness.com
--port PORT Serve streamable HTTP instead of stdio (see below)
--host HOST Interface for --port [default: 127.0.0.1]
--path PATH MCP endpoint path [default: /mcp]
--allowed-hosts HOSTS Host header values to accept; '*' disables the check
--allowed-origins ORIGINS Origin header values to accept
--public-url URL The URL clients reach the server at (with --oauth-issuer)
--oauth-issuer URL Publish OAuth protected-resource metadata for this issuer
--stateful HTTP mode: one MCP session per client
Standalone and hosted server
--port serves streamable HTTP at /mcp:
uvx phone-harness-mcp --port 8931
In this mode the server holds no credential. Every request must carry
Authorization: Bearer <API key>, which is forwarded unchanged to Phone
Harness Cloud, so one process can serve many accounts and leaks none. A
request without a bearer gets a 401 with a WWW-Authenticate challenge.
{
"mcpServers": {
"phone-harness": {
"url": "http://localhost:8931/mcp",
"headers": { "Authorization": "Bearer pck_…" }
}
}
}
Behind a reverse proxy pass the public hostname so the Host header is
checked (DNS-rebinding protection): --host 0.0.0.0 --allowed-hosts mcp.example.com.
mcp.phone-harness.com runs exactly this binary.
Vercel
The repo is also a Vercel project: app.py exposes the same server as one
Python function, stateless, answering JSON. Import the repo in Vercel, add the
domain, done. mcp.phone-harness.com runs this way.
| Environment variable | Default | Means |
|---|---|---|
PHONE_HARNESS_CLOUD_API |
https://api.phone-harness.com |
The API to forward to. |
PHONE_HARNESS_MCP_ALLOWED_HOSTS |
* |
Host header check; off on Vercel, whose edge owns routing. |
PHONE_HARNESS_MCP_OAUTH_ISSUER |
unset | Set to the OAuth authorization server to publish protected-resource metadata. Unset means API keys only. |
PHONE_HARNESS_MCP_PUBLIC_URL |
unset | The URL clients use, required with the issuer. |
vercel.json sets maxDuration to 300 s so phone_start_device can wait
for a phone to boot.
Docker
docker build -t phone-harness-mcp .
docker run --rm -p 8000:8000 phone-harness-mcp --port 8000 --host 0.0.0.0 --allowed-hosts '*'
Security
- The server is a thin HTTPS client. It never holds a phone, and over HTTP it never holds a credential: each request's bearer is the only identity.
- Over stdio it uses this machine's sign-in, like any local MCP server; it is not a security boundary between you and your own account.
- Screenshots and text read from the phone pass through unlogged. API keys are never written to logs.
- Phones are rented by the minute. The
phone_start_devicedescription says so; turn on your client's tool-approval setting if you want to confirm it.
Report vulnerabilities per SECURITY.md.
Development
uv sync
uv run pytest # a fake Phone Harness Cloud API; no account needed
uv run phone-harness-mcp --help
Layout: src/phone_harness_mcp/{cli,server,phone,cloud,screenshots,auth}.py.
cloud.py is the HTTP client, phone.py turns API answers into sentences for
the model, server.py is the tool set, auth.py is hosted-mode bearer handling.
License
MIT
Metadata
Release files for phone-harness-mcp 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| phone_harness_mcp-0.1.0.tar.gz | 31.8 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| phone_harness_mcp-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 57.7 kB
Release files / phone_harness_mcp-0.1.0.tar.gz
| Download URL | phone_harness_mcp-0.1.0.tar.gz |
|---|---|
| Size | 31.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6f2beaca43e72507f1ac73af98ee9543f03d22a6e88a7430b6b0fb61ed939920
|
|
BLAKE2b-256 checksum How to use checksums |
713a7cedc3cc706221049741cc7aff13713f9c98cb3cbbac86b6700f1e78e421
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logRelease files / phone_harness_mcp-0.1.0-py3-none-any.whl
| Download URL | phone_harness_mcp-0.1.0-py3-none-any.whl |
|---|---|
| Size | 25.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ff1d54f27a9767b5a97ea6b0beadd54e20dd8a909003040d5db2d07746d5bbc7
|
|
BLAKE2b-256 checksum How to use checksums |
9066ff2006883212ab1eef92fdf8409ff7bad38319e2f1cd1b21624cd2829c67
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logSecurity for phone-harness-mcp 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for phone_harness_mcp-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.