# Live mode

Watch a Claude / MCP-client session unfold in real time at
[mcpreplay.dev](https://mcpreplay.dev). Live mode pairs the `mcp-tape --serve`
websocket on your machine with a `?live=…` URL in the browser; nothing leaves
your machine, no public host is involved.

```
   ┌─────────────┐ stdio   ┌──────────┐ stdio  ┌────────────┐
   │ Claude Code │ ──────▶ │ mcp-tape │ ─────▶ │ MCP server │
   └─────────────┘         └──────────┘        └────────────┘
                                │
                                │ ws://127.0.0.1:7777 (snapshot + appends)
                                ▼
                       ┌─────────────────┐
                       │ mcpreplay.dev   │
                       │ ?live=ws://…    │
                       └─────────────────┘
```

The websocket lives on `127.0.0.1` only — no TLS, no auth, no remote-access
path. mcp-replay refuses to connect to any host other than `localhost` /
`127.0.0.1`.

## Set it up

**1. Make sure `mcp-tape` is recent enough.** Live mode needs the `--serve`
flag and a couple of subsequent fixes (port fallback, `--no-file`,
per-OS Claude Desktop paths):

```bash
npm i -g mcp-tape
mcp-tape --version
```

**2. Wrap with `--serve`.** The simplest way: run a single server through the
proxy with `--serve`:

```bash
mcp-tape --serve -- npx -y @modelcontextprotocol/server-filesystem /home/me
```

This prints something like:

```
mcp-tape: live mode on ws://127.0.0.1:7777/  (open https://mcpreplay.dev/?live=ws://127.0.0.1:7777)
mcp-tape: tracing to ./mcp-traces/2026-05-15T01-20-00-000Z-server-filesystem.jsonl
```

Open the printed URL — the page connects, hydrates from the snapshot of
everything-so-far, and starts streaming each new JSON-RPC frame as it lands.

**3. (Optional) Enable live mode for every wrapped server.** When you ran
`mcp-tape install`, each server got `mcp-tape` as its launch wrapper. To make
all of them stream live, set the env var your client passes to the wrapper:

```bash
# bash / zsh
export MCP_TAPE_SERVE=7777

# Windows / PowerShell
$env:MCP_TAPE_SERVE = 7777
```

Then restart your MCP client. Every server it launches will broadcast on its
own port — see [Port conflicts](#port-conflicts) below for how mcp-tape picks
a free port when 7777 is taken.

## `--serve` vs `MCP_TAPE_SERVE`

| Setting | What it does | When to reach for it |
|---|---|---|
| `--serve [PORT]` | Enables live mode for the current invocation only. `PORT` is optional; omitted = 7777. | One-off debugging from the command line. |
| `MCP_TAPE_SERVE=PORT` | Default port for every invocation that doesn't pass `--serve` explicitly. | All servers in an installed config. |

The CLI flag wins over the env var when both are set.

## Port conflicts

If port 7777 (or whatever you asked for) is already in use, mcp-tape **does
not fail.** It scans a short range above the requested port, then falls back
to an OS-assigned free port if none of those work. The stderr line tells you
which port it actually bound:

```
mcp-tape: port 7777 was unavailable — bound 7778 instead
mcp-tape: live mode on ws://127.0.0.1:7778/  (open https://mcpreplay.dev/?live=ws://127.0.0.1:7778)
```

The `?live=` URL on the second line is the one to open. (The first one is
just an informational warning.) If you launched several servers through their
wrapped configs, each one will print its own URL on its own port.

## `--no-file`: serve-only mode

For short debug sessions where you don't want a `.jsonl` file to clean up
afterwards, add `--no-file`:

```bash
mcp-tape --serve --no-file -- node my-server.js
```

This streams every frame to the websocket but writes nothing to disk —
useful when you're sharing a tab with someone over screen-share and just
want a clean run with no leftover traces.

`--no-file` requires `--serve`; on its own it has no other output path. If
the websocket fails to bind entirely (extremely rare; usually means every
fallback port refused), mcp-tape automatically reverts to writing a `.jsonl`
so the session isn't silently lost.

## Per-OS Claude Desktop config paths

`mcp-tape install` walks every detected MCP-client config and wraps every
entry. Claude Desktop's config lives in a different directory on each OS:

| OS | Location |
|---|---|
| Windows | `%APPDATA%\Claude\claude_desktop_config.json` |
| macOS | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| Linux | `~/.config/Claude/claude_desktop_config.json` |

(Older mcp-tape installs on Linux used `~/.config/claude/` with lowercase
`claude`; we still detect that one for back-compat.)

`mcp-tape install` does the right thing on every platform automatically — no
arguments needed. `mcp-tape install --dry-run` previews what it would do.

## Troubleshooting

**Connection chip stays on "connecting…" or flips to "disconnected".**

1. Confirm mcp-tape is actually running: the stderr banner with
   `live mode on ws://127.0.0.1:<port>` should be visible.
2. Confirm the URL bar matches the port from that banner. (Port fallback may
   have moved it.)
3. Check that nothing is blocking loopback websockets (security software,
   strict firewall rules). mcp-replay only ever talks to `127.0.0.1`.
4. Reload the page — the loader includes an auto-reconnect with `?since=`,
   but a hard reload resets every state machine cleanly.

**"Refused to connect to …" / "Couldn't connect to live mode."**

Live mode only accepts `ws://127.0.0.1:<port>` or `ws://localhost:<port>`.
This is intentional: there's no auth, so we won't connect to anything else.
The error panel also shows the live-mode setup checklist below the message.

**Frames don't appear, but the chip says "live".**

The session may not have produced any JSON-RPC yet. Wait for your client to
actually call a tool, or check the `.jsonl` on disk to confirm mcp-tape is
seeing frames.

**The chip shows "N dropped".**

The browser fell behind the server, or the server's ring buffer evicted
frames before the page could resume. The session continues; reload to get a
fresh snapshot of everything still in memory.

**"port 7777 was unavailable — bound NNNN instead"** appears every run.

A stale mcp-tape (probably from a crashed Claude session) is still holding
the port. Find and kill it, or just open the URL it prints — both work.
