-
-
A browser agent (ChatGPT Codex, left) drives tableread, calling run_scenario, get_playtest_context and commit_playtest_round round by round.
-
Creator mode: the nine-aspect checklist is complete, card fields filled, and a playtested scenario fills the transcript and QA panels.
-
get_checklist_status returns nine aspects (seven richness aspects, the MVU decision, the beautify direction), each with a decision axis.
-
get_playtest_context lists which world-book entries are live each round; commit_playtest_round applies the regex and the variable patch.
-
The QA panel: rounds run, JSON-Patch misses, world-book entries never triggered, warnings, and every tool call the agent made, in order.
-
Game-publisher mode: compare_scenarios puts two already-run openings side by side and diffs their final variable state.
-
What compare_scenarios is for: world-book entries that fired in some scenarios but not others, an inconsistency a reviewer would miss.
-
The whole card built through tools: every update_card_field / get_checklist_status call, with arguments and result, in the activity log.
Inspiration
I build SillyTavern character cards, and the hardest part isn't the writing — it's knowing whether the thing actually works. A card isn't one prompt. It's an opening scene, a handful of alternate greetings, a world-book of keyword-triggered lore with sticky and cooldown windows, regex scripts that render status bars, and often a block of variables tracked across the whole conversation. After any edit you have to re-check that every opening still feels like the same character, and that the right lore fires in each branch — by reading transcripts and trying to remember what triggered where. It's exactly the kind of bookkeeping a human skims and an LLM just guesses at.
When WebMCP landed, the split was obvious. The page can do the deterministic half perfectly — which entry is in a cooldown right now, whether a variable patch applied, how triggers differ across scenarios — and hand the agent only the part that needs judgment: turning a premise into real choices, and writing the dialogue. So I built the page that does that, and gave the agent ten tools.
What it does
tableread is a browser-agent-collaborative platform for building and playtesting SillyTavern character cards. It registers ten deterministic tools via document.modelContext.registerTool(), so a WebMCP-capable browser agent (ChatGPT's Codex browser, or Chrome with the WebMCP flag) can:
• Write a card from a premise. get_checklist_status returns nine aspects a card needs, each annotated with a "decision axis" — the thing a set of options should actually differ on. The agent proposes two or three concrete directions per gap, the creator picks one, and update_card_field records it.
• Playtest it deterministically. run_scenario / get_playtest_context / commit_playtest_round run the card round by round. The page computes exactly which world-book entries are live each round (keyword match, constant, or still inside a sticky/cooldown/delay window), applies the regex "beautify" scripts and the JSON-Patch variable updates, and reports every warning honestly.
• QA it across scenarios. compare_scenarios takes a batch of already-run openings and flags which world-book entries fired in every one of them versus only some. That inconsistency is the single most useful "does this card hold up across branches" signal — and it's not something any existing SillyTavern tooling, or an agent working from the rendered page, can compute.
The page never calls an LLM. Zero API key. Every line of generated dialogue comes from the calling agent's own model.
How I built it
• Pure frontend, zero bundler: index.html + native ES modules, served by a ~30-line zero-dependency static file server. Deployed on GitHub Pages.
• The playtest engine — world-book scanning, regex rendering, JSON-Patch variable state — is ported from an existing Python tool (playtest_engine.py) to JavaScript, and checked against it with a golden-parity test suite that replays fixed multi-round conversations through both engines and asserts byte-identical output. 81 tests total (unit + golden + Playwright e2e).
• Each of the ten tools is a plain, DOM-free function unit-tested on its own; only one module (registerTools.js) ever touches document.modelContext, and registration is fault-tolerant (per-tool try/catch, a timeout cap) because the API is still experimental.
• Tool descriptions and the whole UI are bilingual (Traditional Chinese / English) and follow a page toggle, so the agent reads whichever language the creator is working in.
Challenges I ran into
• WebMCP is early-preview. The agent side — a browser that actually calls the registered tools, not just accepts them — exists today only in ChatGPT's Codex browser and in Chrome behind a flag. Working out which environment actually calls the tools (a plain ChatGPT account registers them but never calls them) took real testing.
• Getting the agent to emit the exact wrapper the engine parses. Solved by having the tool report the precise format error back to the agent — which turned into a good demonstration that the tool's output, not the agent's "verified, no warnings" self-report, is what tells you whether something worked.
• Keeping the split honest: everything deterministic and stateful on the page, only the in-character writing on the agent — and resisting the urge to let the agent "help" with the bookkeeping.
Accomplishments that I'm proud of
• The full write → playtest → cross-scenario QA loop runs through typed, self-describing tools with zero prompt scaffolding. The agent discovers the whole call sequence from the tool metadata alone.
• compare_scenarios computes a QA signal (world-book entries that trigger inconsistently across branches) that no existing character-card tooling produces.
• Golden-parity tested against the original Python engine, so the ten tools an agent calls are backed by code that doesn't guess.
What I learned
• WebMCP's real value is the division of labour: the page can hand the agent exact state — which entry is inside a cooldown right now, whether a patch actually applied — that the agent could never reconstruct from the rendered page.
• Self-describing tools plus honest error reporting removes almost all of the prompt engineering. The tool descriptions are written for the agent, and the agent just follows them.
What's next for tableread
• A bottom-of-page ideation panel, so option-picking happens in the UI rather than only in chat. • A "load an existing card" import, so creators can QA cards they already have. • Wider agent support as WebMCP rolls out beyond the early-preview browsers.
Log in or sign up for Devpost to join the conversation.