@playwright/mcp 0.0.10 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/program.js CHANGED
@@ -19,9 +19,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
19
19
  };
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
21
  const http_1 = __importDefault(require("http"));
22
- const fs_1 = __importDefault(require("fs"));
23
- const os_1 = __importDefault(require("os"));
24
- const path_1 = __importDefault(require("path"));
25
22
  const commander_1 = require("commander");
26
23
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
27
24
  const sse_js_1 = require("@modelcontextprotocol/sdk/server/sse.js");
@@ -41,43 +38,11 @@ commander_1.program
41
38
  .option('--user-data-dir <path>', 'Path to the user data directory')
42
39
  .option('--vision', 'Run server that uses screenshots (Aria snapshots are used by default)')
43
40
  .action(async (options) => {
44
- let browserName;
45
- let channel;
46
- switch (options.browser) {
47
- case 'chrome':
48
- case 'chrome-beta':
49
- case 'chrome-canary':
50
- case 'chrome-dev':
51
- case 'msedge':
52
- case 'msedge-beta':
53
- case 'msedge-canary':
54
- case 'msedge-dev':
55
- browserName = 'chromium';
56
- channel = options.browser;
57
- break;
58
- case 'chromium':
59
- browserName = 'chromium';
60
- break;
61
- case 'firefox':
62
- browserName = 'firefox';
63
- break;
64
- case 'webkit':
65
- browserName = 'webkit';
66
- break;
67
- default:
68
- browserName = 'chromium';
69
- channel = 'chrome';
70
- }
71
- const launchOptions = {
72
- headless: !!options.headless,
73
- channel,
74
- executablePath: options.executablePath,
75
- };
76
- const userDataDir = options.userDataDir ?? await createUserDataDir(browserName);
77
41
  const serverList = new server_1.ServerList(() => (0, index_1.createServer)({
78
- browserName,
79
- userDataDir,
80
- launchOptions,
42
+ browser: options.browser,
43
+ userDataDir: options.userDataDir,
44
+ headless: options.headless,
45
+ executablePath: options.executablePath,
81
46
  vision: !!options.vision,
82
47
  cdpEndpoint: options.cdpEndpoint,
83
48
  capabilities: options.caps?.split(',').map((c) => c.trim()),
@@ -92,27 +57,16 @@ commander_1.program
92
57
  }
93
58
  });
94
59
  function setupExitWatchdog(serverList) {
95
- process.stdin.on('close', async () => {
60
+ const handleExit = async () => {
96
61
  setTimeout(() => process.exit(0), 15000);
97
62
  await serverList.closeAll();
98
63
  process.exit(0);
99
- });
64
+ };
65
+ process.stdin.on('close', handleExit);
66
+ process.on('SIGINT', handleExit);
67
+ process.on('SIGTERM', handleExit);
100
68
  }
101
69
  commander_1.program.parse(process.argv);
102
- async function createUserDataDir(browserName) {
103
- let cacheDirectory;
104
- if (process.platform === 'linux')
105
- cacheDirectory = process.env.XDG_CACHE_HOME || path_1.default.join(os_1.default.homedir(), '.cache');
106
- else if (process.platform === 'darwin')
107
- cacheDirectory = path_1.default.join(os_1.default.homedir(), 'Library', 'Caches');
108
- else if (process.platform === 'win32')
109
- cacheDirectory = process.env.LOCALAPPDATA || path_1.default.join(os_1.default.homedir(), 'AppData', 'Local');
110
- else
111
- throw new Error('Unsupported platform: ' + process.platform);
112
- const result = path_1.default.join(cacheDirectory, 'ms-playwright', `mcp-${browserName}-profile`);
113
- await fs_1.default.promises.mkdir(result, { recursive: true });
114
- return result;
115
- }
116
70
  async function startSSEServer(port, serverList) {
117
71
  const sessions = new Map();
118
72
  const httpServer = http_1.default.createServer(async (req, res) => {
package/lib/server.js CHANGED
@@ -22,7 +22,7 @@ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
22
22
  const context_1 = require("./context");
23
23
  function createServerWithTools(options) {
24
24
  const { name, version, tools, resources } = options;
25
- const context = new context_1.Context(options);
25
+ const context = new context_1.Context(tools, options);
26
26
  const server = new index_js_1.Server({ name, version }, {
27
27
  capabilities: {
28
28
  tools: {},
@@ -43,9 +43,20 @@ function createServerWithTools(options) {
43
43
  isError: true,
44
44
  };
45
45
  }
46
+ const modalStates = context.modalStates().map(state => state.type);
47
+ if ((tool.clearsModalState && !modalStates.includes(tool.clearsModalState)) ||
48
+ (!tool.clearsModalState && modalStates.length)) {
49
+ const text = [
50
+ `Tool "${request.params.name}" does not handle the modal state.`,
51
+ ...context.modalStatesMarkdown(),
52
+ ].join('\n');
53
+ return {
54
+ content: [{ type: 'text', text }],
55
+ isError: true,
56
+ };
57
+ }
46
58
  try {
47
- const result = await tool.handle(context, request.params.arguments);
48
- return result;
59
+ return await context.run(tool, request.params.arguments);
49
60
  }
50
61
  catch (error) {
51
62
  return {
@@ -75,7 +86,7 @@ class ServerList {
75
86
  this._serverFactory = serverFactory;
76
87
  }
77
88
  async create() {
78
- const server = this._serverFactory();
89
+ const server = await this._serverFactory();
79
90
  this._servers.push(server);
80
91
  return server;
81
92
  }
@@ -20,7 +20,7 @@ const zod_to_json_schema_1 = require("zod-to-json-schema");
20
20
  const waitSchema = zod_1.z.object({
21
21
  time: zod_1.z.number().describe('The time to wait in seconds'),
22
22
  });
23
- const wait = {
23
+ const wait = captureSnapshot => ({
24
24
  capability: 'wait',
25
25
  schema: {
26
26
  name: 'browser_wait',
@@ -31,13 +31,12 @@ const wait = {
31
31
  const validatedParams = waitSchema.parse(params);
32
32
  await new Promise(f => setTimeout(f, Math.min(10000, validatedParams.time * 1000)));
33
33
  return {
34
- content: [{
35
- type: 'text',
36
- text: `Waited for ${validatedParams.time} seconds`,
37
- }],
34
+ code: [`// Waited for ${validatedParams.time} seconds`],
35
+ captureSnapshot,
36
+ waitForNetwork: false,
38
37
  };
39
38
  },
40
- };
39
+ });
41
40
  const closeSchema = zod_1.z.object({});
42
41
  const close = {
43
42
  capability: 'core',
@@ -49,14 +48,43 @@ const close = {
49
48
  handle: async (context) => {
50
49
  await context.close();
51
50
  return {
52
- content: [{
53
- type: 'text',
54
- text: `Page closed`,
55
- }],
51
+ code: [`// Internal to close the page`],
52
+ captureSnapshot: false,
53
+ waitForNetwork: false,
56
54
  };
57
55
  },
58
56
  };
59
- exports.default = [
57
+ const resizeSchema = zod_1.z.object({
58
+ width: zod_1.z.number().describe('Width of the browser window'),
59
+ height: zod_1.z.number().describe('Height of the browser window'),
60
+ });
61
+ const resize = captureSnapshot => ({
62
+ capability: 'core',
63
+ schema: {
64
+ name: 'browser_resize',
65
+ description: 'Resize the browser window',
66
+ inputSchema: (0, zod_to_json_schema_1.zodToJsonSchema)(resizeSchema),
67
+ },
68
+ handle: async (context, params) => {
69
+ const validatedParams = resizeSchema.parse(params);
70
+ const tab = context.currentTabOrDie();
71
+ const code = [
72
+ `// Resize browser window to ${validatedParams.width}x${validatedParams.height}`,
73
+ `await page.setViewportSize({ width: ${validatedParams.width}, height: ${validatedParams.height} });`
74
+ ];
75
+ const action = async () => {
76
+ await tab.page.setViewportSize({ width: validatedParams.width, height: validatedParams.height });
77
+ };
78
+ return {
79
+ code,
80
+ action,
81
+ captureSnapshot,
82
+ waitForNetwork: true
83
+ };
84
+ },
85
+ });
86
+ exports.default = (captureSnapshot) => [
60
87
  close,
61
- wait,
88
+ wait(captureSnapshot),
89
+ resize(captureSnapshot)
62
90
  ];
@@ -15,20 +15,31 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.console = void 0;
19
- exports.console = {
18
+ const zod_1 = require("zod");
19
+ const zod_to_json_schema_1 = require("zod-to-json-schema");
20
+ const consoleSchema = zod_1.z.object({});
21
+ const console = {
22
+ capability: 'core',
20
23
  schema: {
21
- uri: 'browser://console',
22
- name: 'Page console',
23
- mimeType: 'text/plain',
24
+ name: 'browser_console_messages',
25
+ description: 'Returns all console messages',
26
+ inputSchema: (0, zod_to_json_schema_1.zodToJsonSchema)(consoleSchema),
24
27
  },
25
- read: async (context, uri) => {
26
- const messages = await context.currentTab().console();
28
+ handle: async (context) => {
29
+ const messages = await context.currentTabOrDie().console();
27
30
  const log = messages.map(message => `[${message.type().toUpperCase()}] ${message.text()}`).join('\n');
28
- return [{
29
- uri,
30
- mimeType: 'text/plain',
31
- text: log
32
- }];
31
+ return {
32
+ code: [`// <internal code to get console messages>`],
33
+ action: async () => {
34
+ return {
35
+ content: [{ type: 'text', text: log }]
36
+ };
37
+ },
38
+ captureSnapshot: false,
39
+ waitForNetwork: false,
40
+ };
33
41
  },
34
42
  };
43
+ exports.default = [
44
+ console,
45
+ ];
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Microsoft Corporation.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ const zod_1 = require("zod");
19
+ const zod_to_json_schema_1 = require("zod-to-json-schema");
20
+ const handleDialogSchema = zod_1.z.object({
21
+ accept: zod_1.z.boolean().describe('Whether to accept the dialog.'),
22
+ promptText: zod_1.z.string().optional().describe('The text of the prompt in case of a prompt dialog.'),
23
+ });
24
+ const handleDialog = captureSnapshot => ({
25
+ capability: 'core',
26
+ schema: {
27
+ name: 'browser_handle_dialog',
28
+ description: 'Handle a dialog',
29
+ inputSchema: (0, zod_to_json_schema_1.zodToJsonSchema)(handleDialogSchema),
30
+ },
31
+ handle: async (context, params) => {
32
+ const validatedParams = handleDialogSchema.parse(params);
33
+ const dialogState = context.modalStates().find(state => state.type === 'dialog');
34
+ if (!dialogState)
35
+ throw new Error('No dialog visible');
36
+ if (validatedParams.accept)
37
+ await dialogState.dialog.accept(validatedParams.promptText);
38
+ else
39
+ await dialogState.dialog.dismiss();
40
+ context.clearModalState(dialogState);
41
+ const code = [
42
+ `// <internal code to handle "${dialogState.dialog.type()}" dialog>`,
43
+ ];
44
+ return {
45
+ code,
46
+ captureSnapshot,
47
+ waitForNetwork: false,
48
+ };
49
+ },
50
+ clearsModalState: 'dialog',
51
+ });
52
+ exports.default = (captureSnapshot) => [
53
+ handleDialog(captureSnapshot),
54
+ ];
@@ -29,15 +29,24 @@ const uploadFile = captureSnapshot => ({
29
29
  },
30
30
  handle: async (context, params) => {
31
31
  const validatedParams = uploadFileSchema.parse(params);
32
- const tab = context.currentTab();
33
- return await tab.runAndWait(async () => {
34
- await tab.submitFileChooser(validatedParams.paths);
35
- }, {
36
- status: `Chose files ${validatedParams.paths.join(', ')}`,
32
+ const modalState = context.modalStates().find(state => state.type === 'fileChooser');
33
+ if (!modalState)
34
+ throw new Error('No file chooser visible');
35
+ const code = [
36
+ `// <internal code to chose files ${validatedParams.paths.join(', ')}`,
37
+ ];
38
+ const action = async () => {
39
+ await modalState.fileChooser.setFiles(validatedParams.paths);
40
+ context.clearModalState(modalState);
41
+ };
42
+ return {
43
+ code,
44
+ action,
37
45
  captureSnapshot,
38
- noClearFileChooser: true,
39
- });
46
+ waitForNetwork: true,
47
+ };
40
48
  },
49
+ clearsModalState: 'fileChooser',
41
50
  });
42
51
  exports.default = (captureSnapshot) => [
43
52
  uploadFile(captureSnapshot),
@@ -47,10 +47,9 @@ const install = {
47
47
  });
48
48
  });
49
49
  return {
50
- content: [{
51
- type: 'text',
52
- text: `Browser ${channel} installed`,
53
- }],
50
+ code: [`// Browser ${channel} installed`],
51
+ captureSnapshot: false,
52
+ waitForNetwork: false,
54
53
  };
55
54
  },
56
55
  };
@@ -32,12 +32,18 @@ const pressKey = captureSnapshot => ({
32
32
  },
33
33
  handle: async (context, params) => {
34
34
  const validatedParams = pressKeySchema.parse(params);
35
- return await context.currentTab().runAndWait(async (tab) => {
36
- await tab.page.keyboard.press(validatedParams.key);
37
- }, {
38
- status: `Pressed key ${validatedParams.key}`,
35
+ const tab = context.currentTabOrDie();
36
+ const code = [
37
+ `// Press ${validatedParams.key}`,
38
+ `await page.keyboard.press('${validatedParams.key}');`,
39
+ ];
40
+ const action = () => tab.page.keyboard.press(validatedParams.key);
41
+ return {
42
+ code,
43
+ action,
39
44
  captureSnapshot,
40
- });
45
+ waitForNetwork: true
46
+ };
41
47
  },
42
48
  });
43
49
  exports.default = (captureSnapshot) => [
@@ -29,17 +29,21 @@ const navigate = captureSnapshot => ({
29
29
  },
30
30
  handle: async (context, params) => {
31
31
  const validatedParams = navigateSchema.parse(params);
32
- const currentTab = await context.ensureTab();
33
- return await currentTab.run(async (tab) => {
34
- await tab.navigate(validatedParams.url);
35
- }, {
36
- status: `Navigated to ${validatedParams.url}`,
32
+ const tab = await context.ensureTab();
33
+ await tab.navigate(validatedParams.url);
34
+ const code = [
35
+ `// Navigate to ${validatedParams.url}`,
36
+ `await page.goto('${validatedParams.url}');`,
37
+ ];
38
+ return {
39
+ code,
37
40
  captureSnapshot,
38
- });
41
+ waitForNetwork: false,
42
+ };
39
43
  },
40
44
  });
41
45
  const goBackSchema = zod_1.z.object({});
42
- const goBack = snapshot => ({
46
+ const goBack = captureSnapshot => ({
43
47
  capability: 'history',
44
48
  schema: {
45
49
  name: 'browser_navigate_back',
@@ -47,16 +51,21 @@ const goBack = snapshot => ({
47
51
  inputSchema: (0, zod_to_json_schema_1.zodToJsonSchema)(goBackSchema),
48
52
  },
49
53
  handle: async (context) => {
50
- return await context.currentTab().runAndWait(async (tab) => {
51
- await tab.page.goBack();
52
- }, {
53
- status: 'Navigated back',
54
- captureSnapshot: snapshot,
55
- });
54
+ const tab = await context.ensureTab();
55
+ await tab.page.goBack();
56
+ const code = [
57
+ `// Navigate back`,
58
+ `await page.goBack();`,
59
+ ];
60
+ return {
61
+ code,
62
+ captureSnapshot,
63
+ waitForNetwork: false,
64
+ };
56
65
  },
57
66
  });
58
67
  const goForwardSchema = zod_1.z.object({});
59
- const goForward = snapshot => ({
68
+ const goForward = captureSnapshot => ({
60
69
  capability: 'history',
61
70
  schema: {
62
71
  name: 'browser_navigate_forward',
@@ -64,12 +73,17 @@ const goForward = snapshot => ({
64
73
  inputSchema: (0, zod_to_json_schema_1.zodToJsonSchema)(goForwardSchema),
65
74
  },
66
75
  handle: async (context) => {
67
- return await context.currentTab().runAndWait(async (tab) => {
68
- await tab.page.goForward();
69
- }, {
70
- status: 'Navigated forward',
71
- captureSnapshot: snapshot,
72
- });
76
+ const tab = context.currentTabOrDie();
77
+ await tab.page.goForward();
78
+ const code = [
79
+ `// Navigate forward`,
80
+ `await page.goForward();`,
81
+ ];
82
+ return {
83
+ code,
84
+ captureSnapshot,
85
+ waitForNetwork: false,
86
+ };
73
87
  },
74
88
  });
75
89
  exports.default = (captureSnapshot) => [
package/lib/tools/pdf.js CHANGED
@@ -14,6 +14,39 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
17
50
  var __importDefault = (this && this.__importDefault) || function (mod) {
18
51
  return (mod && mod.__esModule) ? mod : { "default": mod };
19
52
  };
@@ -23,6 +56,7 @@ const path_1 = __importDefault(require("path"));
23
56
  const zod_1 = require("zod");
24
57
  const zod_to_json_schema_1 = require("zod-to-json-schema");
25
58
  const utils_1 = require("./utils");
59
+ const javascript = __importStar(require("../javascript"));
26
60
  const pdfSchema = zod_1.z.object({});
27
61
  const pdf = {
28
62
  capability: 'pdf',
@@ -32,14 +66,17 @@ const pdf = {
32
66
  inputSchema: (0, zod_to_json_schema_1.zodToJsonSchema)(pdfSchema),
33
67
  },
34
68
  handle: async (context) => {
35
- const tab = context.currentTab();
69
+ const tab = context.currentTabOrDie();
36
70
  const fileName = path_1.default.join(os_1.default.tmpdir(), (0, utils_1.sanitizeForFilePath)(`page-${new Date().toISOString()}`)) + '.pdf';
37
- await tab.page.pdf({ path: fileName });
71
+ const code = [
72
+ `// Save page as ${fileName}`,
73
+ `await page.pdf(${javascript.formatObject({ path: fileName })});`,
74
+ ];
38
75
  return {
39
- content: [{
40
- type: 'text',
41
- text: `Saved as ${fileName}`,
42
- }],
76
+ code,
77
+ action: async () => tab.page.pdf({ path: fileName }).then(() => { }),
78
+ captureSnapshot: false,
79
+ waitForNetwork: false,
43
80
  };
44
81
  },
45
82
  };