> ## Documentation Index
> Fetch the complete documentation index at: https://docs.phoneuse.com/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP surface

> The tool surface an agent drives, and the contract that keeps it stable.

Phonebase is an MCP server. The unit of its public surface is a tool, not an
endpoint, and driving a device means calling tools rather than posting to
paths.

This page describes the shape of that surface and the rules it evolves under.
For the tools themselves, read the generated references:

<Columns cols={2}>
  <Card title="Tool reference" href="/mcp/reference" icon="wrench">
    Every tool, its input and output schema, its annotations, and the scope it
    needs.
  </Card>

  <Card title="Error reference" href="/mcp/errors" icon="triangle-exclamation">
    The error envelope and every code that can reach you.
  </Card>
</Columns>

Both are generated from the source the server actually registers, so they
cannot describe a tool that does not exist or omit one that does.

## How the surface is organised

Tools fall into four groups, and the groups are also the permission model.

| Group | What it covers |
| - | - |
| Read | Enumerating devices, observing a screen, and looking up a recorded outcome |
| Act | Gestures and input: tapping, swiping, typing, key presses, opening an app |
| Lease | Taking and returning exclusive use of a device |
| Runs | Handing a device to an autonomous run, and watching or ending it |

Each group maps to one scope: `devices:read`, `devices:act`, `devices:lease`,
and `runs:start`. The mapping partitions the surface exactly, so every tool
belongs to exactly one scope and a new tool that forgot to declare one would
be invisible to every credential. See [Authentication](/mcp/auth).

## What is not on this surface

Billing, purchasing, returning an order, revoking a key and leaving an
organisation are **not** on the MCP tool surface. The [REST API](/api/overview)
lets an API key with `orders:place` start a prepaid Checkout order; an admin
must issue or approve that key. Checkout still requires a person to complete
payment. Billing, returning an order, revoking a key and leaving an
organisation remain outside the MCP tool surface.

## Every result is typed

A successful call returns `structuredContent`, validated against the
`outputSchema` that tool publishes. The same value is also written into a text
block, which is what earlier consumers read.

That duplication is transitional. `structuredContent` is the machine readable
source of truth; the text block's promise to parse as JSON is on a deprecation
track and will retire once the typed shape has been stable for at least one
release cycle. Read `structuredContent` in new code.

Errors are the exception, and deliberately so. An error result carries its
envelope in the text block and **never** in `structuredContent`, because a
standard MCP client validates any `structuredContent` it sees against the
tool's output schema with no exemption for errors. See
[Reading a trace](/getting-started/trace) for how to consume one.

## Every tool is annotated

Each tool publishes all four MCP annotation hints explicitly, because an
omitted hint is indistinguishable from an unconsidered one, and because the
protocol's defaults are the wrong ones for a closed device surface.

* `openWorldHint` is `false` everywhere. Tools reach the fleet you configured
  and nothing else.
* `destructiveHint` marks the tools that can destroy state you did not create:
  the two run tools hand the device to an autonomous loop. A tap, a swipe, a hold, typing and a
  key press are not marked, because they do what a finger does and a client
  that prompts on the hint would prompt on every one of them. The flag that
  keeps a person in the loop is `_meta.requiresUserInteraction`, which is ours
  and which no "do not ask again" turns off.
* `idempotentHint` on action tools reflects the idempotency key contract: a
  retry that reuses its key replays the recorded outcome rather than executing
  again.
* `readOnlyHint` marks the tools that only observe.

The exact values per tool are in the [tool reference](/mcp/reference).

## Inputs are strict

Every tool's input schema rejects unknown keys rather than stripping them. A
misspelled `leaseid`, or a parameter your model invented, fails loudly instead
of travelling on to a real device with a silently missing argument.

`leaseId` is optional on every action and observation tool. If you provide it,
it must match the lease your organization currently holds; a stale id returns
`lease_mismatch` without acting. Omit it to use the current lease, or to take a
free device when your key has `devices:lease`. `release_device` still requires
the id. When your organization does not hold the device and the tool will not
take it for you, the refusal is `lease_required`.

## The surface is a frozen contract

The tool surface is frozen from the launch on **2026-09-25** onward. The tool
names and fields published on that date are the baseline, and from then on the
surface evolves under a published program rather than at will.

* **Additive changes** are allowed at any time: new tools, new optional
  parameters, new output fields, new error codes. Existing callers are
  unaffected.
* **A change of meaning without a change of shape** is declared rather than
  versioned: the tool description carries an `@since` note and the
  [changelog](/changelog) records what changed.
* **Renames and removals** are breaking. There is no `tap_v2` family and no
  parallel endpoint. A breaking change goes through the deprecation program in
  [Versioning and compatibility](/api/versioning#after-launch-additive-only),
  and it is announced in the [changelog](/changelog) before anything moves.

Adding a tool is a release cycle event, and a release cycle is a version bump
on the server info the client reads at connect time. That is how a client can
tell one published surface from another instead of counting tools.

<Note>
  Do not hard code a tool count. The count has changed more than once, and any
  number written into prose ages badly. Ask the server: `listTools` reflects
  your credential's true surface at that moment.
</Note>

## The surface is stateless

The HTTP transport builds a fresh server per request. There is no session
affinity, and a lease is not session state: it travels as an explicit
parameter on every call. That is what lets any process holding a `leaseId`
continue a session, and what makes `GET` and `DELETE` on the endpoint
meaningless.

Some capabilities do depend on a live bidirectional connection. Progress
notifications and human takeover both need one, and the stateless HTTP path
cannot carry either. See [Transports](/mcp/transports) and
[Progress and takeover](/mcp/progress-and-takeover).

## Where to go next

<CardGroup cols={2}>
  <Card title="Transports" href="/mcp/transports" icon="network-wired">
    stdio and hosted HTTP, and what actually differs.
  </Card>

  <Card title="Authentication" href="/mcp/auth" icon="key">
    Two credential channels and role-based scopes in one auth context.
  </Card>

  <Card title="First session" href="/getting-started/first-session" icon="mobile">
    The four beats of a session, one call at a time.
  </Card>

  <Card title="Agent tier" href="/agent/overview" icon="robot">
    Hand a goal to a worker instead of driving each step.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.