# How Mako's Computer-Use Module Interacts with Browsers Using Chrome DevTools Protocol (CDP)

> Discover how Mako's computer-use module leverages Chrome DevTools Protocol (CDP) via a secured WebSocket to enable AI-driven browser automation within an Electron window.

- Repository: [The Apache Software Foundation/maka](https://github.com/apache/maka)
- Tags: how-to-guide
- Published: 2026-09-06

---

**Mako's computer-use module exposes a local, secured WebSocket endpoint that tunnels Chrome DevTools Protocol commands to an Electron browser window, enabling AI-driven automation without embedding a separate Chrome instance.**

Mako is an open-source AI agent framework that provides **computer-use capabilities**—allowing models to see and interact with graphical interfaces. Rather than launching a standalone Chrome browser, Mako reuses its existing Electron-rendered window as the automation target. The `computer-use` module achieves this by wrapping the window's native CDP interface in a hardened local bridge that the model can safely invoke.

## How the CDP Bridge Architecture Works

The interaction follows a three-layer design: the **bridge itself** creates the secured endpoint, the **desktop controller** manages its lifecycle per window, and the **runtime tools** translate model intentions into CDP commands.

### CdpBridge: The Secured Local Endpoint

At the core is `CdpBridge`, implemented in [`apps/desktop/src/main/browser/cdp-bridge.ts`](https://github.com/apache/maka/blob/main/apps/desktop/src/main/browser/cdp-bridge.ts). This class creates an HTTP server that listens exclusively on `127.0.0.1` with a cryptographically random secret path, then upgrades connections to a WebSocket that tunnels directly to the Electron debugger.

```typescript
// Simplified bridge initialization from cdp-bridge.ts
const server = http.createServer();
server.listen(0, '127.0.0.1', () => {
  const port = server.address().port;
  const secret = crypto.randomBytes(32).toString('hex');
  this.endpoint = `ws://127.0.0.1:${port}/${secret}`;
});

```

The bridge enforces multiple security constraints baked into the source:

- **Loopback-only binding** — The server refuses non-local connections via explicit `127.0.0.1` binding.
- **Constant-time secret validation** — The path secret is compared using `crypto.timingSafeEqual` to prevent timing attacks.
- **Single-connection limit** — Only one concurrent WebSocket client is permitted; subsequent attempts receive `403 Forbidden`.
- **Host-header pinning** — Requests with mismatched `Host` headers are rejected immediately.

These measures ensure that even if the endpoint leaks, remote attackers cannot exploit it.

### Attaching the Electron Debugger

Once the WebSocket server accepts a client, `CdpBridge` attaches Electron's built-in `debugger` module to the target `WebContents`:

```typescript
// From cdp-bridge.ts: debugger attachment and command forwarding
this.debugger = webContents.debugger;
await this.debugger.attach('1.3');  // Chrome DevTools Protocol 1.3

this.debugger.on('message', (event, method, params) => {
  // Forward CDP events from browser to WebSocket client
  if (this.ws) this.ws.send(JSON.stringify({ method, params }));
});

this.ws.on('message', (data) => {
  // Forward CDP commands from client to browser
  const { id, method, params } = JSON.parse(data);
  this.debugger.sendCommand(method, params)
    .then((result) => this.ws.send(JSON.stringify({ id, result })))
    .catch((error) => this.ws.send(JSON.stringify({ id, error })));
});

```

This bidirectional forwarding allows the computer-use model to issue any standard CDP command—`Input.dispatchMouseEvent`, `Runtime.evaluate`, `DOM.performSearch`, `Page.captureScreenshot`—exactly as it would to a standalone Chrome instance.

## Desktop Controller: Managing Bridge Lifecycle

The `BrowserController` in [`apps/desktop/src/main/browser/controller.ts`](https://github.com/apache/maka/blob/main/apps/desktop/src/main/browser/controller.ts) owns the `CdpBridge` lifecycle, ensuring one bridge per `WebContents` with lazy initialization:

```typescript
// From controller.ts: lazy bridge creation
async getAutomationEndpoint(): Promise<AutomationEndpoint> {
  if (!this.automation) {
    this.automation = new CdpBridge(this.webContents);
  }
  return this.automation.start();
}

```

The controller also handles cleanup. If the `WebContents` is destroyed, navigated, or another debugger attaches (e.g., the user opens Chrome DevTools), the bridge detects this and terminates gracefully. It propagates typed errors—`CdpBridgeError` with codes `target-busy`, `target-destroyed`, or `bridge-start-timeout`—allowing upstream code to distinguish retryable from fatal failures.

## Runtime Integration: From Model Intent to CDP Action

The computer-use runtime in [`packages/runtime/src/computer-use-tools.ts`](https://github.com/apache/maka/blob/main/packages/runtime/src/computer-use-tools.ts) consumes the `AutomationEndpoint` returned by the controller. It establishes a WebSocket connection to `cdpEndpoint` and wraps CDP operations in higher-level abstractions.

### Example: Mouse Click Automation

```typescript
// From computer-use-tools.ts pattern: executing a click via CDP
const client = await connectCdp(cdpEndpoint);

// Press
await client.send('Input.dispatchMouseEvent', {
  type: 'mousePressed',
  x: 150,
  y: 200,
  button: 'left',
  clickCount: 1,
  modifiers: 0
});

// Release
await client.send('Input.dispatchMouseEvent', {
  type: 'mouseReleased',
  x: 150,
  y: 200,
  button: 'left',
  clickCount: 1
});

```

### Example: DOM Query and Interaction

```typescript
// Search for an element and retrieve its box model
const searchResult = await client.send('DOM.performSearch', {
  query: 'button[aria-label="Submit"]'
});

await client.send('DOM.getSearchResults', {
  searchId: searchResult.searchId,
  fromIndex: 0,
  toIndex: 1
});

// Scroll into view if needed
await client.send('DOM.scrollIntoViewIfNeeded', {
  nodeId: nodeId
});

```

The model never manages WebSocket details directly; the runtime handles reconnection, timeout detection, and error classification using the `CdpBridgeError` taxonomy defined in the bridge.

## Error Handling and Security Boundaries

Mako's CDP integration is designed for hostile multi-tenant scenarios where model-generated code runs unsandboxed. The bridge's error hierarchy enables precise recovery strategies:

| Error Code | Trigger | Model Response |
|------------|---------|--------------|
| `target-busy` | External debugger attached (e.g., DevTools) | Delay and retry, or notify user to close DevTools |
| `target-destroyed` | Window closed or navigated | Reacquire `WebContents` from controller |
| `bridge-start-timeout` | Debugger attachment stalled | Abort current operation, log diagnostic |

```typescript
// Error handling pattern from computer-use-tools.ts
try {
  const { cdpEndpoint } = await automation.start();
  // ... execute CDP commands
} catch (err) {
  if (err instanceof CdpBridgeError) {
    switch (err.code) {
      case 'target-busy':
        await sleep(500);
        return retry();  // Exponential backoff
      case 'target-destroyed':
        throw new SessionTerminatedError('Browser window closed');
      case 'bridge-start-timeout':
        metrics.record('cdp_bridge_timeout');
        throw new InfrastructureError('Debugger unresponsive');
    }
  }
  throw err;  // Unknown error, propagate
}

```

## Configuration and Protocol Specification

The CDP bridge behavior is configurable through Mako's core computer-use configuration in [`packages/core/src/computer-use.ts`](https://github.com/apache/maka/blob/main/packages/core/src/computer-use.ts). Key parameters include:

- `cdpPort` — Fixed port override (default: `0` for ephemeral assignment)
- `bridgeTimeoutMs` — Maximum time to wait for debugger attachment
- `secretLength` — Bytes of entropy for the path secret (default: 32)

For protocol documentation between the backend bridge and client tools, see [`packages/computer-use/src/maka-cu-protocol.ts`](https://github.com/apache/maka/blob/main/packages/computer-use/src/maka-cu-protocol.ts), which defines message schemas and version compatibility guarantees.

## Key Source Files

- **[`apps/desktop/src/main/browser/cdp-bridge.ts`](https://github.com/apache/maka/blob/main/apps/desktop/src/main/browser/cdp-bridge.ts)** — `CdpBridge` class: WebSocket server, security enforcement, debugger forwarding.
- **[`apps/desktop/src/main/browser/controller.ts`](https://github.com/apache/maka/blob/main/apps/desktop/src/main/browser/controller.ts)** — `BrowserController`: per-window bridge lifecycle management.
- **[`packages/runtime/src/computer-use-tools.ts`](https://github.com/apache/maka/blob/main/packages/runtime/src/computer-use-tools.ts)** — Runtime CDP client operations and error handling.
- **[`packages/core/src/computer-use.ts`](https://github.com/apache/maka/blob/main/packages/core/src/computer-use.ts)** — Configuration schema for CDP bridge parameters.
- **[`packages/computer-use/src/maka-cu-protocol.ts`](https://github.com/apache/maka/blob/main/packages/computer-use/src/maka-cu-protocol.ts)** — Protocol specification for bridge communication.
- **[`apps/desktop/src/main/__tests__/cdp-bridge.test.ts`](https://github.com/apache/maka/blob/main/apps/desktop/src/main/__tests__/cdp-bridge.test.ts)** — Security contract and error scenario validation.

## Summary

- Mako's **computer-use module** exposes browser automation through a **local CDP bridge** rather than external Chrome processes.
- **`CdpBridge`** ([`cdp-bridge.ts`](https://github.com/apache/maka/blob/main/cdp-bridge.ts)) creates a loopback-only WebSocket endpoint with cryptographic path secrets, attaching Electron's debugger to forward CDP traffic.
- The **desktop controller** ([`controller.ts`](https://github.com/apache/maka/blob/main/controller.ts)) lazily instantiates bridges per `WebContents` and routes `AutomationEndpoint` objects to the runtime.
- **Runtime tools** ([`computer-use-tools.ts`](https://github.com/apache/maka/blob/main/computer-use-tools.ts)) translate model actions into CDP commands like `Input.dispatchMouseEvent` and `DOM.performSearch`, handling reconnection and typed errors.
- Security boundaries include single-connection limits, constant-time secret validation, and automatic teardown on external debugger interference.

## Frequently Asked Questions

### Does Mako spawn a separate Chrome process for computer-use automation?

No. Mako reuses the existing Electron `WebContents` that renders the browser UI. The `CdpBridge` attaches to this window's built-in debugger, avoiding the overhead and permission complexity of launching an external Chrome binary. This design is implemented in [`apps/desktop/src/main/browser/cdp-bridge.ts`](https://github.com/apache/maka/blob/main/apps/desktop/src/main/browser/cdp-bridge.ts).

### What prevents malicious code from accessing the CDP endpoint?

Multiple defense layers: the endpoint binds only to `127.0.0.1`, uses a 256-bit random path secret compared in constant time, accepts exactly one concurrent connection, and validates the `Host` header. These constraints make remote exploitation computationally infeasible even if the port number is discovered.

### How does the computer-use model recover when Chrome DevTools is opened?

Opening DevTools triggers the `debugger` module's single-attacher limit, causing `CdpBridge` to emit a `target-busy` error. The runtime in [`packages/runtime/src/computer-use-tools.ts`](https://github.com/apache/maka/blob/main/packages/runtime/src/computer-use-tools.ts) catches this, applies exponential backoff retry, and eventually surfaces a user-facing message if the conflict persists.

### Can the CDP bridge be disabled or replaced with a remote endpoint?

Yes. The configuration in [`packages/core/src/computer-use.ts`](https://github.com/apache/maka/blob/main/packages/core/src/computer-use.ts) accepts a `cdpPort` override, and the protocol abstraction in [`maka-cu-protocol.ts`](https://github.com/apache/maka/blob/main/maka-cu-protocol.ts) permits alternative implementations. However, the default desktop build expects the local bridge for security isolation.