feat(mcp): snapshot tool + run_code return/console capture + storage state + HTML cleanup#5537
Open
feat(mcp): snapshot tool + run_code return/console capture + storage state + HTML cleanup#5537
Conversation
…ate, HTML cleanup
Adds a `snapshot` MCP tool that captures the current browser state without
performing any action. `run_code` now also returns the value the code
produced and captures `console.*` output (level + message + relative time)
using the existing `safeStringify` from lib/utils.js with a `truncateString`
helper, so models can use debug statements and inspect return values.
Captures cookies + localStorage via Playwright's `grabStorageState()` (with
a `grabCookie + executeScript` fallback for Puppeteer/WebDriver),
normalized to a single shape and saved as `_storage.json` alongside the
other artifacts.
Centralizes the artifact-grab + trace-record glue:
- New `lib/utils/captureSnapshot.js` is the single funnel for HTML / ARIA /
screenshot / console / storage capture.
- New `lib/utils/trace.js` holds shared helpers (`pickActingHelper`,
`traceDirFor`, `snapshotDirFor`, `artifactLinks`, `artifactsToFileUrls`)
used by `aiTrace`, `pageInfo`, and the MCP server.
- aiTrace and pageInfo now go through the same funnel — pageInfo's three
serial recorder steps collapse to one delegated capture.
HTML processing pipeline (`formatHtml` in `lib/html.js`): minify ->
cleanHtml -> beautify. `cleanHtml` (new) drops `<script>` (no src) /
`<style>` / `<noscript>`, strips trash class names (Tailwind utilities,
hashed/scoped/framework-generated), and removes inline `style=""`. Built
on `js-beautify` (already a dep) instead of a hand-rolled pretty-printer.
Browser-log normalization fixes a latent bug where `JSON.stringify` was
called on Playwright `ConsoleMessage` objects, producing files full of
empty objects. Logs are now coerced to plain `{type, text}` (or kept as
strings for WebDriver) before serialization.
`safeStringify` in lib/utils.js is extended to also coerce
Function/Error/BigInt/Symbol values — universal improvements that close
a JSON.stringify(BigInt) crash and don't regress the existing 12 mocha
serialization tests.
Tests: 72 unit tests passing (12 circular_reference + 23 html (incl. new
`#cleanHtml`, `#formatHtml`, `#isTrashClass` describes) + 13 aiTrace + 32
mcp + 1 pending). Docs updated in `docs/mcp.md` and `docs/aitrace.md`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both files belonged to the same flow (capture artifacts -> render trace.md). Splitting them across two files made the import sites awkward, so they're now consolidated: - `lib/utils/captureSnapshot.js` deleted; its `captureSnapshot`, `normalizeBrowserLogs`, and `captureStorageState` moved into `lib/utils/trace.js`. - `writeTraceMarkdown` (was inlined in `bin/mcp-server.js`) moved into `lib/utils/trace.js` too — it's the same trace-record concern. - All three call sites (`bin/mcp-server.js`, `lib/plugin/aiTrace.js`, `lib/plugin/pageInfo.js`) now import from a single module. - Stale `writeFileSync` import dropped from `bin/mcp-server.js`. 72 unit tests still passing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s + truncateString
49 new test cases across two files:
- `test/unit/utils/trace_test.js` (new, 37 cases) — direct coverage of
every export from `lib/utils/trace.js`:
- `pickActingHelper` (all 3 paths)
- `traceDirFor` (determinism, uniqueness, sanitization, missing args)
- `snapshotDirFor` (uniqueness, embedded timestamp)
- `artifactLinks` (each artifact, console-count override, storage
counts, custom indent, empty input, ordering invariant)
- `fileToUrl` / `artifactsToFileUrls`
- `writeTraceMarkdown` (golden output, error block, optional commands,
default `file`)
- `captureSnapshot` (default options, full opt-out matrix, fullPage,
formatHtml integration, ConsoleMessage normalization, Playwright
grabStorageState path, Puppeteer/WebDriver fallback path, empty
storage handled, missing helper methods, error swallowing, default
prefix)
- `test/unit/circular_reference_test.js` (extended, 12 new cases):
- `safeStringify` Function/BigInt/Symbol/Error coercions including
nested mixed types (BigInt was a real bug; JSON.stringify(BigInt)
used to throw and trigger the legacy fallback path)
- `truncateString` under / equal / over maxBytes, non-string coercion,
empty string
Total unit suite: 121 passing (was 72).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
snapshotMCP tool — captures the current browser state (URL, formatted HTML, ARIA, screenshot, console, storage) without performing any action. Useful for AI agents reasoning between steps.run_codeMCP tool now returns the value the code produced and capturesconsole.*output (level + message + relative time). Models can usereturnandconsole.logto inspect intermediate values; output issafeStringify'd + truncated (20 KB return, 100 entries × 2 KB per log message).console.*writes do not pollute MCP stdio.grabStorageState(), with agrabCookie + executeScriptfallback for Puppeteer/WebDriver. Saved as_storage.jsonalongside other artifacts.formatHtml(minify -> cleanHtml -> beautify). NewcleanHtmldrops<script>(nosrc) /<style>/<noscript>content, strips trash class names (Tailwind utilities, framework-generatedv-*/ember-*, hashed/scoped classes), and removes inlinestyle="". Built onjs-beautify(already a dep).lib/utils/captureSnapshot.jsis the single funnel; newlib/utils/trace.jsholds shared helpers (pickActingHelper,traceDirFor,artifactLinks, etc.) used byaiTrace,pageInfo, and the MCP server. aiTrace / pageInfo / mcp-server all delegate to these — a few hundred lines of duplication removed.JSON.stringify'd directly on PlaywrightConsoleMessageobjects, producing files full of empty objects. Logs are now coerced to{type, text}plain objects before serialization.safeStringifyinlib/utils.jsextended to coerce Function / Error / BigInt / Symbol values — universal improvements that close aJSON.stringify(BigInt)crash and don't regress the existing 12 circular-reference tests.Test plan
npx mocha test/unit/circular_reference_test.js test/unit/html_test.js test/unit/plugin/aiTrace_test.js test/unit/mcpServer_test.js— 72 passing (12 circular_reference + 23 html (incl. new#cleanHtml,#formatHtml,#isTrashClass) + 13 aiTrace + 32 mcp + 1 pending).formatHtml,cleanHtml,captureSnapshot(Playwright + fallback paths),safeStringify+truncateStringcomposition, andconsole.*capture in run_code — all expected behavior.Docs
docs/mcp.md— newsnapshotsection, refreshedrun_codeReturns block (commands/logs/returnValue/storage), new "HTML Formatting" and "Storage State" sections, refreshed trace-file structure.docs/aitrace.md— clarified HTML pipeline, browser-log normalization, and the intentional per-step storage opt-out.🤖 Generated with Claude Code