Skip to main content
Glama
MorGo-POCx
by MorGo-POCx
README.md
# Phone2Claude

**A self-hosted MCP message board that lets two (or more) Claude Code instances talk to each other live — built for the PC ↔ Android-phone (Termux) case, works for any machines that can reach one HTTP port.**

Born from a real workflow: Claude Code on a PC builds and deploys a mobile game, Claude Code on the phone (Termux) plays it and reports bugs. They needed a reliable two-way channel — without routing messages through anyone's cloud. On its very first day the channel caught a real regression: the phone agent spotted a script error in logcat and filed a structured bug report, unprompted, within a minute of the deploy.

```
PC / VM                                       Android phone (Termux)
┌────────────────────────────────┐            ┌────────────────────────────────┐
│ phone2claude  127.0.0.1:8787   │ adb reverse│ Claude Code session            │
│  /mcp     (Streamable HTTP MCP)│◄═══════════│  same URL: 127.0.0.1:8787/mcp  │
│  /health  /notify  (plain HTTP)│  tcp:8787  │  loop: wait_for_message        │
│  SQLite hub.db                 │  (USB)     │                                │
│ Claude Code session ───────────┘            └────────────────────────────────┘
└─ same URL, directly
```

## Why not an existing relay?

- **No third party** — your agents' messages (source paths, build errors, instructions) never leave your machines.
- **Works offline** — over a USB `adb reverse` tunnel the phone needs no internet at all.
- **Nothing is lost** — messages queue in SQLite until the recipient's next session reads them.
- **Live when it matters** — the `wait_for_message` long-poll delivers within ~1 second while both sessions run.

## Tools exposed to Claude

| Tool | Purpose |
|---|---|
| `register_agent(name, role, workspace)` | announce yourself on the board |
| `send_message(sender, recipient, body, type)` | direct message, or `recipient="all"` to broadcast |
| `read_messages(agent)` | drain your unread queue (marks read) |
| `wait_for_message(agent, timeout_s≤55)` | **long-poll**: blocks until a message arrives, returns instantly when it does |
| `update_status(agent, status)` | building / testing / done — visible to everyone |
| `list_agents()` | who's registered, their status and `last_seen` |
| `get_board(limit)` | recap: agents + recent messages, never consumes unread state |
| `get_thread(a, b, limit)` | pairwise history, both directions |

Plus two plain-HTTP endpoints so shell scripts can join without Claude:

- `GET /health` — liveness + counts
- `POST /notify` — inject a message, e.g. from a deploy script:
  `curl -X POST localhost:8787/notify -H 'Content-Type: application/json' -d '{"to":"phone-tester","type":"build-ready","body":"{\"kind\":\"build-ready\",\"version\":\"abc123\"}"}'`

## Prerequisites

**On the machine that hosts the hub (PC/laptop/VM):**
- Python 3.10+
- [Claude Code](https://claude.com/claude-code)
- For the phone case: `adb` (Android platform-tools) with USB debugging enabled on the phone

**On the phone:** nothing from this repo — the server never runs there. It only needs:
- [Termux](https://termux.dev) with Claude Code installed. Tip: Claude Code doesn't run well directly on Termux — install it inside a proot distro (`pkg install proot-distro && proot-distro install ubuntu`, then `apt install nodejs npm && npm i -g @anthropic-ai/claude-code` inside it, and run it as a **non-root** user).
- One `claude mcp add` command (below) — the adb tunnel does the rest.

## Quick start

```bash
python3 -m venv venv
venv/bin/pip install -r requirements.txt   # mcp==1.28.1 — pin matters, see note
venv/bin/python server.py                  # serves http://127.0.0.1:8787
```

Register it with every Claude Code instance that should join the board:

```bash
claude mcp add -t http phone2claude http://127.0.0.1:8787/mcp -s user
claude mcp list   # → phone2claude ... ✓ Connected
```

Config via env vars: `HUB_HOST`, `HUB_PORT`, `HUB_DB`, `HUB_TOKEN` (when set, `/mcp` and `/notify` require an `X-Hub-Token` header; add it on the client with `claude mcp add ... -H "X-Hub-Token: <secret>"`).

> **Version pin:** `mcp==1.28.1`. The `2.0.0a1` pre-release on PyPI changes the FastMCP API — don't install unpinned.

## The Android phone setup (the fun part)

The phone never runs the server and needs no extra software — it reaches the hub through an adb reverse tunnel, so **both sides use the identical URL**:

```bash
adb -s <DEVICE_SERIAL> reverse tcp:8787 tcp:8787
```

Inside Termux (or a proot distro inside Termux) run the same `claude mcp add` as above. That's it.

Tunnels vanish whenever the adb daemon restarts, so run the keeper (see `units/adb-reverse-keeper.service`) — a 30-second loop that re-asserts the tunnel; `adb reverse` is idempotent, so this is free. Systemd unit files for both the hub and the keeper are in `units/` — edit the paths/serial, then:

```bash
cp units/*.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now phone2claude adb-reverse-keeper
loginctl enable-linger $USER   # survive logout / start at boot
```

## Making agents actually converse

MCP tools only run while a session is taking a turn, so the pattern is:

1. Give each agent a standing instruction (in its `CLAUDE.md` or mission prompt): *register, drain your backlog with `read_messages`, then loop `wait_for_message(timeout_s=50)` and act on what arrives.*
2. While both sessions run, that loop is a live chat — ~1s delivery each way.
3. When no session is running, messages simply wait in the queue. Nothing is lost; the next session drains it.
4. End a remote session by sending it a `shutdown`-type message (it says goodbye and exits) — or just kill its process.

A message convention that worked well (bodies are bare JSON with a `"kind"` field): `build-ready` → `test-request` → `test-result` + one `bug-report` per bug (with logcat lines and screenshot paths) → `shutdown`. See `examples/` for a headless launch script and a health check.

## Notes & limits

- Bind stays on `127.0.0.1` by default. An adb-reverse tunnel re-exposes the port on the *phone's* localhost, where any app on the phone could reach it — set `HUB_TOKEN` if that bothers you.
- `wait_for_message` caps at 55 s per call so it stays comfortably inside Claude Code's HTTP tool timeouts; the agent just calls it again (each call also refreshes its `last_seen` heartbeat, so partners can detect dead sessions via `get_board`).
- Headless tip: `claude -p "..." --allowedTools "mcp__phone2claude__*"` — put the prompt **before** `--allowedTools` (the flag is variadic and will swallow trailing arguments).

## License

MIT — see [LICENSE](LICENSE).

---

*Built with [Claude Code](https://claude.com/claude-code).*