wtf --mcp lets an AI agent, such as Claude Code, open your traces and read them with the same decoders, searches and records you use in the window. The agent answers from decoded values, and when it has found something, it shows the evidence in your open window.

What it measured

The benchmark gave Claude Haiku (claude-haiku-5-5) four failing RTL testbenches to debug and fix. Each task ran five times with wtf and five times without it. Without wtf, the agent had the RTL, the testbench, the simulation log and make sim, and worked with prints and reruns. With wtf, it also had the MCP server.

An agent fixing failing RTL testbenches, with and without wtf eth-parser-headers: with wtf 5 of 5 runs with every answer right, 134 s, $0.029; without 2 of 5, 270 s, $0.058; eth-parser-stall: with wtf 5 of 5 runs with every answer right, 76 s, $0.014; without 5 of 5, 136 s, $0.024; head-trimmer-boundary: with wtf 5 of 5 runs with every answer right, 85 s, $0.012; without 3 of 5, 203 s, $0.024; axil2mi-stale-addr: with wtf 2 of 5 runs with every answer right, 62 s, $0.015; without 1 of 5, 57 s, $0.015 with wtf without wtf: logs, prints, reruns eth-parser-headers eth-parser-stall head-trimmer-boundary axil2mi-stale-addr Every answer right runs of 5 eth-parser-headers, with wtf: 5 5 eth-parser-headers, without wtf: 2 2 eth-parser-stall, with wtf: 5 5 eth-parser-stall, without wtf: 5 5 head-trimmer-boundary, with wtf: 5 5 head-trimmer-boundary, without wtf: 3 3 axil2mi-stale-addr, with wtf: 2 2 axil2mi-stale-addr, without wtf: 1 1 Time to the fix median eth-parser-headers, with wtf: 134 s 134 s eth-parser-headers, without wtf: 270 s 270 s eth-parser-stall, with wtf: 76 s 76 s eth-parser-stall, without wtf: 136 s 136 s head-trimmer-boundary, with wtf: 85 s 85 s head-trimmer-boundary, without wtf: 203 s 203 s axil2mi-stale-addr, with wtf: 62 s 62 s axil2mi-stale-addr, without wtf: 57 s 57 s Cost per run median eth-parser-headers, with wtf: $0.029 $0.029 eth-parser-headers, without wtf: $0.058 $0.058 eth-parser-stall, with wtf: $0.014 $0.014 eth-parser-stall, without wtf: $0.024 $0.024 head-trimmer-boundary, with wtf: $0.012 $0.012 head-trimmer-boundary, without wtf: $0.024 $0.024 axil2mi-stale-addr, with wtf: $0.015 $0.015 axil2mi-stale-addr, without wtf: $0.015 $0.015
Each task five times with wtf and five without. The table below has every number.
taskwtfpassfixedevidencecost (median)wall (median)
eth-parser-headerson5/55/51.00$0.029134 s
off2/55/50.60$0.058270 s
eth-parser-stallon5/55/51.00$0.01476 s
off5/55/51.00$0.024136 s
head-trimmer-boundaryon5/55/51.00$0.01285 s
off3/55/50.80$0.024203 s
axil2mi-stale-addron2/55/50.80$0.01562 s
off1/55/50.46$0.01557 s

pass counts the runs that fixed the bug and had every answer key right. fixed counts the runs that fixed the bug. evidence is the share of answer keys right. The table leaves out make sim runs, denied commands and tokens, which are in bench/RESULTS.md.

Every run fixed its bug, with wtf or without. These bugs can be found from the log and the code, so the benchmark measures what wtf adds beyond the fix. On the four tasks, 17 of 20 runs with wtf had every answer key right, and 11 of 20 without it. Without wtf, the agents could not read the trace.fst files, so most of their times were unknown. With wtf they gave the times.

A fifth task, ibex-checksum, is harder: a store decoding bug in a 10k-line Ibex core, seen only as a wrong checksum 10 000 cycles later. With wtf, 3 of 5 runs had every answer key right against 1 of 5 without (evidence 0.75 against 0.40), but 4 of 5 fixed the bug against 5 of 5: one run hit the 600 s timeout. Median cost was $0.133 with wtf and $0.139 without, wall time 277 s and 242 s. These runs used wtf at 916c847 (2026-10-26); the four above, d0ebd30 (2026-10-25).

To run the benchmark yourself, from a checkout:

cargo build --release && cargo run -p wtf-bench --release -- agent run

It costs money, because it runs claude -p on each task.

Why the answers are exact

The agent decodes the bus first. decode bus axilite turns each AXI4-Lite transaction into a record with its start and end, its address, data and response, and a check for each protocol rule. An agent writing its own search has to get the handshake right itself; the decoder has it right already, and wtf's tests check it.

Then the agent reads the records with records, filtered by where. The answer lists each failing read with its time and values, taken from the trace.

A session on the demo trace

The example below uses docs/demo.vcd from a clone of the repository, copied to sim/axi_tb.vcd. It is the sequence the agent ran for the question "the second read fails, why?". The first line of each call is the tool and its arguments, and the lines under it are the answer.

open {"trace": "sim/axi_tb.vcd"}
workspace <id> (the user's wtf windows on this trace see your steps; :follow there shows the workspace)
axi_tb.vcd
0ns - 240ns
1 tick = 1ns
19 signals
interfaces a built-in protocol decodes (decode them: exact records, handshake checks built in):
  top.s_axi_* axilite: decode s_axi axilite clock=top.aclk prefix=top.s_axi_

command {"line": "decode s_axi axilite clock=top.aclk prefix=top.s_axi_"}
s_axi: records s_axi.wr 1, s_axi.rd 2; violations s_axi.errors 1, s_axi.wr.unmatched 0, s_axi.rd.unmatched 0; made from s_axi.aw 1, s_axi.w 1, s_axi.b 1, s_axi.ar 2, s_axi.r 2

records {"stream": "s_axi"}
4 of 4
stream         n start end   delay addr data       strb resp rule   kind
s_axi.wr       0 55ns  95ns  20ns  0x10 0xCAFEF00D 0xF  0x0
s_axi.rd       0 115ns 145ns 20ns  0x10 0xCAFEF00D      0x0
s_axi.rd       1 175ns 205ns 20ns  0x40 0x0             0x2
s_axi.errors   0 195ns 205ns                                 r_resp

The second read, at 175ns, is the one at address 0x40, and its response is 0x2. The s_axi.errors record at 195ns names the r_resp rule.

The answer lines above are the server's own, with the hint line after decode left out.

Set it up

wtf has to be on your PATH (Get started has the install). For Claude Code:

claude mcp add wtf -- wtf --mcp

Any other client that speaks MCP over stdin and stdout takes the same command, usually in a JSON file:

{ "mcpServers": { "wtf": { "command": "wtf", "args": ["--mcp"] } } }

The server takes no trace at start. The agent opens the traces it needs, and it finds the wtf windows you have open with sessions.

Ask

Name the trace and what went wrong: "the second read in sim/axi_tb.vcd fails, why?". If you have the trace open, you can say "look at what I'm looking at" instead. The agent reads your window's rows, cursors and visible range, and starts from there.

Watch it work

Your window on the same trace shows agent 4123-1 joined (:follow shows its workspace). Each step the agent takes then shows on the status line, for example agent: find @clk wr && addr == 0x40 · 3 matches, first 120ns, last 180ns. A step does not move your view.

KeysDoes
Space a tthe agent's steps, oldest first; Enter puts the cursor at a step's time
Space a ffollow: the window shows the agent's workspace and each change it makes
Space a Fstop following, and get your own view back

Take it from there

When the agent has its answer, it prepares the rows, the marks and the decodes that show it, and presents them. Your window takes that layout in place of yours, as one change. u gives your own view back, and Ctrl-r gives the agent's again. The decodes it made come along, so :records and Tab in the records table work on them as they do on your own.

The agent works in its own workspace, and nothing is saved until you :w. Your view moves in two cases: when the agent presents its layout, and while you follow it with :follow; :follow off brings back your own view from before. The agent reads your window with user_view, which returns its rows, cursors and visible range and does not change it.

If no window was open, for example after a simulation that failed in CI, present writes the layout next to the trace, as <trace>.agent.session.toml. wtf <trace> shows it when you open the trace, and :trail lists the steps that led to it. If you saved your own session after that, the agent's layout is only offered: Space a s shows it.

open takes any path the agent gives it, so the agent can open any trace its process can read, not only the ones you named. It cannot run :w, :q, :e or :help on your window. A workspace ends when the agent's client exits, so present a layout first if you want to keep it.

What the agent sees

Agent tools has each tool the server offers and the help it reads, word for word, written from wtf itself. The reference is the agent's reference for names, times, expressions and decoding.