Phone MCP
by mjrafg
README.md
# Phone MCP
**A real Android phone with a real SIM, exposed to AI agents as a single MCP tool set: the Company Phone.**
```
AI Agent → Phone MCP (https://phonemcp.agent24.io/mcp) → Phone MCP Bridge (Android) → Real SIM / mobile network
```
An agent signing up for an external service calls `get_phone_number`, types the number into the
form, then calls `wait_for_verification_code` and receives the SMS code seconds later. The owner
pairs the phone once from the web interface and never has to touch it again.
| | |
|---|---|
| Web interface | https://phonemcp.agent24.io/ |
| MCP endpoint (Streamable HTTP) | https://phonemcp.agent24.io/mcp |
| Health | https://phonemcp.agent24.io/health |
| Android APK | `dist/PhoneMCPBridge-1.0.0.apk` |
## What is in the box
| Directory | Contents |
|---|---|
| `server/` | Node.js 22 + TypeScript + Fastify service: MCP server (`@modelcontextprotocol/sdk`), device WebSocket gateway, OTP engine, owner API, SQLite. |
| `web/` | Premium admin + Test Lab interface (Vite, React, TypeScript, Tailwind v4, Framer Motion), served by the server. |
| `android/` | **Phone MCP Bridge** — Kotlin / Jetpack Compose app with a foreground service, offline queue and QR pairing. |
| `deploy/` | `deploy.sh`, systemd unit, Traefik edge compose file. |
| `docs/` | [Architecture](docs/ARCHITECTURE.md) · [Deployment](docs/DEPLOYMENT.md) · [Android setup](docs/ANDROID-SETUP.md) · [MCP usage](docs/MCP-USAGE.md) · [Security](docs/SECURITY.md) |
## MCP tools
| Tool | Purpose |
|---|---|
| `get_devices` | Paired phones and their connection state |
| `get_device_status` | Battery, network, heartbeat, round-trip time |
| `get_phone_number` | The company phone number (`device_id` optional with one phone) |
| `wait_for_verification_code` | Block until the OTP for `service` arrives; returns `{ matched, code }` once |
| `wait_for_sms` | Block until the next SMS (optionally filtered by sender / text) |
| `list_recent_sms` | Recent messages with codes masked |
## Quick start (development)
```bash
export PATH=$HOME/.local/node22/bin:$PATH # Node 22 on this machine
npm install
ADMIN_INITIAL_PASSWORD=devpassword123 npm run dev --workspace=server # http://127.0.0.1:7870
npm run dev --workspace=web # http://localhost:5173 (proxies /api)
npm test # OTP + end-to-end suites
```
Without a phone you can simulate one: generate a pairing code in the web UI, then
```bash
cd server
npx tsx scripts/simulate-device.ts pair http://127.0.0.1:7870 7KQ4-M2XN # prints the device credential
npx tsx scripts/simulate-device.ts run http://127.0.0.1:7870 dev_….pds_… +15551234567
```
and type `+18005550100|Your Phone MCP Test code is 482931` to deliver an SMS.
## Production
`./deploy/deploy.sh` builds the web app and server, syncs them to `/srv/phonemcp` on the
production host, installs the systemd unit, brings up the Traefik edge container and checks
`/health`. The initial owner password is generated into `/srv/phonemcp/phonemcp.env` on the first
deploy. Details: [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md).
## Security model in one paragraph
Three independent credentials: the owner's HttpOnly session cookie for the web UI, a per-device
credential (`dev_….pds_…`) that only the Android app holds, and per-client MCP bearer tokens
(`pmcp_…`) shown once at creation. All three are stored as keyed hashes. Verification codes are
delivered exactly once, never written to logs or audit events, masked in every listing, and deleted
from the database after the OTP retention window (10 minutes by default). SMS history is kept for
48 hours by default. See [docs/SECURITY.md](docs/SECURITY.md).