diff --git a/packages/playwright-core/src/tools/mcp/program.ts b/packages/playwright-core/src/tools/mcp/program.ts index e29d72fed016c..1015336aa74d1 100644 --- a/packages/playwright-core/src/tools/mcp/program.ts +++ b/packages/playwright-core/src/tools/mcp/program.ts @@ -132,14 +132,20 @@ export function decorateMCPCommand(command: Command) { }, disposed: async backend => { clientCount--; - if (sharedBrowserPromise && clientCount > 0) + const browserContext = (backend as BrowserBackend).browserContext; + const browser = browserContext.browser(); + // Decide before awaiting context closure, since client disposals can run concurrently. + const shouldKeepBrowser = !!sharedBrowserPromise && clientCount > 0; + if (shouldKeepBrowser) { + if (config.browser.isolated) + await browserContext.close().catch(() => { }); return; + } testDebug('close browser'); sharedBrowserPromise = undefined; - const browserContext = (backend as BrowserBackend).browserContext; await browserContext.close().catch(() => { }); - await browserContext.browser()?.close().catch(() => { }); + await browser?.close().catch(() => { }); } }; await mcpServer.start(factory, config.server); diff --git a/tests/mcp/http.spec.ts b/tests/mcp/http.spec.ts index 292e7e1275aea..0e938c9e766ec 100644 --- a/tests/mcp/http.spec.ts +++ b/tests/mcp/http.spec.ts @@ -20,7 +20,7 @@ import dns from 'dns'; import { ChildProcess, spawn } from 'child_process'; import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; import { Client } from '@modelcontextprotocol/sdk/client/index.js'; -import { test as baseTest, expect, mcpServerPath, formatLog } from './fixtures'; +import { test as baseTest, expect, mcpServerPath, formatLog, parseResponse } from './fixtures'; import { inheritAndCleanEnv } from '../config/utils'; import type { Config } from '../../packages/playwright-core/src/tools/mcp/config.d'; @@ -182,6 +182,14 @@ test('http transport browser lifecycle (isolated, multiclient)', async ({ server await transport1.terminateSession(); await client1.close(); + await expect.poll(async () => { + const response = parseResponse(await client2.callTool({ + name: 'browser_run_code_unsafe', + arguments: { code: 'page => page.context().browser().contexts().length' }, + })); + return response.result; + }).toBe('1'); + const transport3 = new StreamableHTTPClientTransport(new URL('/mcp', url)); const client3 = new Client({ name: 'test', version: '1.0.0' }); await client3.connect(transport3);