mirror of
https://github.com/microsoft/playwright-mcp.git
synced 2026-03-31 15:23:12 +00:00
Compare commits
1 Commits
v0.0.69
...
roll-1.59.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd1ef543cb |
@@ -53,6 +53,7 @@ FROM base
|
||||
ARG PLAYWRIGHT_BROWSERS_PATH
|
||||
ARG USERNAME=node
|
||||
ENV NODE_ENV=production
|
||||
ENV PLAYWRIGHT_MCP_OUTPUT_DIR=/tmp/playwright-output
|
||||
|
||||
# Set the correct ownership for the runtime user on production `node_modules`
|
||||
RUN chown -R ${USERNAME}:${USERNAME} node_modules
|
||||
@@ -62,8 +63,5 @@ USER ${USERNAME}
|
||||
COPY --from=browser --chown=${USERNAME}:${USERNAME} ${PLAYWRIGHT_BROWSERS_PATH} ${PLAYWRIGHT_BROWSERS_PATH}
|
||||
COPY --chown=${USERNAME}:${USERNAME} packages/playwright-mcp/cli.js packages/playwright-mcp/package.json ./
|
||||
|
||||
# Current working directory must be writable as MCP may need to create default output dir in it.
|
||||
WORKDIR /home/${USERNAME}
|
||||
|
||||
# Run in headless and only with chromium (other browsers need more dependencies not included in this image)
|
||||
ENTRYPOINT ["node", "/app/cli.js", "--headless", "--browser", "chromium", "--no-sandbox"]
|
||||
ENTRYPOINT ["node", "cli.js", "--headless", "--browser", "chromium", "--no-sandbox"]
|
||||
|
||||
@@ -1275,7 +1275,6 @@ http.createServer(async (req, res) => {
|
||||
- Title: Start video
|
||||
- Description: Start video recording
|
||||
- Parameters:
|
||||
- `filename` (string, optional): Filename to save the video.
|
||||
- `size` (object, optional): Video size
|
||||
- Read-only: **true**
|
||||
|
||||
@@ -1292,7 +1291,8 @@ http.createServer(async (req, res) => {
|
||||
- **browser_stop_video**
|
||||
- Title: Stop video
|
||||
- Description: Stop video recording
|
||||
- Parameters: None
|
||||
- Parameters:
|
||||
- `filename` (string, optional): Filename to save the video
|
||||
- Read-only: **true**
|
||||
|
||||
<!-- NOTE: This has been generated via update-readme.js -->
|
||||
|
||||
39
package-lock.json
generated
39
package-lock.json
generated
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"name": "playwright-mcp-internal",
|
||||
"version": "0.0.69",
|
||||
"version": "0.0.68",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "playwright-mcp-internal",
|
||||
"version": "0.0.69",
|
||||
"version": "0.0.68",
|
||||
"license": "Apache-2.0",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.25.2",
|
||||
"@playwright/test": "1.59.0-alpha-1774912654000",
|
||||
"@playwright/test": "1.59.0-alpha-1774656214000",
|
||||
"@types/node": "^24.3.0"
|
||||
}
|
||||
},
|
||||
@@ -854,13 +854,12 @@
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.59.0-alpha-1774912654000",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.0-alpha-1774912654000.tgz",
|
||||
"integrity": "sha512-/OjIU6mfPP7UisfqKLnKqjG8gDq4gwoek55z6VqaWnGgZnRdJwCNBao5Azymk5qmp7hzxu3rXdW0sFmsNXLEfQ==",
|
||||
"version": "1.59.0-alpha-1774656214000",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.0-alpha-1774656214000.tgz",
|
||||
"integrity": "sha512-1BmlLuGD6XAOLv98iCtgbvjRWdVjOdEh2fnDjWqHiD9ygnNXupsJZzkDVOfXSlaoZQoO26kcOfV03qbaUWua/A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright": "1.59.0-alpha-1774912654000"
|
||||
"playwright": "1.59.0-alpha-1774656214000"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
@@ -2624,12 +2623,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.59.0-alpha-1774912654000",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.0-alpha-1774912654000.tgz",
|
||||
"integrity": "sha512-98v8NQ0ZaNTdOhwo24sq7Y79wuh+Q6TShG6DKgyQIUr4cBV5gDRIcRjTVZz5LhwYAwCOXCanvY5er47mowV8ww==",
|
||||
"license": "Apache-2.0",
|
||||
"version": "1.59.0-alpha-1774656214000",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.0-alpha-1774656214000.tgz",
|
||||
"integrity": "sha512-aIR09o0T9Y/LpCcBaEE6tldqNI8wrbxuZYH8uruD8kJx/5GtwyA6LxPujy2n8pOQUOmHpySDGBQYFWfhCbD/uA==",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.59.0-alpha-1774912654000"
|
||||
"playwright-core": "1.59.0-alpha-1774656214000"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
@@ -2646,10 +2644,9 @@
|
||||
"link": true
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.59.0-alpha-1774912654000",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.0-alpha-1774912654000.tgz",
|
||||
"integrity": "sha512-A4zBS+GASROUFrHTMeC0ySI1+f/Wjbm0OJIAUFu4q+0Si3tfLg742dGwq1AezmMBovINt6Fi7fOTjtGZCQQCEw==",
|
||||
"license": "Apache-2.0",
|
||||
"version": "1.59.0-alpha-1774656214000",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.0-alpha-1774656214000.tgz",
|
||||
"integrity": "sha512-pFMQqZDbTSvKntHMh4ZF+iB7jAm5c0nyM5t7rX6tSa7wQdZiswJrFBN+hKmMq338+y6iSJhT61vxuYxORakivA==",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
@@ -3416,7 +3413,7 @@
|
||||
},
|
||||
"packages/extension": {
|
||||
"name": "@playwright/mcp-extension",
|
||||
"version": "0.0.69",
|
||||
"version": "0.0.68",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@types/chrome": "^0.0.315",
|
||||
@@ -3441,11 +3438,11 @@
|
||||
},
|
||||
"packages/playwright-mcp": {
|
||||
"name": "@playwright/mcp",
|
||||
"version": "0.0.69",
|
||||
"version": "0.0.68",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright": "1.59.0-alpha-1774912654000",
|
||||
"playwright-core": "1.59.0-alpha-1774912654000"
|
||||
"playwright": "1.59.0-alpha-1774656214000",
|
||||
"playwright-core": "1.59.0-alpha-1774656214000"
|
||||
},
|
||||
"bin": {
|
||||
"playwright-mcp": "cli.js"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "playwright-mcp-internal",
|
||||
"version": "0.0.69",
|
||||
"version": "0.0.68",
|
||||
"private": true,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -26,7 +26,7 @@
|
||||
],
|
||||
"devDependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.25.2",
|
||||
"@playwright/test": "1.59.0-alpha-1774912654000",
|
||||
"@playwright/test": "1.59.0-alpha-1774656214000",
|
||||
"@types/node": "^24.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Playwright MCP Bridge",
|
||||
"version": "0.0.69",
|
||||
"version": "0.0.68",
|
||||
"description": "Share browser tabs with Playwright MCP server",
|
||||
"permissions": [
|
||||
"debugger",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@playwright/mcp-extension",
|
||||
"version": "0.0.69",
|
||||
"version": "0.0.68",
|
||||
"description": "Playwright MCP Browser Extension",
|
||||
"private": true,
|
||||
"repository": {
|
||||
|
||||
@@ -123,14 +123,6 @@ const test = base.extend<TestFixtures>({
|
||||
},
|
||||
});
|
||||
|
||||
function cliEnv() {
|
||||
return {
|
||||
PLAYWRIGHT_SERVER_REGISTRY: test.info().outputPath('registry'),
|
||||
PLAYWRIGHT_DAEMON_SESSION_DIR: test.info().outputPath('daemon'),
|
||||
PLAYWRIGHT_SOCKETS_DIR: path.join(test.info().project.outputDir, 'ds', String(test.info().parallelIndex)),
|
||||
};
|
||||
}
|
||||
|
||||
async function runCli(
|
||||
args: string[],
|
||||
options: { mcpBrowser?: string, testInfo: any },
|
||||
@@ -141,7 +133,7 @@ async function runCli(
|
||||
const testInfo = options.testInfo;
|
||||
|
||||
// Path to the terminal CLI
|
||||
const cliPath = path.join(__dirname, '../../../node_modules/playwright-core/lib/tools/cli-client/cli.js');
|
||||
const cliPath = path.join(__dirname, '../../../node_modules/playwright/lib/cli/client/program.js');
|
||||
|
||||
return new Promise<CliResult>((resolve, reject) => {
|
||||
let stdout = '';
|
||||
@@ -151,7 +143,9 @@ async function runCli(
|
||||
cwd: testInfo.outputPath(),
|
||||
env: {
|
||||
...process.env,
|
||||
...cliEnv(),
|
||||
PLAYWRIGHT_DAEMON_INSTALL_DIR: testInfo.outputPath(),
|
||||
PLAYWRIGHT_DAEMON_SESSION_DIR: testInfo.outputPath('daemon'),
|
||||
PLAYWRIGHT_DAEMON_SOCKETS_DIR: path.join(testInfo.project.outputDir, 'daemon-sockets'),
|
||||
PLAYWRIGHT_MCP_BROWSER: options.mcpBrowser,
|
||||
PLAYWRIGHT_MCP_HEADLESS: 'false',
|
||||
},
|
||||
@@ -219,7 +213,8 @@ test(`navigate with extension`, async ({ browserWithExtension, startClient, serv
|
||||
});
|
||||
|
||||
const selectorPage = await confirmationPagePromise;
|
||||
await selectorPage.locator('.tab-item', { hasText: 'Playwright MCP extension' }).getByRole('button', { name: 'Connect' }).click();
|
||||
// For browser_navigate command, the UI shows Allow/Reject buttons instead of tab selector
|
||||
await selectorPage.getByRole('button', { name: 'Allow' }).click();
|
||||
|
||||
expect(await navigateResponse).toHaveResponse({
|
||||
snapshot: expect.stringContaining(`- generic [active] [ref=e1]: Hello, world!`),
|
||||
@@ -300,7 +295,8 @@ testWithOldExtensionVersion(`works with old extension version`, async ({ browser
|
||||
});
|
||||
|
||||
const selectorPage = await confirmationPagePromise;
|
||||
await selectorPage.locator('.tab-item', { hasText: 'Playwright MCP extension' }).getByRole('button', { name: 'Connect' }).click();
|
||||
// For browser_navigate command, the UI shows Allow/Reject buttons instead of tab selector
|
||||
await selectorPage.getByRole('button', { name: 'Allow' }).click();
|
||||
|
||||
expect(await navigateResponse).toHaveResponse({
|
||||
snapshot: expect.stringContaining(`- generic [active] [ref=e1]: Hello, world!`),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@playwright/mcp",
|
||||
"version": "0.0.69",
|
||||
"version": "0.0.68",
|
||||
"description": "Playwright Tools for MCP",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -33,8 +33,8 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"playwright": "1.59.0-alpha-1774912654000",
|
||||
"playwright-core": "1.59.0-alpha-1774912654000"
|
||||
"playwright": "1.59.0-alpha-1774656214000",
|
||||
"playwright-core": "1.59.0-alpha-1774656214000"
|
||||
},
|
||||
"bin": {
|
||||
"playwright-mcp": "cli.js"
|
||||
|
||||
@@ -74,10 +74,10 @@ export const test = baseTest.extend<TestFixtures & TestOptions, WorkerFixtures>(
|
||||
},
|
||||
|
||||
startClient: async ({ mcpHeadless, mcpBrowser, mcpMode, mcpArgs }, use, testInfo) => {
|
||||
const configDir = path.dirname(test.info().config.configFile!);
|
||||
const clients: Client[] = [];
|
||||
|
||||
await use(async options => {
|
||||
const cwd = testInfo.outputPath();
|
||||
const args: string[] = mcpArgs ?? [];
|
||||
if (process.env.CI && process.platform === 'linux')
|
||||
args.push('--no-sandbox');
|
||||
@@ -90,7 +90,7 @@ export const test = baseTest.extend<TestFixtures & TestOptions, WorkerFixtures>(
|
||||
if (options?.config) {
|
||||
const configFile = testInfo.outputPath('config.json');
|
||||
await fs.promises.writeFile(configFile, JSON.stringify(options.config, null, 2));
|
||||
args.push(`--config=${path.relative(cwd, configFile)}`);
|
||||
args.push(`--config=${path.relative(configDir, configFile)}`);
|
||||
}
|
||||
|
||||
const client = new Client({ name: options?.clientName ?? 'test', version: '1.0.0' }, options?.roots ? { capabilities: { roots: {} } } : undefined);
|
||||
@@ -103,7 +103,7 @@ export const test = baseTest.extend<TestFixtures & TestOptions, WorkerFixtures>(
|
||||
};
|
||||
});
|
||||
}
|
||||
const { transport, stderr } = await createTransport(args, cwd, mcpMode, testInfo.outputPath('ms-playwright'), options?.extensionToken);
|
||||
const { transport, stderr } = await createTransport(args, mcpMode, testInfo.outputPath('ms-playwright'), options?.extensionToken);
|
||||
let stderrBuffer = '';
|
||||
stderr?.on('data', data => {
|
||||
if (process.env.PWMCP_DEBUG)
|
||||
@@ -182,14 +182,12 @@ export const test = baseTest.extend<TestFixtures & TestOptions, WorkerFixtures>(
|
||||
},
|
||||
});
|
||||
|
||||
async function createTransport(args: string[], cwd: string, mcpMode: TestOptions['mcpMode'], profilesDir: string, extensionToken?: string): Promise<{
|
||||
async function createTransport(args: string[], mcpMode: TestOptions['mcpMode'], profilesDir: string, extensionToken?: string): Promise<{
|
||||
transport: Transport,
|
||||
stderr: Stream | null,
|
||||
}> {
|
||||
if (mcpMode === 'docker') {
|
||||
const relCwd = path.relative(test.info().project.outputDir, cwd);
|
||||
const dockerCwd = path.posix.join('/app/test-results', relCwd.split(path.sep).join('/'));
|
||||
const dockerArgs = ['run', '--rm', '-i', '--network=host', '-v', `${test.info().project.outputDir}:/app/test-results`, '-w', dockerCwd];
|
||||
const dockerArgs = ['run', '--rm', '-i', '--network=host', '-v', `${test.info().project.outputDir}:/app/test-results`];
|
||||
const transport = new StdioClientTransport({
|
||||
command: 'docker',
|
||||
args: [...dockerArgs, 'playwright-mcp-dev:latest', ...args],
|
||||
@@ -203,7 +201,7 @@ async function createTransport(args: string[], cwd: string, mcpMode: TestOptions
|
||||
const transport = new StdioClientTransport({
|
||||
command: 'node',
|
||||
args: [path.join(__dirname, '../cli.js'), ...args],
|
||||
cwd,
|
||||
cwd: path.dirname(test.info().config.configFile!),
|
||||
stderr: 'pipe',
|
||||
env: {
|
||||
...process.env,
|
||||
@@ -224,7 +222,7 @@ type Response = Awaited<ReturnType<Client['callTool']>>;
|
||||
|
||||
export const expect = baseExpect.extend({
|
||||
toHaveResponse(response: Response, object: any) {
|
||||
const parsed = parseResponse(response, test.info().outputPath());
|
||||
const parsed = parseResponse(response);
|
||||
const isNot = this.isNot;
|
||||
try {
|
||||
if (isNot)
|
||||
@@ -248,7 +246,7 @@ export function formatOutput(output: string): string[] {
|
||||
return output.split('\n').map(line => line.replace(/^pw:mcp:test /, '').replace(/user data dir.*/, 'user data dir').trim()).filter(Boolean);
|
||||
}
|
||||
|
||||
function parseResponse(response: any, cwd: string) {
|
||||
function parseResponse(response: any) {
|
||||
const text = response.content[0].text;
|
||||
const sections = parseSections(text);
|
||||
|
||||
@@ -257,7 +255,7 @@ function parseResponse(response: any, cwd: string) {
|
||||
const code = sections.get('Ran Playwright code');
|
||||
const tabs = sections.get('Open tabs');
|
||||
const pageState = sections.get('Page state');
|
||||
const snapshotSection = sections.get('Snapshot');
|
||||
const snapshot = sections.get('Snapshot');
|
||||
const consoleMessages = sections.get('New console messages');
|
||||
const modalState = sections.get('Modal state');
|
||||
const downloads = sections.get('Downloads');
|
||||
@@ -265,19 +263,6 @@ function parseResponse(response: any, cwd: string) {
|
||||
const isError = response.isError;
|
||||
const attachments = response.content.slice(1);
|
||||
|
||||
let snapshot: string | undefined;
|
||||
if (snapshotSection) {
|
||||
const match = snapshotSection.match(/\[Snapshot\]\(([^)]+)\)/);
|
||||
if (match) {
|
||||
try {
|
||||
snapshot = fs.readFileSync(path.resolve(cwd, match[1]), 'utf-8');
|
||||
} catch {
|
||||
}
|
||||
} else {
|
||||
snapshot = snapshotSection.replace(/^```yaml\n?/, '').replace(/\n?```$/, '');
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
error,
|
||||
result,
|
||||
|
||||
Reference in New Issue
Block a user