**Remote MCP lets an agent that is not on your computer drive your Donut Browser.** The desktop app keeps an outgoing connection to the Donut API, and the API relays each MCP message to it. Your desktop answers every call itself, with the tools listed on the [MCP page](/docs/mcp).

Remote MCP is included in the Solo, Pro, Team and Enterprise plans. Free does not include it. It is the only way to use MCP with Donut: the local MCP server has been removed.

- **Solo:** MCP without browser automation. An agent can manage profiles, groups, proxies, VPNs and extensions, and use the human-in-the-loop tools.
- **Pro, Team and Enterprise:** also the browser automation tools (launch, navigate, click, type, read pages) and recipes.

## Set it up

1. In the desktop app, sign in and open **Settings → Integrations → Remote MCP**. Turn on remote control.
2. Open your [account page](/account). The remote control card shows **Desktop connected** when the app is online.
3. Under **Credentials**, click **New credential**. Copy it at once: it starts with `dmk_` and is shown one time only. The desktop app can also create one for you when you add a client.
4. Point your MCP client at the endpoint and send the credential as a bearer token.

```json
{
  "mcpServers": {
    "donut-browser-remote": {
      "url": "https://api.donutbrowser.com/api/mcp",
      "headers": { "Authorization": "Bearer dmk_…" }
    }
  }
}
```

The exact config format depends on your client. What matters is the URL and the `Authorization` header.

## Check the connection

Two calls tell you if the whole chain works, without launching a browser:

```sh
# Is a desktop connected, and does your plan include remote control?
curl -s https://api.donutbrowser.com/api/mcp/status \
  -H "Authorization: Bearer $DONUT_MCP_KEY"

# A round trip to your desktop that changes nothing on it.
curl -s -X POST https://api.donutbrowser.com/api/mcp/test \
  -H "Authorization: Bearer $DONUT_MCP_KEY"
```

`status` answers with `connected` and `entitled`. `test` answers with `ok: true` and `round_trip_ms` when your desktop replies.

## Transport

The endpoint speaks streamable HTTP. A client that used the old local server only changes the URL and adds the credential:

- `POST /api/mcp` sends a JSON-RPC message (`initialize`, `tools/list`, `tools/call`, `ping`).
- The `initialize` response carries an `mcp-session-id` header. Send it back on every later request.
- `DELETE /api/mcp` with that header ends the session.
- `GET /api/mcp` answers `405`: the server does not open streams on its own.

## Credentials

- Each credential acts as you. You can hold up to **10** live credentials at once.
- Revoke a credential on your [account page](/account). It stops working within about 10 seconds.
- A credential can call the MCP endpoint only. It cannot create, list or revoke credentials, so a leaked one cannot make itself permanent.
- One desktop holds the connection at a time: the first app to connect keeps it until it disconnects.

## Errors

| Status | Code                       | What to do                                                              |
| ------ | -------------------------- | ----------------------------------------------------------------------- |
| `401`  |                            | The credential is wrong or revoked. Create a new one.                   |
| `402`  | `MCP_REMOTE_NOT_ENTITLED`  | Your plan does not include remote control. It starts with Solo.         |
| `409`  | `MCP_BRIDGE_NOT_CONNECTED` | No desktop is connected. Open Donut Browser and turn on remote control. |
| `404`  | `MCP_SESSION_NOT_FOUND`    | The session ended. Send `initialize` again.                             |
| `429`  | `MCP_RATE_LIMITED`         | Wait `params.retryAfter` seconds, then retry.                           |
| `503`  | `MCP_BRIDGE_BUSY`          | Your desktop is busy. Retry after the `Retry-After` header.             |
| `502`  | `MCP_RESULT_TOO_LARGE`     | The answer was too large to relay. Ask for less.                        |
| `400`  | `MCP_INVALID_MESSAGE`      | The body is not a JSON-RPC message.                                     |

Tool errors, such as a profile that is already running, come back inside the JSON-RPC response. Two of them depend on your plan; their code is in `error.data.code`:

| Code                   | What to do                                                                                                                                                               |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `PLAN_REQUIRED`        | Your desktop refused a tool that launches, drives or reads a browser, because your plan does not include browser automation. Browser tools need Pro, Team or Enterprise. |
| `RECIPES_NOT_ENTITLED` | Recipe tools need a plan with browser automation: Pro, Team or Enterprise. On Solo, `tools/list` does not list them.                                                     |