Fix browser MCP standalone runtime#60
Conversation
There was a problem hiding this comment.
Pull request overview
This PR targets reliability of the Playwright MCP “browser” runtime when running a Next.js standalone build, ensuring the packaged output includes the required Playwright MCP runtime packages and that the MCP server is launched against Chromium with the expected startup configuration.
Changes:
- Force Playwright MCP stdio server launches to use
chromium(CLI args + env) when not attaching to a CDP endpoint. - Add a standalone build repair step to copy Playwright MCP runtime packages into
.next/standalone/node_modules. - Add tests covering the new browser args/env behavior and the standalone repair copy behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/lib/server/session-tools/web-utils.ts |
Adds explicit Chromium selection for Playwright MCP stdio server launches. |
src/lib/server/session-tools/web-browser-config.test.ts |
Extends tests to validate Chromium args/env and CDP behavior. |
scripts/run-next-build.mjs |
Introduces repairStandaloneBrowserMcpRuntime and invokes it after successful builds. |
scripts/run-next-build.test.mjs |
Adds a test verifying standalone repair copies required Playwright MCP runtime packages. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for (const packageName of REQUIRED_STANDALONE_BROWSER_PACKAGES) { | ||
| const sourceDir = path.join(cwd, 'node_modules', ...packageName.split('/')) | ||
| const targetDir = path.join(standaloneNodeModules, ...packageName.split('/')) | ||
| if (fs.existsSync(targetDir)) continue |
There was a problem hiding this comment.
repairStandaloneBrowserMcpRuntime skips copying when targetDir already exists, but Next.js output tracing can create partially-populated package directories. In that case existsSync(targetDir) will be true even though required files (e.g. package.json / cli.js) are missing, and the repair will incorrectly no-op. Consider changing the guard to verify a sentinel file (like package.json or cli.js) or always copying/merging into targetDir with overwrite to ensure the runtime is complete.
| if (fs.existsSync(targetDir)) continue | |
| const targetPackageJson = path.join(targetDir, 'package.json') | |
| if (fs.existsSync(targetPackageJson)) continue |
|
|
||
| assert.equal(params.command, process.execPath) | ||
| assert.equal(params.args.includes('--browser'), true) | ||
| assert.equal(params.args.includes('chromium'), true) |
There was a problem hiding this comment.
The assertion params.args.includes('chromium') can be a false positive if the profile/output paths ever contain the substring chromium. Make this robust by asserting that the argument immediately following --browser is exactly chromium (e.g., check args[indexOf('--browser') + 1]).
| assert.equal(params.args.includes('chromium'), true) | |
| assert.equal(params.args[params.args.indexOf('--browser') + 1], 'chromium') |
| @@ -145,6 +146,7 @@ export function buildBrowserStdioServerParams( | |||
| env: { | |||
| ...env, | |||
| ...(cdpEndpoint ? { PLAYWRIGHT_MCP_CDP_ENDPOINT: cdpEndpoint } : { | |||
| PLAYWRIGHT_MCP_BROWSER: 'chromium', | |||
| PLAYWRIGHT_MCP_USER_DATA_DIR: profileDir, | |||
| PLAYWRIGHT_MCP_HEADLESS: '1', | |||
There was a problem hiding this comment.
'chromium' is duplicated in both the CLI args and env block. Consider extracting a single constant (e.g., const browserName = 'chromium') to avoid future drift if the default browser is changed again.
|
Thanks @borislavnnikolov. I verified the CLI/build tests, type-check, and production build, merged this into main, and included it in the v1.5.71 release notes. |
Summary
Tests