Skip to content

[Feature]: In extension mode, don't take over the user's tab or window #43657

Closed as duplicate

Description

🚀 Feature Request

When an MCP client works in the user's own browser through the Playwright extension, the user should be able to keep working in their tab while the client works in its own tabs. Today, Playwright regularly takes the user's tab away and can bring the browser to the front.

Example

The user types in a tab of their browser. A client connects with PLAYWRIGHT_MCP_EXTENSION_TOKEN and runs browser_navigate, browser_tabs new and select, browser_click and browser_type.

  • The connected tab is activated and its window focused (packages/extension/src/background.ts, _connectTab).
  • browser_tabs new opens an active tab (chrome.tabs.create in BrowserModel.createTarget).
  • browser_tabs select calls page.bringToFront() (tools/backend/context.ts, selectTab).
  • On macOS, when the user was in another app, the browser came to the front during the client's work.

If the client's tab is not the active one, it stops working. Chrome gives a hidden tab no animation frames and drops key and mouse input. browser_click then times out on the "stable" check, and browser_type with slowly loses the keys without an error. In extension mode, Playwright connects with noDefaults, so the focus emulation that Playwright's own launcher turns on is skipped.

Measured on GitHub's macOS 26 and Windows Server 2025 runners, with two clients making 79 tool calls together while a person types in a tab of the same browser (or in another app). Five scenarios, each run on main and with the changes: Chrome for Testing on macOS and Edge on Windows with the user in the browser and in another app, and Edge on macOS with the user in the browser (CI run):

Main With the proposed changes
The user's tab during the clients' work Another tab activated 12–13 times; another tab active in every poll Stayed active (0 of 172–237 polls saw another tab)
Tool calls that succeeded 67–73 of 79: clicks in hidden tabs timed out, and tabs opened from a hidden tab were missing from the client's tab list 79 of 79 in every run
The browser came to the front (macOS, Chrome for Testing, user in another app) Yes, 412 of 625 samples No, 0 of 157

Motivation

The extension's purpose is to let an agent use the user's own browser, with their logins, while they keep using it. Taking the user's tab, or the whole screen, makes that hard: what they type goes into the wrong tab, and they cannot work while the agent works. Several agents can share one browser, each in its own tab group; this only works if none of them takes the active tab.

Proposal

Three pull requests. Merge #43658 first: #43659 relies on it at runtime, and #43660 uses its test fixture.

  1. Open client tabs in the background and emulate focus in the tabs the relay attaches. Hidden client tabs then render, take clicks and keys, and can be screenshotted. This works with the extension that has already shipped.
  2. Don't activate tabs on the client's behalf in extension mode. browser_tabs select no longer calls bringToFront(). browser_start_recording still does, because the user acts in that tab. This is a policy choice, so it is worth deciding here whether it should be the default.
  3. Extension: connect with a token without taking the user's tab. The connected tab is no longer activated, and the window goes back to the tab that was active before, which the extension records itself. A tab the user picks in the connect page is still shown. Tabs that a client's page opens (target=_blank, window.open) are handed back too and go to the client whose tab opened them. The extension also emulates focus in the tabs it attaches, so it works with older Playwright MCP servers.

Gaps that remain

  • Connect page flash: the relay opens the connect page through the browser's command line, which always shows it as the active tab. The extension hands the window back within about 100–400 ms when its service worker was asleep and 25–100 ms when it was running, but the user still sees a flash on every connect. Removing it needs a way for the relay to reach the extension without opening a page, such as native messaging.
  • Windows taskbar: on Windows, Edge briefly marks its window as focused at connect (about 0.65 s), and its taskbar icon flashes. Windows' own foreground lock keeps the browser from coming forward.
  • Linux: not measured.
  • Only incognito or app windows open: when the user has no normal window open, Chrome opens a new normal window for the connect page, in front.
  • Edge on macOS at connect: in earlier runs, Edge came to the front when the relay opened the first connect page through its command line, on main and with the changes. Chrome for Testing did not. The runs started Edge directly rather than through Launch Services, so whether Edge started from the Dock does the same was not measured.
  • Popup windows: a window.open call with window features opens a new window, and the browser focuses it. The browser did not come to the front when the user was in another app. When the user worked in the browser, the new window held the focus for about 130–400 ms before the user's window had it again.
  • Focus emulation on the user's tabs: while a client is connected, focus emulation also applies to tabs the user picked or dragged into the client's group. Those pages report themselves as focused and visible, so a chat or mail site may hold back notifications.

Activity

  1. github-actions commented on Oct 29, 2026

    @github-actions
  2. github-actions commented on Oct 29, 2026

    @github-actions
    Contributor

    Hi, I'm the Playwright bot and I checked this on Linux.

    Reproduced the tab activation; background operation needs a default-versus-option decision. With Playwright 1.65.0-next at f33f0b751be0dd559d86c1ed27ca8baa5f9cb2b2, Chromium 157.0.8103.15 on Ubuntu 24.04, token connection, browser_tabs new, and browser_tabs select each replaced the user's active tab.

    This comes from explicit activation in the extension and relay, not an unexplained browser focus change. The extension connection also uses noDefaults, which skips Playwright's usual focus emulation. Making connected tabs work in the background changes what those pages observe as focus and visibility, including tabs the user selected. The decision is whether that should apply to every extension connection or require an option. There is no maintainer decision in the thread, so I have not proposed a default change.

    Reproduction and results

    I extended the existing browser_tabs new creates a new tab, browser_tabs select switches the active tab, and bypass connection dialog with token tests. The added assertion queries the extension's chrome.tabs.query({ active: true }) and checks that the active tab is still titled User.

    For the new-tab case, the added setup and assertion are:

    const { browserContext, client } = await startExtensionClient();
    // Create the user's tab before connecting, so it is outside the client's group.
    const userPage = await browserContext.newPage();
    await userPage.setContent('<title>User</title><input aria-label="User input">');
    await connectAndNavigate(browserContext, client, server.HELLO_WORLD);
    await userPage.bringToFront();
    
    await client.callTool({
      name: 'browser_tabs',
      arguments: { action: 'new', url: server.EMPTY_PAGE },
    });
    
    const worker = browserContext.serviceWorkers().find(w => w.url().includes(extensionId))!;
    const activeTitles = await worker.evaluate(async () => {
      const tabs = await (globalThis as any).chrome.tabs.query({ active: true });
      return tabs.map((tab: { title: string }) => tab.title);
    });
    expect(activeTitles).toEqual(['User']); // Fails on main.
    Checkout New-tab assertion Select-tab assertion
    main, f33f0b751be0dd559d86c1ed27ca8baa5f9cb2b2 Fails: Second is active Fails: First is active
    #43658, 27618709729e4d6e943c513cbbc034e967e28535 Passes Fails: First is active
    #43659, 31c4d20dffa9fde0654a24d6a5bbe0fc5101bdb8 Fails: Second is active Passes

    The token assertion fails on main: the client's Title tab is active instead of User.

    I rebuilt each tested checkout and ran:

    xvfb-run -a npm run test-extension -- tab-management --grep 'browser_tabs (new|select)' --retries=0 --workers=1 --reporter=line
    xvfb-run -a npm run test-extension -- extension.spec.ts --grep 'bypass connection dialog with token' --retries=0 --workers=1 --reporter=line

    The first command ran on main and both PR heads; the second ran on main. On #43658, both tests in background-tabs.spec.ts passed, including clicking, slow typing, and screenshots with its unmanaged-browser fixture. On #43659, both tests in no-activate.spec.ts passed, including recording activation.

    These are headed Chromium tests under Xvfb. Firefox and WebKit do not support this extension connection. I did not test macOS, Windows, OS-level foreground changes, or the combined proposals. #43660 was read but not executed; its token-restoration and popup-routing behavior remains unverified here. Neither tested PR alone resolves the full request, and these targeted results are not a merge-ready verdict.

    Triaged on Linux by the Playwright bot - agent run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions