Skip to content

feat(mcp): snapshot tool + run_code return/console capture + storage state + HTML cleanup#5537

Open
DavertMik wants to merge 3 commits into4.xfrom
feat/mcp-capture-snapshot
Open

feat(mcp): snapshot tool + run_code return/console capture + storage state + HTML cleanup#5537
DavertMik wants to merge 3 commits into4.xfrom
feat/mcp-capture-snapshot

Conversation

@DavertMik
Copy link
Copy Markdown
Contributor

Summary

  • New snapshot MCP 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_code MCP tool now returns the value the code produced and captures console.* output (level + message + relative time). Models can use return and console.log to inspect intermediate values; output is safeStringify'd + truncated (20 KB return, 100 entries × 2 KB per log message). console.* writes do not pollute MCP stdio.
  • Storage state capture (cookies + localStorage) — Playwright grabStorageState(), with a grabCookie + executeScript fallback for Puppeteer/WebDriver. Saved as _storage.json alongside other artifacts.
  • Universal HTML cleanup — every captured HTML snapshot flows through formatHtml (minify -> cleanHtml -> beautify). New cleanHtml drops <script> (no src) / <style> / <noscript> content, strips trash class names (Tailwind utilities, framework-generated v-*/ember-*, hashed/scoped classes), and removes inline style="". Built on js-beautify (already a dep).
  • Centralized capture pipeline — new lib/utils/captureSnapshot.js is the single funnel; new lib/utils/trace.js holds shared helpers (pickActingHelper, traceDirFor, artifactLinks, etc.) used by aiTrace, pageInfo, and the MCP server. aiTrace / pageInfo / mcp-server all delegate to these — a few hundred lines of duplication removed.
  • Bug fix: browser logs were being JSON.stringify'd directly on Playwright ConsoleMessage objects, producing files full of empty objects. Logs are now coerced to {type, text} plain objects before serialization.
  • safeStringify in lib/utils.js extended to coerce Function / Error / BigInt / Symbol values — universal improvements that close a JSON.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).
  • Manual smoke tests of formatHtml, cleanHtml, captureSnapshot (Playwright + fallback paths), safeStringify+truncateString composition, and console.* capture in run_code — all expected behavior.
  • Live MCP-client run against a real browser session (deferred — requires user-side configuration).

Docs

  • docs/mcp.md — new snapshot section, refreshed run_code Returns 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

DavertMik and others added 3 commits April 26, 2026 17:27
…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>
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.

1 participant