# ChatGPT, Codex or Claude in the browser (/en/docs/wa/remote)

Connect a browser or phone MCP app, such as ChatGPT or Claude, to `wa` running on a computer you control. This guide connects the app through an HTTPS tunnel with sign-in, explains which actions the `wa` profile allows, and shows how to stop the connection or revoke access.

Terms used on this page:

* **MCP** (Model Context Protocol) is the standard way an AI app calls outside tools. `wa personal mcp` is the MCP
  server that comes with `wa`; the [MCP server guide](/llms.mdx/docs/wa/mcp/content.md) covers apps on your own computer.
* **Remote connection** means the app connects from its own servers, over the internet, to the address you give it.
  `wa personal mcp --http` serves the same tools over HTTP on `127.0.0.1` for this.
* **HTTPS tunnel** is a program that gives a port on your computer a public HTTPS address. This page uses
  [Tailscale Funnel](https://tailscale.com/docs/features/tailscale-funnel); Cloudflare Tunnel also works. You do not
  need to buy a domain for Funnel.
* **Public URL** is that address, for example `https://laptop.tail1234.ts.net`. You pass it to `wa` as
  `--public-url`, and the app gets it with `/mcp` at the end.
* **Access code** is a one-time code that `wa` prints in your terminal. The app's login page asks for it, so only a
  person who sees that terminal can connect an app.

```text
ChatGPT / Claude ──internet──▶ Tailscale Funnel ──▶ wa personal mcp --http (login) ──▶ WhatsApp
```

## What you can do [#what-you-can-do]

| Task | Where |
| --- | --- |
| Connect a browser or phone AI app to WhatsApp | [Start the tunnel and server](#start-the-tunnel-and-server), then [connect the app](#connect-the-app) |
| Run wa beside tg or max | [Run several servers together](#run-several-servers-together) |
| Use Cloudflare instead of Tailscale | [Alternative: Cloudflare Tunnel](#alternative-cloudflare-tunnel) |
| Allow less for one server run | [Permissions for this server process](#permissions-for-this-server-process) |
| Stop the server or log every app out | [Stop or revoke access](#stop-or-revoke-access) |

## Before you start [#before-you-start]

* **Whoever logs in reads your WhatsApp.** The login needs the access code from your terminal. Never put a tunnel in
  front of `wa personal mcp` without `--http`: that mode has no login at all.
* **Profile permissions decide what the app may change.** `deny` and `readonly` block writes; `ask` and `allow` let
  a requested MCP write go through without a form on the server. The app's own approval is separate and depends on
  its settings. The recipient list and the hourly limit still apply
  ([what an agent may do](/llms.mdx/docs/wa/mcp/content.md#what-an-agent-may-do), [permissions](/llms.mdx/docs/wa/permissions/content.md)).
* **The server shares the device's one connection with `serve`[↗](/llms.mdx/docs/wa/commands-setup/content.md#wa-serve "Command reference: wa serve").** Reads from the local store work while `serve`[↗](/llms.mdx/docs/wa/commands-setup/content.md#wa-serve "Command reference: wa serve")
  runs. Unlike the terminal and `wa personal mcp`, the HTTP server does not go through `serve`[↗](/llms.mdx/docs/wa/commands-setup/content.md#wa-serve "Command reference: wa serve"): a send, an edit or a
  reaction from the app is refused until `serve`[↗](/llms.mdx/docs/wa/commands-setup/content.md#wa-serve "Command reference: wa serve") stops, and `serve`[↗](/llms.mdx/docs/wa/commands-setup/content.md#wa-serve "Command reference: wa serve") cannot start while the server holds a connection
  ([one connection, shared with `serve`](/llms.mdx/docs/wa/mcp/content.md#one-connection-shared-with-serve)).
* **The computer that runs `wa` must stay on.** To use it from a phone, or from a laptop with nothing installed, run
  all of this on a small server that is always on, and link `wa` there (`wa personal setup --agent none`). That is
  one more linked device on your phone.
* **Who can add a remote connector:**

Your account must be able to add a remote MCP connector. Check the app’s current account and region requirements before starting the tunnel.

| App | Connector prerequisites |
| --- | --- |
| ChatGPT | [Connection guide](https://developers.openai.com/api/docs/guides/custom-mcp-server) |
| Claude | [Connection guide](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp) |
| Gemini | [Connection guide](https://support.google.com/gemini/answer/17209137?hl=en) |

If a step does not work,
[open an issue](https://github.com/WireCatLabs/whatsapp-cli/issues).

## Start the tunnel and server [#start-the-tunnel-and-server]

Install `wa` and link a profile first ([installation](/llms.mdx/docs/wa/installation/content.md), [linking](/llms.mdx/docs/wa/sessions/content.md)). Use the same
operating system user and profile for setup and for the MCP server. Put a profile before the command when needed:
`wa work mcp`. Tailscale is a separate installation.

Install [Tailscale](https://tailscale.com/download), sign in, and enable
[Funnel](https://tailscale.com/docs/features/tailscale-funnel): MagicDNS, HTTPS certificates and Funnel permission
must be enabled in your tailnet (your Tailscale network). The first Funnel command can print an approval link.

Keep two terminal windows open. The first runs the tunnel. Copy the HTTPS origin it prints into the second window
when asked. The origin is the address without `/mcp` or any other path. Keep an explicit public port such as `:8443`
in it.

The server listens on `127.0.0.1:8765` and prints a one-time access code. It never needs administrator rights; only
the tunnel may need them. What the app may do is the profile's own `permissions`; set them before you start
([permissions for this server process](#permissions-for-this-server-process)).

> > **Windows (PowerShell)**
> >
> > Install the Tailscale Windows app and sign in from its tray menu. Open a new PowerShell window after installing
> > Node.js, `wa` and Tailscale, so it sees the updated PATH. Use `.cmd` for npm commands to avoid PowerShell
> > execution-policy errors.
> >
> > First window: run PowerShell as Administrator for Funnel. The call operator `&` handles the space in the default
> > installation path; change the path if you installed Tailscale elsewhere.
> >
> > ```powershell
> > & "$env:ProgramFiles\Tailscale\tailscale.exe" funnel 8765
> > ```
> >
> > Second window: normal PowerShell, as the user who linked `wa`:
> >
> > ```powershell
> > $mcpPublicUrl = Read-Host 'Paste the HTTPS origin printed by Funnel (no /mcp)'
> > wa.cmd personal mcp --http --port 8765 --public-url $mcpPublicUrl
> > ```
> >
> > Run both processes in Windows itself. WSL is a separate environment: a Windows tunnel that points at the Windows
> > loopback address may not reach a server inside WSL.
>
> > **macOS (Terminal)**
> >
> > Install and sign in to the Tailscale app. Its CLI comes with the app; use this path when `tailscale` is not on PATH
> > ([Tailscale CLI guide](https://tailscale.com/docs/reference/tailscale-cli?tab=macos)). First Terminal window:
> >
> > ```sh
> > TAILSCALE_BE_CLI=1 /Applications/Tailscale.app/Contents/MacOS/Tailscale funnel 8765
> > ```
> >
> > Second Terminal window, as the same user who ran `wa personal setup`. This works in both zsh and bash:
> >
> > ```sh
> > printf 'Paste the HTTPS origin printed by Funnel (no /mcp): '
> > IFS= read -r mcpPublicUrl
> > wa personal mcp --http --port 8765 --public-url "$mcpPublicUrl"
> > ```
>
> > **Linux (Terminal)**
> >
> > Install Tailscale with the [Linux instructions](https://tailscale.com/download/linux), then sign in with
> > `sudo tailscale up`. First terminal window:
> >
> > ```sh
> > sudo tailscale funnel 8765
> > ```
> >
> > Second terminal window: your normal user, without `sudo`, so `wa` finds the device linked by `wa personal setup`:
> >
> > ```sh
> > printf 'Paste the HTTPS origin printed by Funnel (no /mcp): '
> > IFS= read -r mcpPublicUrl
> > wa personal mcp --http --port 8765 --public-url "$mcpPublicUrl"
> > ```

## Run several servers together [#run-several-servers-together]

Each server needs its own local port and its own public HTTPS address. For example, keep `tg` on local `8765` and
public `443`; run a second Funnel command with `--https=8443 8766`, and run `wa` with `--port 8766`. Use the second
Funnel origin, with `:8443`, for `wa`'s `--public-url` and for its connector address ending in `/mcp`. Use your
system's Tailscale command from above for the second Funnel command. Funnel supports public ports `443`, `8443` and
`10000` ([Funnel command reference](https://tailscale.com/docs/reference/tailscale-cli/funnel)).

## Webhook receiver for a bot [#webhook-receiver-for-a-bot]

A WhatsApp Business number on the Cloud API gets its messages only by webhook, so its receiver is published the same
way ([a WhatsApp Business number as a bot](/llms.mdx/docs/wa/bot/content.md)). `wa <profile> bot watch --public-url` listens on local `8766`.
With Funnel, publish it on a public port the MCP server does not use, such as `10000`:

```sh
tailscale funnel --bg --https=10000 8766
wa cloudtest bot watch --public-url https://<name>.ts.net:10000
```

Run `tailscale funnel status` first to see which public ports are taken. With a named Cloudflare Tunnel, add a second
public hostname whose local service is `http://127.0.0.1:8766`. Enter the same origin as the Callback URL in Meta's
App Dashboard. The receiver keeps only deliveries signed with your app secret.

## Alternative: Cloudflare Tunnel [#alternative-cloudflare-tunnel]

If you already use Cloudflare, point its tunnel at the same local MCP server. A fixed hostname needs a Cloudflare
account and a domain on Cloudflare. Follow the
[named tunnel setup](https://developers.cloudflare.com/tunnel/get-started/): install `cloudflared` for your system,
create the tunnel in the dashboard, start its connector and add a public hostname such as `mcp.example.com`. Set its
local service to `http://127.0.0.1:8765`, and run MCP on that same computer.

Start the server in a second terminal:

```sh
wa personal mcp --http --port 8765 --public-url https://mcp.example.com
```

Add `https://mcp.example.com/mcp` to your AI app with OAuth and DCR, as described in
[connect the app](#connect-the-app). `--public-url` is the public origin used for login, not the local address and not
the `/mcp` path. The tunnel does not change profile permissions. Check the JSON at
`https://mcp.example.com/.well-known/oauth-protected-resource/mcp`, then login and the tool list.

**A temporary Quick Tunnel does not work.** `cloudflared tunnel --url http://127.0.0.1:8765` gives a random
`trycloudflare.com` address with no account or domain, but
[Quick Tunnels do not support SSE](https://developers.cloudflare.com/tunnel/get-started/quick-tunnels/) (server-sent
events, the way the HTTP MCP server streams answers). Choose Funnel or a named tunnel.

## Connect the app [#connect-the-app]

The address for the app is your tunnel origin with `/mcp` at the end, for example
`https://<device>.<network>.ts.net/mcp`. Add it as a remote MCP connector:

* **ChatGPT or Codex web:** open **Plugins**, choose **+ Add custom MCP server**, and create a plugin with the
  `/mcp` address and OAuth, as the [OpenAI connection guide](https://developers.openai.com/plugins/quickstart)
  describes. Connect with the access code, install the plugin, and turn it on in your chat (or mention it with `@`).
  Choose dynamic client registration (DCR) when offered; the server supports DCR and the S256 code challenge that
  the [OpenAI OAuth requirements](https://developers.openai.com/plugins/build/auth) ask for.
* **Claude:** add a custom connector with this address, as described in
  [Claude custom connectors](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp).
  In the connector settings you can also set each tool to always allowed, needs approval, or blocked.

The three tools work without MCP prompts, resources or elicitation (questions the server asks the app), so apps
that support only tools can use them.

The app opens the `wa` login page. Check the line that says where the login goes: it must be the app you are
connecting, such as `chatgpt.com` or `claude.ai`. Then type the access code from your terminal. The code expires
after ten minutes; a new code is printed after each login. Five wrong codes stop logins for 30 seconds; after
that a new code is printed.

The app stays logged in as long as it uses the connection at least once every 30 days; it renews its login by
itself. After 30 days without use, it asks for a new code.

## Permissions for this server process [#permissions-for-this-server-process]

The server cannot see whether the app asked you before a call. If the app always allows a tool, the next call can go
through without a new question.

The profile's `permissions` decide what the app may do. Repeat `--permission key=level` to narrow them for this
server process only: `--permission messages.send=readonly` stops sending. It never loosens: an override that would
give more than the profile — `readonly` to `allow`, a `deny` to anything — stops the server at startup with
`validation_error` naming the key. An override leaves the saved configuration, the recipient list and the hourly
limit as they are. To let the app send from a read-only profile, change the profile first:
`wa personal config set permissions.messages.send allow`. Permission keys are on the [permissions](/llms.mdx/docs/wa/permissions/content.md)
page.

## Stop or revoke access [#stop-or-revoke-access]

Press Ctrl-C in both terminal windows to stop the server and this tunnel. Start them again with the same commands;
app logins survive a normal server restart. Stopping the server also lets go of the device's connection, so `serve`[↗](/llms.mdx/docs/wa/commands-setup/content.md#wa-serve "Command reference: wa serve")
can start again.

If you started Funnel with `--bg`, Ctrl-C does not stop that background route. Check `tailscale funnel status` and
turn off only its public port, for example `tailscale funnel --https=443 off`. Do not use `funnel reset` when
another server uses Funnel: it clears all routes.

Taking access back from apps is separate from stopping a process. To log out every app for a profile:

```sh
wa personal mcp --revoke --json
```

Those apps must log in again with a new code. This does not unlink the device from WhatsApp.

## If it does not work [#if-it-does-not-work]

* **The app says it cannot connect:** open `https://<device>.<network>.ts.net/.well-known/oauth-protected-resource/mcp`
  in a browser. It must show a short JSON. If it does not, Funnel is not running, is not enabled in your Tailscale
  network, or points at another port.
* **The login page says "Too many wrong codes":** after five wrong codes it waits 30 seconds, then the terminal shows a
  new code. If you did not type them, someone found your address: stop the server, and think about a new device name
  in Tailscale.
* **The app connects, but there are no tools:** `wa personal mcp check` checks that the MCP server starts and lists
  its tools. It does not check the link to WhatsApp or the tunnel; `wa personal doctor` reads the device's state.
* **Reading works, writing fails:** the HTTP server does not go through `serve`[↗](/llms.mdx/docs/wa/commands-setup/content.md#wa-serve "Command reference: wa serve"), so a write is refused while `serve`[↗](/llms.mdx/docs/wa/commands-setup/content.md#wa-serve "Command reference: wa serve")
  runs; the error names it. Otherwise
  check the profile's permissions and any `--permission` overrides: `deny` and `readonly` block writes whatever the
  app approves.
* **You need a record of the calls:** `wa personal runs list` shows them. Successful calls appear only when recording
  is on; failed calls are kept by default, unless recording was turned off with `"record": false` or `--no-record`
  ([diagnostics](/llms.mdx/docs/wa/diagnostics/content.md)).
