# Connect your agent

You need an agent token and a computer you have approved. For a one-off connection without an account, start with [temporary setup](guest-connections.md). For an account, choose **Connect agent** in the dashboard's **Agents** pane.

## MCP

Use a client that supports a remote Streamable HTTP server with an Authorization header. Save this in its private MCP settings, replacing `<AGENT_TOKEN>` with your agent token:

```json
{
  "mcpServers": {
    "ezremote": {
      "url": "https://tunnel.masti.lol/mcp",
      "headers": { "Authorization": "Bearer <AGENT_TOKEN>" }
    }
  }
}
```

Client field names may differ. Connect, discover tools with `tools/list`, then call `devices`. Choose an online computer. Setup is complete when the client can list that computer and run a permitted check.

MCP accepts scoped agent tokens only. A dashboard token lasts 24 hours; computer and tool permissions are set separately under **Devices**.

### OAuth sign-in

For a client that supports MCP OAuth, enter `https://tunnel.masti.lol/mcp` as a remote
Streamable HTTP server. The client discovers the service's authorization server.
Sign in to ezremote, check the client identity and return address, then approve. No agent token needs
to be copied. Your account can connect before you add any computers.

The MCP client stays connected until you manually disconnect it. It renews hourly
credentials automatically. Computer access has its own timer. Every computer still needs local approval,
with its own permissions and duration. Revoke an OAuth authorization from
**Agents** in your dashboard. Its access credentials and refresh capability stop
working. Clients without refresh support need to authorize again when their
hourly credential expires.

Supported public-client modes are HTTPS Client ID Metadata Documents or dynamic
client registration at `https://tunnel.masti.lol/oauth/register`, with
`token_endpoint_auth_method: "none"`, exact registered redirects, `S256` PKCE,
or a CIMD plural method list containing `none`, and the `resource` parameter set to `https://tunnel.masti.lol/mcp` on authorization, exchange
and refresh. Redirects may use public HTTPS or loopback HTTP. Custom schemes,
implicit grants, device-code grants, client secrets and `private_key_jwt` are not
supported. A client requiring any of those modes cannot connect through this
OAuth implementation. Metadata URLs need a public hostname and a path, with no
custom port, credentials, query or fragment. Redirected metadata is rejected.

Claude's published identity remains supported. ChatGPT documents CIMD with
public token exchange, and Gemini CLI documents dynamic registration. These
protocols are supported here; real signed-in compatibility with those products
has not been verified. Choose their public-client mode where available. Product
settings and eligibility can differ. See the primary
[ChatGPT MCP guide](https://developers.openai.com/api/docs/mcp),
[OpenAI authentication guide](https://developers.openai.com/plugins/build/auth),
and [Gemini CLI MCP guide](https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md).

After connecting, ask your agent for a link to add your computer. It calls
`connect_computer` and shares `setupUrl`. Open it, sign in with the same account,
choose computer access duration, copy and run the setup command, and approve on
the computer. That link expires after ten minutes and never creates computer
access by itself. The original OAuth connection can then list the approved
computer. Do not use anonymous guest setup for an already connected account agent.

### Agents without a browser

An OAuth-capable agent can give the end user its authorization URL. The user
opens it in their own browser, signs in and approves. The client must still
receive the callback and exchange its single-use code using its private PKCE
verifier. An authorization link alone does not make an incompatible client work.
A loopback callback reaches the user's browser computer; a remote agent normally
needs a public HTTPS callback it controls or an explicitly forwarded local port.
Do not ask the user to paste authorization codes or agent tokens into chat.

The dependency-free SDK includes this flow:

```js
import {
  startOAuthConnection,
  completeOAuthConnection,
  refreshOAuthConnection,
} from "@ezremote/sdk";

const pending = await startOAuthConnection({
  url: "https://tunnel.masti.lol",
  clientName: "My agent",
  redirectUri: "https://agent.example.com/oauth/callback",
  // Reuse clientId after the first registration, or supply your CIMD URL.
});
// Store pending privately. Share only pending.authorizationUrl with the user.
// Receive the callback on the exact registered redirect URI, then:
const credential = await completeOAuthConnection(pending, receivedCallbackUrl);
// Save the credentials securely. Before access expires:
const renewed = await refreshOAuthConnection(pending, credential.refreshToken);
```

Serialize refresh calls and save each replacement refresh token before using
it. Reusing any spent refresh token revokes the whole authorization, including
credentials issued by concurrent refreshes. Never retry a refresh after an
uncertain network result. Reauthorize instead. `invalid_grant` with
`reconnect_required` means authorization expired or was revoked, not that a
computer is offline. Error responses include a safe stage/reason and reference;
share that reference with the operator, without codes, tokens or full URLs.

The agent host does not need the computer helper or Node to use remote MCP.

## REST

Send `Authorization: Bearer <AGENT_TOKEN>` on each call.

- List permitted computers: `GET /api/devices`
- Run a tool: `POST /api/invoke`
- Check recent activity: `GET /api/operations`

An invocation body looks like this:

```json
{
  "deviceId": "<DEVICE_UUID>",
  "operation": "files.read",
  "args": { "path": "~/notes.txt" },
  "requestId": "<FRESH_UUID>"
}
```

Use the UUID from the device list. Dotted REST operation names correspond to underscore MCP names, such as `files.read` and `files_read`. See [tool usage](operations.md).

If a call returns `outcome_unknown`, it may have run. Inspect activity or the resulting state before deciding what to do. Never automatically repeat it. Reusing its request ID prevents duplicate execution but does not recover the result.

## JavaScript SDK

The dependency-free `@ezremote/sdk` uses standard `fetch`. It is distributed as a tarball built from the private repository, not through public npm. Build the package from this repository.

Use `requestConnection` and `waitForConnection` for temporary setup, or construct `Ezremote` with an existing agent token and the service URL. List devices before selecting one. Store connection state privately on the agent host.

## Pi

Load the ezremote extension and configure its connection. Call `ezremote_devices` or run `/ezremote devices` to list computers.

Set `device` on `read`, `write`, `edit`, or `bash` to run that call on the selected computer. Without it, the call runs on the Pi host. Use `ezremote_transfer` for small files, `ezremote_bulk_transfer` for large files, and `ezremote_browser` for the approved browser.

If Pi Bash Narrator owns Bash, set `bashOwner: narrator` in the private ezremote Pi configuration to avoid registering Bash twice.

## Per-client setup

- [Claude Code](claude-code.md)
- [Codex](codex.md)
- [Cursor](cursor.md)
- [OpenCode](opencode.md)
- [Hermes](hermes.md)
- [OpenClaw](openclaw.md)
- [VS Code / Copilot](vscode-copilot.md)
- [Other MCP clients](generic-mcp.md)
- [Pi](pi.md)
- [Claude](claude.md)
- [ChatGPT](chatgpt.md)
- [Gemini](gemini.md)
- [Grok](grok.md)
