{
  "markdown": "# onsense — Phone Camera, Sensors & Files for PC AI Agents\n\n<!-- mcp-name: io.github.hufirst/onsense -->\n\n**Don't describe it. Let your AI see it.** `onsense` is the PC-side MCP broker for the [onSense](https://play.google.com/store/apps/details?id=com.shdev.onsense) Android app: run `uvx onsense pair` on your PC, and **any MCP-capable AI** — Claude Code, Claude Desktop, Codex, or your own agent — can see through your phone's camera, read its sensors, and move files & clipboard both ways. **No cloud relay. No ADB.**\n\n![onSense demo — Claude Code reads the phone's sensors and camera through onSense](https://raw.githubusercontent.com/hufirst/onsense/main/docs/split-demo.gif)\n\n> *Claude Code, running on the PC, reads the phone's sensors and looks through its camera to answer — live, over local Wi-Fi.*\n\n![onSense demo — Codex does the same through onSense](https://raw.githubusercontent.com/hufirst/onsense/main/docs/onsense-demo.gif)\n\n> *…and the same with Codex. onSense is a standard MCP server, so any MCP client works.*\n\n---\n\n## Architecture\n\n```text\nAndroid phone (onSense app)           PC (this package)           AI client\n  HTTP provider :8080        ←→    stdio MCP broker          Claude / Codex / …\n  camera frames, photos,           onsense serve               natural-language or\n  sensors, QR pairing              onsense clip :8770          /onsense tool calls\n```\n\n- The **phone** runs an HTTP provider on port 8080. It exposes camera frames, recent photos, and sensor readings, all gated by a pairing token.\n- The **PC package** (`onsense`) is a stdio MCP broker. It translates MCP tool calls from your AI client into HTTP requests to the phone.\n- Discovery uses mDNS (`_onsense._tcp.local.`). If the phone's IP changes, the broker rediscovers it automatically — no manual reconfiguration needed.\n- An optional **clip bridge** daemon on port 8770 lets the phone push camera frames, files, and clipboard content to the PC (and, if enabled, lets the phone pull from the PC clipboard).\n\n---\n\n## Requirements\n\n- [uv](https://docs.astral.sh/uv/) (provides `uvx`; downloads a managed Python automatically — no separate Python install needed)\n- The onSense Android app installed on a phone on the **same local Wi-Fi network** as your PC\n- An MCP-compatible AI client such as [Claude Code](https://claude.ai/code), Codex, or any stdio MCP client\n\n---\n\n## Quick Start\n\n### 0. Fresh PC without uv? Use the app's setup helper (recommended)\n\nOn a clean PC, `uvx` doesn't exist yet and the command below will fail with *\"'uvx' is not recognized\"*. The easiest fix: install the onSense app first, tap **\"Start PC setup helper\"**, and open the shown address on your PC. It gives you a one-line command that installs uv, pairs, and registers the MCP server in one go — and because the PC connects *out* to the phone, it also sidesteps the Windows firewall entirely.\n\nPrefer installing uv yourself? One line:\n\n```powershell\n# Windows (PowerShell)\nirm https://astral.sh/uv/install.ps1 | iex\n$env:Path = \"$env:USERPROFILE\\.local\\bin;$env:Path\"   # make uvx visible in this same shell\n```\n\n```bash\n# macOS / Linux\ncurl -LsSf https://astral.sh/uv/install.sh | sh\nexport PATH=\"$HOME/.local/bin:$PATH\"\n```\n\n### 1. Pair your phone\n\nRun this once. The PC prints a QR code; scan it from the onSense app (\"Scan PC QR\"). The phone pushes its address and token to the PC, which registers the MCP server automatically.\n\n```bash\nuvx onsense pair\n```\n\nAfter pairing, restart your AI client once so it picks up the new `onsense` MCP server.\n\n### 2. Use it\n\nAsk your AI client naturally:\n\n> \"Take a photo of what's in front of my phone.\"\n> \"What are the current sensor readings?\"\n> \"Show me the last 5 photos on my phone.\"\n\nThe MCP tools are called automatically. In Claude Code you can also use the bundled **`/onsense`** slash command (installed during pairing).\n\n### 3. Diagnose problems\n\n```bash\nuvx onsense doctor           # checks Python, uv, MCP, Claude registration, mDNS, phone reachability\nuvx onsense doctor --base http://192.168.1.5:8080 --token YOUR_PAIRING_TOKEN\n```\n\n### Windows: QR scan times out?\n\nThe #1 cause is the Windows firewall dropping the phone's inbound connection:\n\n- If your Wi-Fi network profile is **Public**, inbound traffic is blocked hard. Switch it to **Private** (Settings → Network → your Wi-Fi → Network profile), or just use the app's **PC setup helper**, which needs no inbound port at all.\n- On a Private network, add a durable allow rule once (run as administrator):\n\n  ```powershell\n  uvx onsense pair --fix-firewall\n  ```\n\n  The first-run firewall popup is *program-path-scoped* and uvx's ephemeral paths silently invalidate it — the port-scoped rule above (TCP 8765–8774, Private) is what actually sticks.\n- If port 8765 is taken, `pair` automatically falls back to the next free port (up to 8774) and the QR reflects the real port — no action needed.\n\n---\n\n## Subcommands\n\n| Command | What it does |\n| --- | --- |\n| `uvx onsense pair` | Display a QR code on the PC; phone scans it and pushes `{base, token}`; registers MCP automatically |\n| `uvx onsense pair \"onsense://pair?base=...&token=...\"` | Parse a pairing URI directly (phone-displayed QR → manual copy) |\n| `uvx onsense pair --img screenshot.png` | Decode a pairing QR from a screenshot file (requires opencv) |\n| `uvx onsense serve` | Run the MCP server (stdio). Also starts the clip daemon automatically unless `--no-clip` is passed |\n| `uvx onsense serve --no-clip` | Run the MCP server only, without starting the clip daemon |\n| `uvx onsense clip` | Run the clip bridge daemon standalone (port 8770) |\n| `uvx onsense clip --allow-pull` | Enable phone→pull: phone can GET /clip to retrieve the PC clipboard |\n| `uvx onsense clip --set-clipboard` | Auto-inject received content into the PC OS clipboard (off by default) |\n| `uvx onsense pair --fix-firewall` | (Windows, admin) Add the durable port-scoped firewall rule, then pair as usual |\n| `uvx onsense doctor` | Diagnose installation, connectivity, and phone reachability |\n| `uvx onsense stats` | Show local-only activation stats (`--json`, `--reset`). Nothing is uploaded automatically |\n\n---\n\n## Other MCP clients (Claude Desktop, Codex, …)\n\n`uvx onsense pair` auto-registers the server with **Claude Code** (via `claude mcp add`) — this one-line auto-setup is Claude Code-specific. The MCP server itself is standard stdio MCP, so **any MCP-compatible client works** once you add it manually. Run `uvx onsense pair` once first: it saves your phone's address and token to `~/.onsense/pair.json`, which the server reads at request time — so you don't put the token in each client's config, and the phone's IP is auto-tracked via mDNS.\n\n**Claude Desktop** — add to `claude_desktop_config.json`:\n\n```json\n{ \"mcpServers\": { \"onsense\": { \"command\": \"uvx\", \"args\": [\"onsense\", \"serve\"] } } }\n```\n\n**Codex** — register via the CLI (verified with Codex CLI 0.142.5):\n\n```bash\ncodex mcp add onsense -- uvx onsense serve\n```\n\n> **Codex sandbox note:** Codex's default sandbox blocks network access, so the MCP server can't reach your phone and tool calls fail silently. Run Codex with network access enabled (e.g. `--sandbox danger-full-access`, or a sandbox policy that permits network) so onsense can talk to the phone on your LAN.\n\nor add to `~/.codex/config.toml`:\n\n```toml\n[mcp_servers.onsense]\ncommand = \"uvx\"\nargs = [\"onsense\", \"serve\"]\n```\n\nRestart the client once after adding the server. Any other stdio MCP client works the same way — point it at `uvx onsense serve`.\n\n---\n\n## MCP Tools\n\nThese tools are exposed to your AI client after pairing:\n\n| Tool | Description |\n| --- | --- |\n| `get_live_frame()` | Capture the current camera frame from the phone (returns JPEG image) |\n| `read_sensors()` | Return phone sensor readings as JSON: battery level/charging state, ambient light (lux), accelerometer (x/y/z) |\n| `recent_photos(limit=10)` | List recent photos on the phone: id, name, date_added, size, width, height |\n| `get_photo(id, max_width=1024, save_original=False)` | Fetch a specific photo by id (downscaled to max_width); use ids from `recent_photos`. **Also saves it** to the same Downloads folder as `get_reference` and returns the path, so the image survives the conversation. `save_original=True` archives full resolution instead of the downscaled copy |\n| `rename_photo(path, name)` | Rename a fetched file to describe what the image actually shows (`photo_1000024531.jpg` → `whiteboard_sprint_plan.jpg`). The AI calls this right after viewing, so it costs no extra tokens |\n| `get_reference()` | Fetch whatever the phone has designated as its **reference source** — a live camera frame, a captured photo, or an arbitrary file (incl. non-images) — saved to disk with path + metadata. The core of the file/capture bridge |\n| `get_cam_fps()` | Read the phone's current camera FPS setting (15 = high-performance, 2 = balanced, 0 = on-demand) |\n| `set_cam_fps(fps)` | Set the phone's camera FPS: `15` (high-performance), `2` (balanced, recommended), or `0` (power-saving / on-demand) |\n\nIf the phone's IP changes, the broker retries using mDNS autodiscovery before surfacing an error.\n\n---\n\n## Clip Bridge (Phone ↔ PC File & Clipboard)\n\n`onsense serve` automatically starts a clip daemon on **port 8770**. You can also run it standalone with `onsense clip`.\n\n### Phone → PC push (POST /clip)\n\nThe onSense Android app can push content to the PC:\n\n- **Images** are saved to disk as `latest.jpg` (in `ONSENSE_CLIP_DIR`, default `<tempdir>/onsense/`).\n- **Text files** are saved to disk.\n- **Other files** (video, PDF, etc.) are saved to disk by filename.\n- If `--set-clipboard` is active, images and text are also injected into the PC OS clipboard so you can paste with Ctrl+V immediately.\n\nBy default, `--set-clipboard` is **off** — files are saved to disk but the clipboard is not touched.\n\n### PC → Phone pull (GET /clip)\n\nOff by default. Enable with:\n\n```bash\nuvx onsense clip --allow-pull\n```\n\nWhen enabled, the phone can GET /clip to retrieve the current PC clipboard content (copied files first, then images, then text). Returns 204 if the clipboard is empty.\n\n### Ports and environment variables\n\n| Variable | Default | Purpose |\n| --- | --- | --- |\n| `PHONE_BASE` | (from pairing) | Phone HTTP base URL, e.g. `http://192.168.1.5:8080` |\n| `PHONE_TOKEN` | (from pairing) | Pairing auth token |\n| `ONSENSE_CLIP_DIR` | `<tempdir>/onsense` | Directory where pushed files are saved |\n| `ONSENSE_CLIP_MAX_MB` | `200` | Maximum incoming file size in MB (0 = unlimited) |\n| `ONSENSE_CLIP_ALLOW_PULL` | `0` | Set to `1` to enable PC→phone pull without the CLI flag |\n| `ONSENSE_CLIP_SET_CLIPBOARD` | `0` | Set to `1` to auto-inject into OS clipboard without the CLI flag |\n| `ONSENSE_TEST_FRAME` | (unset) | Local JPEG path returned by `get_live_frame` when phone is unreachable |\n\n---\n\n## Security\n\n**Local network only.** The PC-side services bind to all interfaces but reject connections from non-private IP addresses at the application layer. They are not intended to be exposed to the internet.\n\n**HMAC request signing.** The pairing token is never sent in cleartext. Every authenticated request is signed with HMAC-SHA256 over `METHOD\\npath\\ntimestamp\\nnonce\\nalgorithm`, keyed by a signing key derived from the pairing token via HKDF-SHA256 (a key separate from the encryption key), and carries `X-Ts` / `X-Nonce` / `X-Sig` / `X-Enc` headers. Servers verify in constant time, reject timestamps outside a ±300 s window, and reject reused nonces — so a sniffed request cannot be replayed and the token cannot be stolen off the wire. New Android installs generate a 128-bit random token, stored on the phone and in `~/.onsense/pair.json` (chmod 600) after pairing. (The `/health` endpoint is unauthenticated.) See [PROTOCOL.md](PROTOCOL.md) for the exact wire format.\n\n**Pull and clipboard injection are off by default.** `GET /clip` (phone pulls PC clipboard) and OS clipboard auto-injection (phone push → Ctrl+V) are disabled unless you explicitly pass `--allow-pull` or `--set-clipboard`.\n\n**Encrypted bodies (AES-256-GCM).** Sensitive payloads — camera frames, photos, sensor data, files, and clipboard content — are encrypted between the phone and your PC with AES-256-GCM (no cloud in between, so there is no third party to decrypt them). The key is derived from the pairing token via HKDF-SHA256 (a key separate from the signing key); each message uses a fresh 96-bit nonce, and the GCM tag authenticates the body and binds it to its request (so tampering or response substitution is rejected). A passive sniffer on the same Wi-Fi sees only ciphertext. Low-sensitivity metadata stays plaintext: the open `/version` and `/health` endpoints, HTTP error responses, and the saved-file path returned after a push. (Note: large transfers currently buffer the body in memory while encrypting/decrypting — chunked streaming for very large files is a future step. Transport-level TLS is also a possible future hardening.)\n\n**File size cap.** Incoming pushes are rejected if they exceed `ONSENSE_CLIP_MAX_MB` (default 200 MB). Set to `0` to remove the cap.\n\n**Reporting a vulnerability.** Please report security issues privately — see [SECURITY.md](SECURITY.md). Do not open a public issue for security reports.\n\n---\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n",
  "bytes": 13325,
  "sha": "693bf7a4a731d5dc06c34be5e9f64c49620accfe0abca4cab61f29b577b72a47",
  "repo_slug": "hufirst/onsense",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_hufirst_onsense_0a44ed37/readme"
}