diff --git a/package-lock.json b/package-lock.json
index 828a03cdba7e9..6824d966f2696 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,7 +24,7 @@
"@stylistic/eslint-plugin": "^5.2.3",
"@types/codemirror": "^5.60.7",
"@types/formidable": "^2.0.4",
- "@types/node": "18.19.76",
+ "@types/node": "25.0.3",
"@types/react": "^19.2.1",
"@types/react-dom": "^19.2.1",
"@types/ws": "^8.5.3",
@@ -1811,12 +1811,13 @@
}
},
"node_modules/@types/node": {
- "version": "18.19.76",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.76.tgz",
- "integrity": "sha512-yvR7Q9LdPz2vGpmpJX5LolrgRdWvB67MJKDPSgIIzpFbaf9a1j/f5DnLp5VDyHGMR0QZHlTr1afsD87QCXFHKw==",
+ "version": "25.0.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
+ "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==",
"devOptional": true,
+ "license": "MIT",
"dependencies": {
- "undici-types": "~5.26.4"
+ "undici-types": "~7.16.0"
}
},
"node_modules/@types/react": {
@@ -7667,10 +7668,11 @@
}
},
"node_modules/undici-types": {
- "version": "5.26.5",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
- "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
- "devOptional": true
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "devOptional": true,
+ "license": "MIT"
},
"node_modules/universal-user-agent": {
"version": "6.0.1",
diff --git a/package.json b/package.json
index f4fa02b71f5e5..80ca729fb86d7 100644
--- a/package.json
+++ b/package.json
@@ -61,7 +61,7 @@
"@stylistic/eslint-plugin": "^5.2.3",
"@types/codemirror": "^5.60.7",
"@types/formidable": "^2.0.4",
- "@types/node": "18.19.76",
+ "@types/node": "25.0.3",
"@types/react": "^19.2.1",
"@types/react-dom": "^19.2.1",
"@types/ws": "^8.5.3",
diff --git a/packages/playwright/src/mcp/browser/tools/runCode.ts b/packages/playwright/src/mcp/browser/tools/runCode.ts
index 537ffdb06444c..58fa46cbb9cc5 100644
--- a/packages/playwright/src/mcp/browser/tools/runCode.ts
+++ b/packages/playwright/src/mcp/browser/tools/runCode.ts
@@ -53,7 +53,9 @@ const runCode = defineTabTool({
__end__.reject(e);
}
})()`;
- await vm.runInContext(snippet, context);
+ await vm.runInContext(snippet, context, {
+ importModuleDynamically: vm.constants?.USE_MAIN_CONTEXT_DEFAULT_LOADER,
+ });
const result = await __end__;
if (typeof result === 'string')
response.addResult(result);
diff --git a/tests/mcp/run-code.spec.ts b/tests/mcp/run-code.spec.ts
index a8e3149714198..3768568106f2c 100644
--- a/tests/mcp/run-code.spec.ts
+++ b/tests/mcp/run-code.spec.ts
@@ -166,3 +166,66 @@ test('browser_run_code return value', async ({ client, server }) => {
result: '{"message":"Hello, world!"}',
});
});
+
+test('browser_run_code dynamic import', async ({ client, server }) => {
+ test.skip(process.platform === 'win32', 'windows only works with file:// imports');
+ server.setContent('/', `
+
+ `, 'text/html');
+ await client.callTool({
+ name: 'browser_navigate',
+ arguments: { url: server.PREFIX },
+ });
+
+ const scriptPath = test.info().outputPath('helper.mjs');
+ await fs.writeFile(scriptPath, `
+export async function clickSubmit(page) {
+ await page.getByRole("button", { name: "Submit" }).click();
+}
+`);
+
+ const code = `async (page) => {
+ const { clickSubmit } = await import(${JSON.stringify(scriptPath)});
+ await clickSubmit(page);
+}`;
+ expect(await client.callTool({
+ name: 'browser_run_code',
+ arguments: {
+ code,
+ },
+ })).toHaveResponse({
+ code: `await (${code})(page);`,
+ consoleMessages: expect.stringContaining('- [LOG] Submit'),
+ });
+});
+
+test('browser_run_code dynamic import (file: proto)', async ({ client, server }) => {
+ server.setContent('/', `
+
+ `, 'text/html');
+ await client.callTool({
+ name: 'browser_navigate',
+ arguments: { url: server.PREFIX },
+ });
+
+ const scriptPath = test.info().outputPath('helper.mjs');
+ await fs.writeFile(scriptPath, `
+export async function clickSubmit(page) {
+ await page.getByRole("button", { name: "Submit" }).click();
+}
+`);
+
+ const code = `async (page) => {
+ const { clickSubmit } = await import(${JSON.stringify('file://' + scriptPath)});
+ await clickSubmit(page);
+}`;
+ expect(await client.callTool({
+ name: 'browser_run_code',
+ arguments: {
+ code,
+ },
+ })).toHaveResponse({
+ code: `await (${code})(page);`,
+ consoleMessages: expect.stringContaining('- [LOG] Submit'),
+ });
+});