mcp-replay Vol. II · Field-book series paired with PlatAtlas
How it records Plates Open a trace Live mode Format
recording demo Open demo trace →
Published · mcpreplay.dev Edition 2026.05 WHILE-YOU-WATCH

The field-book
of every agent
traverse.

mcp-replay renders the JSON-RPC traces recorded by mcp-tape — the stdio proxy that sits between your agent and any MCP server. Every tool call, every argument, every byte that came back. The receipt for the journey.

Format
JSONL · v1 stable
Runtime
Static page · no backend
Live mode
WS · 127.0.0.1 only
Open the demo trace → Get mcp-tape ↗

Tip: drag & drop .jsonl traces anywhere — one file opens it, two files diff them, more files merge them by timestamp.

Plate · Frontispiece Field-book of an agent traverse — recorded by mcp-tape
TRAVERSE №024
2026·05·12 · 15:00
§ 01 · How it records

A stdio proxy, a JSONL file,
and a static page.

The simplest reliable design we could draw. mcp-tape is a transparent proxy — it wraps any MCP server, observes every frame on the in/out pipes, writes them to a JSONL file. mcp-replay reads that file and shows you everything.

⊙ STAGE 01 · CLIENT

MCP client

Claude Code · Desktop · Antigravity · Gemini

Whatever's making the agent calls. Sees the proxy as a normal MCP server — no client changes.

stdin · stdout (JSON-RPC 2.0)
in · request▶
▲ STAGE 02 · INSTRUMENT

mcp-tape

The recording theodolite

The stdio proxy. Sits between client and server, observes every frame on both pipes, redacts known secret shapes, writes the JSONL trace. Optionally serves a live websocket.

./mcp-traces/<session>.jsonl + ws://127.0.0.1:<port>
out · response◀
⊙ STAGE 03 · SERVER

MCP server

filesystem · github · sqlite · …

The real MCP server. Unmodified. Tape spawns it as a child process and pipes through it — no monkey-patching.

spawned via <command> after --
The point.  When an agent does something puzzling — a slow tool call, a prompt-injection that loaded too much, a write that touched the wrong path — the trace is the only honest answer. mcp-replay is what makes it readable.
§ 02 · Plates I–V

Five plates,
one trace.

A single .jsonl trace rendered five ways. Click a plate name to flip the leaf — same data, different question answered.

Plate I Itinerary — every frame, in order 13 frames · 5 tool-calls · 15.5s session
ALL IN OUT ERR 13 frames · 5 tool-calls
TIMESTAMPDIRMETHOD · ARGSFRAME #
15:00:00.045► INinitializeid 1 · protocolVersion=2024-11-05001
15:00:00.123◄ OUTinitializeid 1 · serverInfo=filesystem +0.08s002
15:00:00.131► INnotifications/initializednotification · no reply expected003
15:00:00.140► INtools/listid 2004
15:00:00.198◄ OUTtools/listid 2 · 3 tools · +0.06s005
15:00:02.500► INtools/callid 3 · list_directory /home/me/proj/src006
15:00:02.580◄ OUTtools/callid 3 · 3 entries · 142 b · +0.08s007
15:00:05.100► INtools/callid 4 · read_text_file index.ts008
15:00:05.220◄ OUTtools/callid 4 · 48 b read · +0.12s009
15:00:08.400► INtools/callid 5 · read_text_file /etc/passwd010
15:00:08.420◄ OUTtools/callid 5 · ERR -32602 Access denied · +0.02s011
15:00:12.000► INtools/callid 6 · write_file notes.md (30 b)012
15:00:12.090◄ OUTtools/callid 6 · wrote 30 b · +0.09s013
BEGIN · 2026·05·12 15:00:00.000Z END · 2026·05·12 15:00:15.500Z · exit 0 · 15.5s
Plate II Instruments — tools the agent reached for aggregate readings per instrument · sorted by use
5 INSTRUMENTS · 30 READINGS · 1 ERR (3.3%)
SORT · CALL COUNT ▾
INSTRUMENT (TOOL)CALLSPROFILEMEANp95ERR
⊙read_text_file12
95%
118 ms280 ms1 (8.3%)
⊙list_directory8
64%
84 ms180 ms—
⊙write_file6
48%
102 ms220 ms—
⊙search_files3
24%
412 ms740 ms—
⊙move_file1
8%
74 ms74 ms—
Reading.  read_text_file dominates this traverse (40% of calls) and accounts for the only error — an attempt at /etc/passwd the server correctly rejected. Investigate the prompt that produced it.
Plate III Shot List — request paired with response in / out shown as facing pages · errors flagged on the spine
5 CALLS · SORTED · BY ID
SHOT
03
80 ms
►IN · CLIENT → SERVER
tools/call · list_directory
{
  "path": "/home/me/proj/src"
}
◄OUT · result
[FILE] index.ts
[FILE] util.ts
[DIR] components
SHOT
04
120 ms
►IN · CLIENT → SERVER
tools/call · read_text_file
{
  "path": "/home/me/proj/src/index.ts"
}
◄OUT · result
export function main() {
  console.log("hello");
}
SHOT
05
20 ms
►IN · CLIENT → SERVER
tools/call · read_text_file
{
  "path": "/etc/passwd"
}
◄OUT · error -32602
Access denied: /etc/passwd is outside allowed directories
Plate IV Field Notes — the raw .jsonl in ink · out highway · err oxblood · meta + end faded
14 LINES · 2682 BYTES · UTF-8 NDJSON
examples/filesystem-short.jsonl
01{"v":1,"type":"meta","startedAt":"2026-05-12T15:00:00.000Z","label":"fs-demo",
02 "command":["npx","-y","@modelcontextprotocol/server-filesystem","/home/me/proj"]}
03{"t":"2026-05-12T15:00:00.045Z","dir":"in","raw":{"jsonrpc":"2.0","id":1,
04 "method":"initialize","params":{"protocolVersion":"2024-11-05",
05 "clientInfo":{"name":"claude-code","version":"2.0.0"}}}}
06{"t":"2026-05-12T15:00:00.123Z","dir":"out","raw":{"jsonrpc":"2.0","id":1,
07 "result":{"protocolVersion":"2024-11-05","serverInfo":{...}}}}
08{"t":"2026-05-12T15:00:02.500Z","dir":"in","raw":{"jsonrpc":"2.0","id":3,
09 "method":"tools/call","params":{"name":"list_directory",
10 "arguments":{"path":"/home/me/proj/src"}}}}
11{"t":"2026-05-12T15:00:02.580Z","dir":"out","raw":{"jsonrpc":"2.0","id":3,
12 "result":{"content":[{"type":"text","text":"[FILE] index.ts ..."}]}}}
13{"t":"2026-05-12T15:00:08.420Z","dir":"out","raw":{"jsonrpc":"2.0","id":5,
14 "error":{"code":-32602,"message":"Access denied: /etc/passwd ..."}}}
15{"t":"2026-05-12T15:00:15.500Z","type":"end","exitCode":0,"durationMs":15500}
► IN client→server   ◄ OUT server→client   ✕ ERROR SPEC v1 →
Plate V Discrepancy — two traces, overlaid ═ same · ≠ changed · + added · − removed
DISCREPANCY · 2 TRACES ALIGNED BY ORDINAL
3 SAME · 1 CHANGED · 1 ADDED · 1 REMOVED
TRACE A · session-2026-05-12-15:00.jsonl
⇄
TRACE B · session-2026-05-13-09:14.jsonl
list_directory(/proj/src)80ms
═
list_directory(/proj/src)76ms
read_text_file(index.ts)120ms
═
read_text_file(index.ts)132ms
read_text_file(/etc/passwd)20ms
≠
read_text_file(util.ts)88ms
—
+
list_directory(/proj/lib)92ms
write_file(notes.md)90ms
═
write_file(notes.md)88ms
move_file(notes.md)74ms
-
—
How to read.  Calls are paired by ordinal (1st of A vs 1st of B). ═ same · ≠ changed args/result · + added in B · - removed from B.
§ 03 · From zero to a trace

Five steps.
No staging, no upload.

The whole pipeline runs locally. mcp-tape install is idempotent; uninstall reverses every change. Traces never leave your machine unless you mail one.

STEP 01 · install

Install mcp-tape globally

npm i -g mcp-tape@alpha

Requires Node 20+. -g (not npx) so the wrapped binary path in your client config survives upgrades.

STEP 02 · wrap

Wrap every MCP server

mcp-tape install

Auto-detects Claude Code, Desktop, Antigravity, Gemini configs. Backs up to <file>.mcp-tape.bak. --dry-run previews.

STEP 03 · use

Restart your client & use it normally

(no command)

Every server writes a session trace to ./mcp-traces/ — one file per session per server, ISO-timestamped.

STEP 04 · open

Drop the trace here

drag-and-drop

Click Open local trace… or drag the .jsonl onto this page. The file stays in your browser.

STEP 05 · share

Mail the file

send the .jsonl

A collaborator drops the same file into mcpreplay.dev. No public URL, no upload, no auth — just the bytes.

§ 04 · Live mode

Or watch the frames
as they're recorded.

Add --serve to stream a running session straight into this page instead of opening a finished file. The websocket binds to 127.0.0.1 only — mcp-replay refuses any other host.

SURVEY IN PROGRESS

Open notebook. No file, just frames.

Run mcp-tape with --serve. It prints a localhost websocket URL on stderr. Open that URL here as ?live=… and frames arrive as they're written. New connections receive a snapshot, then live appends.

◑ Reconnects automatically · resumes with ?since=<lastSeq>
◑ Port-conflict fallback · always reads the actual port from stderr
◑ --no-file for serve-only (no on-disk trace)
> mcp-tape --serve -- npx -y \
    @modelcontextprotocol/server-filesystem ~/projects

mcp-tape: live mode on ws://127.0.0.1:7777/
  open: mcpreplay.dev/?live=ws://127.0.0.1:7777
§ 05 · The format · v1

One JSON object
per line.

The trace format is open and stable. Any producer can emit it. mcp-tape is the reference producer; the spec is documented enough that any team can reach the format without depending on a specific tool.

Line typeRequired?Description
meta first line The bookplate. {v, type, startedAt, label, command, mcpTapVersion?} — format version, start timestamp, server label, and the argv used to launch the server.
message (in) repeated An inbound bearing. {t, dir:"in", raw: <JSON-RPC>} — client → server frame. Request or notification.
message (out) repeated An outbound reading. {t, dir:"out", raw: <JSON-RPC>} — server → client frame. Response or error.
end last line The endpiece. {t, type:"end", exitCode, durationMs} — written when the proxy closes. exitCode 0 = clean.
Redaction. Producers may replace sensitive substrings with "[REDACTED]" before writing. Multi-server. One file per server; merge by timestamp at render time using ?trace=a;b;c. Versioning. Additive changes don't bump v; renderers ignore unknown fields.
Full format spec ↗
mcp-replay
VOL. II · ED. 2026.05

The field-book of every agent traverse. The receipt for the journey — readable, shareable, replayable.

Vol. I · PlatAtlas The plat — map of what your company is.
VOL. I →

Pair it with

  • mcp-tape (the recorder)↗
  • PlatAtlas (the plat)↗
  • Claude Code↗
  • MCP spec↗

Project

  • Source↗
  • README↗
  • Format spec↗
  • Live mode guide↗
  • File an issue↗

URL grammar

  • ?trace=<url>
  • ?trace=<a>;<b> · merge
  • ?diff=<a>;<b> · discrepancy
  • ?live=ws://127.0.0.1:<port>
  • ?since=<lastSeq> · resume
EDITION · 2026.05
HOSTED · mcpreplay.dev · Cloudflare Pages
⊙ Scale · Trace Duration
0 s10s1 m10m1 h+
SURVEYED BY · PlatAtlas
MIT LICENSE · 2026
◑ mcp-replay · mcpreplay.dev ※ set with Spectral & IBM Plex Mono on cream stock · paired with Vol. I
INST.
mcp-replay
connecting…

Couldn't load that trace.

Live-mode setup checklist

  1. Confirm mcp-tape is running with --serve (or MCP_TAPE_SERVE set on its environment). The stderr banner shows live mode on ws://127.0.0.1:<port>/ when it's listening.
  2. Confirm the port in your URL matches the port from that banner. Port fallback may have moved it: look for port N was unavailable — bound M instead.
  3. Live mode only accepts ws://127.0.0.1:<port> or ws://localhost:<port>. Remote hosts are refused intentionally.
  4. Security software or strict firewall rules may block loopback websockets. Try connecting from curl or another browser tab to isolate.

Full guide: docs/live-mode.md.

← back