DSH Echo — Record MCP once. Replay it safely.

CI License: Apache-2.0 Node.js 22.19+ or 24+ DSH 0.1.1-rc.2

An unofficial DeepSeek Harness plugin for deterministic MCP record/replay.
Capture a real tool session once, replay it without credentials or network, and inspect every match and contract change inside DSH.

简体中文 · Website · Install · Quick start · Architecture · Security

> [!NOTE] > DSH Echo is not affiliated with or endorsed by DeepSeek. Version 0.1 is > installable from source but has not been published to npm. ## Why DSH Echo? MCP tools often sit in front of APIs, databases, and services that make agent tests slow, costly, nondeterministic, or unsafe. DSH Echo puts a cassette between DSH and the real MCP server: record once against reality, then replay the exact conversation offline. | Record reality | Replay safely | | --- | --- | | stdio and Streamable HTTP/SSE | deterministic request matching | | append-only, versioned JSONL | fail-closed on an unrecorded call | | secrets redacted before disk | no real server on a normal replay | | duration and response captured | structured nearest-call diff | | See what happened | Guard the contract | | --- | --- | | Session-scoped Web inspector | contract snapshots | | arguments, results, hit/miss state | classified schema drift | | compact Trajectory annotations | breaking-change CI gate | | second redaction pass before UI | scanned fixture export | ## Install into DSH Requirements: - Node.js 22.19+ on the Node 22 line, or Node.js 24+ - DeepSeek Harness compatibility baseline: 0.1.1-rc.2 - a fresh or explicitly selected DSH profile Build a local package and install it into an isolated profile: ~~~bash git clone https://github.com/bleakbelladonnals/dsh-echo.git cd dsh-echo npm ci npm run build npm pack --ignore-scripts export DSH_HOME="$(mktemp -d)" dsh plugin --profile web add ./dsh-echo-0.1.0.tgz dsh --profile web --no-open ~~~ Open a Session and choose the **Echo / 录制回放** tab. Installation adds the plugin with bindings: [], so it does not intercept or change an MCP server until you explicitly bind one. To remove it: ~~~bash dsh plugin --profile web remove dsh-echo ~~~ Omit the temporary DSH_HOME only when you intentionally want to install into your normal DSH profile. ## Record and replay Record a stdio MCP server. Redaction is enabled by default: ~~~bash dsh-echo record -o .dsh-echo/demo.cassette.jsonl -- \ node examples/fixture/server.mjs ~~~ Replay the same interaction offline: ~~~bash dsh-echo replay .dsh-echo/demo.cassette.jsonl ~~~ An unrecorded request returns JSON-RPC error -32601 and exits non-zero. The real server is not started. Live fallback exists only when you explicitly pass --on-miss passthrough together with a real server command. Streamable HTTP works through a loopback endpoint: ~~~bash # Record dsh-echo record -o .dsh-echo/http.cassette.jsonl \ --http http://127.0.0.1:3000/mcp \ --listen 127.0.0.1:6402 # Replay dsh-echo replay .dsh-echo/http.cassette.jsonl \ --listen 127.0.0.1:6402 ~~~ ## Connect one DSH MCP server DSH currently constructs MCP transports directly, so DSH Echo uses a reversible profile overlay instead of modifying DSH or editing your source profile in place: ~~~bash dsh-echo profile patch \ --source ./cordis.yml \ --out ./cordis.echo.yml \ --recovery ./cordis.echo.recovery.json \ --root ./.dsh-echo \ --server-row mcp-demo \ --cassette-id demo \ --cassette demo.cassette.jsonl \ --mode replay ~~~ The generated replay row does not contain the original server command. Record/passthrough rows use argv arrays, never a shell-concatenated command. The source file is untouched. Restore the saved row into another file with: ~~~bash dsh-echo profile restore \ --recovery ./cordis.echo.recovery.json \ --out ./cordis.restored.yml ~~~ Review generated YAML before applying it. HTTP record/replay is supported; HTTP passthrough is intentionally unavailable in v0.1. ## What appears in DSH For DSH tool names shaped like mcp__<serverName>__<tool>, the Host plugin adds a compact tool/result.meta.dshCassette reference. Full tool payloads stay in the cassette rather than being duplicated into the Session log. The **Echo / 录制回放** tab shows: - cassette identity, mode, transport, format, and redaction status; - each interaction's arguments, result, duration, and origin; - recorded, hit, miss, passthrough, and error state; - a structured argument diff for the nearest miss; - contract and schema drift, plus a snapshot action; - the annotations attached to the current Session/Trajectory. Host APIs accept configured cassette IDs rather than caller-provided paths. All cassette and snapshot paths must resolve below the configured root, and values are redacted again before reaching the browser. ## Contract gate Capture a baseline: ~~~bash dsh-echo snapshot --stdio "node examples/fixture/server.mjs" \ -f mcp-contract.snapshot.json ~~~ Fail CI when a tool is removed or a breaking schema change appears: ~~~bash dsh-echo snapshot --check --fail-on breaking \ --stdio "node examples/fixture/server.mjs" \ -f mcp-contract.snapshot.json ~~~ The repository's own CI fixture deliberately removes a tool and adds a required property, then asserts that the gate fails. ## Export a reviewable fixture Raw recordings live under the ignored .dsh-echo/ directory. Export is a separate, explicit step: ~~~bash dsh-echo export-fixture \ .dsh-echo/demo.cassette.jsonl \ --root ./fixtures \ --out demo.cassette.jsonl ~~~ Export refuses paths outside the selected fixture root and blocks detected secrets. A passing scan still requires human review: pattern-based redaction is defense in depth, not proof that a recording is safe to publish. ## Architecture ~~~mermaid flowchart LR DSH["DeepSeek Harness"] --> Adapter["DSH Echo profile adapter"] Adapter --> Core["record / replay core"] Core --> Live["real MCP server"] Core --> Tape[("versioned cassette")] Tape --> Core Core --> Session["Session annotations"] Session --> UI["Echo Web inspector"] ~~~ The transport engine remains usable as a standalone CLI, while the DSH layer lives under src/dsh/. See [architecture](docs/architecture.md) for lifecycle, trust boundaries, and integration decisions. ## Development ~~~bash npm ci npm run lint npm run typecheck npm test npm run test:e2e npm run audit:pack npm pack --dry-run --ignore-scripts ~~~ The current suite covers 463 tests, including real stdio record/replay, a no-server replay tripwire, profile recovery, path containment, UI redaction, contract drift, lifecycle cleanup, and package inspection. Acceptance runs use temporary homes, loopback listeners, isolated npm caches, and isolated DSH profiles; they do not read or write the user's DSH configuration. More detail: - [Validation evidence](docs/validation.md) - [Security model](docs/security.md) - [Upstream audit](docs/upstream-audit.md) - [DSH integration alternatives](docs/dsh-integration-options.md) - [Fixture server](examples/fixture/README.md) ## Upstream and license DSH Echo is derived from [`ivermin1123/mcp-cassette`](https://github.com/ivermin1123/mcp-cassette) at commit 9e48be26cbf1f7fca5edde142673a9b102a25e86 (upstream version 0.4.0). The retained engine provides stdio and Streamable HTTP/SSE record/replay, matching, redaction, contract diff, safety lint, and Vitest integration. See [UPSTREAM.md](UPSTREAM.md) and [NOTICE](NOTICE) for the exact import and modification record. Licensed under [Apache-2.0](LICENSE).