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

# Transports

> stdio and HTTP publish the same tools. Here is what actually differs.

The control plane speaks MCP over two transports, and both publish the same
tool surface. That equality is checked rather than assumed, so a tool you can
call on one you can call on the other.

What differs is authentication, statefulness, and which optional protocol
features can physically reach you.

## At a glance

| | stdio | HTTP |
| - | - | - |
| How it starts | Your client spawns a local process | Your client posts to `POST /mcp` |
| Credential | None. The process boundary is the boundary | A bearer token, or a development token locally |
| Tenancy | Local mode only | Local or hosted |
| Lifetime | One long lived process | One server per request |
| Progress notifications | Yes | No |
| Human takeover | Yes | No |
| Device rows | Read once at startup | Read on every request |

Config blocks for both are in [MCP config](/getting-started/mcp-config).

## stdio

Your client spawns the control plane as a child process and talks to it over
standard input and output. There is no port and no token, because the trust
boundary is the process boundary: a process your own client spawned is already
running as you.

Three consequences follow, and all three are deliberate.

**Local mode only.** A process configured for hosted tenancy refuses to start
over stdio. If it did start, it would be a second control channel that bypasses
every check the HTTP endpoint performs, and the guarantee that all
authentication runs through one pipeline would stop being true.

**Nothing but the transport may write to standard output.** Standard output
is the wire. Diagnostics go to standard error, which is where the startup
line you see comes from.

**One control plane per device set.** The receipt ledger takes a lock at
startup, so a second process over the same devices is refused immediately and
loudly rather than surfacing later as an opaque failure mid action. If you are
already running an HTTP control plane over the same devices, stop it, or give
the stdio process its own ledger path.

The stdio process also binds an ephemeral loopback port for the agent tier's
device channel. It does so unconditionally at startup, even in a session that
never starts a run, so it is not accurate to say an unused feature adds no
surface. What is accurate is that the port is loopback only and every request
to it is guarded by a run token.

## HTTP

The endpoint is `POST /mcp`. It speaks JSON-RPC, in JSON response mode, and it
is stateless.

Stateless means a fresh server and transport are built for each request, and
that leases travel as explicit parameters rather than living in a session.
There is nothing to resume and nothing to tear down, which is why `GET` and
`DELETE` on the same path answer `405` with an `Allow: POST` header.

Two protections run on every request, and a client that trips either is
refused:

* **Host checking.** In hosted mode only the deployment's public host is
  accepted. Locally only loopback hosts are.
* **Origin refusal.** Any request carrying a browser `Origin` header is
  refused. Without this, a web page could rebind its own hostname to loopback
  and drive your device with no cross origin preflight standing in its way.
  Loopback binding stops callers on other machines; it does not stop this.

Authentication happens before anything else. Local mode may require a
development token; a hosted deployment requires OAuth or an API key. See
[Authentication](/mcp/auth).

## What stateless HTTP cannot carry

Two protocol features need a live bidirectional session, and this is the part
worth reading before you design a loop around them.

**Progress notifications.** An action blocks until the device commits, and on
the physical tier that takes as long as a physical movement takes. Over stdio,
a client that sent a progress token is told the action was dispatched and is
awaiting device commit. Over stateless HTTP there is no channel for a mid
request notification, so nothing arrives. The server does not fail the action
over it: progress is a transport capability, never a promise the tool surface
makes.

**Human takeover.** Elicitation is a request from the server to the client, so
it also needs a live session. Over stateless HTTP it never happens, and the
caller sees a terminal outcome instead.

The substitute for both is the same one, and it is not a workaround: pass an
`idempotencyKey` with your action and use `get_receipt` to find out what
happened. That path works on every transport, survives a lost response, and
survives a restarted client. See
[Progress and takeover](/mcp/progress-and-takeover).

## Choosing

Use **stdio** when your client spawns local processes, when you are working
against your own devices, and when you want progress and takeover. It is the
better development experience.

Use **HTTP** for anything shared, anything remote, anything with more than one
caller, and anything that needs a real credential. It is the only transport a
hosted deployment offers.

Whichever you choose, the calls you write do not change.


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