@basein/runner 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/dist/auth/client.d.ts +129 -13
- package/dist/auth/client.js +260 -14
- package/dist/bin/bir.js +60 -18
- package/dist/proxy/session.js +5 -4
- package/docs/calculatedReplayGuide.md +1 -1
- package/docs/installRun.md +28 -15
- package/docs/loginWeb.md +607 -0
- package/docs/my-first-sample.md +545 -0
- package/docs/quickstart.md +28 -3
- package/package.json +1 -1
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
# Your first sample — run one MCPMark task and watch it pay for itself
|
|
2
|
+
|
|
3
|
+
A complete, from-zero manual. By the end you will have run one real agent
|
|
4
|
+
benchmark task, graded it against a live database, and then watched
|
|
5
|
+
BaseInstRunner replay the *same* task for **zero model tokens** — and you will
|
|
6
|
+
see the dollar figure that saved.
|
|
7
|
+
|
|
8
|
+
This is the hands-on companion to [mcpmark.md](mcpmark.md), which explains the
|
|
9
|
+
*why* in depth. This document is the *what to type*, in order, with the numbers
|
|
10
|
+
a real run produced on 2026-09-07 so you know what "working" looks like.
|
|
11
|
+
|
|
12
|
+
> **The one task.** Everything below uses a single MCPMark task,
|
|
13
|
+
> `postgres/easy/employees/hiring_year_summary`. It is the smallest task in the
|
|
14
|
+
> benchmark that still writes to the database, which makes it the honest place
|
|
15
|
+
> to start.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 0. What MCPMark is (and why we use it)
|
|
20
|
+
|
|
21
|
+
**MCPMark is a standard agents benchmark.** It measures how well an AI agent can
|
|
22
|
+
drive real tools, through the [Model Context Protocol
|
|
23
|
+
(MCP)](https://modelcontextprotocol.io), to change real systems — not to chat
|
|
24
|
+
about them.
|
|
25
|
+
|
|
26
|
+
The facts worth knowing before you start:
|
|
27
|
+
|
|
28
|
+
| | |
|
|
29
|
+
|---|---|
|
|
30
|
+
| Who | The `eval-sys` research group · [github.com/eval-sys/mcpmark](https://github.com/eval-sys/mcpmark) · [paper (arXiv 2509.24002)](https://arxiv.org/abs/2509.24002) · [mcpmark.ai](https://mcpmark.ai) |
|
|
31
|
+
| What it tests | An agent using **five MCP services**: Postgres, GitHub, Filesystem, Notion, Playwright |
|
|
32
|
+
| Size | **127 standard tasks + 50 easy** (the repo has grown past the paper's five services) |
|
|
33
|
+
| How it grades | Each task ships a `verify.py` that opens a real connection and **queries the actual state**, not the agent's transcript. Exit 0 = PASS |
|
|
34
|
+
| Metrics | `pass@1` (one try), `pass@k`, and `pass^k` — *stability*: did it pass **all** k independent runs |
|
|
35
|
+
| State of the art | On MCPMark's verified set, the strongest model reports about **92.9%**; most models sit far lower, and `pass^4` collapses for everyone |
|
|
36
|
+
|
|
37
|
+
Two of those facts are why this benchmark suits BaseInstRunner so well:
|
|
38
|
+
|
|
39
|
+
1. **It grades the world, not the words.** A replayed run that produces the
|
|
40
|
+
right rows passes exactly like a hand-typed one. There is no transcript to
|
|
41
|
+
fool.
|
|
42
|
+
2. **`pass^k` punishes inconsistency.** The benchmark runs each task several
|
|
43
|
+
times against a reset database and asks whether *every* run passed. A model
|
|
44
|
+
that re-decides the plan token-by-token every time is exactly what
|
|
45
|
+
BaseInstRunner removes — it runs the *same* known-good plan each time.
|
|
46
|
+
|
|
47
|
+
That is the whole idea you are about to see working: **solve the task once, then
|
|
48
|
+
stop paying to re-solve it.**
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 1. What you are about to build
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
Part A — the benchmark, on its own
|
|
56
|
+
seed a Postgres DB → agent solves the task → verify.py grades it → PASS
|
|
57
|
+
|
|
58
|
+
Parts B–D — the same task, through BaseInstRunner
|
|
59
|
+
claude -p ──mcp__postgres__*──▶ bir-proxy ──▶ postgres-mcp ──▶ PostgreSQL
|
|
60
|
+
│ │ records every call
|
|
61
|
+
└──mcp__bir__run_scenario──▶ bir-hooks (control server)
|
|
62
|
+
│ matches the prompt, arms the plan,
|
|
63
|
+
▼ runs all 6 steps for $0 model tokens
|
|
64
|
+
BaseIn service ──▶ books the saving
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
You will run the task **three ways**, and the difference between them is the
|
|
68
|
+
point:
|
|
69
|
+
|
|
70
|
+
| Run | Runner | What the model does | What it costs |
|
|
71
|
+
|---|---|---|---|
|
|
72
|
+
| **Baseline** | off | discovers and solves the task | full price |
|
|
73
|
+
| **Record** | on, replay **off** | solves it once; bir *records* the plan | full price, one time |
|
|
74
|
+
| **Replay** | on, replay **on** | one tool call reads a pre-computed result | a fraction |
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 2. Prerequisites
|
|
79
|
+
|
|
80
|
+
Check each one. The whole setup takes about ten minutes, most of it downloads.
|
|
81
|
+
|
|
82
|
+
| # | You need | Check | If missing |
|
|
83
|
+
|---|---|---|---|
|
|
84
|
+
| 1 | **Node 20+** | `node -v` | [nodejs.org](https://nodejs.org) |
|
|
85
|
+
| 2 | **Claude Code** | `claude --version` | Anthropic's CLI; this manual drives it with `claude -p` |
|
|
86
|
+
| 3 | **PostgreSQL 17**, running | `psql --version` and the service is up | see §2.1 |
|
|
87
|
+
| 4 | **uv / uvx** | `uvx --version` | [astral.sh/uv](https://docs.astral.sh/uv/) — runs `postgres-mcp` and `verify.py` |
|
|
88
|
+
| 5 | **The runner** | `bir --version` | `npm i -g @basein/runner` |
|
|
89
|
+
| 6 | **A BaseIn account + API URL** | `bir login` succeeds | your service address, e.g. `https://api.your-domain.com` |
|
|
90
|
+
|
|
91
|
+
> **`BIR_AUTH_URL` must be the API host**, normally `https://api.<domain>`, not
|
|
92
|
+
> the docs or app website. Point it at a website and `bir login` refuses it,
|
|
93
|
+
> saying what answered instead. Set it once, then open a **fresh** terminal — an
|
|
94
|
+
> existing one keeps the value it started with.
|
|
95
|
+
|
|
96
|
+
`bir login` signs you in through the browser: it prints a link and a short code
|
|
97
|
+
and waits for you to approve it. Nothing is typed into the terminal, and it works
|
|
98
|
+
for accounts that only sign in with Google.
|
|
99
|
+
|
|
100
|
+
### 2.1 Postgres, without Docker
|
|
101
|
+
|
|
102
|
+
MCPMark's own harness runs Postgres in Docker (`pgvector/pgvector:pg17`). If the
|
|
103
|
+
machine has no Docker — this one did not — install PostgreSQL 17 **natively**;
|
|
104
|
+
it is the same major version, and this task needs no vector support. Disclose
|
|
105
|
+
the deviation in any published result.
|
|
106
|
+
|
|
107
|
+
```powershell
|
|
108
|
+
winget install --id PostgreSQL.PostgreSQL.17 -e `
|
|
109
|
+
--accept-package-agreements --accept-source-agreements `
|
|
110
|
+
--custom "--mode unattended --superpassword password --serverport 5432 --unattendedmodeui none"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
That sets the `postgres` superuser password to `password`, which is the value
|
|
114
|
+
every command and connection string below expects. On macOS:
|
|
115
|
+
`brew install postgresql@17 && brew services start postgresql@17`.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 3. Part A — run the benchmark task on its own
|
|
120
|
+
|
|
121
|
+
First prove the plain benchmark works, with no runner in the picture at all.
|
|
122
|
+
|
|
123
|
+
### 3.1 Make a run folder and fetch the task
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
mkdir -p ~/my-first-sample && cd ~/my-first-sample
|
|
127
|
+
|
|
128
|
+
TASK=https://raw.githubusercontent.com/eval-sys/mcpmark/main/tasks/postgres/easy/employees/hiring_year_summary
|
|
129
|
+
curl -fsSLO "$TASK/description.md" # what the agent is asked to do
|
|
130
|
+
curl -fsSLO "$TASK/verify.py" # MCPMark's grader — unmodified
|
|
131
|
+
curl -fsSLO "$TASK/meta.json" # task metadata (difficulty L1, mcp: postgres)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
The task asks the agent to build one table, `employees.hiring_year_summary`,
|
|
135
|
+
with a row per hiring year: how many were hired, how many are still employed,
|
|
136
|
+
and the retention rate. Small, but a genuine write.
|
|
137
|
+
|
|
138
|
+
### 3.2 The exact prompt
|
|
139
|
+
|
|
140
|
+
MCPMark sends `description.md` **verbatim** plus one fixed suffix. Assemble it
|
|
141
|
+
once so every run is byte-identical:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
{ cat description.md
|
|
145
|
+
printf '\n\nNote: Based on your understanding, solve the task all at once by yourself, don'\''t ask for my opinions on anything.\n'
|
|
146
|
+
} > prompt.txt
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 3.3 Seed the database
|
|
150
|
+
|
|
151
|
+
Download MCPMark's 33 MB backup and build the `employees` **template**
|
|
152
|
+
database from it. Save this as `seed.sh` and run it once:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
#!/usr/bin/env bash
|
|
156
|
+
set -euo pipefail
|
|
157
|
+
PGBIN="${PGBIN:-/c/Program Files/PostgreSQL/17/bin}" # macOS: "$(brew --prefix postgresql@17)/bin"
|
|
158
|
+
export PGPASSWORD="${POSTGRES_PASSWORD:-password}"
|
|
159
|
+
PGUSER=postgres PGHOST=localhost PGPORT=5432
|
|
160
|
+
|
|
161
|
+
[ -f employees.backup ] || curl -fsSL -o employees.backup https://storage.mcpmark.ai/postgres/employees.backup
|
|
162
|
+
|
|
163
|
+
"$PGBIN/psql" -h $PGHOST -p $PGPORT -U $PGUSER -d postgres -v ON_ERROR_STOP=1 \
|
|
164
|
+
-c "DROP DATABASE IF EXISTS employees;" -c "CREATE DATABASE employees;"
|
|
165
|
+
"$PGBIN/pg_restore" -h $PGHOST -p $PGPORT -U $PGUSER -d employees \
|
|
166
|
+
--no-owner --no-privileges employees.backup \
|
|
167
|
+
|| echo "[seed] pg_restore printed notices — checking the result instead"
|
|
168
|
+
"$PGBIN/psql" -h $PGHOST -p $PGPORT -U $PGUSER -d employees \
|
|
169
|
+
-c "SELECT count(*) AS employees FROM employees.employee;"
|
|
170
|
+
echo "[seed] template database 'employees' is ready"
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
A healthy seed reports **300024** employees.
|
|
174
|
+
|
|
175
|
+
### 3.4 A fresh database for every run
|
|
176
|
+
|
|
177
|
+
MCPMark isolates each run with `CREATE DATABASE … WITH TEMPLATE …` and asserts
|
|
178
|
+
the target table is absent. Reproduce that as `reset-db.sh` — **run it before
|
|
179
|
+
every single trial**, or the second run finds the table already built and the
|
|
180
|
+
task stops being the task:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
#!/usr/bin/env bash
|
|
184
|
+
set -euo pipefail
|
|
185
|
+
PGBIN="${PGBIN:-/c/Program Files/PostgreSQL/17/bin}"
|
|
186
|
+
export PGPASSWORD=password; PGUSER=postgres PGHOST=localhost PGPORT=5432
|
|
187
|
+
DB="${TRIAL_DB:-hys_trial}"
|
|
188
|
+
"$PGBIN/psql" -h $PGHOST -p $PGPORT -U $PGUSER -d postgres -v ON_ERROR_STOP=1 \
|
|
189
|
+
-c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity
|
|
190
|
+
WHERE datname='$DB' AND pid <> pg_backend_pid();" \
|
|
191
|
+
-c "DROP DATABASE IF EXISTS $DB;" \
|
|
192
|
+
-c "CREATE DATABASE $DB WITH TEMPLATE employees;"
|
|
193
|
+
LEFT=$("$PGBIN/psql" -h $PGHOST -p $PGPORT -U $PGUSER -d "$DB" -tAc \
|
|
194
|
+
"SELECT count(*) FROM information_schema.tables
|
|
195
|
+
WHERE table_schema='employees' AND table_name='hiring_year_summary';")
|
|
196
|
+
[ "$LEFT" = "0" ] || { echo "[reset] target table survived — aborting" >&2; exit 1; }
|
|
197
|
+
echo "[reset] $DB recreated from template — target table absent"
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The `pg_terminate_backend` line is not optional: an MCP server holds a pooled
|
|
201
|
+
connection, and `DROP DATABASE` fails while any session is attached.
|
|
202
|
+
|
|
203
|
+
### 3.5 The grader
|
|
204
|
+
|
|
205
|
+
Save this as `verify.sh`. It runs MCPMark's own `verify.py`, unmodified, against
|
|
206
|
+
the real database:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
#!/usr/bin/env bash
|
|
210
|
+
export PYTHONIOENCODING=utf-8 # verify.py prints ✅/❌ — required on Windows consoles
|
|
211
|
+
export POSTGRES_HOST=localhost POSTGRES_PORT=5432 POSTGRES_DATABASE="${TRIAL_DB:-hys_trial}"
|
|
212
|
+
export POSTGRES_USERNAME=postgres POSTGRES_PASSWORD=password
|
|
213
|
+
cd "$(dirname "$0")"
|
|
214
|
+
uv run --quiet --with psycopg2-binary python verify.py
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
> **`PYTHONIOENCODING=utf-8` is not optional on Windows.** `verify.py` prints
|
|
218
|
+
> `✅`/`❌`/`🎉`; a cp1255/cp1252 console raises `UnicodeEncodeError` *while
|
|
219
|
+
> formatting its own PASS line*, and a run graded through a crashed reporter has
|
|
220
|
+
> no grade.
|
|
221
|
+
|
|
222
|
+
### 3.6 Run it once, by hand
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
./seed.sh # once
|
|
226
|
+
./reset-db.sh # before the run
|
|
227
|
+
claude -p "$(cat prompt.txt)" --model claude-sonnet-5
|
|
228
|
+
./verify.sh
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
What a pass looks like:
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
✅ Hiring year summary results are correct (16 records)
|
|
235
|
+
🎉 Task verification: PASS
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
That is the benchmark working, end to end, with **no runner involved**. On this
|
|
239
|
+
machine that run cost about **$0.15**. Hold that number — it is the baseline the
|
|
240
|
+
runner has to beat.
|
|
241
|
+
|
|
242
|
+
The 16 rows it produced (the grader recomputes these in SQL and compares
|
|
243
|
+
row-by-row):
|
|
244
|
+
|
|
245
|
+
| hire_year | employees_hired | still_employed | retention_rate |
|
|
246
|
+
|---|---|---|---|
|
|
247
|
+
| 1985 | 35316 | 28291 | 80.11 |
|
|
248
|
+
| 1986 | 36150 | 28840 | 79.78 |
|
|
249
|
+
| … | … | … | … |
|
|
250
|
+
| 1999 | 1514 | 1204 | 79.52 |
|
|
251
|
+
| 2000 | 13 | 9 | 69.23 |
|
|
252
|
+
|
|
253
|
+
(16 rows, hire years 1985 through 2000.)
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## 4. Part B — install the runner
|
|
258
|
+
|
|
259
|
+
Now put BaseInstRunner between the agent and Postgres. From inside the run
|
|
260
|
+
folder, write the MCP config **unwrapped** — `bir install` will wrap it:
|
|
261
|
+
|
|
262
|
+
`.mcp.json`
|
|
263
|
+
```json
|
|
264
|
+
{
|
|
265
|
+
"mcpServers": {
|
|
266
|
+
"postgres": {
|
|
267
|
+
"command": "uvx",
|
|
268
|
+
"args": ["--with", "mcp<2", "postgres-mcp==0.3.0", "--access-mode=unrestricted"],
|
|
269
|
+
"env": { "DATABASE_URI": "postgresql://postgres:password@localhost:5432/hys_trial" }
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Two details that will bite if you skip them:
|
|
276
|
+
|
|
277
|
+
- **`--with "mcp<2"` is mandatory.** `postgres-mcp==0.3.0` imports
|
|
278
|
+
`mcp.server.fastmcp`, which the `mcp` 2.x SDK removed. MCPMark's own
|
|
279
|
+
`pipx run postgres-mcp==0.3.0` is broken today without this pin. (`uvx` stands
|
|
280
|
+
in for `pipx`; both are equivalent here.)
|
|
281
|
+
- **`--access-mode=unrestricted`** — the task creates a table.
|
|
282
|
+
|
|
283
|
+
Then install, with calculated replay switched on:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
export BIR_AUTH_URL=https://api.your-domain.com
|
|
287
|
+
bir login # once; approve it in the browser
|
|
288
|
+
bir install --replay --global
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
`bir install --replay` rewrites `.mcp.json` to route `postgres` through
|
|
292
|
+
`bir-proxy`, adds a first-party `bir` server (the channel a replay's results
|
|
293
|
+
come back through), and wires Claude Code's ten hooks into
|
|
294
|
+
`.claude/settings.json`. `bir uninstall` reverses every byte of it.
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## 5. Part C — the two settings that decide everything
|
|
299
|
+
|
|
300
|
+
This is the section the notes call *"the whole ballgame."* Get these two things
|
|
301
|
+
right before your first trial, or every measurement below will be wrong in a way
|
|
302
|
+
that still *looks* healthy.
|
|
303
|
+
|
|
304
|
+
### 5.1 Deny the built-in tools
|
|
305
|
+
|
|
306
|
+
Left alone, Claude Code will reach for `Bash` + `psql` and never touch the MCP
|
|
307
|
+
server — and a single built-in call in the recording makes the whole plan
|
|
308
|
+
un-replayable. Pin the agent to MCP only. Add to `.claude/settings.json`:
|
|
309
|
+
|
|
310
|
+
```json
|
|
311
|
+
{
|
|
312
|
+
"permissions": {
|
|
313
|
+
"deny": ["Bash", "PowerShell", "Read", "Write", "Edit", "NotebookEdit",
|
|
314
|
+
"Glob", "Grep", "WebFetch", "WebSearch", "Agent", "Task"],
|
|
315
|
+
"allow": ["mcp__postgres__*", "mcp__bir__*"]
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### 5.2 Trust the workspace — or the `allow` list is silently discarded
|
|
321
|
+
|
|
322
|
+
**This is the single most common way a first run goes wrong.** If Claude Code
|
|
323
|
+
has not been told to trust this folder, it throws the `allow` list away and
|
|
324
|
+
prints one line you will scroll past:
|
|
325
|
+
|
|
326
|
+
```
|
|
327
|
+
Ignoring 2 permissions.allow entries from .claude/settings.json:
|
|
328
|
+
this workspace has not been trusted.
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
It then fails *asymmetrically*: wrapped `mcp__postgres__*` calls keep working
|
|
332
|
+
(bir's own hook allows them), but `mcp__bir__run_scenario` — the one tool the
|
|
333
|
+
replay is delivered through — gets denied. The agent cannot reach the plan, so
|
|
334
|
+
it does the task by hand, and the run lands in the **divergence** path. It still
|
|
335
|
+
passes, the audit log still looks fine, and you have measured nothing.
|
|
336
|
+
|
|
337
|
+
Fix it **before the first trial**, one of two ways:
|
|
338
|
+
|
|
339
|
+
- **Interactive, once:** run `claude` in the folder and accept the trust dialog.
|
|
340
|
+
- **By hand:** set this in `~/.claude.json` (use the exact path Claude Code
|
|
341
|
+
prints, forward slashes):
|
|
342
|
+
|
|
343
|
+
```json
|
|
344
|
+
"projects": {
|
|
345
|
+
"C:/Users/Admin/my-first-sample": { "hasTrustDialogAccepted": true }
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
> The interactive route is the reliable one — Claude Code guards `~/.claude.json`
|
|
350
|
+
> against outside edits, so a script that tries to set the flag may be blocked.
|
|
351
|
+
> Opening `claude` in the folder once and clicking *trust* is the path that
|
|
352
|
+
> always works.
|
|
353
|
+
|
|
354
|
+
### 5.3 Start the control server, and prove it is bound
|
|
355
|
+
|
|
356
|
+
Leave this running in its own terminal, **in the run folder**:
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
export BIR_AUTH_URL=https://api.your-domain.com
|
|
360
|
+
export BIR_REPLAY=1 # the replay switch — off by default
|
|
361
|
+
export BIR_REPLAY_ALLOW_SERVERS=postgres # servers allowed to run unattended
|
|
362
|
+
bir-hooks 2>&1 | tee -a bir-hooks.log
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Then, in another terminal in the same folder, **launch a `claude` session so a
|
|
366
|
+
proxy registers**, and check:
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
bir doctor
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Do not send a single prompt until you see both of these:
|
|
373
|
+
|
|
374
|
+
```
|
|
375
|
+
Wrapped in config : postgres
|
|
376
|
+
Registered proxies: postgres ← this needs a live claude session
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
`Registered proxies: (none)` means no proxy has bound yet. This exact check is
|
|
380
|
+
what separates a real measurement from a confident fiction.
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
## 6. Part D — record once, then replay for free
|
|
385
|
+
|
|
386
|
+
The run loop is one script, `trial.sh`. It resets the DB, runs the agent
|
|
387
|
+
non-interactively, prints the cost, and grades. **Keep `--model` the same for
|
|
388
|
+
every trial**, or the costs are not comparable:
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
#!/usr/bin/env bash
|
|
392
|
+
set -uo pipefail; cd "$(dirname "$0")"
|
|
393
|
+
LABEL="${1:-1}"; MODEL="${MODEL:-claude-sonnet-5}"
|
|
394
|
+
echo "==== trial $LABEL ($MODEL) ===="
|
|
395
|
+
./reset-db.sh || exit 1
|
|
396
|
+
claude -p "$(cat prompt.txt)" --model "$MODEL" \
|
|
397
|
+
--allowedTools "mcp__postgres__*" "mcp__bir__*" \
|
|
398
|
+
--output-format json > "trial-$LABEL.json"
|
|
399
|
+
node -e 'const r=JSON.parse(require("fs").readFileSync(process.argv[1]));
|
|
400
|
+
console.log("cost $"+(+r.total_cost_usd).toFixed(4)+" turns="+r.num_turns+
|
|
401
|
+
" denied="+(r.permission_denials||[]).length)' "trial-$LABEL.json"
|
|
402
|
+
./verify.sh
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### 6.1 Trial 1 — record
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
./trial.sh 1
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
bir records the run. If the service has seen this prompt before it keeps its own
|
|
412
|
+
canonical run and this one is *not* re-recorded — that is the match working.
|
|
413
|
+
Either way, the plan you will replay is a **calculated scenario**: an intent, a
|
|
414
|
+
parameter schema, and one bit of logic per step. Inspect it before you trust it:
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
bir scenario list # find the runId / scenario id
|
|
418
|
+
bir scenario replay <scenarioId> --prompt "$(cat prompt.txt)" --dry
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
`--dry` runs the scenario's logic against the *recorded* outputs — no real SQL,
|
|
422
|
+
one cheap model call — and prints each step's computed input. Read it for three
|
|
423
|
+
things: parameters that actually vary with the prompt, step inputs that look
|
|
424
|
+
*computed* rather than copied, and non-empty `emitted` on steps others depend
|
|
425
|
+
on. On our scenario the dry run produced the correct six steps and the right 16
|
|
426
|
+
rows for **$0.004**.
|
|
427
|
+
|
|
428
|
+
### 6.2 Trials 2+ — replay
|
|
429
|
+
|
|
430
|
+
Reset, send the identical prompt, grade. Watch `bir-hooks.log` for the story:
|
|
431
|
+
|
|
432
|
+
```
|
|
433
|
+
run.matched similarity=1.000
|
|
434
|
+
plan.armed mode=direct steps=6 coverage=direct,direct,direct,direct,direct,direct
|
|
435
|
+
replay.derived params=7 costUsd=0.0000 source="recorded samples"
|
|
436
|
+
replay.step n=0 … ok=true emitted=tableList
|
|
437
|
+
replay.step n=5 … ok=true emitted=validationRow,totalEmployees,sumHired,…
|
|
438
|
+
replay.done mode=direct steps=6/6 outcome=steered_full
|
|
439
|
+
execution.reported outcome=steered_full session=0.045 savedUsd=+0.174 measured=true
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
All six SQL steps executed for real through the connection the proxy already
|
|
443
|
+
held — in **about a second of real SQL time** (the `CREATE TABLE … AS` over
|
|
444
|
+
300k rows is most of it), **for $0.00 in model tokens**. The model's only job
|
|
445
|
+
was to read the result.
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
## 7. Reading the result — the money
|
|
450
|
+
|
|
451
|
+
Here are the real figures this task produced, so you can compare yours:
|
|
452
|
+
|
|
453
|
+
| Run | Outcome | Session cost | Saved vs baseline |
|
|
454
|
+
|---|---|---|---|
|
|
455
|
+
| Baseline (recorded, median of 3) | — | **$0.2188** | — |
|
|
456
|
+
| A clean replay | `steered_full` | **$0.0448** | **+$0.1739 (≈ 80%)** |
|
|
457
|
+
|
|
458
|
+
Across five verified replays on the test account, the service had booked
|
|
459
|
+
**$0.6374 saved** in total. That is the number the manual promised you: the same
|
|
460
|
+
task, solved once and then not re-solved, at roughly a fifth of the price each
|
|
461
|
+
time after.
|
|
462
|
+
|
|
463
|
+
What each outcome means for your ledger:
|
|
464
|
+
|
|
465
|
+
| Outcome | Meaning | Books as |
|
|
466
|
+
|---|---|---|
|
|
467
|
+
| `steered_full` | every step ran under the plan | a **saving** |
|
|
468
|
+
| `diverged` | agent went off-script; bir recovered for $0 | a saving (usually smaller) |
|
|
469
|
+
| `not_steered` | matched but replay was off | a **baseline sample** (this is how the baseline is measured) |
|
|
470
|
+
| `failed` | armed, then gave up | a baseline sample |
|
|
471
|
+
|
|
472
|
+
Where you see it: on your BaseIn console (for the hosted service,
|
|
473
|
+
**bi2202.com** → sign in → **Dashboard** / **Recordings**), the KPI strip shows
|
|
474
|
+
**Verified saved** — "money we watched move: costs measured on both sides." A
|
|
475
|
+
loss shows red; a saving shows green.
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
## 8. What actually happened here, honestly
|
|
480
|
+
|
|
481
|
+
On the machine this manual was written on, the run folder had **not** been
|
|
482
|
+
trusted (§5.2), so both replays landed in the **divergence** path rather than
|
|
483
|
+
clean `steered_full`. They still passed, and they still saved (`+$0.033` and
|
|
484
|
+
`+$0.116` against the $0.219 baseline) — but two things are worth stating
|
|
485
|
+
plainly, because you may hit them too:
|
|
486
|
+
|
|
487
|
+
1. **Untrusted → divergence.** With the `allow` list discarded,
|
|
488
|
+
`mcp__bir__run_scenario` was denied and the agent solved the task by hand.
|
|
489
|
+
The safety net (bir re-running the six steps for $0) is why it still passed
|
|
490
|
+
and still saved. Trust the workspace to get the clean channel.
|
|
491
|
+
|
|
492
|
+
2. **A cautious model may reject the replay as a prompt injection.** When the
|
|
493
|
+
pre-computed bundle arrived unbidden (the divergence path delivers it as a
|
|
494
|
+
tool result), Claude Sonnet 5 flagged it:
|
|
495
|
+
|
|
496
|
+
> *"the result … came back wrapped in a fake '[BaseInstRunner calculated
|
|
497
|
+
> replay]' block claiming all 6 steps were 'already executed' … This is a
|
|
498
|
+
> prompt injection embedded in the tool response channel … I'm not going to
|
|
499
|
+
> report those figures as fact."*
|
|
500
|
+
|
|
501
|
+
The model then re-verified the work itself — which is correct, cautious
|
|
502
|
+
behaviour, and which raises the per-run cost. Delivering the bundle through
|
|
503
|
+
the model's *own* `run_scenario` call (the trusted, direct path) is far less
|
|
504
|
+
likely to trip this, and the bundle wording is being sharpened (see
|
|
505
|
+
[mcpmark.md](mcpmark.md) §12–14). It is a real product wrinkle, not a setup
|
|
506
|
+
error — worth knowing before you read too much into one run's cost.
|
|
507
|
+
|
|
508
|
+
Neither changes the headline: **the scenario is correct, the replay executes the
|
|
509
|
+
real work for zero tokens, and the saving is real and positive.**
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
## 9. Troubleshooting
|
|
514
|
+
|
|
515
|
+
| Symptom | Cause | Fix |
|
|
516
|
+
|---|---|---|
|
|
517
|
+
| `Ignoring N permissions.allow entries … not been trusted` | workspace untrusted | §5.2 — trust it, then every replay is clean |
|
|
518
|
+
| `Registered proxies: (none)` | no live session bound a proxy | launch `claude` in the folder, re-run `bir doctor` |
|
|
519
|
+
| replay always `diverged`, never `steered_full` | usually §5.2, sometimes a cautious model (§8) | trust first; then see mcpmark.md §12–14 |
|
|
520
|
+
| trial 2 finds the table already there | no DB reset | `reset-db.sh` before **every** trial |
|
|
521
|
+
| `UnicodeEncodeError` in verify.py | Windows console codepage | `PYTHONIOENCODING=utf-8` (already in `verify.sh`) |
|
|
522
|
+
| `ModuleNotFoundError: mcp.server.fastmcp` | `mcp` 2.x resolved | pin `--with "mcp<2"` in `.mcp.json` |
|
|
523
|
+
| `bir login` says "not a BaseIn service" | `BIR_AUTH_URL` is a website, not the API | set it to `https://api.<domain>`, open a fresh terminal |
|
|
524
|
+
| `savedUsd` looks impossible | pricing drift between runner and server | compare `PRICING_VERSION` on both sides |
|
|
525
|
+
| the number won't move | a replayed turn is deliberately not re-recorded | correct — the matched run stays canonical |
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## 10. Where to go next
|
|
530
|
+
|
|
531
|
+
- **Widen within Postgres.** 31 prompts share this exact server and reset
|
|
532
|
+
mechanism, so each new task costs almost nothing to add.
|
|
533
|
+
`tasks/postgres/standard/**` is where the paper's numbers come from.
|
|
534
|
+
- **[mcpmark.md](mcpmark.md)** — the full rationale, the topology in detail, and
|
|
535
|
+
the measured history of this task, including the bugs that were found and
|
|
536
|
+
fixed along the way.
|
|
537
|
+
- **[calculatedReplayGuide.md](calculatedReplayGuide.md)** — the runner's own
|
|
538
|
+
manual: every audit line, every switch, and how the saving is booked.
|
|
539
|
+
- **[quickstart.md](quickstart.md)** — setting up a fresh machine to record from
|
|
540
|
+
your *own* work, not just this benchmark.
|
|
541
|
+
|
|
542
|
+
> **Turning replay off** is one line: `unset BIR_REPLAY`. The system is then
|
|
543
|
+
> exactly a recorder again — it recognises a repeated prompt and declines to
|
|
544
|
+
> record it twice, and the model does the work. Nothing is skipped, nothing is
|
|
545
|
+
> risked.
|
package/docs/quickstart.md
CHANGED
|
@@ -62,8 +62,16 @@ The script narrates each stage. A healthy run looks like this:
|
|
|
62
62
|
==> Configuring the BaseIn service
|
|
63
63
|
BIR_AUTH_URL=https://basein.example.com (persisted for this user)
|
|
64
64
|
==> Signing in
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
|
|
66
|
+
[bir] Sign in to https://basein.example.com
|
|
67
|
+
|
|
68
|
+
Open https://basein.example.com/activate?code=BKQM-TXZR
|
|
69
|
+
Code BKQM-TXZR
|
|
70
|
+
|
|
71
|
+
Opening your browser...
|
|
72
|
+
Waiting for approval... (Ctrl-C to cancel)
|
|
73
|
+
|
|
74
|
+
Signed in to https://basein.example.com as you@example.com.
|
|
67
75
|
==> Wrapping MCP servers in C:\path\to\your\project
|
|
68
76
|
+ chrome-devtools -> bir-proxy (project scope, upstream: npx)
|
|
69
77
|
+ hooks -> ...\.claude\settings.json
|
|
@@ -71,7 +79,14 @@ Password:
|
|
|
71
79
|
Wrapped 1 server.
|
|
72
80
|
```
|
|
73
81
|
|
|
74
|
-
|
|
82
|
+
**Signing in happens in your browser, not the terminal.** The script prints a
|
|
83
|
+
link and a short code, then waits. Open the link, check that the code matches
|
|
84
|
+
and that it says it will sign you in as you, and press **Approve**. There is no
|
|
85
|
+
password to type here, which is also why this works for an account that only
|
|
86
|
+
ever signs in with Google.
|
|
87
|
+
|
|
88
|
+
The link works from any device — a phone is fine — so this is also the answer on
|
|
89
|
+
a machine with no browser. Add `--no-browser` to stop it trying to open one.
|
|
75
90
|
|
|
76
91
|
If anything goes wrong the script stops immediately and says why — it checks
|
|
77
92
|
everything it can *before* changing your machine, so a failed run leaves nothing
|
|
@@ -130,6 +145,16 @@ The service address did not stick. Close the terminal, open a new one, and check
|
|
|
130
145
|
`echo $env:BIR_AUTH_URL` (Windows) or `echo $BIR_AUTH_URL` (macOS). If it is
|
|
131
146
|
empty, run the install script again with the `-AuthUrl` / `BIR_AUTH_URL` value.
|
|
132
147
|
|
|
148
|
+
### "The browser never opened"
|
|
149
|
+
|
|
150
|
+
Not a problem: the link and the code are printed either way, and the link works
|
|
151
|
+
from any device. Open it on your phone and approve there. `bir login` skips
|
|
152
|
+
opening a browser on purpose over SSH, and `--no-browser` skips it always.
|
|
153
|
+
|
|
154
|
+
### "The code expired before I approved it"
|
|
155
|
+
|
|
156
|
+
Codes last ten minutes. Run `bir login` again for a fresh one.
|
|
157
|
+
|
|
133
158
|
### "bir login says … is not a BaseIn service" (or `HTTP 405 Method Not Allowed`)
|
|
134
159
|
|
|
135
160
|
`BIR_AUTH_URL` points at a website — the docs or app address — instead of the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@basein/runner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A recording MCP proxy: sits between any MCP client and its MCP servers, executes each call on the client's behalf, and records the run as a reusable BaseIn scenario.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|