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

# Connect with CDP

> Drive a runtime yourself with Playwright, Puppeteer, or any CDP-compatible client.

Starting a runtime mints a credentialed CDP endpoint scoped to that run. Connect Playwright, Puppeteer, or any CDP client and drive the browser directly. BCTRL records the [run](/sdk/runs) in the background.

## Get a connect URL

```ts
const runtime = await bctrl.runtimes.create({ type: "browser", name: "main" });
const { connectUrl, runId } = await bctrl.runtimes.start(runtime.id);
```

The `connectUrl` stays live for the lifetime of the run. Each `start` mints a fresh one.

## Playwright

```ts
import { chromium } from "playwright";

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

await page.goto("https://example.com");
```

The runtime launches with a default context and page already open. Reuse them instead of creating new ones.

## Puppeteer

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

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

await page.goto("https://example.com");
```

## The controller lock

A runtime allows one controller at a time. While you hold a CDP connection, a [hosted invocation](/sdk/hosted-agents) on the same runtime fails with `runtime.controller_busy`. The reverse holds too: an active invocation blocks a new CDP connection.

```ts
try {
  await bctrl.runtimes.invocations.create(runtime.id, {
    action: "act",
    instruction: "Click submit.",
  });
} catch (error) {
  if (Bctrl.isControllerBusy(error)) {
    // release the CDP connection first
  }
}
```

## Next

* [Hosted agents](/sdk/hosted-agents) - let BCTRL drive instead
* [Runtime configuration](/sdk/runtime-config) - stealth, proxy, fingerprint
* [Runs](/sdk/runs) - what gets recorded while you drive