Skip to main content
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:

Tool reference

Every tool, its input and output schema, its annotations, and the scope it needs.

Error reference

The error envelope and every code that can reach you.
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. 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.

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 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 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.

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 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, and it is announced in the 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.
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.

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 and Progress and takeover.

Where to go next

Transports

stdio and hosted HTTP, and what actually differs.

Authentication

Two credential channels and role-based scopes in one auth context.

First session

The four beats of a session, one call at a time.

Agent tier

Hand a goal to a worker instead of driving each step.