~/satyajit

The 245x factors: 15x of interface, 16x of price

mdjsonmcp

2026-09-19 · 23 min · explainer · agents · llm · benchmarks · architecture

Two articles ago I argued, from TypeSafe's docs, that Jev must be a per-option scalar scorer. Then an independent benchmark put it at 0 of 100 on relational choice, which is what that architecture predicts and nothing else does. Both pieces were about a constraint: a model that picks from a caller-supplied option list and cannot emit a string.

WindTunnel is the first place I have seen someone ship a real system into that constraint, measure it against twenty other configurations on the same tasks, and publish everything needed to check the arithmetic. Jev + Mercury 2.5 over WebMCP solves 49 of 49 tasks and is at rank one on a 21-configuration board. The claims that travelled are the cost multipliers: 112x cheaper than GPT-6 Astra on code execution, 245x cheaper than Astra on screenshots.

Both numbers are real. Both are also products of two independent ratios, and the board contains the cells needed to pull them apart. That is what most of this piece is.

The denominator, first

49 tasks, 3 attempts each, 8 self-hosted applications, 600 s per-attempt cap. A task is solved when a majority of its three attempts pass. Scoring is outcome-based: the harness reads the application's own state — is the order in the database, does the appointment exist — so no model judges a run.

The task set is 49 and not 50. tasks/learnhouse.yaml carries a tenth task, lh-4, marked excluded: api-only-data:

# tasks/learnhouse.yaml — max_steps elided
- id: lh-4
  excluded: api-only-data
  tier: answer
  prompt: What tags does the Evaluation Foundations course carry?
  predicate:
    type: answer
    contains:
      - webmcp

Course tags are not rendered on the page. A WebMCP tool can return them; a screenshot agent cannot see them. Dropping it removes a task that WebMCP would have won by construction, and the benchmark's authors dropped it. Worth noticing before any of the rest.

WindTunnel's leaderboard chart ranking 21 configurations by a composite of attempt success, median cost and median agent time. Jev plus Mercury 2.5 on WebMCP is rank 1 at 96.5 with a 95.9% pass rate, $0.0011 median cost and 3.2 s median agent time. Ranks 2 to 9 are all WebMCP configurations. GPT-6 Astra on code execution is rank 11 at $0.119. Jev plus Mercury 2.5 on DOM ultrafast is rank 12 with a 51.7% pass rate and the cheapest median cost on the board, $0.0008. GPT-6 Astra on computer use is rank 18 at $0.261.
All nine WebMCP configurations rank above every screen- and page-driving one. Note rank 12: the same model pair driving the page is the cheapest row on the board per attempt, and solves half as many tasks (WindTunnel, README leaderboard chart).

Ten of the 21 configurations solve 49/49 — all nine WebMCP rows plus GPT-6 Astra on code execution. So a 100% task-solve rate separates nothing here. Cost and time do, and that is where the argument lives. Jev + Mercury's attempt-level number is 141/147; the six failures are two abstentions, two stalls and two answers that missed their predicate. None of them hit a budget or a timeout.

A model that cannot write a string

Jev takes state plus typed questions and returns a choice with a probability distribution. It does not generate text. So the action menu has to be the answer space, and under WebMCP the menu is whatever tools the page has registered. From experiments/jev/frozen/arms/decision-agent.mjs:

const menu = [...snapshot.actions, FINISH, ABSTAIN];
if (menu.length > 255) throw new Error('action menu exceeds 255 choices');
const state = decisionState(task, menu, history, snapshot.observation, audit);
const selection = await select(state, 'Choose the next action to advance the user task. …',
  menu.map(a => ({ value: a.id, label: `${a.id}: ${a.description ?? ''}` })));

FINISH and ABSTAIN are synthetic options bolted onto the site's real tools, because "I am done" and "I am stuck" have to be choosable too. The 255 cap is Jev's Choice limit; decisionState caps the serialized state at 48,000 characters and drops history oldest-first to stay under it.

What Jev cannot do is fill in search_products(query=…). That job goes to Mercury 2.5, Inception Labs' diffusion LLM, called at reasoning_effort: 'instant' with the selected tool's JSON Schema forced as the only callable function (experiments/jev/webmcp/mercury.mjs):

const body = { model: 'mercury-2.5', reasoning_effort: 'instant', max_completion_tokens: 4096,
  messages: [{ role: 'system', content: INSTRUCTIONS + … },
             { role: 'user', content: JSON.stringify({ state, selected: { description, name } }) }],
  tools: [{ type: 'function', function: { name: 'submit_arguments', parameters: outboundSchema } }],
  tool_choice: { type: 'function', function: { name: 'submit_arguments' } } };

The system prompt is explicit that the choice is already made: "The action and its target are already chosen. Do not choose a different action." Mercury never selects; Jev never types. One detail that matters for the cost arithmetic later: if the chosen tool's schema has no properties and nothing required, the scaffold skips the Mercury call entirely and passes {}.

This is the two families argument cashed out in a shipping system. Option isolation buys order-invariance and costs you the ability to say anything the caller did not already write down. The fix is not to make the decision model talk — it is to put a talker next to it and give it no decisions.

Here is one real attempt, end to end.

task md-8 · nextjs-starter-medusa · tier: sensitive action

“Buy one Medusa T-Shirt, size L, color Black — add it to the cart and complete guest checkout as Jane Tester, jane.tester@example.test, 1 High Street, London, N1 9GU, United Kingdom, phone +44 20 7946 0000, using the store’s test payment method. Report the order confirmation.”

step 18 options offered
jev
search_productsp=1.00

2,467 in / 82 out · 1,111 ms · $0.000104

mercury
{"query":"t-shirt","category":"t-shirts","max_results":10}

1,824 in / 52 out · 967 ms · $0.000404

site

error: Unknown category 't-shirts'. valid_categories: Merch, Sweatshirts, Pants, Shirts

step 28 options offered
jev
search_productsp=0.99 · ask_site 0.01

2,597 in / 82 out · 393 ms · $0.000109

mercury
{"query":"t-shirt","category":"shirts","max_results":10}

1,914 in / 52 out · 821 ms · $0.000422

site

count: 1 — Medusa T-Shirt, handle t-shirt, price_from €10.00, options Size[S,M,L,XL] Color[Black,White]

step 38 options offered
jev
add_to_cartp=1.00

3,166 in / 82 out · 324 ms · $0.000133

mercury
{"variant_id":"variant_01M2RSQZMH97…","quantity":1}

2,298 in / 60 out · 837 ms · $0.000505

site

added — cart.item_count 1, "Medusa T-Shirt", variant "L / Black", total €10.00

step 48 options offered
jev
complete_checkoutp=1.00

3,387 in / 82 out · 347 ms · $0.000142

mercury
{"email":"jane.tester@example.test","first_name":"Jane","last_name":"Tester","address_1":"1 High Street","city":"London","postal_code":"N1 9GU","country_code":"gb","phone":"+44 20 7946 0000"}

2,682 in / 136 out · 895 ms · $0.000638

site

error: Choose a shipping option and call again with shipping_option_id. choices: Standard Shipping (so_01M2RSQZJ9J5…), Express Shipping (so_01M2RSQZJ9YA…)

step 510 options offered
jev
complete_checkoutp=0.91 · __abstain__ 0.07, ask_site 0.01, begin_checkout 0.01

4,126 in / 98 out · 410 ms · $0.000173

mercury
{…same eight fields…,"shipping_option_id":"so_01M2RSQZJ9J5…"}

3,171 in / 175 out · 985 ms · $0.000765

site

order_01M2RTVM… placed — display #1, total 20 EUR, 1 × Medusa T-Shirt (L / Black), payment pp_system_default

step 610 options offered
jev
__finish__p=0.98 · __abstain__ 0.02

4,580 in / 98 out · 321 ms · $0.000192

mercury
{"answer":"Order confirmed. Order ID: order_01M2RTVM… Total: €20.00 …"}

3,221 in / 121 out · 918 ms · $0.000735

site

attempt ends — scored by reading the store's database: orders == 1

6 steps · 12 model calls5 tool calls executedJev $0.000854 (20%)Mercury $0.003469 (80%)total $0.004323 · 10.478 s agent time · pass
Verbatim from the published transcript for run md-8_wm-jev-mercury-v3_0f360b4e. Note steps 1 and 4: the tool rejects Mercury’s arguments and returns the legal values, and the next step fixes them. Error recovery here is a tool contract, not a model capability.

Two things in that trace are worth more than the totals. At step 1 Mercury invents a category that does not exist, and the tool answers with an error and the four valid categories; step 2 uses one. At step 4 the checkout tool refuses and returns the two shipping options; step 5 passes one back. Neither recovery required the agent to be clever. It required the tool to reply with the legal values — which is a property of the code on the website, not of either model.

What est_cost_usd counts

Before any multiplier: the cost column is model tokens at a pinned price table, and nothing else. docs/SPEC.md says "provider list prices, with cached input priced at each provider's cached rate". The table lives in harness/lib.mjs and is serialized into every run.json so a published result can be re-costed later:

// $ per million: input, output, cached read, cache write
export const PRICES = [
  ["typesafe-ai/jev",  [0.042, 0,  0.042, 0.042]],
  ["gpt-6-astra",      [10,    50, 1,     12.5 ]],

];

Mercury is priced separately inside the arm, at $0.20 / $0.02 / $0.75 per million (input / cached / output). Jev goes in at $0.042 per million for every token type, with output at zero — the run records both as standard_prices_per_million, and the zero is at least consistent with the shape of the thing, whose output is a choice id and a probability vector rather than a stream.

Every number on the board is an estimate of this kind: token counts multiplied by a list price, never a provider invoice. The code has a path that uses a gateway's billed figure when one comes back; this run used TypeSafe's API directly, so all 294 Jev rows carry cost_estimated: true.

Nothing else enters the column. Not the browser, not the Docker capsule, not the site, and — this is the one to keep hold of — not a line of the tool code that makes WebMCP work at all.

I recomputed the three GPT-6 Astra medians from the four token columns and that price table. All three reproduce to the cent, so the reported column really is the stated formula:

cu-openai   × gpt-6-astra   reported $0.2607800   recomputed $0.2607800
code-openai × gpt-6-astra   reported $0.1186760   recomputed $0.1186760
wm-gpt      × gpt-6-astra   reported $0.0171000   recomputed $0.0171000

The decomposition

Every headline multiplier compares Jev + Mercury on WebMCP against GPT-6 Astra on something else. Two things change at once. The board contains the cell that holds one of them fixed — Astra has its own WebMCP row — so the product comes apart.

median model cost per attempt · 49 tasks × 3 attempts each

GPT-6 Astra

WebMCP

$0.017100

49/49 solved · 2,575 tok

baseline for this row

code execution

$0.118676

49/49 solved · 10,982 tok

6.94x the WebMCP cell

computer use

$0.260780

45/49 solved · 20,560 tok

15.25x the WebMCP cell

16.07x — same interface, same 49 tasks, different models

Jev 1.13.0 + Mercury 2.5

WebMCP

$0.001064

49/49 solved · 9,793 tok

the rank-1 row

245.10x = 15.25x interface × 16.07x price

111.54x = 6.94x interface × 16.07x price

Both headline multipliers carry the same 16.07x price term. What WebMCP itself buys, with the model held fixed, is the other factor: 15.25x against screenshots and 6.94x against code execution.

16.07x of that is price, and it is the same term in both headlines. Hold the interface at WebMCP and swap the models: Astra $0.017100, Jev + Mercury $0.001064. What is left — 15.25x against screenshots, 6.94x against code execution — is what WebMCP itself buys with the model held fixed.

The price term does not come from reading less. Jev + Mercury's median attempt processes 9,793 tokens against Astra's 2,575 — 3.8x more — and still costs 16x less, because its blended rate is $0.109 per million tokens against Astra's $7.01. Call it 61x on the medians, 64x on the run totals. The decision model is cheap the way a classifier is cheap.

The interface term does come from reading less, mostly. Astra's screenshot row processes 20,560 median tokens against its own WebMCP row's 2,575 — 8.0x — and the remaining 1.9x is rate mix. That mix has a mechanism worth naming:

Astra configurationuncached inputcache readscache writesoutput
WebMCP26.8%51.4%18.9%2.9%
code execution1.4%19.6%77.3%1.7%
computer use2.6%0.1%96.1%1.2%

A WebMCP prompt is a stable list of tool schemas, so half of it comes back as a cache read at a tenth of the input price. A screenshot prompt carries new image bytes every turn, so the prefix never repeats and essentially nothing caches: one part in a thousand. That is a property of the modality, not of Jev, and it is doing real work in the 245x.

It also makes the headline sensitive to one price-table line. Cache writes are billed at 1.25x input, and they are 93.5% of the Astra computer-use bill. Price them at plain input instead and the same medians give 199x and 93x. The 1.25x is disclosed in a comment above the table; I cannot independently verify it, and it is the single assumption the multiplier leans on hardest.

receiptscaptured 2026-09-19

Jev + Mercury 2.5 over WebMCP is 16.3x to 245.1x cheaper per attempt than the eleven screen- and page-driving configurations on WindTunnel's canonical board; the median of that range is 58.9x. The published 245x is the top of the range - GPT-6 Astra, the priciest model on the board, on screenshot computer use. The cheapest screen-driving configuration, GPT-5.6 Luna on computer use, is 16.3x.

configurationinterfacesolvedmedian $/attemptmedian tokensblended $/Mtokx Jev+Mercury
Jev + Mercury 2.5DOM (ultrafast)25/49$0.00077813,892$0.0500.7x
Jev + Mercury 2.5WebMCP49/49$0.0010649,793$0.109
GPT-5.6 LunaWebMCP49/49$0.0024362,596$0.8462.3x
Gemini 3.6 Flash · Stagehand v4WebMCP49/49$0.0043724,371$0.9974.1x
Gemini 3.6 FlashWebMCP49/49$0.0043964,453$1.0024.1x
Sonnet 5WebMCP49/49$0.0091325,172$1.7168.6x
Sonnet 5 · Stagehand v4WebMCP49/49$0.0095115,161$1.7328.9x
GPT-5.6 SOLWebMCP49/49$0.0121002,573$4.23911.4x
Claude Opus 5WebMCP49/49$0.0140134,770$3.47213.2x
GPT-6 AstraWebMCP49/49$0.0171002,575$7.00616.1x
GPT-5.6 Lunacomputer use45/49$0.01737020,914$0.97716.3x
GPT-5.6 Lunaa11y tree40/49$0.01953818,517$1.05018.4x
Gemini 3.6 Flashcomputer use43/49$0.02015523,857$0.80118.9x
GPT-5.6 LunaDOM + vision43/49$0.03282529,561$1.10230.9x
Sonnet 5a11y tree42/49$0.03778210,762$3.25535.5x
GPT-5.6 SOLcomputer use46/49$0.06269316,235$4.92058.9x
Sonnet 5computer use39/49$0.06970557,701$1.36565.5x
GPT-6 Astracode execution49/49$0.11867610,982$10.826111.5x
Claude Opus 5computer use45/49$0.13885747,141$2.911130.5x
Sonnet 5DOM + vision48/49$0.21020764,424$3.326197.6x
GPT-6 Astracomputer use45/49$0.26078020,560$12.855245.1x

est_cost_usd counts model tokens at pinned list prices and nothing else - no browser, no Docker capsule, no site hosting, and none of the engineering that puts the tools on the page. Jev is priced at $0.042/M for every token type with output free; Mercury at $0.20/$0.02/$0.75 per M (input/cached/output). A 5x Mercury promotional discount was recorded and deliberately not applied.

method Ratio of medians of est_cost_usd over each configuration's 147 attempts, taken from results/canonical/results.csv at commit 5ca8644. 'Blended $/Mtok' is that configuration's total est_cost_usd divided by its total processed tokens (uncached input + cache reads + cache writes + output). Solved is majority-of-three, as the board reports it. I recomputed est_cost_usd from the four token columns and harness/lib.mjs PRICES for all three GPT-6 Astra rows; all three medians reproduce to the cent.
data /articles/webmcp-windtunnel/data/cost-ledger.json (21 rows, 5.5 KB)

The cheap fast model is 78% of the bill

The framing everywhere is that argument-writing is the easy part, delegated to something small. That is true about the cognition and false about the invoice.

receiptscaptured 2026-09-19

The decision model is not where the money goes. Across the 147 WebMCP attempts, Jev costs $0.045 and Mercury costs $0.156 - the argument writer is 77.6% of the bill. Under the DOM setup the split inverts to 75.7% Jev, because page control mostly means picking an element and only a quarter of steps need any text written at all.

setupmodelrolecalls/ steptokenscostshare$/Mtok
WebMCPJev 1.13.0picks the action4131.001,104,774$0.04511122.4%$0.041
WebMCPMercury 2.5writes the arguments3850.93737,101$0.15644777.6%$0.212
WebMCPboth7981.931,841,875$0.201557100.0%$0.109
DOM (ultrafast)Jev 1.13.0picks the action1,0971.004,721,064$0.18585875.7%$0.039
DOM (ultrafast)Mercury 2.5writes the arguments2780.25229,003$0.05953024.3%$0.260
DOM (ultrafast)both1,3751.254,950,067$0.245387100.0%$0.050

Jev is billed at $0.042 per million tokens of every kind, with output free, so its effective rate barely moves. Mercury is $0.20 input / $0.02 cached / $0.75 output. Neither rate includes the 5x Mercury promotional discount that the run recorded as promotional_est_cost_usd and left unapplied.

method Summed provider_usage.{jev,mercury}.{calls,cost,usage} over every record in results/2026-09-18-jev-mercury/webmcp-traces.jsonl.gz and dom-traces.jsonl.gz (147 attempts each). 'per step' divides calls by the total number of agent steps in the same transcripts. The 'both' cost rows reproduce PROVENANCE.md's scored-attempt totals of $0.201557376 and $0.245387386 exactly.
data /articles/webmcp-windtunnel/data/provider-split.json (6 rows, 3.0 KB)

Across the 147 WebMCP attempts Jev costs $0.0451 and Mercury costs $0.1564. The decision model is 22.4% of the spend. Under the DOM setup the split inverts to 75.7% Jev — and the reason is in the call rates, not the prices. Mercury runs on 0.93 calls per step under WebMCP and 0.25 under DOM. Almost every tool call needs arguments; almost every click needs only an index.

So "cheap decision model plus cheap fast writer" is really "nearly-free decision model plus the actual cost centre", and the lever on a WebMCP agent's bill is the argument writer, not the chooser.

One more thing the transcripts settle. The announcement sells Mercury at "1,000+ tokens/sec", which is a decode rate. End to end in this harness its median call is 614 ms for a median of 40 output tokens — about 65 tok/s wall-clock, because a forty-token JSON object is all prefill and round trip. Jev's median call is 403 ms. Two calls a step, a median of two steps, and the attempt's 3.21 s median agent time is about 63% model latency. The fast model's headline speed mostly does not survive being called this way, and the configuration is still the fastest row on the board.

25/49 to 49/49, and an 18% that is not what it looks like

The second claim is the interesting one: adding WebMCP nearly doubled solved tasks and reduced model cost by 18%. Doubling accuracy while spending less is the kind of result that deserves a second look.

It checks out, and it is not a per-attempt saving. Per median attempt, WebMCP is 37% more expensive — $0.001064 against $0.000778 — and the README says so plainly. The 18% is a total across 147 attempts each, straight out of the release's own PROVENANCE.md: $0.201557376 against $0.245387386, which is 17.9% lower.

The difference is failure. Attempts that fail grind through their step budget:

setupsolvedspend on passing attemptsspend on failing attemptsfailures' share
WebMCP49/49$0.187752 (141)$0.013805 (6)6.8%
DOM (ultrafast)25/49$0.073198 (76)$0.172189 (71)70.2%

Seventy percent of the page setup's bill buys nothing. Divide by what each actually delivered and the comparison stops being counterintuitive: $0.0041 per solved task against $0.0098 — a factor of 2.4, not 1.18. Per passing attempt it is 2.3.

Two caveats in the other direction, both from the release's own disclosures. It publishes an unknown-usage reserve — requests whose token counts never came back — excluded from the leaderboard medians: $0.0144 for WebMCP against $0.0032 for DOM. Fold both reserves and the non-scored requests back in and the 18% becomes 12.9%. And the DOM arm's failure taxonomy is not random noise:

36  agent blocked            (Jev chose the BLOCKED operation)
19  agent budget exhausted
 8  Text helper returned no valid field value; nothing typed
 6  predicate failed        (3 state, 3 answer)
 1  invalid TypeSafe response; no action executed
 1  Mercury content-filter refusal

Half of the failures are the agent deciding, explicitly, that no supported operation makes progress. It is not flailing. It is giving up — and it was given the larger step budget of the two (20 against 12 by default, 32 against 15 on the checkout task).

The compression claim, measured

The authors' hypothesis for why the page setup underperforms is that WebMCP "compresses a sequence of clicks into one tool call", shrinking the decision space. TypeSafe's own documentation points the same way without using the word — it tells you to keep each question to "the kind of judgment a highly knowledgeable person could make in a few seconds" and to decompose anything that "would require extended reasoning or weighs multiple independent factors". A journey through a checkout flow is exactly the thing it says not to ask in one question.

Split by journey length, the two curves differ in slope, not offset:

Jev + Mercury 2.5 · median model cost per attempt by journey length · log scale
$0.0005$0.001$0.002$0.005$0.010answer1–2 steps · 21 tasksaction, short3–5 steps · 16 tasksaction, long6–10 steps · 8 taskssensitive8–15 steps · 4 tasksWebMCPDOM (ultrafast)
tiertasksWebMCP solvedtool callsDOM solvedpage actions
answer2121/21113/211
action, short1616/1629/163
action, long88/832/88
sensitive44/441/422
“Tool calls” and “page actions” are the same column — median actions_or_tool_calls — counted once as tool invocations and once as clicks, types and selects. In the last tier the page setup performs 22 of them where WebMCP performs 4.

On one- and two-step answers the page setup is 2.4x cheaper and solves 13 of 21. By the four checkout-shaped tasks it is 2.0x dearer and solves 1 of 4. Across the four tiers WebMCP's median attempt cost grows 4.0x and the page setup's grows 19.5x. The median tool calls per attempt go 1 → 2 → 3 → 4; the median page actions go 1 → 3 → 8 → 22.

That is the compression claim with a number on it, and it also explains the aggregate: 37 of the 49 tasks are in the two short tiers where the page setup is genuinely cheaper per attempt, which is why WebMCP's median is higher and its total is lower.

Screenshot of the Jev Ultrafast inspector running a Google Flights task. The left pane shows a live browser page with every interactive control outlined and numbered. The right pane shows the decision: NEXT ACTION 'Change ticket type. Round trip', 25 elements, decision time 351 ms, target confidence 91%, operation CLICK, and a row of operation probabilities reading CLICK 76%, TYPE_TEXT 23%, BLOCKED 1%, WAIT 0.0%, DONE 0.0%, SCROLL_DOWN 0.0%. Below it the indexed elements are ranked by Jev at 93%, 6%, 1%, 0.0%, 0.0%. A footnote reads 'Operation and target are separate choices in one request. Text is generated only for TYPE_TEXT.'
The other side of the comparison: without WebMCP the menu is the page's own controls, and the operation and target heads are answered speculatively in one request. BLOCKED is one of Ultrafast's eight operations — and on this benchmark it is the single most common way a failing attempt ends, 36 times out of 71 (browser-use/jev-ultrafast, docs/inspector.png).

What the website pays

The cost column stops at the model. The work does not.

Getting these eight applications to expose tools took 7,302 added lines across 44 tools, committed as one reference patch per site in goldens/:

sitetoolsadded lines
nextjs-starter-medusa81,352
hi-events71,418
easyappointments71,074
idurar-erp-crm7951
learnhouse6874
bulletproof-react5620
directory-9d83593
tailwind-nextjs-blog1420

About 166 lines per tool, and the good ones are not thin wrappers. md-8's whole journey collapses into one call because somebody wrote the collapse:

// goldens/nextjs-starter-medusa.reference.patch — schema body elided
{
  name: "complete_checkout",
  description:
    "Complete guest checkout for the current cart in one step: sets the contact email and " +
    "shipping/billing address, picks the shipping option and payment provider (automatically " +
    "when the store offers exactly one of each, otherwise returns the choices so you can pass " +
    "shipping_option_id / payment_provider_id), places the order, and returns the order " +
    "confirmation (order id, display number, total, items). Use after add_to_cart; " +
    "begin_checkout is not required.",
  inputSchema: { /* 13 properties, 7 required */ },
  annotations: { readOnlyHint: false },
  async execute(args) { … }
}

Read the description again. "Otherwise returns the choices so you can pass shipping_option_id" is the step-4 error in the trace above, designed in advance. That is a tool written by someone who knew a model would be calling it.

And that tool exists because WebMCP could not finish without it. The v1.1 changelog is unusually direct about it: before September 6, all eight WebMCP configurations scored 0/3 on md-8 because the store's tools handed guest checkout back to the page. Adding complete_checkout took all eight to 3/3, 24 attempts for $0.93. The entry calls the old state "a limitation of one demo store's tool surface, not of the approach", which is fair — and the consequence is still that the tool surface under test is co-designed with the benchmark. 49/49 is a score for WebMCP-as-implemented-by-its-advocates, against a standard of tool design that almost no site in the wild currently meets.

That is not a knock on the result. It is the correct reading of it: the number measures the interface and the quality of the tools behind it, and here those were built by people who wanted the interface to win.

The 49th task rests on a 79-millisecond wait

The Jev WebMCP arm's tool_version is decision-scaffold-v5-mercury-readiness-v1, and the readiness half is a harness behaviour no other WebMCP configuration has. Every arm shares prepareWebMCPPage, which waits up to 30 s for the site's first registerTool call — hydration is slow and that is fair to everyone. What this arm adds is a wait on every subsequent observation: if the live tool list comes back empty mid-run, poll every 25 ms for up to a second before deciding anything.

It is disclosed, and better than disclosed — the run audits every observation. Across 147 attempts and 413 observations it fired four times, for 275 ms total:

ea-7   step 5   0 → 4 tools   28 ms   ready-after-wait   failed anyway
hev-8  step 2   0 → 1 tool    85 ms   ready-after-wait   pass
hev-8  step 2   0 → 1 tool    83 ms   ready-after-wait   pass
hev-8  step 2   0 → 1 tool    79 ms   ready-after-wait   pass

Three of the four are the same task, all three at the same step, and all three passed. hev-8 is "Log in to the organizer dashboard … and report the current status of the WebMCP Community Workshop event" — three steps, two tool calls, and the empty list lands on step 2, immediately after the login redirect. Hi.Events is also the application the shared arm's own comment singles out: "Heavy SSR apps (hi-events) hydrate well past 5s".

This next part is reasoned, not measured. Without that poll I think hev-8 is 0/3 and the headline is 48/49. The shared WebMCP arms throw no live WebMCP tools registered the moment a mid-run list comes back empty. This scaffold would not throw; it would assemble a menu of exactly two options, __finish__ and __abstain__, and ask Jev to choose between reporting a status it has not read and giving up. Both are scored failures, and the task's predicate rejects the hedge explicitly — not_contains: ["could not determine", …].

It is the same race that cost the Medusa store a task: the golden's own comment says complete_checkout was moved into the always-on group because registering it with the cart-conditioned one "left a gap during the begin_checkout navigation in which an agent's next call found no such tool." Tools that come and go with the route are a real property of real single-page applications, and the 1 s poll is the right engineering answer. It is also an advantage one configuration has and eight do not.

Credit, which this release has earned

Five things here were published against their author's interest.

It reproduces

The benchmark is Apache-2.0 and the Jev release ships its runner, its source hashes and 294 redacted attempt transcripts. Everything below needs npm ci, a Chromium and a Python 3.12 venv — and no API key, no Docker and no money. All of it passed on first go:

$ python3 scripts/verify-jev-release.py
PASS: 294 matching attempts, 14044 transcript events, 30 source hashes;
      redaction regression checks and artifact scans.
 
$ node experiments/jev/run.mjs --interface webmcp
{"interface":"webmcp","tasks":49,"attempts":147,"mode":"check-only"}
$ node experiments/jev/run.mjs --interface dom
{"interface":"dom","tasks":49,"attempts":147,"mode":"check-only"}
 
$ node experiments/jev/webmcp/fixture-check.mjs
PASS: portable WebMCP method selects, fills, executes and finishes with both
      measured providers; no predicate leakage.
$ node experiments/jev/webmcp/signal-seam-check.mjs
PASS: actual providerFactory signal seam caps remaining attempt;  (2 real Chromium scenarios).
$ node experiments/jev/ultrafast/fixture-check.mjs
PASS: upstream policy bridge, real Chromium, accounting, timeout and fail-closed provider checks

Without --run the launcher re-hashes all 30 frozen runner files and all 49 task definitions and refuses to start if either moved. The fixture checks stand up a real Chromium, register a WebMCP tool on a local page, and drive the actual select-then-fill loop with scripted provider responses — so the code path in the article is the code path that ran, not a description of it. 294 and 14044 are 147 + 147 attempts and 3782 + 10262 transcript events.

I did not spend a dollar to check a single number in this piece. That should be unremarkable and currently is not.

The number that survives

The headline that survives all of this is not 245x. It is that a model which cannot produce text, paired with a small one that can, is the cheapest and fastest row on a 21-configuration board — and that most of the margin is the interface, available to any model that uses it. Astra over WebMCP also solves 49/49, at 3.8x fewer tokens than the Jev pair. The decision model wins on price per token, not on economy of reading.

What would change my mind

6 claims above, and what would falsify each

  1. The 245x is 15.25x of interface and 16.07x of price, and the interface term is the transferable one.

    Both factors are ratios of published medians, so the arithmetic is not in doubt — the reading is. Run any third model on both its own WebMCP row and its own computer-use row and see whether the interface term stays near 15x. If it collapses for cheap models (Luna's own pair is 7.1x) then "what WebMCP buys" is not a constant and the factorization is descriptive of Astra rather than of the interface.

  2. Cache-write pricing at 1.25x input carries a third of the Astra computer-use multiplier.

    93.5% of that row's dollars are cache-write tokens. If GPT-6 Astra does not in fact bill a write premium — the table's only support is a source comment — the honest multipliers are 199x and 93x, not 245x and 112x. A provider invoice for the same token mix settles it in one line.

  3. Mercury, not Jev, is where a WebMCP agent's money goes.

    77.6% of the WebMCP spend, from summing provider_usage over 147 transcripts. Swap Mercury for a cheaper argument writer — or, better, let the site declare which tools take no arguments so the scaffold's existing skip path fires more often — and re-measure. If total cost barely moves, the split is an artifact of these tools' schemas rather than of the architecture.

  4. Without the readiness poll the Jev WebMCP row is 48/49.

    Reasoned, and settled by one re-run: hev-8 three times with the readiness timeout set to 0, which createReadinessAdapter already takes as a parameter. If it still passes 3/3, the empty tool list at step 2 was recoverable some other way and this caveat is wrong. About a dollar of API credit and ten minutes of Docker.

  5. 49/49 measures the tools as much as the interface.

    The complete_checkout tool was added because WebMCP could not otherwise finish md-8, and took all eight WebMCP configurations from 0/3 to 3/3. Point the harness at a site whose tools were written by someone with no stake in the benchmark — or degrade the goldens to thin wrappers over existing endpoints, keeping the interface identical — and re-run. If 49/49 survives thin tools, the score is about WebMCP; if it does not, it is about these tools.

  6. The 25/49 page result is not an interface ablation.

    It is two different agent implementations, which the release states. runPageHybrid in the frozen scaffold runs the identical select-then-fill loop against page controls. Running that arm over the same 49 tasks would separate "WebMCP beats page control" from "this scaffold beats that harness", and it is the single cheapest experiment left on the table.

Cite this article

For attribution, please use the following reference or BibTeX:

Satyajit Ghana, "The 245x factors: 15x of interface, 16x of price", ai.thesatyajit.com, September 2026.

bibtex
@misc{ghana2026webmcpwindtunnel,
  author = {Satyajit Ghana},
  title  = {The 245x factors: 15x of interface, 16x of price},
  url    = {https://ai.thesatyajit.com/articles/webmcp-windtunnel},
  year   = {2026}
}
share