Skip to content

Add MCP debugger server for REVM - #2

Merged
boedegoat merged 10 commits into
mainfrom
mcp_server
Sep 24, 2026
Merged

boedegoat merged 10 commits into
mainfrom
mcp_server

Conversation

@boedegoat

@boedegoat boedegoat commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Merge the REVM-based main into mcp_server and wire sevm mcp to the REVM debugger.
  • Expose navigation, inspection, mutation, bytecode search/parity, opcode provenance, and trace export as MCP tools. Drop checkpoint/batch operations that require paused-state restore, which the REVM bridge does not provide.
  • Fix stale Solidity internal frames crossing REVM transaction boundaries. The regression verifies the new transaction’s backtrace and NEXT through the loop body.
  • Document the MCP tools and add independent Python and Rust CI jobs.

Verification

GitHub Actions Verify run passed: Ruff lint/format, mypy, the focused stepping regression, 857 passed / 5 skipped in the full Python suite, and cargo test --workspace --locked. The five skipped tests require network access.

qwertyaxl and others added 10 commits September 10, 2026 23:00
sevm mcp serves the debugger over MCP stdio as a third frontend, shaped
for a consumer that cannot scroll panes or watch them change:

- every windowed read takes offset/size and returns {items, total,
  truncated}; every navigation tool returns the same stop report (pc,
  opcode, gas, sp, location, stack top, stack delta since the previous
  stop); sevm_diff_since_last_stop reports stack pushes/pops with
  values, changed memory ranges and gas spent
- 37 tools: session lifecycle (web3.py driver scripts and .t.sol
  Foundry tests, restart with new args), navigation, windowed state
  reads, decoded storage/locals/arguments/logs, source and disassembly
  windows, breakpoints and watchpoints, Solidity evaluation against the
  paused frame, live mutation (stack/gas/memory/storage/pc), and a raw
  gdb-verb passthrough (vm.* cheatcodes, Yul, jump, reseat)
- failures return {"error": ...} dicts with the fix in the message

Both targets and a new find HEX command share a fresh find_needle
inspect op: every offset of a byte pattern in the RUNNING code, with
instruction alignment and the nearest preceding JUMPDEST — the address
an indirect jump can reach. bytes(CodeStream) iterates from the current
pc, so the op reads _raw_code_bytes to search the whole program.

Verified: full suite 802 passed; stdio JSON-RPC smoke (initialize,
tools/list, call_tool); in-memory client round trip; and a TokyoPayload
gadget hunt through the driver locating POP4/CALLDATALOAD/DELEGATECALL/
STOP gadgets in the optimized runtime.
The AI-review package: the reviewer's top asks, built for a consumer
that needs ground truth on 'why did this operand get this value' and
cheap branching from a deep stop.

Checkpoints (snap/restore, sevm_save/restore_checkpoint): capture a
stop — per-frame stack/memory/gas/pc/logs, the py-evm journal
(state.snapshot()/revert(), the mechanism the evaluator already
exercised), cheat state, gas profiles, watchpoint baselines — then
experiment and roll everything back without re-running the prefix.
Restoring re-arms the consumed journal checkpoint so a save restores
any number of times; a skip-flag in the opcode loop cancels the stale
iteration after any pc write, which also makes jump/set $pc exact;
restoring discards later checkpoints (journal nesting) and refuses
when the frame stack changed or the program finished.

Provenance (provenance on, why N, sevm_why_stack): opt-in per-opcode
recording (before/after stacks, gas, pc) and a concrete backward
slice driven by an opcode arity table: PUSH resolves to constants,
DUP/SWAP to their copies, consumers fan in, MLOAD/SLOAD chain to the
last same-offset MSTORE/SSTORE, CALLDATALOAD names its calldata
window, survivors are frame-entry slots. A checkpoint restore
truncates the record to stay consistent.

Batch experiments (sevm_run_experiments): a list of
{set_stack, set_gas, write_memory, run_until_pc, read_stack} runs,
each from one saved base checkpoint — 32 variants for one prefix; a
run that finishes ends the batch with a named cause.

Bytecode parity (parity verb, sevm_check_parity,
start_session(reference_runtime_hex=...) hard-fail): metadata-stripped,
immutable-masked comparison reporting the first divergence.

structLog export (export, sevm_export_trace): anvil/geth
debug_traceTransaction-shaped rows from the provenance recording, to
a file or as a windowed slice.

Also: set $stack[N] = 0x... now parses hex literals directly (bare
0x literals are not valid Solidity expressions); script tracebacks
are surfaced by all three frontends and a crashed script exits 1.

Full suite 813 passed; storage-level equivalence proven for
restore-then-continue vs natural runs; TokyoPayload's FMP corruption
traces to calldata via why.
@boedegoat
boedegoat merged commit d6d15a1 into main Sep 24, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants