WebCodecs Census docs

MCP server

@motionvector/webcodecs-census-mcp

Let an agent find a leaked VideoFrame — with the allocation stack — without a browser UI in the loop.

Existing media tooling is built for human eyes: panels, flamegraphs, screenshots. An agent cannot read those efficiently. This exposes the same information as small, structured text.

Register it

{
  "mcpServers": {
    "webcodecs-census": {
      "command": "npx",
      "args": ["-y", "@motionvector/webcodecs-census-mcp"]
    }
  }
}

It speaks stdio and holds one session at a time. SIGINT and SIGTERM detach and close any browser it launched.

Tools

ToolWhat it answers
webcodecs_attachInstrument a page and every one of its Web Workers. Call first.
webcodecs_censusWhat is open right now, as a digest.
webcodecs_leak_sitesWhich line is leaking — grouped by allocation site, worst first.
webcodecs_timelineLive counts, throughput, queue depth and media readiness over time.
webcodecs_evaluateDrive the app so the census has activity to observe.
webcodecs_detachStop, and close any browser this launched.

webcodecs_attach

ParameterTypeDefaultWhat it does
urlstringPage to open and instrument. The server navigates, then settles briefly before returning.
browserURLstringDevTools endpoint of a Chrome already started with --remote-debugging-port.
executablePathstringChrome binary to launch instead, with a throwaway profile.
headlessbooleantrueOnly meaningful with executablePath.
sampleIntervalMsnumber250Timeline sampling interval. The server keeps 400 samples per context.

One of browserURL or executablePath is required. Calling it while already attached returns a note rather than replacing the session — detach first.

webcodecs_census

Returns summarize() for every instrumented context, plus anything that could not be instrumented, under a Not instrumented: heading. Small by design — use webcodecs_leak_sites for stacks.

Takes waitMs (default 0) to settle before sampling.

webcodecs_leak_sites

ParameterDefaultWhat it does
typeevery tracked typeRestrict to one type, e.g. VideoFrame.
limit10Maximum sites returned.
minAgeMs0Ignore sites whose oldest object is younger than this.
Attribution is not a verdict

This tool covers every tracked type unless you name one. It deliberately does not inherit the assertion API's default of frame-like types only: an agent asking "which line is leaking" about a codec leak would otherwise get nothing back.

webcodecs_timeline

Takes context (restrict to one context name) and lastN (default 40).

webcodecs_timeline { context: "worker", lastN: 20 }

### worker (20 samples)
  t(ms)  live      dec/out  queued  media(stalled)
  12000  VF=59     0/0      0       4(0)
  58000  VF=58     0/0      0       4(1)
 162000  VF=58     0/0      0       4(1)

VF is VideoFrame; other types abbreviate to their initial. dec/out is decode calls over output callbacks in that interval, and queued is summed codec queue depth.

Decoder idle, queue empty, count frozen: wedged, not buffering. A single snapshot cannot tell those apart, and the difference is between chasing a resource limit that does not exist and finding the actual bug.

webcodecs_evaluate

Runs an expression in the page — click something, start playback — so the census has activity to observe. Takes expression, which is required. Returns the value as JSON.

webcodecs_detach

Stops instrumenting and closes any browser the server launched. No parameters.

A session

webcodecs_attach { executablePath: "/path/to/chrome", url: "http://localhost:5173/" }
→ Attached.
  Instrumented 3 context(s):
    page   http://localhost:5173/
    worker http://localhost:5173/decode-worker.js
    worker blob:http://localhost:5173/…
  Workers are instrumented before their first line, so allocations at worker
  startup are counted.

webcodecs_census
→ worker (20s): VideoDecoder=1 VideoFrame=58
  1 VideoFrame garbage collected without close() — definitively leaked.

webcodecs_leak_sites { type: "VideoFrame" }
→ 58x VideoFrame — decoded, oldest 124609ms, in worker
      at PackagerWorker.setupDecoder (worker.js:1756:21)
      (frame emitted by this VideoDecoder)

Running alongside chrome-devtools-mcp

Several CDP clients can attach to one page at the same time, so this runs happily beside chrome-devtools-mcp. They cover different ground: heap snapshots measure the JS heap, and a WebCodecs resource lives outside it — a page-target snapshot reports tens of bytes for frames holding megabytes of GPU memory, does not cover worker isolates, and cannot see a frame GC already collected without close().

Trust boundary

webcodecs_evaluate runs arbitrary JavaScript in the page under test — that is how an agent drives the app it is measuring. Anything driving this server can run code in any page it attaches to. Point it at applications you control, and never expose a debugging port beyond localhost.