# MCP tools

> Every MCP tool of fones, its arguments and its scope, plus the 0 to 1000 grid, the look-act-look loop and errors.

Source: https://fones.ai/docs/mcp

The MCP server at `https://api.fones.ai/mcp` has these tools. They share their code with the [REST API](https://fones.ai/docs/api), so the two always agree. A tool your key has no scope for is not listed to your agent.

## The loop

Phones change under your agent: a dialog opens, a page loads late. The tools are built for a short loop:

1. **Look.** `screenshot` for an image, `ui_snapshot` for the screen as text. `ui_snapshot` is cheaper and gives exact centers; take a screenshot when the screen is mostly pictures.
2. **Act.** One `tap`, `swipe`, `type_text` or `press_key`.
3. **Look again** before the next action, to see what the action did.

## Coordinates

Every point is on a grid from 0 to 1000 on both axes, whatever the phone's resolution: `(0, 0)` is the top left corner, `(1000, 1000)` the bottom right, `(500, 500)` the middle. `ui_snapshot` returns each node's center on the same grid, so a node's `x` and `y` can go straight into `tap`.

## Which phone

Tools that act on a phone take `phone_id` (`ph_...`, from `list_phones`). When the account has exactly one phone, `phone_id` can be left out.

## Tools

### Look

| Tool | Arguments | Scope | What |
| --- | --- | --- | --- |
| `list_phones` | `limit?, before?` | `phones:read` | Your phones with status, number and IP. |
| `get_phone` | `phone_id` | `phones:read` | One phone. |
| `screenshot` | `phone_id` | `phones:read` | The screen as an image. |
| `ui_snapshot` | `phone_id` | `phones:read` | The screen as nodes: text, description, id, class, clickable, center x and y and bounds on the grid. |

### Act

| Tool | Arguments | Scope | What |
| --- | --- | --- | --- |
| `tap` | `phone_id, x, y` | `phones:control` | Tap a point. |
| `long_press` | `phone_id, x, y, ms?` | `phones:control` | Press and hold a point. |
| `swipe` | `phone_id, x1, y1, x2, y2, ms?` | `phones:control` | Swipe from one point to another. |
| `type_text` | `phone_id, text` | `phones:control` | Type into the focused field. |
| `press_key` | `phone_id, key` | `phones:control` | One key: home, back, enter, recents, delete, tab, power, wake. |
| `open_app` | `phone_id, package` | `phones:control` | Start an app by package name. |
| `shell` | `phone_id, command` | `phones:control` | Run a root shell command. Output is cut at 64 KB. |

### Apps

| Tool | Arguments | Scope | What |
| --- | --- | --- | --- |
| `list_apps` | none | `phones:read` | The app catalog. |
| `install_app` | `phone_id, app` | `phones:control` | Install an app from the catalog. |

### Texts

| Tool | Arguments | Scope | What |
| --- | --- | --- | --- |
| `read_sms` | `phone_id, since?, limit?, before?` | `phones:read` | Texts on the phone's number, newest first, with the number that got each and the code when one is found. |

### Buy

| Tool | Arguments | Scope | What |
| --- | --- | --- | --- |
| `get_catalog` | none | `phones:read` | What can be bought, the prices and your balance. |
| `buy_phone` | `tier, model?, number?, proxy?, label?, idempotency_key?` | `phones:buy` | Buy a phone from the balance. |
| `add_number` | `phone_id, idempotency_key?` | `phones:buy` | Add a US number to a ready phone: the rest of the period now, then renewed with it. |
| `add_proxy` | `phone_id, idempotency_key?` | `phones:buy` | Add a static IP to a ready phone: the rest of the period now, then renewed with it. |
| `release_phone` | `phone_id, now?` | `phones:buy` | Stop renewing, or release at once with `now`. One still provisioning is cancelled and refunded. |

Keys for `press_key`: `home`, `back`, `enter`, `recents`, `delete`, `tab`, `power`, `wake`. `ms` is a duration in milliseconds. `since` is an ISO 8601 time.

## What comes back

- `screenshot`: an image (PNG or JPEG).
- `ui_snapshot`: `{ "nodes": [...] }`, each with `text`, `description`, `id`, `class`, `clickable`, its center `x`, `y` and its `bounds` `[left, top, right, bottom]`, all on the grid; empty nodes nothing can tap are left out.
- `read_sms`: texts newest first, each with `number` (the number that received it), `from`, `text`, `code` (the verification code found in the text, or null) and `received_at`.
- `buy_phone`: the new phone with status `provisioning`. It is ready when `get_phone` says `ready`, or when your webhook gets `phone.ready`. Pass `idempotency_key` and a retry never buys twice.
- `add_number`, `add_proxy`: the phone, with `number_status` or `proxy_status` `provisioning` until the number or IP shows. They charge the rest of the phone's current period now; once ready the add-on is part of the phone's renewal price. They accept `idempotency_key` too.
- `list_phones`, `read_sms`, `list_apps`: `{ "data": [...] }`; the first two page with `limit` and `before` (`next_before`).
- Actions: `{ "ok": true }`, or for `shell` `{ "ok": true, "output": "..." }`.

## Scopes

| Scope | Tools |
| --- | --- |
| `phones:read` | look at phones, screens, texts, apps, catalog |
| `phones:control` | gestures, text, keys, apps, shell |
| `phones:buy` | buy phones, numbers and static IPs, release phones |

## Errors

A failed call returns an error result with `{ "error": { "code", "message" } }`, the same body as REST. The codes are listed in the [REST API](https://fones.ai/docs/api#errors). Three worth knowing: `phone_not_ready` (the phone is still provisioning, try again shortly), `insufficient_balance` (top up first; `missing_cents` says how much) and `phone_error` (the phone did not answer: retry, or look first when the message says so).

Two cases are protocol errors (JSON-RPC `-32602`) instead, and nothing runs: arguments that do not fit the tool (a wrong type, a value out of range, or an argument the tool does not have, such as `numbr`), with a message naming the argument; and a tool outside your key's scopes, which answers `Tool <name> not found`. That is on purpose: such a tool is not listed for that key either, so to your agent it does not exist.

The server is stateless: `tools/list` and `tools/call` work without an `initialize` first.
