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

> The exact config block for each transport, and what changes between them.

Phonebase speaks MCP over two transports. Both publish the same tool surface,
so the config block is the only thing that differs.

* **HTTP** talks to a control plane over the network, at `POST /mcp`.
* **stdio** spawns the control plane as a local child process and talks to it
  over its standard input and output.

Pick HTTP unless your client can only spawn processes. See
[Transports](/mcp/transports) for the behavioural differences that follow from
this choice.

## HTTP against a local control plane

Start the control plane first. It listens on loopback and reads its device
rows from your machine.

```bash theme={null}
PHONEBASE_DEV_TOKEN=dev corepack pnpm dev
```

```text theme={null}
phonebase-api listening on http://127.0.0.1:8788 (MCP at /mcp, tenancy=local)
```

Then give your client the endpoint:

```json theme={null}
{
  "mcpServers": {
    "phonebase": {
      "type": "http",
      "url": "http://127.0.0.1:8788/mcp",
      "headers": { "Authorization": "Bearer dev" }
    }
  }
}
```

`PHONEBASE_DEV_TOKEN` is the local development credential. Set it, and the
endpoint requires that exact value as a bearer token. Leave it unset, and the
endpoint accepts unauthenticated requests, which is safe only because local
mode refuses to bind anywhere but loopback.

<Warning>
  Local mode grants every scope. It is a single operator on a single machine,
  not a permission model. Do not expose that port.
</Warning>

## stdio

Your client spawns the process and owns its lifetime. There is no port, no
token, and no header.

```json theme={null}
{
  "mcpServers": {
    "phonebase": {
      "command": "corepack",
      "args": ["pnpm", "cli", "mcp", "--stdio"],
      "cwd": "/absolute/path/to/phonebase-api"
    }
  }
}
```

Two things follow from the process boundary being the trust boundary:

* stdio serves **local mode only**. A process configured for hosted tenancy
  refuses to start over stdio, because it would be a second control channel
  past every check the HTTP endpoint performs.
* Device rows are read **once, at startup**. Editing them with
  `phonebase rows` needs a client restart, unlike the HTTP server, which reads
  rows on every request.

The stdio process also binds an ephemeral loopback port for the agent tier's
device channel, unconditionally, even in a session that never starts a run.
That port is loopback only and is guarded by a run token, but it is there.

## Connecting a hosted control plane

The hosted control plane lives at `https://mcp.phonebase.co/mcp`. Phonebase is
onboarding design partners, and keys are issued through
[phonebase.co](https://phonebase.co). See
[Getting a key](/mcp/auth#getting-a-key).

A hosted control plane replaces the development token with one of two real
credential channels:

```json theme={null}
{
  "mcpServers": {
    "phonebase": {
      "type": "http",
      "url": "https://mcp.phonebase.co/mcp",
      "headers": { "Authorization": "Bearer pbk_<keyId>_<secret>" }
    }
  }
}
```

Most clients cannot expand an environment variable inside a config file, so
paste the key value itself. With Claude Code, one command does it:

```bash theme={null}
export PHONEBASE_API_KEY="pbk_<keyId>_<secret>"
claude mcp add --transport http phonebase https://mcp.phonebase.co/mcp \
  --header "Authorization: Bearer $PHONEBASE_API_KEY"
```

The endpoint's protected resource metadata names Clerk
(`https://clerk.phonebase.co`) as its authorization server. That server offers
no dynamic client registration, so a client that tries to register itself and
open a browser sign in has nothing to register with, and a browser flow from a
generic MCP client has not been verified end to end. Use an API key. See
[Authentication](/mcp/auth).

Keep the client entry named `phonebase`. This site's docs MCP server is a
different server with a different name, and you want to be able to tell them
apart in a client list. See [For agents](/getting-started/for-agents).

## Checking the connection

The MCP Inspector talks to the endpoint without a client in the way:

```bash theme={null}
npx @modelcontextprotocol/inspector --transport http \
  --server-url http://127.0.0.1:8788/mcp \
  --header "Authorization: Bearer dev"
```

Call `list_devices` first. It needs no lease, so it answers immediately, and
what it returns tells you whether the control plane can see your hardware.

If the tool list looks shorter than you expect, that is authentication rather
than a fault: a tool your credential is not scoped for is never registered, so
it does not appear at all.

## Two things clients get wrong

**Host and origin are checked.** The HTTP transport refuses requests carrying
a browser `Origin` header, and accepts only the host it was configured for.
Without those checks any web page could rebind its own hostname to loopback
and drive your device. A client that sets a browser style origin will be
refused.

**`GET` and `DELETE` on `/mcp` return 405.** The endpoint is stateless: there
is no stream to resume and no session to tear down. Post your requests.


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