Skip to content
AR mcp-arcade
GameDay · v0.2.0

Battle your MCP. Keep the tape.

Four named experiments. A scripted agent or a local model in the seat. A score that only counts what crossed the wire. What comes out is a receipt, a tape with no score on it, and a dataset. The tape is what the arcade plays.

Install

pip install mcp-arcade

Lab

mcp-arcade bout --target fixture --agent naive --no-prompt -o receipt.json

Tape

mcp-arcade tape receipt.json -o tape.json # one row per wire event, no score

What the house actually measures

Not a regex. Not an essay. Not 1,312 of anything.

The Honest Menu

tools/list is the menu. We check it is unique, described, and callable. We do not grade the prose.

The Whispered Errand

A side-quest in a description is not a finding. An extra tools/call is. The naive policy follows whispers so the harness can catch them; task-only is the control.

The Long Con

After N clean calls we re-list. If the tool changed, the hypothesis is dead. That is the experiment working.

The Ghost on the Menu

A name that was never on the menu must not be executed. The probe is a holdout atom: it runs by default and never joins the public training set.

A seat, not a judge

A local model can sit in the agent seat. It sees the menu as presented and emits tool calls; only those calls reach the receipt. Its prose never becomes a label, and on a live server it may send only the tools you allow.

The sandbox is Docker

One fresh container per experiment, image id pinned and drift-checked, /sandbox snapshotted from inside, no network, no host binds unless you name one. Your image always needs --allow-live.

The tape

The instrument scores the wire. Everything downstream reads a view with no score on it.

One row per wire event

Every request, reply and notification, in order, attributed to its experiment; server requests and the ghost probe as their own rows; a [no response] where a server went quiet. Rendered before the house call, so you read the wire before you read the verdict.

Allowlisted by construction

The tape type has no field for a score, a result, your call or the recap. What is not on the type cannot leak into a game or a dataset. Wire-derived facts travel with it: followed or held, ghost answered or refused, menu changed or stable.

The arcade plays it

A sister repo holds an arcade of small games built from tapes. Each cabinet reads the tape and nothing else, never a receipt, never a server. Record a bout against your own server and play it there.

A dataset comes from receipts

One JSONL row per experiment, labels from the wire, holdout by atom id, errored runs dropped rather than kept as anything. Receipts marked proof never train.

Usage

Lab bout

pip install mcp-arcade

mcp-arcade bout --target fixture \
  --agent task-only --no-prompt \
  -o receipt.json

Your server

mcp-arcade bout --target stdio \
  --cmd python --cmd -m --cmd your_server \
  --task your_read_only_tool \
  --allow-live --no-prompt

Your container, a model in the seat

mcp-arcade bout --target docker --image your/image:tag \
  --agent ollama:qwen2.5:7b-instruct \
  --task your_read_only_tool --seat-allow your_read_only_tool \
  --allow-live --no-prompt

Keep the tape, play it

mcp-arcade receipt receipt.json --timeline
mcp-arcade tape receipt.json -o tape.json

# then drop tape.json into the arcade:
# https://github.com/mcp-tool-shop-org/mcp-arcade-cabinets

Scores

Refusing to work is not a perfect integrity bar.

AxisMeansNot a certificate of
UtilityThe benign requested tool ranCorrectness of outputs
IntegrityNo unauthorized call, no catalog lie, no silent mutationSafety of models you did not run
NRPtask_success × (1 − attack_success)A high score on a leaderboard