> 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 AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://platform.bctrl.ai/_mcp/server.

# Connect with WebMCP

> Connect an MCP client to tools exposed by the active webpage.

The WebMCP connection is a **Run-scoped MCP server**. It lets an AI agent
discover and call tools exposed by the active webpage, such as `search`,
`add_to_cart`, or `book_appointment`.

BCTRL returns a credentialed `webMcpUrl` when the browser Runtime starts. Pass
that URL to any MCP client that supports Streamable HTTP.

## Complete example

Install the BCTRL SDK and the official TypeScript MCP SDK:

```bash
pnpm add @bctrl/sdk @modelcontextprotocol/sdk
```

This example creates a browser Runtime, opens a WebMCP-enabled store, connects
an MCP client, discovers the page tools, and calls `search_catalog`.

Replace the example URL, tool name, and arguments with those exposed by the
site you want to automate.

```ts
import { Bctrl } from "@bctrl/sdk";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const bctrl = new Bctrl({
  apiKey: process.env.BCTRL_API_KEY,
});

// 1. Create and start a browser Runtime.
const runtime = await bctrl.runtimes.create({
  type: "browser",
  name: "shopping-agent",
});

let mcp: Client | undefined;

try {
  const connection = runtime.connection;
  if (!connection?.webMcpUrl) {
    throw new Error("This browser host does not offer WebMCP");
  }

  // 2. Open a page that exposes WebMCP tools.
  const openCall = await bctrl.tools.start("browser.pages.open", {
    url: "https://your-webmcp-site.example/products",
  }, { runtimeId: runtime.id });

  await bctrl.toolCalls.result(openCall.id, {
    waitSeconds: 30,
  });

  // 3. Connect a standard MCP client to the active browser Run.
  mcp = new Client({
    name: "shopping-agent",
    version: "1.0.0",
  });

  await mcp.connect(
    new StreamableHTTPClientTransport(
      new URL(connection.webMcpUrl),
    ),
  );

  // 4. Discover the tools declared by the webpage.
  const { tools } = await mcp.listTools();
  console.log("Page tools:", tools.map((tool) => tool.name));

  const searchTool = tools.find(
    (tool) => tool.name === "search_catalog",
  );

  if (!searchTool) {
    throw new Error("The page does not expose search_catalog");
  }

  // 5. Call a page tool through WebMCP.
  const result = await mcp.callTool({
    name: searchTool.name,
    arguments: {
      query: "camera",
    },
  });

  console.log("Search result:", result.content);
} finally {
  // 6. Close the MCP connection and browser Runtime.
  await mcp?.close();
  await bctrl.runtimes.stop(runtime.id);
}
```

The MCP client can access only the tools declared by the active webpage. It
does not receive CDP access or general control of the browser.

## Get the WebMCP URL

An ephemeral Runtime starts during creation:

```ts
const runtime = await bctrl.runtimes.create({
  name: "shopping-agent",
});

const connection = runtime.connection;
if (!connection?.webMcpUrl) {
  throw new Error("This browser host does not offer WebMCP");
}

const { webMcpUrl, runId } = connection;
```

For a reusable profile-backed Runtime, start it when you need a new session:

```ts
const runtime = await bctrl.runtimes.create({
  name: "shopping-agent",
  profile: true,
});

const started = await bctrl.runtimes.start(runtime.id);
const { webMcpUrl, runId } = { ...started.connection, runId: started.runId };

if (!webMcpUrl) {
  throw new Error("This browser host does not offer WebMCP");
}
```

The connection fields are:

| Field                     | Type                   | Always present | Meaning                                            |
| ------------------------- | ---------------------- | -------------- | -------------------------------------------------- |
| `connection.webMcpUrl`    | `string`               | No             | Run-scoped MCP endpoint. Omitted when unavailable. |
| `connection.cdpUrl`       | `string`               | Yes            | CDP endpoint for the same browser.                 |
| `connection.webDriverUrl` | `string`               | Yes            | WebDriver endpoint for the same browser Run.       |
| `runId`                   | `string`               | Yes            | Run that owns the connections.                     |
| `connection.recording`    | `{ enabled: boolean }` | Yes            | Whether Run recording is enabled.                  |

The URL remains stable while the Run is active and stops working when the Run
ends. Treat it as a credential: do not log it, persist it, or expose it to page
code.

## Open a WebMCP-enabled page

WebMCP exposes tools from the current webpage. Navigate the browser to a site
that supports WebMCP before listing tools.

You can use a hosted browser Tool:

```ts
const call = await bctrl.tools.start("browser.pages.open", {
  url: "https://your-webmcp-site.example",
}, { runtimeId: runtime.id });

await bctrl.toolCalls.result(call.id, {
  waitSeconds: 30,
});
```

You can also navigate with [CDP](/sdk/connect-cdp), [WebDriver](/sdk/connect-webdriver),
or a hosted Agent.

## Connect an MCP client

Connect using the Runtime's `webMcpUrl`:

```ts
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const client = new Client({
  name: "shopping-agent",
  version: "1.0.0",
});

const transport = new StreamableHTTPClientTransport(
  new URL(webMcpUrl),
);

await client.connect(transport);
```

## List and call page tools

List the tools exposed by the active page:

```ts
const { tools } = await client.listTools();

for (const tool of tools) {
  console.log(tool.name, tool.description);
}
```

Call a tool by name with the arguments defined by its input schema:

```ts
const result = await client.callTool({
  name: "search_catalog",
  arguments: {
    query: "camera",
  },
});

console.log(result.content);
```

Tool names and arguments are defined by the website. If the active page does
not expose WebMCP tools, `tools/list` returns an empty list. Navigating to
another page can change the available tools, so list them again after
navigation.

## Close the connection

Close the MCP client when the agent is finished, then stop the Runtime:

```ts
await client.close();
await bctrl.runtimes.stop(runtime.id);
```

Stopping the Runtime ends the active Run and invalidates its WebMCP URL. Use
`runId` with [Runs](/sdk/runs) to inspect the recording, trace, events, usage,
and files from the session.

## Page requirements and safety

The active page must register tools with the browser's native WebMCP API.
During the browser rollout, a site may also need to meet Chromium's
feature-availability or origin-trial requirements.

Tool descriptions, schemas, and results come from the webpage. Treat them as
untrusted input and keep approval checks for tools that purchase items, submit
forms, or change account state.

The WebMCP URL grants access only to page-declared tools. It does not grant raw
CDP or WebDriver access.

## Next

* [Connect with CDP](/sdk/connect-cdp) — attach Playwright or Puppeteer
* [Connect with WebDriver](/sdk/connect-webdriver) — attach Selenium
* [Runtimes](/sdk/runtimes) — configure and manage browsers
* [Runs](/sdk/runs) — inspect activity and recordings