cdp-use-rs 0.1.1

Type-safe Chrome DevTools Protocol client for Rust with auto-generated bindings from official CDP specs
Documentation

CDP Use (Rust)

Rust implementation of cdp-use -- a type-safe Chrome DevTools Protocol client with auto-generated bindings from the official CDP protocol specifications.

Features

  • Type-safe commands & events -- Strongly typed structs for all 53 CDP domains, generated at build time via build.rs
  • Async WebSocket client -- Built on tokio + tokio-tungstenite with proper timeout, close ordering, and concurrent event dispatch
  • Typed event registration -- Register async closures for CDP events with full type safety
  • Configurable connection -- Custom WebSocket frame size, headers, and command timeout via CdpClientConfig
  • Zero manual maintenance -- Code generated directly from browser_protocol.json and js_protocol.json

Installation

Add to your Cargo.toml:

[dependencies]
cdp-use-rs = "0.1"

# or from git
cdp-use-rs = { git = "https://github.com/netkn001/cdp-use-rs" }

Quick Start

use cdp_use::page;

#[tokio::main]
async fn main() -> Result<(), cdp_use::CdpError> {
    // Connect to Chrome running with --remote-debugging-port=9222
    let cdp = cdp_use::CdpClient::connect("ws://127.0.0.1:9222/devtools/browser/...").await?;

    let send = cdp.send();
    let register = cdp.register();

    // List targets
    let targets = send.target.get_targets(Default::default(), None).await?;
    println!("Found {} targets", targets.target_infos.len());

    // Attach to a page target
    let page_target = targets.target_infos.iter()
        .find(|t| t.r#type == "page")
        .expect("No page target");

    let session = send.target.attach_to_target(
        cdp_use::target::commands::AttachToTargetParams {
            target_id: page_target.target_id.clone(),
            flatten: Some(true),
            ..Default::default()
        },
        None,
    ).await?;
    let sid = session.session_id;

    // Enable Page domain and register event handler
    send.page.enable(Default::default(), Some(&sid)).await?;

    register.page.frame_navigated(|event, _session_id| async move {
        println!("Navigated: {} -> {}", event.frame.id, event.frame.url);
    });

    // Navigate
    let nav = send.page.navigate(
        page::commands::NavigateParams {
            url: "https://example.com".into(),
            ..Default::default()
        },
        Some(&sid),
    ).await?;
    println!("Frame: {}", nav.frame_id);

    cdp.close().await?;
    Ok(())
}

Custom Configuration

use cdp_use::{CdpClient, CdpClientConfig};
use std::time::Duration;

let config = CdpClientConfig {
    max_message_size: Some(100 * 1024 * 1024), // 100 MiB (default)
    max_frame_size: Some(16 * 1024 * 1024),    // 16 MiB
    command_timeout: Duration::from_secs(60),    // default: 30s
    additional_headers: [("Authorization".into(), "Bearer token".into())].into(),
};

let cdp = CdpClient::connect_with_config("ws://...", config).await?;

Architecture

cdp-use-rs/
  build.rs              # Code generator -- reads protocol JSON, emits generated.rs
  protocols/
    browser_protocol.json
    js_protocol.json
  src/
    lib.rs              # Public API re-exports
    client.rs           # WebSocket client, event registry, CdpClientConfig
    error.rs            # CdpError enum
  examples/
    basic.rs            # Full usage example
  tests/
    codegen_test.rs     # Type/serialization/config tests
    integration_test.rs # Live browser integration tests

At build time, build.rs parses the CDP protocol JSON files and generates a generated.rs containing:

  • Types -- Structs, enums, and type aliases for each domain (page::types::Frame, network::types::RequestId, ...)
  • Commands -- *Params / *Returns structs and typed async methods on domain clients
  • Events -- Event structs and typed registration methods
  • CdpLibrary / CdpRegistrationLibrary -- Top-level accessors: cdp.send().page.navigate(...), cdp.register().page.frame_navigated(...)

Comparison with Other Browser Automation Tools

Selenium Puppeteer Playwright cdp-use (Python) cdp-use-rs
Language Java, Python, C#, JS, Ruby Node.js Node.js, Python, Java, .NET Python Rust
Protocol WebDriver (W3C) CDP CDP + custom protocol CDP (raw) CDP (raw)
Browser support Chrome, Firefox, Safari, Edge Chrome, Firefox (experimental) Chrome, Firefox, WebKit Chrome (any CDP target) Chrome (any CDP target)
Abstraction level High-level High-level High-level Low-level (1:1 CDP mapping) Low-level (1:1 CDP mapping)
Type safety Varies by language TypeScript types TypeScript types TypedDict + Literal Structs + enums + serde
Auto-wait / retries Implicit/explicit waits Built-in auto-wait Built-in auto-wait None (manual) None (manual)
Selectors CSS, XPath CSS, XPath, aria CSS, XPath, text, role, test-id N/A (raw CDP) N/A (raw CDP)
Code generation N/A N/A codegen CLI Runtime script Build-time build.rs
CDP domain coverage Partial (abstracted away) Partial (high-level API) Partial (high-level API) Full 53 domains Full 53 domains
Use case Cross-browser E2E testing Chrome automation & scraping Cross-browser E2E testing Full CDP control, AI agent infra Full CDP control, AI agent infra

When to use cdp-use-rs

  • You need direct access to all 53 CDP domains without abstraction layers getting in the way
  • You're building AI browser agents or infrastructure that requires low-level protocol control
  • You want compile-time type safety and zero-cost abstractions over the CDP protocol
  • Performance matters -- Rust's async runtime and zero-overhead WebSocket handling
  • You're integrating CDP into a larger Rust system (e.g., an agent framework, a proxy, or a custom browser controller)

When to use Playwright / Puppeteer / Selenium instead

  • You need cross-browser support (Firefox, WebKit/Safari)
  • You want high-level APIs with built-in auto-wait, selectors, and test assertions
  • You're writing E2E tests for web applications
  • You prefer a batteries-included approach with screenshots, tracing, and network interception out of the box

Comparison with Python cdp-use

Python (cdp-use) Rust (cdp-use-rs)
Domains 53 53
Type system TypedDict + Literal Structs + enums with serde
Optional fields NotRequired[T] Option<T> + skip_serializing_if
Naming camelCase (JS convention) snake_case + #[serde(rename_all = "camelCase")]
Async runtime asyncio tokio
WebSocket websockets tokio-tungstenite
Code generation Runtime script Build-time build.rs
Event handlers Sync or async callbacks Async closures (spawned per event)

Development

cargo build    # Generates CDP bindings and compiles
cargo test     # Run all tests
cargo doc      # Generate documentation

# Run the example (requires Chrome with --remote-debugging-port=9222)
cargo run --example basic -- "ws://127.0.0.1:9222/devtools/browser/GUID"

Related

License

MIT