# How to Send Direct CDP Messages Using Ego-Lite: The Complete Guide

> Learn to send direct CDP messages with Ego-Lite. This guide covers the cdp helper function for seamless Chrome DevTools Protocol command execution with automatic retries and target attachment.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: how-to-guide
- Published: 2026-08-27

---

**Ego-Lite exposes a `cdp` helper function that proxies Chrome DevTools Protocol commands through a managed session layer, handling retries, timeouts, and target attachment automatically.**

Ego-Lite provides a thin wrapper around the Chrome DevTools Protocol (CDP) that enables agents to issue raw CDP commands from JavaScript running inside the **ego-browser** runtime. Whether you need to capture network traffic, evaluate expressions in the page context, or manage browser targets, knowing how to send direct CDP messages using ego-lite unlocks the full power of browser automation without managing WebSocket connections manually.

## The CDP Message Architecture in Ego-Lite

The message flow follows a strict pipeline from your agent script down to the native Chrome bridge. Understanding this flow helps you choose the right abstraction level for your use case.

The architecture consists of four layers:

- **`cdp` helper** ([`package/ego-browser/src/cdp-eval.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/cdp-eval.ts)): The public API imported from the helper context that agents call directly.
- **State management** ([`package/ego-browser/src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/state.ts)): The `state.send` function receives requests and forwards them to the runtime implementation.
- **Session management** ([`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts)): The `browserCdp` function guarantees valid CDP sessions exist, handles automatic retries on session loss, and delegates to `rawCdp`.
- **Native bridge**: The `rawCdp` function emits JSON via `globalThis.ego.sendCDPMessage`, which is exposed by the ego-browser native runtime.

According to the citrolabs/ego-lite source code, only `rawCdp` interacts directly with `globalThis.ego.sendCDPMessage`, while all higher layers manage Promise resolution, timeouts, and error translation.

## Sending CDP Commands with the `cdp` Helper

The `cdp` helper is the recommended entry point for sending direct CDP messages. It is automatically injected into the helper context (`helperContext`) defined in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts), making it available to all scripts executed by the ego-browser runtime.

### Basic Usage

Import the helper and invoke CDP methods by name:

```javascript
// Inside a script executed by ego-browser
const { cdp } = await import('ego-browser');

// Enable the Network domain to capture requests
await cdp('Network.enable');

// Evaluate a JavaScript expression in the page context
const title = await cdp('Runtime.evaluate', {
  expression: 'document.title',
  returnByValue: true,
});

console.log('Page title:', title.result?.value);

```

The `cdp` function signature accepts the CDP method name as the first argument, an optional parameters object as the second, and an optional session ID as the third. It returns a Promise that resolves with the CDP response or rejects on timeout or protocol errors.

### Working with Specific Targets

To send commands to a specific target (such as a particular tab or iframe), first attach to the target to obtain a session ID, then pass that ID to subsequent calls:

```javascript
// Attach to a specific target ID obtained from Target.getTargets
const attachResult = await cdp('Target.attachToTarget', {
  targetId: 'TARGET_ID_HERE',
  flatten: true,
});

const sessionId = attachResult.sessionId;

// Navigate within that specific target
await cdp('Page.navigate', { url: 'https://example.com' }, sessionId);

```

As implemented in [`package/ego-browser/src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/state.ts), the `state.send` function automatically routes these calls through `browserCdp`, which manages the lifecycle of the session.

## Low-Level Raw CDP Access

For debugging or scenarios requiring complete control over the protocol payload, you can bypass the session management layer and interact directly with the native bridge. This approach requires you to handle JSON serialization, message IDs, and response correlation manually.

Check for the existence of the native bridge and send raw JSON:

```javascript
if (globalThis.ego && typeof globalThis.ego.sendCDPMessage === 'function') {
  const payload = JSON.stringify({
    id: 1,
    method: 'Runtime.evaluate',
    params: { 
      expression: 'navigator.userAgent', 
      returnByValue: true 
    },
  });
  
  // Directly hand the JSON to the native bridge
  globalThis.ego.sendCDPMessage(payload);
  
  // Note: Responses are delivered asynchronously to the internal CDP dispatcher
}

```

Use this pattern only when the high-level `cdp` helper does not expose the specific functionality you need, as you lose automatic session validation, retry logic, and Promise-based response handling.

## Session Management and Error Handling

The `browserCdp` implementation in [`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts) (lines 79-102) ensures that every CDP command executes within a valid session. If the session detaches due to a page crash or target closure, the function automatically reattaches to the target and retries the command.

Key characteristics of the session layer:

- **Automatic attachment**: If no session exists for the target, `browserCdp` creates one before sending the command.
- **Retry logic**: Transient session-loss errors trigger an automatic retry with a fresh session.
- **Timeout handling**: The `rawCdp` function registers timeouts for every outbound message to prevent hanging Promises when Chrome fails to respond.

This robust handling means your agents can rely on the `cdp` helper without implementing complex reconnection logic when sending direct CDP messages using ego-lite.

## Summary

- **Use the `cdp` helper** imported from `ego-browser` for nearly all CDP operations; it handles sessions, retries, and timeouts automatically.
- **Reference the source** in [`package/ego-browser/src/cdp-eval.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/cdp-eval.ts) to understand how the helper wraps `state.send`, and in [`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts) to see the low-level `rawCdp` implementation.
- **Pass session IDs** explicitly when targeting specific browser contexts, obtained via `Target.attachToTarget`.
- **Access `globalThis.ego.sendCDPMessage`** directly only for debugging, as you must manually manage JSON serialization and response correlation.

## Frequently Asked Questions

### How do I access the `cdp` helper in my ego-lite scripts?

The `cdp` helper is automatically available through the helper context injected by the ego-browser runtime. Import it using `const { cdp } = await import('ego-browser')` as shown in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts) (lines 28-33). The runtime pre-populates this module with the necessary context, so you do not need to install additional packages.

### What happens if a CDP session disconnects while a command is in flight?

The `browserCdp` function in [`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts) detects session-loss errors and automatically reattaches to the target before retrying the failed command. This retry logic is transparent to your code; the Promise returned by `cdp` will resolve normally once the command succeeds on the fresh session, or reject only if the retry also fails.

### Can I send CDP messages to targets other than the default page?

Yes. Use `Target.attachToTarget` to obtain a session ID for any specific target (background page, service worker, or iframe), then pass that `sessionId` as the third argument to the `cdp` function. The `state.send` implementation routes the message to the correct Chrome DevTools Protocol session based on this ID.

### Where is the actual Chrome DevTools Protocol message dispatched?

The final dispatch occurs in `rawCdp` within [`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts), which calls `globalThis.ego.sendCDPMessage`. This function is exposed by the native ego-browser bridge and transmits the JSON payload directly to the Chrome instance. All higher-level functions in the ego-lite codebase eventually funnel through this single native interface.