7-Step Guide: Wire AI Agents to Multi-Account Browser Profiles via Playwright MCP

12 min read

Picture a typical agent run: collect pricing from three supplier portals your company has accounts on. The agent fires up Playwright MCP, opens the first URL and lands on a login page. The stock server launches its own browser, separate from the profile where your signed-in sessions, proxy and settings live, so the agent starts without any of them.

That is the gap in most agentic-browsing demos. The agent can plan, reason and execute, but the browser underneath isn't the one your work lives in. For real workflows in accounts you are authorized to use, you want a browser that keeps its own cookies, storage and proxy between runs.

This guide wires that up: an AI agent connected to JustBrowser profiles via Playwright MCP.

What We're Building

By the end of this tutorial, you'll have:

  1. A Playwright MCP server configured to connect to a JustBrowser profile
  2. An AI agent (Claude, GPT-4, or any MCP-compatible model) that browses inside a persistent, isolated profile
  3. A pattern for running multiple agents on separate profiles simultaneously

The agent navigates, clicks, takes screenshots and extracts text inside a profile that keeps its own session. No plugins and no JavaScript overrides: plain Playwright over CDP. (Worth the setup time — the CDP connection logic is the fiddly part, everything else is copy-paste.)

Prerequisites

  • Node.js 20+ installed
  • JustBrowser with at least one profile configured (the seven-day trial works fine — profiles are unlimited on the plan, trial included). JustBrowser runs on Windows x64 and macOS (Apple Silicon).
  • Basic familiarity with MCP (Model Context Protocol) servers
  • An MCP-compatible AI client (Claude Desktop, Cursor, or your own integration)

If you need background on how multi-account browsers integrate with Playwright at the protocol level, the Playwright/Puppeteer integration guide covers the fundamentals. Not required reading, but helpful context.

Step 1: Understand the Architecture

Here's how the pieces connect:

AI Agent (Claude/GPT-4)
    ↓ MCP protocol
Playwright MCP Server
    ↓ CDP WebSocket
JustBrowser Profile
    ↓ HTTP/TLS
Target Website

The standard Playwright MCP server launches and manages its own browser. We're replacing that with a CDP connection to a pre-launched JustBrowser profile.

The profile holds the browser's identity (its own cookies, storage, proxy and 40+ identity parameters). The MCP server handles agent tool calls. The agent just browses.

This separation matters. The agent's prompt doesn't change. Its capabilities don't change. Only its browser backend changes. Swap between a throwaway browser and a persistent profile by changing one endpoint.

Step 2: Set Up Your Profile

Open JustBrowser and create a profile for agent browsing. Some things to configure:

Identity: Pick a hardware profile that suits the workflow, such as a laptop or desktop configuration, and keep it consistent between runs. The WebGL fingerprinting guide explains what these parameters cover.

Proxy: Assign a proxy whose location matches the profile's timezone and language, for example a US proxy for a US-English profile. A mismatch tends to produce wrong-currency or wrong-language pages.

Cookies: For sites requiring login, sign in once in the profile yourself, with an account you are allowed to use, and save the session. The agent inherits those cookies, so it won't need to log in every run.

Note your profile's ID. You'll use it to launch via REST API.

Step 3: Launch the Profile via REST API

JustBrowser's one plan ($9.99/month or $99.99/year, unlimited profiles) includes REST API access, and so does the trial. The API runs locally — bound to 127.0.0.1, port configurable — and every request needs a bearer token pulled from the app's AI tab.

Launch your profile programmatically:

async function launchProfile(profileId) {
  const response = await fetch(`http://127.0.0.1:36542/api/v1/profiles/${profileId}/start`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.JUSTBROWSER_TOKEN}`
    },
    body: JSON.stringify({ headless: false })
  });

  const body = await response.json();
  if (!response.ok) throw new Error(body.error.code); // branch on error.code, not the message
  return { cdpUrl: body.data.cdp_url };
}

// Returns something like:
// { cdpUrl: "ws://127.0.0.1:PORT/devtools/browser/<guid>" }

That cdp_url is a standard Chrome DevTools Protocol URL. Any tool that speaks CDP can connect — including Playwright. Leave the body out and you get a headless browser by default; headless: false is what gets you a window you can actually watch.

Keep the profile running. The MCP server will connect to it. When you're done, end the session with POST /profiles/{id}/stop — browser.close() only disconnects Playwright and leaves the profile running.

Step 4: Create a Custom MCP Server

A stock Playwright MCP setup launches its own browser. We'll write a small server that connects to our pre-launched profile instead. (The official Playwright MCP server also has an option for attaching to a running browser over CDP; check its README. The small server below shows what happens underneath and gives you full control over the tools.)

In a new folder, run npm install @modelcontextprotocol/sdk playwright, then create justbrowser-mcp-server.mjs:

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { chromium } from 'playwright';

const CDP_ENDPOINT = process.env.CDP_ENDPOINT; // the cdp_url returned in Step 3

let browser;
let page;

const server = new Server({
  name: 'justbrowser-playwright',
  version: '1.0.0',
}, {
  capabilities: {
    tools: {}
  }
});

async function ensureBrowser() {
  if (!browser) {
    // Connect to the JustBrowser profile instead of launching a new browser
    browser = await chromium.connectOverCDP(CDP_ENDPOINT);
    const context = browser.contexts()[0]; // never newContext() — that spins up an incognito context outside the profile
    page = context.pages()[0] || await context.newPage();
  }
  return page;
}

server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: 'navigate',
        description: 'Navigate to a URL',
        inputSchema: {
          type: 'object',
          properties: {
            url: { type: 'string', description: 'URL to navigate to' }
          },
          required: ['url']
        }
      },
      {
        name: 'screenshot',
        description: 'Take a screenshot of the current page',
        inputSchema: { type: 'object', properties: {} }
      },
      {
        name: 'click',
        description: 'Click an element',
        inputSchema: {
          type: 'object',
          properties: {
            selector: { type: 'string', description: 'CSS selector' }
          },
          required: ['selector']
        }
      },
      {
        name: 'get_text',
        description: 'Extract text content from the page',
        inputSchema: {
          type: 'object',
          properties: {
            selector: { type: 'string', description: 'CSS selector (optional)' }
          }
        }
      }
    ]
  };
});

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;
  const p = await ensureBrowser();

  switch (name) {
    case 'navigate':
      await p.goto(args.url, { waitUntil: 'networkidle' });
      return { content: [{ type: 'text', text: `Navigated to ${args.url}` }] };

    case 'screenshot':
      const buffer = await p.screenshot({ type: 'png' });
      return {
        content: [{
          type: 'image',
          data: buffer.toString('base64'),
          mimeType: 'image/png'
        }]
      };

    case 'click':
      await p.click(args.selector);
      return { content: [{ type: 'text', text: `Clicked ${args.selector}` }] };

    case 'get_text':
      const text = args.selector
        ? await p.locator(args.selector).textContent()
        : await p.evaluate(() => document.body.innerText);
      return { content: [{ type: 'text', text }] };

    default:
      throw new Error(`Unknown tool: ${name}`);
  }
});

const transport = new StdioServerTransport();
await server.connect(transport);

The critical line is chromium.connectOverCDP(CDP_ENDPOINT). Instead of chromium.launch(), we connect to the already-running profile. Same Playwright API. Different browser.

(Fair warning: the error handling here is oversimplified. Production code needs reconnection logic, timeouts, the whole works. But this gets you a working demo.)

Step 5: Wire Up Your AI Client

For Claude Desktop, add the server to your MCP config (claude_desktop_config.json):

{
  "mcpServers": {
    "justbrowser-profile": {
      "command": "node",
      "args": ["/path/to/justbrowser-mcp-server.mjs"],
      "env": {
        "CDP_ENDPOINT": "paste the cdp_url your Step 3 start call returned"
      }
    }
  }
}

Restart Claude Desktop. The agent now has navigate, screenshot, click, and get_text tools that execute against your profile.

For Cursor or other MCP clients, the configuration is similar — point to your custom server with the CDP endpoint as an environment variable.

Step 6: Test on a Page You Are Allowed to Automate

Ask your agent to work on a page you own or have permission to automate, such as your own storefront dashboard or a staging site. Something like:

Navigate to your-store-dashboard.example, screenshot the orders page, and extract the order totals from the table.

Confirm the agent sees the signed-in state you saved in Step 2. If a site shows a challenge or verification step, that's the site's decision: complete it yourself in the visible window if it's your account, and don't have the agent try to get past it (see Common Errors below). Respect each site's terms and robots rules: some sites don't permit automated access, and a profile is not a way around that.

Step 7: Scale to Multiple Agents

Each profile is isolated. Different cookies, different storage, different proxy. To run multiple agents:

const profiles = ['profile-1-id', 'profile-2-id', 'profile-3-id'];

async function spawnAgents() {
  const agents = await Promise.all(profiles.map(async (profileId) => {
    const { cdpUrl } = await launchProfile(profileId);
    const browser = await chromium.connectOverCDP(cdpUrl);
    const context = browser.contexts()[0];
    const page = context.pages()[0] || await context.newPage();
    return { profileId, browser, page };
  }));

  return agents;
}

Each agent operates independently. One can work in your storefront dashboard, another in a reporting tool, another in a client's ad account you manage with their permission. Sessions don't mix, and if one fails, the others keep running.

For teams running QA, ad verification or price monitoring, the same pattern extends to more concurrent agents as long as your machine has the memory for the browser processes. Teammates can also be given access to shared profiles through free team seats.

Annoying truth: managing this many profiles gets messy. You'll want a naming convention and some automation around profile creation. It's an easy mistake to make — connect to the wrong profile and lose an hour debugging an agent that's "acting weird" for no reason at all.

Common Errors and Fixes

Error: "Failed to connect to browser"

Cause: The profile isn't running, or the CDP endpoint is wrong.

Fix: Verify the profile is launched via REST API. Check that CDP_ENDPOINT matches the cdp_url returned by the start call, and that your bearer token is valid. A rejected call comes back with a non-2xx status and an {"error": {"code", "message"}} body, so check error.code before reading data.

Error: "Page crashed" or "Target closed"

Cause: The profile was stopped externally, or a navigation caused an unhandled error.

Fix: Wrap page operations in try/catch. Re-connect to the CDP endpoint if the browser process died. Consider implementing a health check that pings the profile before each agent task.

Error: The agent keeps hitting challenge or verification pages

Cause: The site's bot management is noticing automated traffic, which is what it's there for. It looks at many signals, from TLS fingerprints to how a session behaves.

Fix: If it's your own site or staging environment, allowlist the agent's traffic in your bot-management settings. If it's a service you use, check whether it offers an API or an approved route for automation. If its terms don't allow automated access, don't automate it, and don't try to work around the challenge.

Error: "Context or page not found"

Cause: Your profile has no open pages, or the context was closed.

Fix: The ensureBrowser() function in our example handles this by creating a page if none exists. Make sure you're not calling context.close() or page.close() elsewhere in your code without re-initializing.

Next Steps

Once your agent is browsing reliably through profiles:

  1. Add more tools — form filling, scrolling, waiting for selectors, JavaScript evaluation. The MCP server pattern extends naturally.

  2. Build agent-specific profiles — each recurring task gets its own profile with its own saved session, proxy and settings, so runs pick up where the last one left off.

  3. Review agent actions — log what each agent does in each profile, and keep a human in the loop for anything that spends money or changes an account.

MCP gives agents browser control. A persistent profile gives them a stable, separate place to work. Put them together and you've got agents that can do real work in your own accounts, not just demo well on toy sites.

Look, this setup is not trivial. But once it's running, your agents can keep their sessions between runs instead of starting from nothing.

Frequently Asked Questions

What is Playwright MCP and why do AI agents need it?

Playwright MCP is a Model Context Protocol server that gives AI agents browser control capabilities. The MCP standard lets agents call tools like navigate, click, and screenshot through a structured interface. Without MCP, agents would need custom browser automation code for each task. With MCP, the agent requests actions and the server handles execution. By default the server launches and manages its own browser, so the logins, proxy and settings in your browser profiles aren't part of it.

Why give an AI agent its own browser profile?

A fresh automation browser starts with nothing: no cookies, no saved sessions, no proxy, no consistent identity. A JustBrowser profile keeps its own cookies, storage, proxy and identity parameters between runs, so an agent working in a client or store account you are authorized to use stays signed in and separate from every other agent and from your own browsing.

How does routing an agent through a JustBrowser profile work?

You start the profile through the local REST API, which returns a CDP URL. Your MCP server connects Playwright to that URL with connectOverCDP instead of launching a browser. The agent's actions then run inside the profile, with its cookies, storage and proxy. Whether a site lets automation in is governed by that site's terms, so confirm your use is permitted.

Can I run multiple AI agents on different profiles simultaneously?

Yes. Each profile runs as a separate browser process with its own identity parameters, cookies, and proxy. Launch profiles via REST API, get their CDP endpoints, and point each agent's MCP server at a different endpoint. The agents operate in isolation from one another. Scale by adding profiles, as far as your machine's memory allows.


Try JustBrowser

A multi-account browser for teams, built on a custom Chromium engine with native C++ integration (not extension-based). Each profile is its own browser with its own cookies, storage, proxy and 40+ identity parameters. Local REST API + CDP for Playwright, Puppeteer, Selenium. 7-day free trial, card required — then $9.99/month or $99.99/year, unlimited profiles, free team seats.

Get started → · How it differs from Multilogin / GoLogin / AdsPower

Tags
  • playwright-mcp
  • ai-agent-browser
  • multi-account-automation
  • cdp-integration
  • buildinpublic
  • saasstudio
  • aiworkforce
  • buildwithclaude

Ready to manage multiple accounts?

Seven days free, then $9.99/month — one plan, everything included.

We'd like to use Google Analytics, a Google service, to understand how our website is used. It sets two cookies in your browser and runs only if you click Accept. You can change your choice at any time with Cookie settings. Cookie Policy

Sign-in cookies and the cookie that remembers this choice are always on; the website needs them to work.

Google Analytics, a Google service, helps us understand how our website is used. It sets two cookies, _ga and _ga_TVZHQ99TZW. It is now onoff in this browser. If your browser sends a Global Privacy Control or Do Not Track signal, it stays off. Cookie Policy