Generated banner for Cookie Sync
Generated logo for Cookie Sync

Cookie Sync

BrowserbaseOfficialPublished by Browserbase

  • Skill
  • Tool integration
  • Automation and integrations
  • MIT
  • Low risk
  • Has scripts

From the author

Sync cookies from local Chrome to a Browserbase persistent context so the browse CLI can access authenticated sites. Use when the user wants to browse as themselves, sync cookies, or log into sites via Browserbase.
— the description by Browserbase

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

Cookie Sync enables users to export authentication cookies from a local Chromium-based browser into a Browserbase persistent context. This allows developers to perform automated browsing tasks in the cloud while maintaining the authenticated state of their local browser sessions. It supports domain-specific filtering, context refreshing, and integration with Browserbase features like verified browser mode and residential proxies to help manage site access.

What it does

  • Exports cookies from a local Chrome or Chromium instance to a Browserbase persistent context
  • Allows filtering of synced cookies by specific domains and subdomains
  • Refreshes cookies within an existing Browserbase context to extend session validity
  • Supports integration with Browserbase Identity and residential proxies for improved site access
  • Facilitates authenticated browsing sessions in the cloud using the browse CLI

When to use it

  • When you need to access authenticated websites via Browserbase using your local credentials
  • When you want to maintain persistent sessions for scheduled or recurring cloud-based tasks
  • When you need to bypass site-specific restrictions by matching your local IP geolocation
  • When you need to refresh expired cookies in an existing cloud context

Advantages

  • Enables seamless transition from local browser authentication to cloud-based automation
  • Supports persistent contexts that survive across multiple sessions
  • Provides granular control over which cookies are synced to the cloud
  • Helps mitigate authentication rejection through proxy and verified browser options

Usage examples

“Sync my GitHub cookies to a new Browserbase context.”
The tool extracts GitHub-related cookies from the local browser and returns a new context ID for use with the browse CLI.
“Refresh the cookies in my existing context ctx_abc123.”
The tool re-injects current local cookies into the specified context, updating it for continued use.
“Sync cookies for twitter.com using a San Francisco residential proxy.”
The tool syncs the specified cookies and configures the context to route traffic through a San Francisco-based residential proxy.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/browserbase/cookie-sync/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip cookie-sync.zip -d .agents/skills

Documentation by Browserbase

Exports cookies from your local Chrome and saves them into a Browserbase persistent context. After syncing, use the browse CLI to open authenticated sessions with that context.

Supports domain filtering (only sync cookies you need) and context reuse (refresh cookies without creating a new context).

Prerequisites

  • Chrome (or Chromium, Brave, Edge) with remote debugging enabled

  • If your browser build exposes chrome://flags/#allow-remote-debugging, enable it and restart the browser

  • Otherwise, launch with --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug and set CDP_URL=ws://127.0.0.1:9222

  • At least one tab open in Chrome

  • Node.js 22+

  • Environment variable: BROWSERBASE_API_KEY

Setup

Install dependencies before first use:

cd .claude/skills/cookie-sync && npm install

Usage

Basic — sync all cookies
node .claude/skills/cookie-sync/scripts/cookie-sync.mjs

Creates a persistent context with all your Chrome cookies. Outputs a context ID.

Filter by domain — only sync specific sites
node .claude/skills/cookie-sync/scripts/cookie-sync.mjs --domains google.com,github.com

Matches the domain and all subdomains (e.g. google.com matches accounts.google.com, mail.google.com, etc.)

Refresh cookies in an existing context
node .claude/skills/cookie-sync/scripts/cookie-sync.mjs --context ctx_abc123

Re-injects fresh cookies into a previously created context. Use this when cookies have expired.

Verified browser mode
node .claude/skills/cookie-sync/scripts/cookie-sync.mjs --verified

Enables Browserbase Identity with a Verified browser to improve access on protected sites. Recommended for sites like Google that fingerprint browsers.

Residential proxy with geolocation
node .claude/skills/cookie-sync/scripts/cookie-sync.mjs --proxy "San Francisco,CA,US"

Routes through a residential proxy in the specified location. Format: "City,ST,Country" (state is 2-letter code). Helps match your local IP's geolocation so auth cookies aren't rejected.

Combine flags
node .claude/skills/cookie-sync/scripts/cookie-sync.mjs --domains github.com,google.com --verified --proxy "San Francisco,CA,US"

Browsing Authenticated Sites

After syncing, use the browse CLI with the context ID:

SESSION_JSON="$(browse cloud sessions create --context-id <ctx-id> --persist --keep-alive)"
SESSION_ID="$(echo "$SESSION_JSON" | jq -r .id)"
CONNECT_URL="$(echo "$SESSION_JSON" | jq -r .connectUrl)"

browse open https://mail.google.com --cdp "$CONNECT_URL"

The --persist flag on browse cloud sessions create saves any new cookies or state changes back to the context when the cloud session is released, keeping the session fresh for next time.

Full workflow example:

# Step 1: Sync cookies for Twitter
node .claude/skills/cookie-sync/scripts/cookie-sync.mjs --domains x.com,twitter.com
# Output: Context ID: ctx_abc123

# Step 2: Browse authenticated Twitter
SESSION_JSON="$(browse cloud sessions create --context-id ctx_abc123 --persist --keep-alive)"
SESSION_ID="$(echo "$SESSION_JSON" | jq -r .id)"
CONNECT_URL="$(echo "$SESSION_JSON" | jq -r .connectUrl)"

browse open https://x.com/messages --cdp "$CONNECT_URL"
browse snapshot
browse screenshot
browse stop
browse cloud sessions update "$SESSION_ID" --status REQUEST_RELEASE

Reusing Contexts for Scheduled Jobs

Contexts persist across sessions, making them ideal for scheduled/recurring tasks:

  1. Once (laptop open): Run cookie-sync → get a context ID

  2. Scheduled jobs: Create a Browserbase session with browse cloud sessions create --context-id <ctx-id> --persist --keep-alive, then attach with browse open <url> --cdp <connectUrl> — no local Chrome needed

  3. Re-sync as needed: When cookies expire, run cookie-sync again with --context <ctx-id> to refresh

Troubleshooting

  • "No DevToolsActivePort found" → Enable chrome://flags/#allow-remote-debugging if your browser build exposes it, or launch with --remote-debugging-port=9222 and set CDP_URL=ws://127.0.0.1:9222

  • "No open page targets found" → Open at least one tab in Chrome

  • "WebSocket error" → Chrome may be hung; force quit and reopen it

  • Cookies expired in context → Re-run cookie-sync with --context <id> to refresh

  • Auth rejected by site → Try adding --verified and/or --proxy with a location near you

Files

scripts/cookie-sync.mjs

#!/usr/bin/env node
// cookie-sync — Export cookies from local Chrome into a Browserbase persistent context.
// Uses Stagehand for browser connections and @browserbasehq/sdk for API calls.
//
// Setup:
//   cd skills/cookie-sync && npm install
//
// Usage:
//   node scripts/cookie-sync.mjs                                        # sync all cookies into a new context
//   node scripts/cookie-sync.mjs --domains google.com,github.com        # only sync cookies for these domains
//   node scripts/cookie-sync.mjs --context ctx_abc123                   # refresh cookies in an existing context
//   node scripts/cookie-sync.mjs --verified                             # enable Browserbase Verified browser mode
//   node scripts/cookie-sync.mjs --proxy "San Francisco,CA,US"          # use residential proxy with geolocation
//
// After syncing, use the browse CLI to open an authenticated session:
//   SESSION_JSON="$(browse cloud sessions create --context-id <ctx-id> --persist --keep-alive)"
//   CONNECT_URL="$(echo "$SESSION_JSON" | jq -r .connectUrl)"
//   browse open https://example.com --cdp "$CONNECT_URL"
//
// Env vars:
//   BROWSERBASE_API_KEY    — required
//   BROWSERBASE_CONTEXT_ID — optional, reuse an existing context
//   CDP_URL                — optional, Chrome debugging endpoint or browser WS URL
//   CDP_PORT_FILE          — optional, path to DevToolsActivePort if non-standard
//   CDP_HOST               — optional, host for DevToolsActivePort-based connections

import { Stagehand } from '@browserbasehq/stagehand';
import Browserbase from '@browserbasehq/sdk';
import { readFileSync, existsSync } from 'fs';
import { execSync } from 'child_process';
import { homedir } from 'os';
import { resolve } from 'path';

// ---------------------------------------------------------------------------
// CLI args
// ---------------------------------------------------------------------------

function parseArgs() {
  const args = process.argv.slice(2);
  const result = { domains: [], contextId: null, verified: false, proxy: null };

  for (let i = 0; i < args.length; i++) {
    if (args[i] === '--domains' && args[i + 1]) {
      result.domains = args[++i].split(',').map(d => d.trim().toLowerCase()).filter(Boolean);
    } else if (args[i] === '--context' && args[i + 1]) {
      result.contextId = args[++i];
    } else if (args[i] === '--verified') {
      result.verified = true;
    } else if (args[i] === '--proxy' && args[i + 1]) {
      const parts = args[++i].split(',').map(s => s.trim());
      if (!parts[0] || !parts[1]) {
        console.error('Error: --proxy requires "City,State,Country" (e.g. "San Francisco,CA,US")');
        process.exit(1);
      }
      result.proxy = { city: parts[0], state: parts[1], country: parts[2] || 'US' };
    }
  }

  return result;
}

const CLI = parseArgs();

// ---------------------------------------------------------------------------
// Env validation
// ---------------------------------------------------------------------------

const API_KEY = process.env.BROWSERBASE_API_KEY;

if (!API_KEY) {
  console.error('Error: BROWSERBASE_API_KEY is required');
  process.exit(1);
}

// ---------------------------------------------------------------------------
// Find local Chrome DevTools WebSocket URL
// ---------------------------------------------------------------------------

async function resolveCdpUrl(cdpUrl) {
  if (/^wss?:\/\/.+\/devtools\/browser\//.test(cdpUrl)) {
    return cdpUrl;
  }

  const base = cdpUrl.replace(/^wss?/i, m => m.length === 3 ? 'https' : 'http').replace(/\/+$/, '');
  const versionUrl = base.endsWith('/json/version') ? base : `${base}/json/version`;
  const res = await fetch(versionUrl);

  if (!res.ok) {
    throw new Error(`Could not resolve CDP_URL via ${versionUrl} (${res.status})`);
  }

  const info = await res.json();
  if (!info.webSocketDebuggerUrl) {
    throw new Error(`CDP_URL did not expose webSocketDebuggerUrl at ${versionUrl}`);
  }

  return info.webSocketDebuggerUrl;
}

async function getLocalCdpUrl() {
  if (process.env.CDP_URL) {
    return resolveCdpUrl(process.env.CDP_URL);
  }

  const home = homedir();
  const IS_WINDOWS = process.platform === 'win32';

  const macBrowsers = [
    'Google/Chrome', 'Google/Chrome Beta', 'Google/Chrome for Testing',
    'Chromium', 'BraveSoftware/Brave-Browser', 'Microsoft Edge',
  ];
  const linuxBrowsers = [
    'google-chrome', 'google-chrome-beta', 'chromium',
    'vivaldi', 'vivaldi-snapshot',
    'BraveSoftware/Brave-Browser', 'microsoft-edge',
  ];

  const candidates = [
    process.env.CDP_PORT_FILE,
    ...macBrowsers.flatMap(b => [
      resolve(home, 'Library/Application Support', b, 'DevToolsActivePort'),
      resolve(home, 'Library/Application Support', b, 'Default/DevToolsActivePort'),
    ]),
    ...linuxBrowsers.flatMap(b => [
      resolve(home, '.config', b, 'DevToolsActivePort'),
      resolve(home, '.config', b, 'Default/DevToolsActivePort'),
    ]),
    ...(IS_WINDOWS ? ['Google/Chrome', 'BraveSoftware/Brave-Browser', 'Microsoft/Edge'].flatMap(b => {
      const base = process.env.LOCALAPPDATA || resolve(home, 'AppData/Local');
      return [
        resolve(base, b, 'User Data/DevToolsActivePort'),
        resolve(base, b, 'User Data/Default/DevToolsActivePort'),
      ];
    }) : []),
  ].filter(Boolean);

  const portFile = candidates.find(p => existsSync(p));
  if (!portFile) {
    throw new Error(
      'No DevToolsActivePort found.\n' +
      'Enable remote debugging: chrome://flags/#allow-remote-debugging (Chrome 146+)\n' +
      'Or launch Chrome with --remote-debugging-port=9222 and set CDP_URL=ws://127.0.0.1:9222'
    );
  }

  const lines = readFileSync(portFile, 'utf8').trim().split(/\r?\n/);
  if (lines.length < 2 || !lines[0] || !lines[1]) {
    throw new Error(`Invalid DevToolsActivePort file: ${portFile}`);
  }

  const host = process.env.CDP_HOST || '127.0.0.1';
  return `ws://${host}:${lines[0]}${lines[1]}`;
}

// ---------------------------------------------------------------------------
// Chrome version check
// ---------------------------------------------------------------------------

function checkChromeVersion() {
  if (process.env.CDP_URL || process.env.CDP_PORT_FILE) return;

  const chromePaths = [
    // macOS
    '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
    '/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta',
    '/Applications/Chromium.app/Contents/MacOS/Chromium',
    // Linux (resolved via PATH)
    'google-chrome',
    'google-chrome-stable',
    'chromium-browser',
    'chromium',
  ];
  for (const p of chromePaths) {
    try {
      const out = execSync(`"${p}" --version`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
      const match = out.match(/(\d+)\./);
      if (match) {
        const major = parseInt(match[1], 10);
        if (major < 146) {
          console.warn(`Chrome ${major} detected. Chrome 146+ supports the allow-remote-debugging flag.`);
          console.warn('For older Chrome, launch with --remote-debugging-port=9222 and set CDP_URL=ws://127.0.0.1:9222.');
          return;
        }
        console.log(`Chrome ${major} detected`);
        return;
      }
    } catch { /* try next */ }
  }
}

// ---------------------------------------------------------------------------
// Cookie helpers
// ---------------------------------------------------------------------------

function filterCookies(cookies, domains) {
  if (domains.length === 0) return cookies;
  return cookies.filter(cookie => {
    const cookieDomain = cookie.domain.replace(/^\./, '').toLowerCase();
    return domains.some(d => cookieDomain === d || cookieDomain.endsWith('.' + d));
  });
}

function toCookieParams(cookies) {
  return cookies.map(c => {
    const param = {
      name: c.name,
      value: c.value,
      domain: c.domain,
      path: c.path,
      httpOnly: c.httpOnly,
      secure: c.secure,
    };
    if (c.expires > 0) param.expires = c.expires;
    if (c.sameSite === 'Strict' || c.sameSite === 'Lax') {
      param.sameSite = c.sameSite;
    } else if (c.sameSite === 'None' && c.secure) {
      param.sameSite = 'None';
    }
    return param;
  });
}

// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------

async function main() {
  checkChromeVersion();

  // Step 1: Connect to local Chrome via Stagehand and export cookies
  const cdpUrl = await getLocalCdpUrl();
  const local = new Stagehand({
    env: 'LOCAL',
    localBrowserLaunchOptions: { cdpUrl },
    verbose: 0,
    disablePino: true,
  });
  await local.init();
  console.log('Connected to local Chrome');

  const allCookies = await local.context.cookies();
  console.log(`Exported ${allCookies.length} cookies from local Chrome`);
  await local.close();

  // Step 2: Filter cookies by domain if requested
  const cookies = filterCookies(allCookies, CLI.domains);
  if (CLI.domains.length > 0) {
    console.log(`Filtered to ${cookies.length} cookies matching: ${CLI.domains.join(', ')}`);
  }
  if (cookies.length === 0) {
    console.warn('Warning: No cookies to sync. Check your domain filters or Chrome login state.');
    process.exit(0);
  }

  // Step 3: Set up context (create new or reuse existing)
  const bb = new Browserbase({ apiKey: API_KEY });
  let contextId = CLI.contextId || process.env.BROWSERBASE_CONTEXT_ID;

  if (!contextId) {
    const ctx = await bb.contexts.create({});
    contextId = ctx.id;
    console.log(`Created context: ${contextId}`);
  } else {
    console.log(`Using existing context: ${contextId}`);
  }

  // Step 4: Create a temporary Browserbase session to inject cookies
  const browserSettings = { context: { id: contextId, persist: true } };
  if (CLI.verified) browserSettings.verified = true;

  const cloud = new Stagehand({
    env: 'BROWSERBASE',
    apiKey: API_KEY,
    disableAPI: true,
    browserbaseSessionCreateParams: {
      browserSettings,
      ...(CLI.proxy && {
        proxies: [{ type: 'browserbase', geolocation: CLI.proxy }],
      }),
    },
    verbose: 0,
    disablePino: true,
  });
  await cloud.init();
  console.log(`Injecting cookies via session: ${cloud.browserbaseSessionID}`);

  // Step 5: Inject cookies and close the session (context persists independently)
  const cookieParams = toCookieParams(cookies);
  await cloud.context.addCookies(cookieParams);
  console.log(`Injected ${cookies.length} cookies into context`);
  await cloud.close();

  // Step 6: Summary
  console.log('');
  console.log('Cookies synced to context.');
  console.log(`Context ID: ${contextId}`);
  console.log('');
  console.log('Browse authenticated sites with:');
  console.log(`  SESSION_JSON="$(browse cloud sessions create --context-id ${contextId} --persist --keep-alive)"`);
  console.log('  SESSION_ID="$(echo "$SESSION_JSON" | jq -r .id)"');
  console.log('  CONNECT_URL="$(echo "$SESSION_JSON" | jq -r .connectUrl)"');
  console.log('  browse open <url> --cdp "$CONNECT_URL"');
  console.log('  # when done: browse stop && browse cloud sessions update "$SESSION_ID" --status REQUEST_RELEASE');
  console.log('');
  console.log('To refresh cookies later:');
  console.log(`  node cookie-sync.mjs --context ${contextId}`);

  process.exit(0);
}

main().catch(e => {
  console.error(`Error: ${e.message}`);
  process.exit(1);
});

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Declares allowed tools, Needs an API key, Needs Node.js

The author says: Requires Node.js 22+, a local Chromium-based browser (Chrome, Brave, Edge) with remote debugging enabled, and BROWSERBASE_API_KEY. Run `npm install` in the skill directory before first use.

Security

What our scanner noticed· Low risk

  • Scripts that your agent may run

This skill includes scripts that your agent may run. Read them before you use it.

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
Browserbase
Publisher
Browserbase (Official)
Source
browserbase/skills · skills/cookie-sync · commit 6811ca3
Licence
MIT License (redistribution allowed)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Browserbase.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download cookie-sync.zip