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

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): The public API imported from the helper context that agents call directly.
  • State management (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): 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, making it available to all scripts executed by the ego-browser runtime.

Basic Usage

Import the helper and invoke CDP methods by name:

// 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:

// 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, 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:

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 (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 to understand how the helper wraps state.send, and in 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 (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 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, 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →