wolfbook — driving the live Wolfram kernel + notebook via MCP
The mcp__wolfbook__* tools talk to a running Wolfram kernel inside a VS Code window, editing
and evaluating a live .wb/.nb notebook. This is the opposite end from wolfram-headless
(one-shot wolframscript): here state persists, cells have stable IDs, and you can
checkpoint/restore.
Requires the Wolfbook extension with the MCP enabled
("wolfbook.mcpEnabled": true); and the server registered with Codex
under [mcp_servers] in ~/.codex/config.toml or the project's .codex/config.toml
(see Wolfbook's own docs for the command/endpoint). Run /mcp in Codex to confirm the
server is connected and to list its tools; without it, use wolfram-headless for .wls
runs instead.
Decision: MCP live kernel vs headless wolframscript
- Live kernel (MCP) — interactive work on the project notebook: read/edit/run cells, probe
state, small-to-medium evaluations, debugging, building up definitions incrementally. State is
preserved between calls;
kernelControlgives real checkpoint/restore. - Headless (
wolframscript, seewolfram-headless) — heavy batch jobs (a big symbolic solve) that would block or OOM the interactive kernel, or that you want to run in the background and poll a result file. The live kernel in VS Code contends for the license seat with headless jobs — close/idle the Wolfbook window before a long headless run, or target a worker client. - Both report a kernel crash as "The product exited because of a license error" — it is almost
never licensing (see
wolfram-headlessRULE 2). On the MCP side usewolfbook_kernelCrashLog(source:"debug"/"crash") to get the real stack/cause.
Tool map (right tool for the job)
Orient before touching anything
wolfbook_list_clients— list VS Code windows, their open notebooks, primary/worker role.wolfbook_setTarget {client_id, notebook}— pin a default so you stop passing them every call (set it once at session start and omitnotebookfrom subsequent calls).wolfbook_getNotebookContext action:"read"(orbrief:true/action:"summary") — full or one-line-per-cell view. Always read first; cell numbers shift after structural edits, so grab the stable CellId and use that. (Its outputs are a CACHED snapshot — see "Read kernel errors" below.)wolfbook_searchCells {query, regex}— locate a symbol/def/error in a long notebook without reading it whole; returns CellId + whether the hit was in source or output.wolfbook_getKernelState {pattern}— list definedGlobal*` symbols (values / DownValue counts) before editing, to avoid clobbering an existing definition (watch for name-collision hazards; record known ones in your AGENTS.md).wolfbook_getCellOutput {cellId}— read a cell's existing (stored) output without re-running it.
Evaluate / run
wolfbook_evaluateExpression {expression, multiLine, outputForm, timeoutSeconds}— eval WL in the live kernel. ⚠ Multi-statement pitfall: several newline-separated subexpressions WITHOUT semicolons are parsed asTimes[...], not sequential statements. Either put;between statements or setmultiLine:true(each complete statement eval'd separately,;suppresses its output). This is the safe way to run multi-statement code thatrunCellwould mis-split.outputForm:"TeXForm"for LaTeX,"Short"to preview a huge result,"MatrixForm"/"TableForm"for structure.wolfbook_runCell {cellId | startCell,endCell}— re-run EXISTING cell source (nocodeparam). ⚠ It splits a cell at top-level newlines, so a single statement that spans a soft-wrapped line can be torn in two (classically: a factor silently dropped, a definition turned into a product). Keep cells one physical line per statement (the$nb-to-wolfbookskill enforces this); preferevaluateExpressionfor anything multi-line. Range mode runs a block (stopOnError,errorsOnly). It re-executes and surfaces a⚠ Kernel messagessection — read it (see below).wolfbook_validateSyntax {cellId | startCell,endCell}— kernelSyntaxQ(offline bracket-match fallback). Run on an edited cell before executing.
Edit the notebook
wolfbook_editCell— replace cell source (single or batchcells:[{cellId,content}]; batch evaluates by default — catches errors immediately). Pass real newlines, not literal\n.wolfbook_insertCells— add code/markdown cells (single or contiguous block); evaluates by default. UseafterCellId, not a number.wolfbook_deleteCell/wolfbook_restoreDeletedCells— delete (logged) and undo.wolfbook_moveCell— reorder, or copy/move a cell between open notebooks (handy for promoting agenerated/cell into the main notebook — but for.wb↔.nbmirroring use the$sync-wb-nbskill, which verifies box-equality).- After any
.wbedit, run$sync-wb-nbto mirror into the.nb(standing rule if you keep a paired.nb).
Kernel lifecycle — use checkpoints instead of hand-rolled .mx
wolfbook_kernelControl action:"checkpoint" tag:"..."saves allGlobal`` defs to an.mx;action:"restore" [path]doesClearAll+Get. This is the *live-kernel* equivalent of the manualDumpSave/Get["/tmp/...mx"]dance — use it for safe rollback points **before a risky refactor or a long rebuild**, and to recover after an abort.action:"abort"stops a hung evaluation without nuking state;action:"restart"is the destructive last resort. (For *headless* jobs there is nokernelControl— keep usingDumpSave/Putto a file, perwolfram-headless`.)wolfbook_debugCell— step-through debugger (analyze → start → stepOver/Into/Out → continue), breakpoints, watch variables, inline timings. Reach for it on a complex cell that returns garbage rather than peppering it withPrint.
Lookup / discovery
wolfbook_lookupSymbol {symbol, fetchWeb}— usage message + signature + options for a built-in OR user symbol;fetchWeb:truepulls the full reference page (use for e.g.NDSolveMethod details).wolfbook_findPackage {query}— search Paclet Server + GitHub; returns the exactPacletInstall[...]command (then run it viaevaluateExpressionwithtimeoutSeconds>=120— do not guess install syntax).wolfbook_paperSearch— INSPIRE-HEP (arXiv fallback):search(title/author/abstract/ID),bibtex,bibitem,references,citations. For HEP/physics work INSPIRE is the canonical source, so this is a good citation-verification path and gives ready\bibitem/BibTeX (pair with$verify-citation).wolfbook_fileOps(read/write/list) andwolfbook_runTerminal— file + shell on the notebook's machine. From Codex you already have shell and file access; these matter when targeting a remote client.wolfbook_latex(latexmk build) exists, but for.texprefer the$latex-compileskill — it forces a realpdflatexpass and reads warnings from stdout, dodging latexmk's stale-log trap.
Common gotchas
NotebookDirectory[]fails under Wolfbook/headless — use an explicit absolute path.- Greek literals are fine in the live kernel/notebook but corrupt in headless
.wls(wolfram-headlessRULE 1) — don't copy notebook Greek into a.wlswithout escaping. - Add project-specific gotchas (known symbol collisions, required sanity checks before trusting numbers, memory references for recurring bugs) to your project's AGENTS.md. Codex reads them at session start and applies them throughout.
Read kernel errors — they are signal, not noise
A human running cells interactively fixes an undefined-symbol error the instant they see it. Do the
same — one undefined symbol silently invalidates everything downstream. (Full write-up:
docs/wolfbook-kernel-errors.md.)
- Treat these as STOP-AND-FIX (an undefined symbol / cell-order forward-reference / typo, NOT a
benign warning):
ReplaceAll::reps("X is neither a list of replacement rules") — a stray/.then poisons the symbol and propagates downstream;Set::shape;Part::partd;Set::write/Protected; any "not defined". Forward-references (a symbol defined LATER than the cell that uses it) are normal in working notebooks — one top-to-bottom pass breaks it; fix = define it first, then re-run the dependent cell (a 2nd pass). runCellsurfaces messages;getNotebookContextis a cached snapshot. (Verified against the v2.7.14 source.)runCellre-executes and prints a dedicated⚠ Kernel messagessection (with aSyntax::flag) — READ and act on it, don't judge from the result line.getNotebookContextre-shows each cell's STORED output and does NOT re-evaluate, so never read its outputs as a fresh result. Confirm state by EVALUATING:Head[sym],ValueQ[sym],FreeQ[sym, ReplaceAll]— pick a check that survives evaluation (FreeQ[_,ReplaceAll],Head===Plus);FreeQ[_,foo]is bogus becausefoo[]evaluates away.- Sanity-sweep after any multi-cell setup, before trusting a build:
Select[{<your key symbols>}, ! FreeQ[#, ReplaceAll] &]must be{}, andValueQ /@ {<your data lists>}allTrue.
Quick start
wolfbook_list_clients→wolfbook_setTargetyour project notebook.wolfbook_getNotebookContext brief:trueto map cells;searchCellsto find the target; grab CellIds.getKernelStatebefore editing;kernelControl checkpointbefore anything risky.- Edit with
editCell/insertCells;validateSyntax; run withevaluateExpression(multiLinefor multi-statement) orrunCell. $sync-wb-nbto mirror.wb→.nb(if paired); checkpoint the good state.