--- name: drive-cdp-web description: Drive a live cdp-web app running in Chrome through its WebMCP agent tools, using the chrome-devtools MCP as the transport. Use whenever the user wants Claude to control, puppeteer, or remote-drive cdp-web in a browser — connect to the cdp-web tab or installed PWA, build or edit a patch live on the canvas, load sounds, set parameters, render, or test the WebMCP tool surface — including phrases like "control the mcp", "drive cdp-web", "build a patch in the browser", or "connect to the cdp-web app". For authoring .cdp files or share links offline (no browser), use build-cdp-web-patches instead; for rendering audio offline via cdp-wasm, use cdp-sound-design. --- # Drive cdp-web live Control a running cdp-web instance from the outside: the app registers ~14 agent tools (get_patch, add_node, connect, set_params, render, …) on WebMCP, and the chrome-devtools MCP's `evaluate_script` is the transport for calling them. The result is live, undoable edits on the user's patch canvas — not offline file authoring (that is `build-cdp-web-patches`) and not offline audio rendering (that is `cdp-sound-design`). ## 1. Connect Try `list_pages` first. If a cdp-web page is listed (localhost, https://cdp-web.app, or an installed PWA window — PWA windows appear as ordinary pages), `select_page` it and skip to §2. If the browser itself is missing, how to get one depends on the chrome-devtools MCP's mode, visible in its config args: - **Spawned mode** (default, no `--browser-url`): the MCP launches its own Chrome. Just `new_page` to `https://cdp-web.app`, or to the local dev server when working in the cdp-web repo (`node serve.mjs 8000` → `http://localhost:8000`). - **Attach mode** (`--browser-url=http://127.0.0.1:9222`): the MCP expects an already-running Chrome with remote debugging. Probe it with `curl -s --max-time 3 http://127.0.0.1:9222/json/version`; if it is down, relaunch **detached** so it survives Claude Code restarts: ```sh open -na "Google Chrome" --args --user-data-dir="$HOME/.chrome-debug" \ --remote-debugging-port=9222 https://cdp-web.app ``` The separate `--user-data-dir` is not optional: Chrome 136+ silently ignores the debugging port on the default profile, so the user's everyday profile can never be attached to. Do not launch this Chrome as a plain background shell child — it dies with the session; `open -na` detaches it. Origin matters only for *native* WebMCP: `document.modelContext` exists on https://cdp-web.app (origin-trial token) but not on localhost. Either way the app also exposes `window.__webmcp`, a shim with the identical tools — drive through the shim and the origin difference disappears. ## 2. Call tools through evaluate_script The working pattern — a tiny helper, then tool calls: ```js async () => { const t = async (name, input) => (await window.__webmcp.call(name, input)).content[0].text; return [await t('get_patch', {}), await t('list_sounds', {})]; } ``` Rules that save round-trips: - **Wait for the app**: if `window.__webmcp` is undefined the page is still booting — retry after a moment rather than concluding the surface is absent. - **Read schemas, never guess them**: `window.__webmcp.defs` holds every tool's name, description, and `inputSchema`. Argument names are not guessable from tool names — `set_params` takes `{id, params}` (not `{node, values}`), `connect` takes `{from_node, to_node}` (not `{from, to}`) — and this applies to *every* tool, not just those two: before the first call to any tool in a session, read its `inputSchema` from `defs`. Schemas evolve with the app, so `defs` is the authority, including over this document. - **Ids come from the app**: `add_node` replies like `added n5 pvocAnalyse` — the id is assigned by the app and does not follow any counter you keep. When batching steps in one script, parse it and use it in the same script: `const id = reply.match(/added (\w+)/)[1]`. - **Errors are reply text, not exceptions**: failures come back as `error: …` strings with the valid options listed. Check each reply before building on it. ## 3. Build patches Start every session with `get_patch` — the canvas may hold the user's work. Edits are undoable (the `undo` tool / Edit ▸ Undo), but `load_patch` replaces the whole document: never call it over a non-empty patch without the user's say-so. - Discover, then act: `list_sounds` → `set_source`; `search_effects` / `describe_effect` for ids, parameter names, and ranges. Never guess an id or range. - **Serve the musical intent, not just the graph**: a technically correct chain that sounds dull is a failed patch. When the user asks for a character ("ghostly", "underwater", "shattered"), search the catalog with words from that character and read the blurbs — the obvious first tool (time stretch, again) is rarely what makes the sound. Prefer combining two or three complementary processes over one, push parameters away from their defaults (defaults are demo-safe, not expressive), and treat `render` as a first draft: reconsider parameter choices against the reported result, and invite the user to listen and react. The sibling `cdp-sound-design` skill carries the deeper CDP sound-design craft — lean on it when the brief is aesthetic. - **Spectral adapters**: CDP spectral effects (blur.*, stretch.time, …) have spectral in/out ports, while sources and outputs are audio. A type-mismatch error on `connect` means an adapter is missing — add it yourself with `add_node {type:'pvocAnalyse'}` (audio → spectral) or `{type:'pvocResynth'}` (spectral → audio). The error's "double-click the ◇" hint describes the human UI gesture, not the agent path. - **Verify by rendering**: `render` runs the patch and reports duration, channels, and sample rate — metadata only, never audio bytes. Sanity-check it (a ×3 time stretch of 1 s should report ~3 s). `take_screenshot` shows the canvas state. Audible playback needs a user gesture; invite the user to press Play rather than trying to force it. - **Arrange the nodes when you're done**: nodes added via `add_node` cascade into overlapping windows in the top-left corner — fine for the graph, useless for the human watching the canvas. After building or reshaping a patch, call the `arrange` tool (cosmetic: lays the graph out left-to-right, same as Edit ▸ Arrange nodes / ⌘L; `load_patch` already does it automatically). On an older deployment where `defs` has no `arrange`, fall back to the keyboard shortcut — the handler accepts synthetic events: ```js window.dispatchEvent(new KeyboardEvent('keydown', { key: 'l', metaKey: true })); ``` Then `take_screenshot` to confirm the patch reads cleanly. ## 4. Save and share `get_patch {detail:'full'}` returns the complete `cdp-web-patch` v1 JSON as text. To keep a `.cdp` file, `JSON.parse` it inside the script and return the object, then write it with the Write tool (evaluate_script's `filePath` option saves the return value JSON-quoted — a double-encoded file that validators reject). Convention: `./cdp-patches/.cdp`. A patch that references demo sounds or stored audio by `audioKey` is a local record — the audio does not travel. For validated shareable `#patch=` links, hand the saved `.cdp` to the `build-cdp-web-patches` skill and use its validator and URL tools.