Skip to main content

mcptape

mcptape

ci PyPI Python Dependency License: MIT

mcptape is a tape recorder for the Model Context Protocol: wrap any MCP server, record a session once (REC ⏺), replay it offline forever (PLAY ▶), and diff exactly where the tool behavior drifted (⏩).

Your agent talks to MCP servers — filesystem, browser, search, your own tools. Those servers change, their tools change, their results change, and your agent breaks in ways you cannot reproduce without paying for a dozen live runs. mcptape records the whole session — every tools/call, every result, every notification — as local JSONL. From then on, replay serves the recorded results offline for free and byte-identically, and one command diffs two recordings to the first drifted call.

Zero dependencies: pure Python standard library. Nothing leaves your machine — a trace is a local file.

mcptape demo: record / replay / diff, rendered from real command output

(The screenshot is rendered from real command output by docs/make_demo_svg.py — not staged.)

中文文档:README.zh-CN.md

How it works

flowchart LR
    A["MCP client<br/>(your agent)"] -->|"stdio"| B["mcptape rec<br/>transparent proxy ⏺"]
    B -->|"stdio"| C["real MCP server"]
    B --> D[".mcptape/runs/<br/>trace.jsonl + meta.json"]
    A -->|"stdio"| E["mcptape play<br/>served from tape ▶"]
    D -.->|"byte-for-byte"| E
    F["second recording"] --> G["mcptape diff<br/>first drifted call"]
    E -.-> G
    G -->|"exit 0 / 1 / 2"| H["CI ✅ ❌"]

In rec mode mcptape is a transparent proxy: it spawns the real server, forwards every JSON-RPC line both ways unchanged, and files each message to the tape. Your agent cannot tell it is being recorded.

In play mode mcptape is the server: it answers initialize, tools/list and tools/call from the tape — no child process, no network, identical results.

30 seconds

pip install mcptape

# 1. record a session: put mcptape between your client and any MCP server
python examples/demo_client.py -- mcptape rec --name demo-stable -- python examples/demo_server.py

# 2. replay it offline -- same results, no server process, $0.00
python examples/demo_client.py -- mcptape play demo-stable

# 3. the server changes; record again and diff
MCPTAPE_DEMO_DRIFT=1 python examples/demo_client.py -- mcptape rec --name demo-drift -- python examples/demo_server.py
mcptape diff demo-stable demo-drift

Real output (paste the commands and you get this verbatim):

=== replay ===
server: demo-tools
tools: echo, add
echo: tapedeck, but for MCP
add(2, 3): 5

=== diff demo-stable demo-drift ===
run A: demo-stable (11 events)
run B: demo-drift (11 events)
event 10 (s2c response):
  result changed
  A: {"result": {"content": [{"type": "text", "text": "5"}]}}
  B: {"result": {"content": [{"type": "text", "text": "6"}]}}
  context (events …i):
    c2s/request tools/call
    s2c/notification notifications/message

diff exit codes are designed for CI: 0 identical, 1 output drift, 2 structural drift.

In your agent config

rec is one config edit away from any stdio-based MCP client (Claude Desktop, Claude Code, Cursor, ...):

{
  "mcpServers": {
    "my-tools": {
      "command": "mcptape",
      "args": ["rec", "--name", "daily-run", "--", "python", "-m", "my_tools"]
    }
  }
}

Flip "rec" to "play", "daily-run" and the agent runs against the tape — full regression pass with zero live calls. The client cannot tell the difference: same tools, same results, same notifications.

Why matching is by content, not position

tapedeck aligns calls by position; MCP clients pipeline and run tool calls in parallel, so positional alignment would false-positive on every concurrent session. mcptape matches each incoming call against the earliest unconsumed recorded exchange with the same method and JSON-equal arguments. Identical repeated calls consume identical recorded entries in order; reordered or parallel calls match freely; a call whose arguments changed matches nothing — that is the drift, reported with the exact arguments.

initialize is matched leniently on parameters (client name/version/protocol bumps are not behavior drift) and its recorded capabilities answer is served verbatim.

Why not LangSmith / LangFuse / OTel platforms

Cloud observability mcptape
Position watch runs (dashboard) reproduce runs (recorder)
Replay re-calls the tools (state changed, money spent) replays recorded results (free, deterministic)
Data uploaded to a third party local files only
Dependencies SDK + reporting config zero, two commands

Complementary, not competing: platforms show trends; mcptape does forensics and regression. The same role tapedeck plays for model calls, one layer down.

CLI

mcptape rec --name RUN -- <server command>   # proxy + record a live session
mcptape play RUN [--lenient]                 # serve a recording as an MCP server
mcptape diff A B                             # first drift + context; exit 0/1/2
mcptape ls                                   # list recorded runs
mcptape report RUN                           # per-message table

mcptape ls and report, rendered from real command output

Trace format

One directory per run, two plain files:

.mcptape/runs/20261031-011921-demo-stable/
    meta.json     # server command, protocol version, counts
    trace.jsonl   # one JSON-RPC message per line, with request echoes
{"v": 1, "i": 10, "dir": "s2c", "kind": "response", "id": 4,
 "payload": {"result": {"content": [{"type": "text", "text": "5"}]}},
 "for_method": "tools/call", "for_params": {"name": "add", "arguments": {"a": 2, "b": 3}},
 "wall_ms": 0.9}

Every response carries an echo of the request it answers, so each line is self-contained — replay and diff never need to join across lines.

Honest boundaries

  • stdio transport only (v0.1). HTTP/SSE-based MCP servers are not wrapped yet; streamable-HTTP support is on the roadmap.
  • Sessions with server-initiated requests cannot be replayed. If the server asked the client to sample or elicit, the tape would have to impersonate the client — play refuses such recordings up front (exit 2); diff still works on them.
  • Replay is deterministic only up to the tape. Client-side logic that reads the clock, uses randomness, or branches on anything outside the session may take a different path than during recording — that is true of every record/replay tool and is exactly what diff between two real recordings is for.
  • Server-side state is frozen at record time. Replaying a tools/call that wrote a file serves the recorded result without writing again; replay never touches the real world. If you need the side effects re-applied, that is a live run, not a replay.
  • No monkeypatching. rec is a proxy process; the client config opts in explicitly.

Roadmap

  • streamable-HTTP transport wrapping
  • --subset replay (serve only named tools from the tape, live for the rest)
  • trace signing (tamper-evident hash chain)
  • OTel GenAI span import/export

The tape suite

The same "record once, replay forever" idea, at three layers:

  • tapedeck — model & tool calls inside your agent (Python, PyPI)
  • mcptape — the MCP tool-protocol boundary (this repo)
  • mocktape — HTTP traffic behind your frontend (TypeScript, npm)

Design trade-offs live in the source docstrings; every image in this README is rendered from real command output.

Metadata

Release files for mcptape 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mcptape 0.1.0
File Size Uploaded Provenance Actions
mcptape-0.1.0.tar.gz 21.9 kB With provenance Details for mcptape-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for mcptape 0.1.0
File Interpreter ABI Platform Actions
mcptape-0.1.0-py3-none-any.whl Python 3 none any Details for mcptape-0.1.0-py3-none-any.whl

Total release size: 40.1 kB

Release files / mcptape-0.1.0.tar.gz

Download URL mcptape-0.1.0.tar.gz
Size 21.9 kB
Tags Source
SHA-256 checksum
How to use checksums
11aae19587480a31f5e6b6f6b47b772720e0b004bf2dd2744a66ef9b5d3d91a1
BLAKE2b-256 checksum
How to use checksums
cdef74fa0ae833314fd19fcf7d16c547196675daca4f28f7ed73d2f62087c842
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

Release files / mcptape-0.1.0-py3-none-any.whl

Download URL mcptape-0.1.0-py3-none-any.whl
Size 18.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
17d4d27967d44f7f9e8e7bacb2002a49fce4887a338f341e4657e7a629147db0
BLAKE2b-256 checksum
How to use checksums
1393875cf6c10453a799066f20759453cef475dfd0727eba9b8426c0cb61dc65
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

Security for mcptape 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for mcptape-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page