hub-mcp: agents can see render errors — local WASM validation, render-checked writes, fix_errors prompt - #452
Open
icarusz wants to merge 9 commits into
Open
hub-mcp: agents can see render errors — local WASM validation, render-checked writes, fix_errors prompt#452icarusz wants to merge 9 commits into
icarusz wants to merge 9 commits into
Conversation
…ion) Pivot per review guidance: drop the CRDT diagnostics sidecar entirely; get_errors will render the files the MCP already holds using the same wasm-quarto-hub-client module the browser preview runs (feasibility spike in plan doc), plus execution errors from the existing captures sidecar. Carries over from v1: the local-prod WS proxy crash fix and the two get_errors test files (to be reworked onto the new backing). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
renderDiagnostics(files, path) fills the WASM VFS with the project the MCP already holds and calls render_page_in_project — the same wasm-quarto-hub-client module the browser preview runs — returning the structured errors/warnings plus sibling pass-1 failures and the sha256 of exactly the text it rendered. The module lives in a prebundled host (scripts/build-wasm-host.mjs: esbuild with the /src/wasm-js-bridge/* aliases and dart-sass bundled in, since html theme compilation needs it and the embedded bundle has no node_modules), loaded lazily on first use. Tests run the real WASM, no mocks — including a pin that `title: "broken` front matter is a warning, not an error, exactly as the preview reports it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… was checked The tool renders the requested document (or every .qmd, capped at 25) through the local WASM pipeline and reports structured errors/warnings plus the sha256 of exactly the content it checked; sibling pass-1 failures surface under their own paths, and execution errors still come from the captures sidecar (now mirrored into ProjectState via onCapturesChange). No staleness concept: after an edit, calling the tool again validates the new content immediately. Live integration test drives the real server binary + real WASM over stdio against the in-process test hub: broken doc reported at line 5, patch_file fix, immediate clean re-check. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bundle.mjs now builds wasm-host.mjs (bridges + dart-sass inlined) and copies wasm_quarto_hub_client_bg.wasm alongside index.mjs, so the embedded `q2 mcp` and the npx channel can run get_errors' local validation with no node_modules. bundle.test.ts pins the artifacts. Note: the q2 binary now embeds a second copy of this WASM (the preview SPA has its own) — dedupe tracked as follow-up in the v2 plan. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Agents get structured fields (code, problem, line/col, sub-details) — the ariadne escape-code blob is token noise. Plan doc records the end-to-end run: broken doc via q2 mcp against a real hub reported Q-2-13 at 5:24 with the checked content hash; patch_file then an immediate clean re-check. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Shrink the speculative failed-render fallback to a plain correctness guard (a failed render must never read as clean) — which also fixes its ANSI-strip regex, previously missing the ESC byte. Tidy the CLI-main check in build-wasm-host.mjs to reuse the existing node:url import. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Clients surface it as a slash command (Claude Code: /mcp__quarto-hub__fix-errors <project-or-share-url> [path]). The prompt expands to loop instructions — get_errors, minimal patch_file fixes at the reported line/column, re-check until clean, report — and the LLM does the fixing; the server stays a set of primitives. Protocol round-trip test pins the prompts capability declaration. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
write_file/patch_file/create_file on a .qmd now stage the new text over the project file map, render it locally, and append the outcome to the tool response (clean / error list / check-unavailable note). Error visibility becomes part of completing an update instead of a separate call the agent must remember; the fix-errors prompt now leans on the in-response check with one confirming get_errors at the end. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Member
|
Minor question: |
Member
This is probably worth a fix before merging. |
Member
Author
|
Yupyup. Makes sense. Claude and I are on it. |
All tool names in this server are snake_case; the prompt now matches. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Member
Author
|
OK made the change to standardize that. Claude felt the reasoning was sound (APIs are snake case but slash commands, like all of claudes, are kebab case) but that difference is annoying. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Agents using the Quarto Hub MCP server can now see render errors and verify their own edits:
get_errorstool (read-only, available in--read-onlymode): renders project documents locally using the samewasm-quarto-hub-clientmodule the browser preview runs, hosted in the MCP's Node process, and reports structured diagnostics (kind, title,Q-*code, line/column, hints) for exactly the content it read —checkedContentSha256names the bytes. Passpathfor one document, omit it to check every.qmd(capped at 25). Engine execution errors are surfaced from the existingcapturessidecar (they happen on executors elsewhere and can't be recomputed locally).write_file/patch_file/create_fileon a.qmdstage the new text over the project file map, render it, and append the outcome to the tool response —Render check: clean.or the structured error list. Error visibility is part of completing an update, not a separate call the agent must remember. A check that cannot run degrades to a pointer atget_errorsand never fails the write. Non-.qmdwrites are unchanged.fix_errorsMCP prompt (surfaced as a slash command, e.g./mcp__quarto-hub__fix_errorsin Claude Code): one-command entry into the loop — check, read, minimal patch, repeat until clean. The prompt only instructs; the LLM does the fixing with the existing tools.Why this architecture
v1 of this work had the browser preview publish diagnostics into an automerge index-doc sidecar. Review guidance from @cscheid killed that approach:
v2 (this PR) makes validity a function of content: render what you hold, report what you rendered. No schema change, no new API entry point, no cross-peer choreography, hub-client untouched. (v1 is preserved on
feature/hub-mcp-get-errorsif a human-facing "see collaborators' preview state" feature is ever wanted.)How
src/local-render.ts— lazy-loaded WASM host (server startup stays instant; the ~38 MB init is paid on first use), VFS fill with the/project/prefix, renders serialized via a promise chain (the VFS is instance-global), diagnostics mapped and stripped of ANSI/$schemanoise.scripts/build-wasm-host.mjs— esbuild-bundles the wasm-bindgen JS for plain Node: aliases the Vite-root bridge imports (/src/wasm-js-bridge/*) tots-packages/wasm-js-bridgesources, bundles dart-sass in (the embedded bundle has nonode_modules), inits from bytes. Consumed by both the tsc dev build (dist/) and the embedded bundle (dist-bundle/, viacargo xtask build-hub-mcp-bundle).src/prompts.ts— prompts capability +fix_errors.src/tools.ts—get_errorshandler,renderCheckSuffixon the write tools.Note: the q2 binary now embeds a second copy of
wasm_quarto_hub_client_bg.wasm(~38 MB; the preview SPA already ships one). Deduping at the launcher level is a known follow-up, deliberately not in this PR.Testing
local-render.test.ts— real WASM, no mocks: broken YAML/markdown → structured diagnostics with line/col; sibling pass-1 attribution; binary tolerance; render serialization.get-errors-handler.test.ts,write-render-check.test.ts— realregisterToolsdispatch against a fake connection manager, renderer mocked at the module seam; fail-first TDD.get-errors-live.test.ts— the real server binary + in-process test hub over stdio: create broken project →get_errorsreports Q-2-13 at 5:24 →patch_file(response carriesRender check: clean) →get_errorsimmediately clean.bundle.test.ts— pins that dist-bundle shipswasm-host.mjs+ the.wasm.q2 mcpbinary — found and fixed a real unclosed-**error in a live deck, then authored a full revealjs deck through the MCP with every write returning its render check inline.Plan doc with the full verification record:
claude-notes/plans/2026-07-28-hub-mcp-get-errors-v2.md.🤖 Generated with Claude Code