Browser CLI
@msw-dev-tool/browser-cli lets an AI agent or script control HTTP and WebSocket scenarios in a browser MSW Dev Tool session through Chrome DevTools Protocol (CDP). Set a scenario with the CLI, inspect the same target through CDP, and verify the user-visible result.
Configuration
Configure Chrome DevTools MCP
For Codex, use .codex/config.toml:
[mcp_servers.chrome-devtools]
command = "npx"
args = ["-y", "chrome-devtools-mcp@latest", "--browser-url=http://127.0.0.1:9222"]
startup_timeout_sec = 120.0For Claude Code, use the root .mcp.json:
{
"mcpServers": {
"chrome-devtools": {
"command": "npx",
"args": [
"-y",
"chrome-devtools-mcp@latest",
"--browser-url=http://127.0.0.1:9222"
]
}
}
}See the Chrome DevTools MCP documentation for other clients and connection options.
Install and connect the runtime
pnpm add -D @msw-dev-tool/browser-cli @msw-dev-tool/core mswThe target tab must run setupDevToolWorker(...handlers). Start a separate Chrome profile with remote debugging enabled:
macOS
open -na "Google Chrome" --args --remote-debugging-address=127.0.0.1 --remote-debugging-port=9222 --user-data-dir=/private/tmp/chrome-debug-9222Choose a target
List browser page targets, confirm the selected tab has initialized setupDevToolWorker(...handlers), and use that exact target ID for every mutation. sessionStorage is tab-scoped.
msw-dev-tool-browser tabs --cdp-url http://127.0.0.1:9222
target_id="paste-the-id-of-the-tab-that-runs-setupDevToolWorker"
cdp_args=(--cdp-url http://127.0.0.1:9222 --target "$target_id")
msw-dev-tool-browser session "${cdp_args[@]}"The remote-debugging endpoint gives local processes browser-debugging access. Keep the separate Chrome profile running and do not use a regular browsing profile.
Commands & API
Successful commands print machine-readable JSON to stdout. Errors are plain text on stderr and exit with a non-zero status. The examples use the cdp_args array from the target-selection step.
HTTP scenarios
msw-dev-tool-browser list "${cdp_args[@]}"
msw-dev-tool-browser get '{"path":"/api/items","method":"get"}' "${cdp_args[@]}"
msw-dev-tool-browser set-behavior '{"path":"/api/items","method":"get"}' delay "${cdp_args[@]}"
msw-dev-tool-browser set-enabled '{"path":"/api/items","method":"get"}' false "${cdp_args[@]}"
msw-dev-tool-browser set-mock-enabled false "${cdp_args[@]}"
msw-dev-tool-browser set-custom-response '{"path":"/api/items","method":"get"}' --json '{"status":"200","contentType":"application/json","response":"[]","delay":100}' "${cdp_args[@]}"
msw-dev-tool-browser set-behavior '{"path":"/api/items","method":"get"}' 'custom response' "${cdp_args[@]}"
msw-dev-tool-browser add-temp --json '{"path":"/api/preview","method":"get","contentType":"application/json","status":"200","response":"{\\"ok\\":true}"}' "${cdp_args[@]}"
msw-dev-tool-browser remove-temp '{"path":"/api/preview","method":"get"}' "${cdp_args[@]}"
msw-dev-tool-browser reset "${cdp_args[@]}"{"ok":true,"handlers":[{"id":"{\\"path\\":\\"/api/items\\",\\"method\\":\\"get\\"}","path":"/api/items","method":"get","behavior":"default","enabled":true}],"mockEnabled":true}set-enabled <handlerId> <true|false> bypasses only that HTTP handler. set-mock-enabled <true|false>
is the global HTTP and WebSocket switch; its result includes mockEnabled. Both leave selected
behavior and custom response data intact, so they resume when enabled again.
WebSocket scenarios
msw-dev-tool-browser ws-list "${cdp_args[@]}"
endpoint_id="$(msw-dev-tool-browser ws-add-endpoint \
--json '{"kind":"string","value":"ws://localhost:8080/preview"}' "${cdp_args[@]}" \
| jq -r '.endpoint.endpointId')"
listener_id="$(msw-dev-tool-browser ws-add-listener "$endpoint_id" --json '{"behavior":{"preset":"default"},"response":{"type":"send","dataType":"string","value":"temp response","delay":300,"repeat":{"interval":500,"repetitions":3}},"customResponse":{"type":"send","dataType":"string","value":"custom response","delay":100}}' "${cdp_args[@]}" \
| jq -r '.listener.info.id')"
msw-dev-tool-browser ws-get-endpoint "$endpoint_id" "${cdp_args[@]}"
msw-dev-tool-browser ws-set-endpoint-enabled "$endpoint_id" false "${cdp_args[@]}"
msw-dev-tool-browser ws-set-listener-behavior "$listener_id" --json '{"preset":"close"}' "${cdp_args[@]}"
msw-dev-tool-browser ws-set-listener-response "$listener_id" --json '{"type":"send","dataType":"string","value":"scheduled","delay":300,"repeat":{"interval":500,"repetitions":"Infinity"}}' "${cdp_args[@]}"
msw-dev-tool-browser ws-set-listener-enabled "$listener_id" false "${cdp_args[@]}"
msw-dev-tool-browser ws-remove-listener "$listener_id" "${cdp_args[@]}"
msw-dev-tool-browser ws-remove-endpoint "$endpoint_id" "${cdp_args[@]}"{"ok":true,"endpoint":{"endpointId":"<value returned in endpoint.endpointId>","enabled":false,"listeners":[]}}ws-set-*-enabled commands apply only to the specified WebSocket endpoint, listener, or logical event branch; the global set-mock-enabled switch takes precedence. ws-add-endpoint, ws-add-listener, and ws-set-listener-behavior use JSON input.
Temporary listeners default to { "preset": "default" }. response and customResponse are independent configurations containing payload, delay, and repeat; the selected behavior chooses which one runs. Repetitions include the first response, and "Infinity" requires a positive interval. Use a local test client for an unbounded sequence because it can flood the client. Stop it by updating or removing the listener, closing the client, or resetting the Dev Tool.
Use ws-set-listener-response and ws-set-listener-custom-response to replace each complete configuration. Send responses use dataType: "string", "Blob", or "ArrayBuffer"; binary values are space-separated hexadecimal bytes. Close responses accept optional code and reason. Browser changes apply immediately to the in-memory store and persist to the selected tab’s sessionStorage; refresh only to confirm persistence.
After the scenario, run msw-dev-tool-browser reset "${cdp_args[@]}" and inspect the returned JSON before reusing the tab. Then remove temporary items and close the dedicated Chrome debug profile. Code-discovered WebSocket endpoints and listeners cannot be deleted, while temporary ones can.