> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.chaser.sh/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.chaser.sh/_mcp/server.

# CDP Connection

How to connect to a Chaser session, what works, and what to avoid.

## The endpoint

Every ready session exposes a standard Chrome DevTools Protocol WebSocket:

```
wss://cdp.chaser.sh/sessions/{session_id}
```

This is the same protocol your local browser uses. Any CDP-compatible client works.

## Connecting with patchright (recommended)

```typescript
import { chromium } from "patchright";

const browser = await chromium.connectOverCDP(cdpUrl);
const context = browser.contexts()[0];
const page = context.pages()[0] ?? await context.newPage();

await page.goto("https://www.cloudflare.com");
console.log(await page.title());
```

`connectOverCDP` attaches to the existing browser process. Do not call `chromium.launch()` — that starts a local browser, which is not what you want.

## Connecting with Puppeteer

```javascript
import puppeteer from "puppeteer-core";

const browser = await puppeteer.connect({ browserWSEndpoint: cdpUrl });
const [page] = await browser.pages();

await page.goto("https://www.cloudflare.com");
console.log(await page.title());
```

## Connecting with raw CDP

Any WebSocket client that speaks CDP works. Here's a minimal example using the `ws` package:

```typescript
import WebSocket from "ws";

const ws = new WebSocket(cdpUrl);

ws.on("open", () => {
  // Navigate
  ws.send(JSON.stringify({
    id: 1,
    method: "Page.navigate",
    params: { url: "https://www.cloudflare.com" },
  }));
});

ws.on("message", (data) => {
  const msg = JSON.parse(data.toString());
  console.log(msg));
});
```

For production use, prefer a full CDP client library. The protocol is message-based and asynchronous — you send commands with incrementing `id` fields and match responses by `id`.

## The default context

Each session has one browser context with pre-warmed state: cookies, localStorage, and navigation history that a real browser would have. Use it:

```typescript
const context = browser.contexts()[0];
```

Do not call `browser.createBrowserContext()` or `browser.newContext()`. Additional contexts are not supported and will error.

## Pages

The default context may have a blank page open. You can use it or create a new one:

```typescript
const pages = context.pages();
const page = pages.length > 0 ? pages[0] : await context.newPage();
```

## Timeouts

CDP connections to Chaser sessions are subject to the session's TTL. If the session expires while you're connected, the WebSocket closes. Handle the close event and re-create the session if needed:

```typescript
browser.on("disconnected", () => {
  console.log("Session ended — create a new one to continue");
});
```

## What you can do

The full CDP surface is available:

- `Page.navigate`, `Page.reload`, `Page.goBack`
- `Runtime.evaluate` (run JavaScript in the page)
- `DOM.getDocument`, `DOM.querySelector`
- `Input.dispatchKeyEvent`, `Input.dispatchMouseEvent`
- `Network.setCookie`, `Network.getCookies`
- `Emulation.setDeviceMetricsOverride` (avoid — it breaks fingerprint consistency)

## What to avoid

| Action | Why |
|---|---|
| `Emulation.setDeviceMetricsOverride` | Overrides the screen metrics, breaking fingerprint consistency |
| `Emulation.setUserAgentOverride` | Overrides the user-agent, breaking client-hint consistency |
| `Network.setBlockedURLs` | Unnecessary — the session already routes through residential egress |
| Creating new browser contexts | Not supported; use the default context |
| `browser.close()` | This closes your local connection, not the remote session. Use `DELETE /v1/sessions/{id}` to end the session. |