Halmurat T.
Halmurat T.

Senior SDET

Home Blog Books ask About

The Dispatch

Weekly QA notes from the trenches.

Welcome aboard!

You're on the list. Expect real-world QA insights — no fluff, no spam.

© 2026 Halmurat T.

Automation 28
  • Selenium
  • Playwright
  • Appium
  • Cypress
AI Testing 67
CI/CD 12
  • GitHub Actions
  • Slack Reporting
QA Strategy 10
Case Studies 8
Blog/AI Testing
AI TestingHalmurat T./October 29, 2026/Updated November 5, 2026/15 min

Parallel Playwright MCP Agents Need Their Own Connection

Filed underai-testing/playwright/ai-agents/claude/parallel-execution
Parallel Playwright MCP Agents Need Their Own Connection

Table of Contents
  • What did the #1804 report actually say?
  • How I reproduced the collision
  • Why do parallel agents collide in one Playwright MCP browser?
  • Does isolatedContext fix parallel agents?
  • What does “Browser is already in use” mean?
  • How do you isolate parallel agents in Playwright MCP?
  • Option 1: one HTTP server, one connection per agent
  • Option 2: one stdio server per agent
  • Option 3: give each agent its own logged-in session
  • Does this work with Claude Code subagents?
  • Why the tools allowlist matters
  • The agents noticed. The scripts didn’t.
  • How is this different from parallel Playwright tests?
  • Fix it in your setup today

On this page

  • What did the #1804 report actually say?
  • How I reproduced the collision
  • Why do parallel agents collide in one Playwright MCP browser?
  • Does isolatedContext fix parallel agents?
  • What does “Browser is already in use” mean?
  • How do you isolate parallel agents in Playwright MCP?
  • Option 1: one HTTP server, one connection per agent
  • Option 2: one stdio server per agent
  • Option 3: give each agent its own logged-in session
  • Does this work with Claude Code subagents?
  • Why the tools allowlist matters
  • The agents noticed. The scripts didn’t.
  • How is this different from parallel Playwright tests?
  • Fix it in your setup today

Four agents, four technicians, four work orders to close. After one parallel pass through a single Playwright MCP server, my demo app’s audit log had three closed orders, every one signed tech-3. Tech-1’s note sat on the right order under the wrong name, and tech-3’s own note landed on tech-2’s order. Playwright MCP shipped a multi-account feature, isolatedContext, on October 31. Giving each parallel agent its own isolatedContext didn’t help: across 5 runs, the agents closed 0 of 20 orders correctly.

The fix that worked, 20 out of 20 in my scripted harness and 12 out of 12 with real Claude Code subagents, is boring: give every agent its own MCP connection to a server started with --isolated. Isolation in Playwright MCP is per connection. It isn’t per agent, and it isn’t per tab.

Server layout (4 scripted agents, 5 runs)Orders closed correctly
One server, one shared connection0 / 20
Shared connection + isolatedContext per agent0 / 20
One server per agent, no --isolated5 / 20
One --isolated HTTP server, one connection per agent20 / 20
One --isolated stdio server per agent20 / 20

What did the #1804 report actually say?

On October 28, 2026, a user filed microsoft/playwright-mcp#1804: 8 parallel Claude Code agents on SAP Fiori test cases “navigate over each other, corrupt form state, and produce unreliable results.” Maintainer Dmitry Gozman closed it the same day as not planned (“This issue doesn’t seem like it is using playwright mcp”). The reporter replied with the error they saw, Browser is already in use ... use --isolated to run multiple instances of the same browser, and argued --isolated can’t help when every subagent talks to one server process.

Three days later, Playwright MCP v0.0.84 added isolatedContext to browser_tabs, to “work with multiple accounts without sharing their cookies or storage.” It reads like the answer to #1804. My demo says it isn’t.

How I reproduced the collision

The app under test is 35 lines of Node: a login form that sets a session cookie, and a /orders/WO-10N page with a closing-note textarea. Every save goes into an audit log with the order, the note, and the technician the server thinks is signed in. Agent N signs in as tech-N, opens WO-10N, and saves Valve replaced (tech-N). A save counts only if all three line up.

I drove it two ways against @playwright/mcp@0.0.84 (bundling Playwright 1.65.0-alpha-2026-10-30) with headless Chrome:

  • A scripted harness: 86 lines of Node on the MCP TypeScript SDK. Four async “agents” make real tool calls, each preceded by a random 200–1,200 ms pause to stand in for model latency. Five seeded runs per setup (three for the storage-state variant).
  • Real Claude Code subagents: Claude Code 2.1.319, four Sonnet subagents dispatched in one message, two to three runs per setup.

Every scripted agent runs this function at the same time:

agents.mjs
async function agent(i) {
const c = clients[i], tech = `tech-${i + 1}`, order = `WO-10${i + 1}`;
const call = async (name, args) => {
await think();
const r = await c.callTool({ name, arguments: args });
if (r.isError) errors.push(`${tech} ${name}: ${r.content[0].text.replace('### Error\n','').split('\n')[0].slice(0, 150)}`);
return r;
};
if (mode === 'shared-isolatedContext')
await call('browser_tabs', { action: 'new', isolatedContext: tech });
if (mode !== 'per-agent-storage') {
await call('browser_navigate', { url: `${APP}/login` });
await call('browser_type', { target: 'input[name=tech]', text: tech });
await call('browser_click', { target: 'button' });
}
await call('browser_navigate', { url: `${APP}/orders/${order}` });
await call('browser_type', { target: 'textarea', text: `Valve replaced (${tech})` });
await call('browser_click', { target: 'button' });
}

Only clients[i] changes between setups: one shared client, or one per agent, pointed at different server layouts.

Why do parallel agents collide in one Playwright MCP browser?

Because Playwright MCP’s action tools act on “the current tab,” and the current tab belongs to the connection, not to whoever is calling. browser_click, browser_type and browser_navigate take no tab index or agent ID. When four agents share one connection, the last agent to navigate decides where everyone else’s next click lands.

Across 5 shared-connection runs, the audit log recorded 10 saves and none was correct: 5 had an empty note, 4 carried another technician’s note, and 4 landed on the wrong order (some had more than one fault). The 45 tool errors were the noisy part, like "textarea" does not match any elements after another agent had moved the tab back to the login page. The saves that went through are the problem: "Valve replaced (tech-2)" on WO-104, signed by tech-4. With 8 agents, the count in #1804, it was 0 of 24 across 3 runs, and 14 of the 17 saves had an empty note.

[ NOTE ]

Same bug class as two earlier posts, one layer up. Shared test users collide in the app’s database, and one session cookie loaded into eight contexts collides on the server. Here the collision happens in the browser driver itself, before the app sees anything.

Does isolatedContext fix parallel agents?

No. isolatedContext isolates cookies and storage, not the current tab. Each agent opened its own tab with isolatedContext: "tech-N", but opening a tab makes it current for the whole connection. This probe shows it in three calls:

current-tab-probe.mjs
await call('browser_tabs', { action: 'new', isolatedContext: 'agent-a', url: 'http://localhost:4400/login' }); // agent A opens its tab
await call('browser_tabs', { action: 'new', isolatedContext: 'agent-b', url: 'http://localhost:4400/login' }); // agent B opens its tab
await call('browser_navigate', { url: 'http://localhost:4400/orders/WO-101' }); // agent A's next step
console.log(await call('browser_tabs', { action: 'list' }));
output
- 0: [](http://localhost:4400/login) [isolatedContext: agent-a]
- 1: (current) [](http://localhost:4400/orders/WO-101) [isolatedContext: agent-b]

Agent A’s navigation landed in agent B’s tab, inside agent B’s cookies. That’s worse than plain sharing: an agent acts inside another agent’s identity, and nothing in the tool result tells it so.

The feature is good at what the release note says it’s for: one agent juggling several accounts in sequence, like an admin approving a request a user just submitted. It isn’t a concurrency primitive.

What does “Browser is already in use” mean?

That error is the good failure: two Playwright MCP servers tried to launch Chrome on the same persistent profile (by default ms-playwright-mcp/mcp-chrome-<hash> in your cache directory). One server per agent without --isolated gave me the reporter’s error:

per-agent server, no --isolated
Error: Browser is already in use for ~/Library/Caches/ms-playwright-mcp/mcp-chrome-357a142, use --isolated to run multiple instances

One agent per run got the browser and closed its order; the other three failed on every call (90 errors over 5 runs), so 5 of 20 closed. Nothing was corrupted, and a loud failure beats a quiet wrong write. An HTTP server without --isolated was worse: 1 of 20 closed, and in 4 runs the one save that landed was signed by nobody, because its agent’s login had failed with the same error. The error’s advice is correct. It just doesn’t help when agents share a connection.

How do you isolate parallel agents in Playwright MCP?

Give each agent its own MCP connection, and start the server with --isolated so each connection gets an in-memory profile instead of fighting over the one on disk. Two layouts do this, and both scored 20 out of 20 with zero tool errors. They differ in memory:

Setup (4 agents, 5 runs)Closed correctlyBrowsersChrome memory (summed RSS)
One shared connection (baseline)0 / 201~630 MB
One stdio server per agent, --isolated20 / 204~2.2–2.5 GB
One HTTP server, --isolated, one connection per agent20 / 201~1.3–1.8 GB

Memory is the summed resident memory of every Chrome process in the run, recorded on three runs per layout. Summed RSS double-counts shared pages, so treat it as a rough comparison, not a budget.

Option 1: one HTTP server, one connection per agent

This is the layout I’d pick. Run a single server over HTTP with --isolated (flags are documented in the Playwright MCP README):

terminal
npx @playwright/mcp@0.0.84 --headless --isolated --port 8931

Every client that connects to http://localhost:8931/mcp gets its own browser context on one shared Chrome, and since 0.0.84 each isolated client has its own idle timer. One flag undoes all of this: --shared-browser-context “reuse[s] the same browser context between all connected HTTP clients.” With it on, I got 2 out of 20.

I ran the demo on the standalone 0.0.84 package. The server bundled with Playwright 1.65.0, npx playwright mcp, lists the same --isolated, --port, --storage-state and --shared-browser-context flags, and it’s the one I’d pin in a real repo: 0.0.84 runs a 1.65.0-alpha core that drops agent refs for every control outside an open modal.

Option 2: one stdio server per agent

More memory, no shared process. Each agent launches its own npx @playwright/mcp@0.0.84 --isolated. Pick it when agents need different launch flags, like different --device or --proxy-server values.

Option 3: give each agent its own logged-in session

--storage-state seeds an isolated session from a file, so agents can skip the login form completely. Start one stdio server per agent, each with that agent’s file:

terminal
npx @playwright/mcp@0.0.84 --headless --isolated --storage-state=auth/tech-1.json

That got 12 out of 12 over 3 runs, at 3.3–4.0 seconds per run instead of 6.1–6.9, because no agent typed into a login form. One file per agent, though. On the single HTTP server, --storage-state is a server flag, so every connection starts as the same user: the shared-session-cookie bug all over again. And keep those files cookie-only, for the reasons in the storageState contamination post.

Does this work with Claude Code subagents?

Only if each subagent gets its own connection and can only see that connection. As covered in how Claude Code subagents get their tools and context, subagents inherit from the parent session, and the Claude Code subagents docs say an mcpServers entry that names a configured server will “share the parent session’s connection.” Four subagents pointed at one playwright server are the shared-connection setup from the first table. Here’s what four real Sonnet subagents did:

Claude Code setup (4 subagents)Closed correctly
One playwright server in .mcp.json3 / 12
Inline --isolated server inside one subagent definition2 / 12
Four named connections, no tools allowlist4 / 8 (2 runs)
Four named connections to one --isolated HTTP server, tools allowlist12 / 12

The working layout is four server names that all point at the Option 1 server:

.mcp.json
{
"mcpServers": {
"pw-1": { "type": "http", "url": "http://localhost:8931/mcp" },
"pw-2": { "type": "http", "url": "http://localhost:8931/mcp" },
"pw-3": { "type": "http", "url": "http://localhost:8931/mcp" },
"pw-4": { "type": "http", "url": "http://localhost:8931/mcp" }
}
}

Then each subagent is pinned to one of them. Line 5 is the one that matters:

.claude/agents/wo-closer-2.md
---
name: wo-closer-2
description: Closes one work order in the local work-order app as a given technician.
model: sonnet
tools: mcp__pw-2__browser_navigate, mcp__pw-2__browser_type, mcp__pw-2__browser_click, mcp__pw-2__browser_snapshot, mcp__pw-2__browser_fill_form
mcpServers:
- pw-2
---

Why the tools allowlist matters

Without tools:, a subagent “inherits every tool available to subagents,” which means all four servers. In both runs without the allowlist, several subagents piled onto pw-1, first in the list, and collided there; one reported that it had switched to “idle pw-2.” The mcpServers entry made the server available. It didn’t make it exclusive.

The inline-server setup failed differently. I gave one subagent definition an inline --isolated stdio server and dispatched four instances of it. They still saw each other’s sessions in all three runs, and a process sampler never showed more than two Playwright MCP servers alive at once. I can’t see the mechanism from outside Claude Code, but the rule is the same: one definition per parallel slot.

[ WARNING ]

Inline mcpServers in a project’s .claude/agents/ file only load after you trust that folder (Claude Code v2.1.238+). In an untrusted folder, my four subagents all reported “the Playwright MCP browser tools weren’t available” and stopped. Named references to servers you’ve already configured, like the layout above, skip that agent-folder trust check.

The agents noticed. The scripts didn’t.

The real subagents did better than my scripted ones because they read the page. When a page said “Signed in as tech-3,” a subagent signed in as tech-4 usually stopped instead of saving, and across the 24 attempts in the first two setups, the audit log shows no note saved under the wrong technician. That’s the model saving you, not the setup. My page printed the signed-in user in plain text; many SAP Fiori screens don’t. And one shared run still wrote WO-102 twice: the subagent decided its first save had failed and retried. That’s the duplicate-side-effect problem, set off by a browser another agent had just moved.

How is this different from parallel Playwright tests?

Playwright Test gives each test its own browser context by construction, so you opt in to sharing state. MCP turns that around: one connection is one shared session until you split it. It’s a concrete case, beyond the comparison of Playwright MCP, CLI and Test Agents, where a test runner’s defaults protect you and an agent tool’s don’t.

Server-side isolation is still on you. Separate connections stop agents from sharing a tab, not from editing the same record as one shared QA user. Use one identity per agent, the same way you’d use one per worker.

Fix it in your setup today

Open your MCP config and count how many parallel agents can reach each Playwright server name. If any server has more than one, start one --isolated HTTP server, give it one name per parallel slot, and lock each subagent to its own name with an explicit tools: list. Then run your parallel dispatch once against a page that prints the signed-in user, and check every save against that user on the server side, not in the agent’s summary.

§ Frequently Asked FAQ
+ Does Playwright MCP support multiple agents at the same time?

Yes, if each agent has its own MCP connection to a server started with --isolated. Agents that share one connection share one current tab. In my demo, 4 agents on one connection closed 0 of 20 work orders correctly.

+ Does isolatedContext make parallel agents safe?

No. isolatedContext, added to browser_tabs in Playwright MCP v0.0.84, isolates cookies and storage between tabs, but the current tab is still shared across the connection. In my demo, agents using it on one connection still closed 0 of 20 orders correctly.

+ How do I fix 'Browser is already in use, use --isolated to run multiple instances'?

Add --isolated to every Playwright MCP server that runs at the same time as another one. Without it, each server tries to launch Chrome on the same persistent profile directory, and all but one fail.

+ Do Claude Code subagents share the Playwright MCP browser?

If they reference the same configured server, yes. The Claude Code subagent docs say string references “share the parent session’s connection.” Give each parallel subagent its own server name and restrict its tools: to that server.

+ Is one HTTP server or one server per agent better?

One HTTP server with --isolated ran a single Chrome at about 1.3–1.8 GB summed RSS in my demo, against four Chromes at 2.2–2.5 GB for one stdio server per agent. Both closed 20 of 20. Use per-agent servers when agents need different launch flags or their own --storage-state files.

§ Further Reading 03 of 03
01AI Testing

AI Agents Hallucinate Domains: --allowed-origins Leaks

AI agents navigate to hallucinated domains when nobody gives them a URL. In my tests, Playwright MCP's --allowed-origins still let Chrome connect first.

Read →
02AI Testing

Reviewing AI-Generated Tests with Playwright Screencast

Playwright 1.56's planner, generator, and healer agents weren't reviewable at PR speed. 1.59's Screencast API turns each agent into a 90-second receipt.

Read →
03AI Testing

Playwright's Healer Agent Will Hide Your Race Conditions

Playwright's Healer agent auto-fixes red tests to green. In a parallel enterprise suite, that's how real regressions ship silently. Here's the guardrail I use.

Read →

Don't miss a thing

Subscribe to get updates straight to your inbox.

HT

No spam · Unsubscribe anytime

Welcome aboard!

You're on the list. Expect real-world QA insights — no fluff, no spam.

§ Colophon

Halmurat T. — Senior SDET writing about test automation, CI/CD, and QA strategy from 10+ years in the enterprise trenches.

Set in
IBM Plex Sans, Lora, and IBM Plex Mono.
Built with
Astro, MDX, Tailwind CSS & Expressive Code. Served by Vercel.
Privacy
No cookies. No tracking scripts on the main thread — analytics run sandboxed via Partytown.
Source
github.com/Halmurat-Uyghur
Terminal
Try /ask to query Halmurat's notes in a shell prompt.

© 2026 Halmurat T. · Written in plain text, shipped in plain time.

Search
Esc

Search is not available in dev mode.

Run npm run build then npm run preview:local to test search locally.