Skip to content

[Bug]: cli-client extension mode rejects all browser-level CDP commands (cookie-list, state-save) with "Unsupported command without sessionId" #40687

@riba2534

Description

@riba2534

What happened?

In MCP extension mode, all browser-level CDP commands fail with:

Error: Protocol error (Storage.getCookies): Unsupported command without sessionId: Storage.getCookies

This breaks every cli-client subcommand that touches browser-scoped state:

  • playwright-cli cookie-list (and cookie-get / cookie-set / cookie-delete / cookie-clear)
  • playwright-cli state-save

The browser session itself is healthy — goto, eval, snapshot, click all work fine. Only commands that hit Storage.* (and presumably Browser.* / target-less Target.*) are affected.

The same commands work correctly in non-extension (standalone) mode.

Root cause (already located)

The relay's forwardToExtension unconditionally requires a sessionId:

packages/playwright-core/src/tools/mcp/cdpRelayV2.ts (visible in shipped bundle around coreBundle.js:65914-65918):

async forwardToExtension(method, params, sessionId) {
  if (!sessionId)
    throw new Error(`Unsupported command without sessionId: ${method}`);
  return await this._model.sendCommand(sessionId, method, params);
}

But Storage.getCookies / Storage.setCookie / Storage.clearDataForOrigin etc. are browser-level CDP commands, routed by browserContextId, not by a target sessionId. The Playwright CRBrowser already calls them that way:

// coreBundle.js:35671
const { cookies } = await this._browser._session.send(
  "Storage.getCookies",
  { browserContextId: this._browserContextId }
);

So the relay's "must have sessionId" guard rejects commands that are legitimately session-less. This looks like a regression from the cli-client/extension-mode generalization landed in April–May 2026 (around #40191 / #40411).

Suggested fix

In cdpRelayV2.ts::forwardToExtension, route browser-level commands through the model's browser-level channel instead of throwing. Something like:

async forwardToExtension(method, params, sessionId) {
  if (!sessionId) {
    // browser-level CDP commands (Storage.*, Browser.*, target-less Target.*)
    // are routed by browserContextId, not by a target sessionId.
    if (method.startsWith('Storage.') || method.startsWith('Browser.'))
      return await this._model.sendBrowserCommand(method, params);
    throw new Error(`Unsupported command without sessionId: ${method}`);
  }
  return await this._model.sendCommand(sessionId, method, params);
}

(sendBrowserCommand may need to be added on BrowserModel to forward to the extension's root debugger session — happy to send a PR if that direction sounds right.)

Steps to reproduce

# 1. open browser in extension mode (relay to user's local Chrome)
PLAYWRIGHT_MCP_EXTENSION=true playwright-cli open
PLAYWRIGHT_MCP_EXTENSION=true playwright-cli goto https://example.com/

# 2. any cookie or state command fails
PLAYWRIGHT_MCP_EXTENSION=true playwright-cli cookie-list
# => ### Error
# => Error: Protocol error (Storage.getCookies): Unsupported command without sessionId: Storage.getCookies

PLAYWRIGHT_MCP_EXTENSION=true playwright-cli state-save /tmp/state.json
# => same error

# 3. but interaction commands work fine
PLAYWRIGHT_MCP_EXTENSION=true playwright-cli eval "document.title"
# => works

Same cookie-list against a standalone (non-extension) browser works correctly.

Expected behavior

In extension mode, cookie-list / cookie-get / state-save / etc. should return the cookies from the connected browser context — same behavior as in non-extension mode.

This matters because extension mode is the only way to read the user's logged-in HttpOnly cookies (e.g. for tooling that proxies authenticated requests through the user's existing browser session). document.cookie via eval is not a workaround for HttpOnly cookies.

Version

playwright-cli (@playwright/cli): 0.1.12
node: v25.8.2
OS: macOS 26.4.1 (arm64)
Chrome: connected via MCP extension

Workarounds

  • ❌ Headless / standalone mode — works for cookie-list, but loses access to the user's logged-in session (defeats the purpose of extension mode).
  • document.cookie via eval — only returns non-HttpOnly cookies; auth cookies are usually HttpOnly.
  • ✅ Local patch to cdpRelayV2.ts along the lines suggested above.

Happy to send a PR if a maintainer can confirm the direction.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions