@szymonrybczak/playwright-mcp 0.0.16 → 0.0.17

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
4
4
 
5
- const version = "0.0.16";
5
+ const version = "0.0.17";
6
6
  const json = {
7
7
  version};
8
8
 
@@ -31,25 +31,14 @@ const playwright__namespace = /*#__PURE__*/_interopNamespaceDefault(playwright);
31
31
 
32
32
  const testDebug = debugStub.default("pw:mcp:test");
33
33
  function contextFactory(browserConfig) {
34
- console.log("[contextFactory] START");
35
- console.log("[contextFactory] browserConfig:", JSON.stringify(browserConfig, null, 2));
36
- if (browserConfig.remoteEndpoint) {
37
- console.log("[contextFactory] Using RemoteContextFactory");
34
+ if (browserConfig.remoteEndpoint)
38
35
  return new RemoteContextFactory(browserConfig);
39
- }
40
- if (browserConfig.cdpEndpoint) {
41
- console.log("[contextFactory] Using CdpContextFactory");
36
+ if (browserConfig.cdpEndpoint)
42
37
  return new CdpContextFactory(browserConfig);
43
- }
44
- if (browserConfig.isolated) {
45
- console.log("[contextFactory] Using IsolatedContextFactory");
38
+ if (browserConfig.isolated)
46
39
  return new IsolatedContextFactory(browserConfig);
47
- }
48
- if (browserConfig.browserAgent) {
49
- console.log("[contextFactory] Using BrowserServerContextFactory");
40
+ if (browserConfig.browserAgent)
50
41
  return new BrowserServerContextFactory(browserConfig);
51
- }
52
- console.log("[contextFactory] Using PersistentContextFactory");
53
42
  return new PersistentContextFactory(browserConfig);
54
43
  }
55
44
  class BaseContextFactory {
@@ -59,26 +48,17 @@ class BaseContextFactory {
59
48
  constructor(name, browserConfig) {
60
49
  this.name = name;
61
50
  this.browserConfig = browserConfig;
62
- console.log(`[BaseContextFactory] Created factory: ${name}`);
63
51
  }
64
52
  async _obtainBrowser() {
65
- console.log(`[BaseContextFactory._obtainBrowser] START (${this.name})`);
66
- console.log(`[BaseContextFactory._obtainBrowser] _browserPromise exists:`, !!this._browserPromise);
67
- if (this._browserPromise) {
68
- console.log(`[BaseContextFactory._obtainBrowser] Returning existing promise`);
53
+ if (this._browserPromise)
69
54
  return this._browserPromise;
70
- }
71
55
  testDebug(`obtain browser (${this.name})`);
72
- console.log(`[BaseContextFactory._obtainBrowser] Calling _doObtainBrowser...`);
73
56
  this._browserPromise = this._doObtainBrowser();
74
57
  void this._browserPromise.then((browser) => {
75
- console.log(`[BaseContextFactory._obtainBrowser] Browser obtained successfully`);
76
58
  browser.on("disconnected", () => {
77
- console.log(`[BaseContextFactory._obtainBrowser] Browser disconnected`);
78
59
  this._browserPromise = void 0;
79
60
  });
80
- }).catch((err) => {
81
- console.log(`[BaseContextFactory._obtainBrowser] Browser promise rejected:`, err.message);
61
+ }).catch(() => {
82
62
  this._browserPromise = void 0;
83
63
  });
84
64
  return this._browserPromise;
@@ -87,22 +67,15 @@ class BaseContextFactory {
87
67
  throw new Error("Not implemented");
88
68
  }
89
69
  async createContext() {
90
- console.log(`[BaseContextFactory.createContext] START (${this.name})`);
91
70
  testDebug(`create browser context (${this.name})`);
92
- console.log(`[BaseContextFactory.createContext] Calling _obtainBrowser...`);
93
71
  const browser = await this._obtainBrowser();
94
- console.log(`[BaseContextFactory.createContext] Got browser`);
95
- console.log(`[BaseContextFactory.createContext] Calling _doCreateContext...`);
96
72
  const browserContext = await this._doCreateContext(browser);
97
- console.log(`[BaseContextFactory.createContext] Got browserContext`);
98
- console.log(`[BaseContextFactory.createContext] END`);
99
73
  return { browserContext, close: () => this._closeBrowserContext(browserContext, browser) };
100
74
  }
101
75
  async _doCreateContext(browser) {
102
76
  throw new Error("Not implemented");
103
77
  }
104
78
  async _closeBrowserContext(browserContext, browser) {
105
- console.log(`[BaseContextFactory._closeBrowserContext] START (${this.name})`);
106
79
  testDebug(`close browser context (${this.name})`);
107
80
  if (browser.contexts().length === 1)
108
81
  this._browserPromise = void 0;
@@ -113,7 +86,6 @@ class BaseContextFactory {
113
86
  await browser.close().catch(() => {
114
87
  });
115
88
  }
116
- console.log(`[BaseContextFactory._closeBrowserContext] END`);
117
89
  }
118
90
  }
119
91
  class IsolatedContextFactory extends BaseContextFactory {
@@ -142,106 +114,10 @@ class CdpContextFactory extends BaseContextFactory {
142
114
  super("cdp", browserConfig);
143
115
  }
144
116
  async _doObtainBrowser() {
145
- console.log("[CdpContextFactory._doObtainBrowser] START");
146
- console.log("[CdpContextFactory._doObtainBrowser] cdpEndpoint:", this.browserConfig.cdpEndpoint);
147
- console.log("[CdpContextFactory._doObtainBrowser] cdpEndpoint type:", typeof this.browserConfig.cdpEndpoint);
148
- const endpoint = this.browserConfig.cdpEndpoint;
149
- const isCloudfareFetcher = typeof endpoint !== "string";
150
- console.log("[CdpContextFactory._doObtainBrowser] isCloudfareFetcher:", isCloudfareFetcher);
151
- try {
152
- let browser;
153
- if (isCloudfareFetcher) {
154
- const fetcher = endpoint;
155
- console.log("[CdpContextFactory._doObtainBrowser] Fetcher detected, using connect() method...");
156
- const connectUrls = [
157
- "https://cloudflare",
158
- "https://cloudflare/",
159
- "wss://cloudflare"
160
- ];
161
- let socket = null;
162
- let successUrl = "";
163
- for (const url of connectUrls) {
164
- try {
165
- console.log(`[CdpContextFactory._doObtainBrowser] Trying fetcher.connect("${url}")...`);
166
- const connection = await fetcher.connect(url);
167
- console.log("[CdpContextFactory._doObtainBrowser] connect() returned:", typeof connection);
168
- console.log("[CdpContextFactory._doObtainBrowser] connection keys:", Object.keys(connection || {}));
169
- console.log("[CdpContextFactory._doObtainBrowser] connection.socket:", typeof connection?.socket);
170
- if (connection && typeof connection === "object") {
171
- if ("accept" in connection && typeof connection.accept === "function") {
172
- console.log("[CdpContextFactory._doObtainBrowser] Connection has accept(), calling it...");
173
- connection.accept();
174
- }
175
- if ("send" in connection && "close" in connection) {
176
- socket = connection;
177
- successUrl = url;
178
- console.log("[CdpContextFactory._doObtainBrowser] Got WebSocket-like object directly");
179
- break;
180
- }
181
- if ("socket" in connection) {
182
- socket = connection.socket;
183
- successUrl = url;
184
- console.log("[CdpContextFactory._doObtainBrowser] Got socket from connection.socket");
185
- break;
186
- }
187
- if ("webSocket" in connection) {
188
- socket = connection.webSocket;
189
- if (socket && "accept" in socket) {
190
- socket.accept();
191
- }
192
- successUrl = url;
193
- console.log("[CdpContextFactory._doObtainBrowser] Got socket from connection.webSocket");
194
- break;
195
- }
196
- console.log("[CdpContextFactory._doObtainBrowser] Connection structure:", JSON.stringify(Object.getOwnPropertyNames(connection)));
197
- const proto = Object.getPrototypeOf(connection);
198
- if (proto) {
199
- console.log("[CdpContextFactory._doObtainBrowser] Connection prototype:", Object.getOwnPropertyNames(proto));
200
- }
201
- }
202
- } catch (e) {
203
- console.log(`[CdpContextFactory._doObtainBrowser] connect("${url}") error:`, e.message);
204
- }
205
- }
206
- if (!socket) {
207
- throw new Error("Could not establish WebSocket via Fetcher.connect(). Check logs above.");
208
- }
209
- console.log(`[CdpContextFactory._doObtainBrowser] WebSocket obtained from ${successUrl}`);
210
- console.log("[CdpContextFactory._doObtainBrowser] Socket type:", typeof socket);
211
- console.log("[CdpContextFactory._doObtainBrowser] Socket readyState:", socket.readyState);
212
- console.log("[CdpContextFactory._doObtainBrowser] Socket methods:", Object.getOwnPropertyNames(Object.getPrototypeOf(socket)));
213
- console.log("[CdpContextFactory._doObtainBrowser] playwright.chromium methods:", Object.keys(playwright__namespace.chromium));
214
- if (typeof playwright__namespace.chromium.connectOverCDPWithSocket === "function") {
215
- console.log("[CdpContextFactory._doObtainBrowser] Using connectOverCDPWithSocket...");
216
- browser = await playwright__namespace.chromium.connectOverCDPWithSocket(socket);
217
- } else if (typeof playwright__namespace.chromium.connectWithWebSocket === "function") {
218
- console.log("[CdpContextFactory._doObtainBrowser] Using connectWithWebSocket...");
219
- browser = await playwright__namespace.chromium.connectWithWebSocket(socket);
220
- } else {
221
- throw new Error(`WebSocket connection established but patchright needs a method to accept raw WebSocket for CDP. Available methods: ${Object.keys(playwright__namespace.chromium).join(", ")}`);
222
- }
223
- } else {
224
- console.log("[CdpContextFactory._doObtainBrowser] Using chromium.connectOverCDP() for CDP URL...");
225
- browser = await playwright__namespace.chromium.connectOverCDP(endpoint);
226
- }
227
- console.log("[CdpContextFactory._doObtainBrowser] Connection completed");
228
- return browser;
229
- } catch (err) {
230
- console.log("[CdpContextFactory._doObtainBrowser] ERROR:", err.message, err.stack);
231
- throw err;
232
- }
117
+ return playwright__namespace.chromium.connectOverCDP(this.browserConfig.cdpEndpoint);
233
118
  }
234
119
  async _doCreateContext(browser) {
235
- console.log("[CdpContextFactory._doCreateContext] START");
236
- console.log("[CdpContextFactory._doCreateContext] isolated:", this.browserConfig.isolated);
237
- try {
238
- const ctx = this.browserConfig.isolated ? await browser.newContext() : browser.contexts()[0];
239
- console.log("[CdpContextFactory._doCreateContext] Got context");
240
- return ctx;
241
- } catch (err) {
242
- console.log("[CdpContextFactory._doCreateContext] ERROR:", err.message, err.stack);
243
- throw err;
244
- }
120
+ return this.browserConfig.isolated ? await browser.newContext() : browser.contexts()[0];
245
121
  }
246
122
  }
247
123
  class RemoteContextFactory extends BaseContextFactory {
@@ -70,29 +70,9 @@ class Context {
70
70
  await this._currentTab.page.bringToFront();
71
71
  }
72
72
  async ensureTab() {
73
- console.log("[Context.ensureTab] START");
74
- console.log("[Context.ensureTab] _currentTab exists:", !!this._currentTab);
75
- console.log("[Context.ensureTab] Calling _ensureBrowserContext()...");
76
- let browserContext;
77
- try {
78
- const result = await this._ensureBrowserContext();
79
- browserContext = result.browserContext;
80
- console.log("[Context.ensureTab] _ensureBrowserContext() completed");
81
- } catch (err) {
82
- console.log("[Context.ensureTab] ERROR in _ensureBrowserContext():", err.message, err.stack);
83
- throw err;
84
- }
85
- if (!this._currentTab) {
86
- console.log("[Context.ensureTab] No current tab, creating new page...");
87
- try {
88
- await browserContext.newPage();
89
- console.log("[Context.ensureTab] New page created");
90
- } catch (err) {
91
- console.log("[Context.ensureTab] ERROR creating new page:", err.message, err.stack);
92
- throw err;
93
- }
94
- }
95
- console.log("[Context.ensureTab] END, returning tab");
73
+ const { browserContext } = await this._ensureBrowserContext();
74
+ if (!this._currentTab)
75
+ await browserContext.newPage();
96
76
  return this._currentTab;
97
77
  }
98
78
  async listTabsMarkdown() {
@@ -269,51 +249,29 @@ ${code.join("\n")}
269
249
  }
270
250
  }
271
251
  _ensureBrowserContext() {
272
- console.log("[Context._ensureBrowserContext] START");
273
- console.log("[Context._ensureBrowserContext] _browserContextPromise exists:", !!this._browserContextPromise);
274
252
  if (!this._browserContextPromise) {
275
- console.log("[Context._ensureBrowserContext] Creating new browser context promise...");
276
253
  this._browserContextPromise = this._setupBrowserContext();
277
- this._browserContextPromise.catch((err) => {
278
- console.log("[Context._ensureBrowserContext] Promise rejected:", err.message);
254
+ this._browserContextPromise.catch(() => {
279
255
  this._browserContextPromise = void 0;
280
256
  });
281
257
  }
282
- console.log("[Context._ensureBrowserContext] END, returning promise");
283
258
  return this._browserContextPromise;
284
259
  }
285
260
  async _setupBrowserContext() {
286
- console.log("[Context._setupBrowserContext] START");
287
- console.log("[Context._setupBrowserContext] Calling _browserContextFactory.createContext()...");
288
- let result;
289
- try {
290
- result = await this._browserContextFactory.createContext();
291
- console.log("[Context._setupBrowserContext] createContext() completed");
292
- } catch (err) {
293
- console.log("[Context._setupBrowserContext] ERROR in createContext():", err.message, err.stack);
294
- throw err;
295
- }
261
+ const result = await this._browserContextFactory.createContext();
296
262
  const { browserContext } = result;
297
- console.log("[Context._setupBrowserContext] Got browserContext");
298
- console.log("[Context._setupBrowserContext] Setting up request interception...");
299
263
  await this._setupRequestInterception(browserContext);
300
- console.log("[Context._setupBrowserContext] Request interception done");
301
- console.log("[Context._setupBrowserContext] Processing existing pages...");
302
264
  for (const page of browserContext.pages())
303
265
  this._onPageCreated(page);
304
266
  browserContext.on("page", (page) => this._onPageCreated(page));
305
- console.log("[Context._setupBrowserContext] Pages processed");
306
267
  if (this.config.saveTrace) {
307
- console.log("[Context._setupBrowserContext] Starting tracing...");
308
268
  await browserContext.tracing.start({
309
269
  name: "trace",
310
270
  screenshots: false,
311
271
  snapshots: true,
312
272
  sources: false
313
273
  });
314
- console.log("[Context._setupBrowserContext] Tracing started");
315
274
  }
316
- console.log("[Context._setupBrowserContext] END");
317
275
  return result;
318
276
  }
319
277
  }
@@ -50,40 +50,66 @@ class Tab {
50
50
  }));
51
51
  }
52
52
  async navigate(url) {
53
- console.log("[Tab.navigate] START, url:", url);
54
- console.log("[Tab.navigate] Clearing collected artifacts...");
53
+ const navStartTime = Date.now();
54
+ console.log(`[Tab.navigate] --------- TAB NAVIGATION START ---------`);
55
+ console.log(`[Tab.navigate] URL: ${url}`);
56
+ console.log(`[Tab.navigate] Current page URL: ${this.page.url()}`);
57
+ console.log(`[Tab.navigate] Page is closed: ${this.page.isClosed()}`);
58
+ console.log(`[Tab.navigate] Clearing collected artifacts...`);
55
59
  this._clearCollectedArtifacts();
56
- console.log("[Tab.navigate] Artifacts cleared");
57
- console.log("[Tab.navigate] Setting up download event listener...");
60
+ console.log(`[Tab.navigate] Artifacts cleared. Console messages: ${this._consoleMessages.length}, Requests: ${this._requests.size}`);
61
+ console.log(`[Tab.navigate] Setting up download event listener...`);
58
62
  const downloadEvent = utils.callOnPageNoTrace(this.page, (page) => page.waitForEvent("download").catch(() => {
59
63
  }));
60
- console.log("[Tab.navigate] Download event listener ready");
64
+ console.log(`[Tab.navigate] Download event listener set up`);
65
+ console.log(`[Tab.navigate] Calling page.goto() with waitUntil: 'domcontentloaded'`);
66
+ const gotoStartTime = Date.now();
61
67
  try {
62
- console.log("[Tab.navigate] Calling page.goto()...");
63
- await this.page.goto(url, { waitUntil: "domcontentloaded" });
64
- console.log("[Tab.navigate] page.goto() completed successfully");
68
+ const response = await this.page.goto(url, { waitUntil: "domcontentloaded" });
69
+ const gotoTime = Date.now() - gotoStartTime;
70
+ console.log(`[Tab.navigate] page.goto() completed in ${gotoTime}ms`);
71
+ if (response) {
72
+ console.log(`[Tab.navigate] Response status: ${response.status()}`);
73
+ console.log(`[Tab.navigate] Response URL: ${response.url()}`);
74
+ console.log(`[Tab.navigate] Response headers:`, JSON.stringify(response.headers(), null, 2));
75
+ } else {
76
+ console.log(`[Tab.navigate] Response is null (might be same-document navigation or cached)`);
77
+ }
65
78
  } catch (_e) {
66
79
  const e = _e;
67
- console.log("[Tab.navigate] page.goto() threw error:", e.message);
80
+ const gotoTime = Date.now() - gotoStartTime;
81
+ console.log(`[Tab.navigate] page.goto() threw after ${gotoTime}ms`);
82
+ console.log(`[Tab.navigate] Error name: ${e.name}`);
83
+ console.log(`[Tab.navigate] Error message: ${e.message}`);
68
84
  const mightBeDownload = e.message.includes("net::ERR_ABORTED") || e.message.includes("Download is starting");
85
+ console.log(`[Tab.navigate] Might be download: ${mightBeDownload}`);
69
86
  if (!mightBeDownload) {
70
- console.log("[Tab.navigate] Not a download, re-throwing error");
87
+ console.error(`[Tab.navigate] ERROR: Not a download, rethrowing error`);
88
+ console.error(`[Tab.navigate] Error stack:`, e.stack);
71
89
  throw e;
72
90
  }
73
- console.log("[Tab.navigate] Might be download, waiting for download event...");
91
+ console.log(`[Tab.navigate] Waiting for potential download event (up to 1000ms)...`);
92
+ const downloadWaitStart = Date.now();
74
93
  const download = await Promise.race([
75
94
  downloadEvent,
76
95
  new Promise((resolve) => setTimeout(resolve, 1e3))
77
96
  ]);
97
+ console.log(`[Tab.navigate] Download wait completed in ${Date.now() - downloadWaitStart}ms`);
98
+ console.log(`[Tab.navigate] Download detected: ${!!download}`);
78
99
  if (!download) {
79
- console.log("[Tab.navigate] No download received, re-throwing error");
100
+ console.error(`[Tab.navigate] ERROR: No download detected, rethrowing original error`);
80
101
  throw e;
81
102
  }
82
- console.log("[Tab.navigate] Download detected");
103
+ console.log(`[Tab.navigate] Download detected, continuing without error`);
83
104
  }
84
- console.log("[Tab.navigate] Waiting for load state...");
105
+ console.log(`[Tab.navigate] Waiting for 'load' state (timeout: 5000ms)...`);
106
+ const loadWaitStart = Date.now();
85
107
  await this.waitForLoadState("load", { timeout: 5e3 });
86
- console.log("[Tab.navigate] END");
108
+ console.log(`[Tab.navigate] 'load' state wait completed in ${Date.now() - loadWaitStart}ms`);
109
+ console.log(`[Tab.navigate] Final page URL: ${this.page.url()}`);
110
+ console.log(`[Tab.navigate] Final page title: ${await this.page.title().catch(() => "N/A")}`);
111
+ console.log(`[Tab.navigate] Total navigation time: ${Date.now() - navStartTime}ms`);
112
+ console.log(`[Tab.navigate] --------- TAB NAVIGATION END ---------`);
87
113
  }
88
114
  hasSnapshot() {
89
115
  return !!this._snapshot;
@@ -5,9 +5,6 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
5
5
  const zod = require('zod');
6
6
  const tool = require('./tool.js');
7
7
 
8
- const log = (msg, data) => {
9
- console.log(`[browser_navigate] ${msg}`, data !== void 0 ? JSON.stringify(data, null, 2) : "");
10
- };
11
8
  const navigate = (captureSnapshot) => tool.defineTool({
12
9
  capability: "core",
13
10
  schema: {
@@ -20,39 +17,49 @@ const navigate = (captureSnapshot) => tool.defineTool({
20
17
  type: "destructive"
21
18
  },
22
19
  handle: async (context, params) => {
23
- log("=== NAVIGATE HANDLER START ===");
24
- log("Params received:", params);
25
- log("captureSnapshot:", captureSnapshot);
26
- log("About to call context.ensureTab()...");
20
+ const startTime = Date.now();
21
+ console.log(`[browser_navigate] ========== NAVIGATION START ==========`);
22
+ console.log(`[browser_navigate] Target URL: ${params.url}`);
23
+ console.log(`[browser_navigate] Timestamp: ${(/* @__PURE__ */ new Date()).toISOString()}`);
24
+ console.log(`[browser_navigate] captureSnapshot setting: ${captureSnapshot}`);
25
+ console.log(`[browser_navigate] Ensuring tab is available...`);
26
+ const ensureTabStart = Date.now();
27
27
  let tab;
28
28
  try {
29
29
  tab = await context.ensureTab();
30
- log("context.ensureTab() completed successfully");
31
- log("Tab page URL:", tab.page?.url?.());
32
- } catch (err) {
33
- log("ERROR in context.ensureTab():", { message: err.message, stack: err.stack });
34
- throw err;
30
+ console.log(`[browser_navigate] Tab ensured successfully in ${Date.now() - ensureTabStart}ms`);
31
+ console.log(`[browser_navigate] Current page URL before navigation: ${tab.page.url()}`);
32
+ } catch (error) {
33
+ console.error(`[browser_navigate] ERROR: Failed to ensure tab after ${Date.now() - ensureTabStart}ms`);
34
+ console.error(`[browser_navigate] Error details:`, error);
35
+ throw error;
35
36
  }
36
- log(`About to call tab.navigate("${params.url}")...`);
37
+ console.log(`[browser_navigate] Starting navigation to: ${params.url}`);
38
+ const navigationStart = Date.now();
37
39
  try {
38
40
  await tab.navigate(params.url);
39
- log("tab.navigate() completed successfully");
40
- } catch (err) {
41
- log("ERROR in tab.navigate():", { message: err.message, stack: err.stack });
42
- throw err;
41
+ console.log(`[browser_navigate] Navigation completed successfully in ${Date.now() - navigationStart}ms`);
42
+ console.log(`[browser_navigate] Current page URL after navigation: ${tab.page.url()}`);
43
+ } catch (error) {
44
+ console.error(`[browser_navigate] ERROR: Navigation failed after ${Date.now() - navigationStart}ms`);
45
+ console.error(`[browser_navigate] Error type: ${error.constructor.name}`);
46
+ console.error(`[browser_navigate] Error message: ${error.message}`);
47
+ console.error(`[browser_navigate] Error stack:`, error.stack);
48
+ throw error;
43
49
  }
44
50
  const code = [
45
51
  `// Navigate to ${params.url}`,
46
52
  `await page.goto('${params.url}');`
47
53
  ];
48
- log("About to return result...");
49
- const result = {
54
+ const totalTime = Date.now() - startTime;
55
+ console.log(`[browser_navigate] Total execution time: ${totalTime}ms`);
56
+ console.log(`[browser_navigate] Returning with captureSnapshot: ${captureSnapshot}, waitForNetwork: false`);
57
+ console.log(`[browser_navigate] ========== NAVIGATION END ==========`);
58
+ return {
50
59
  code,
51
60
  captureSnapshot,
52
61
  waitForNetwork: false
53
62
  };
54
- log("=== NAVIGATE HANDLER END ===");
55
- return result;
56
63
  }
57
64
  });
58
65
  const goBack = (captureSnapshot) => tool.defineTool({
@@ -1,4 +1,4 @@
1
- const version = "0.0.16";
1
+ const version = "0.0.17";
2
2
  const json = {
3
3
  version};
4
4
 
@@ -8,25 +8,14 @@ import { userDataDir } from './fileUtils.js';
8
8
 
9
9
  const testDebug = createDebug("pw:mcp:test");
10
10
  function contextFactory(browserConfig) {
11
- console.log("[contextFactory] START");
12
- console.log("[contextFactory] browserConfig:", JSON.stringify(browserConfig, null, 2));
13
- if (browserConfig.remoteEndpoint) {
14
- console.log("[contextFactory] Using RemoteContextFactory");
11
+ if (browserConfig.remoteEndpoint)
15
12
  return new RemoteContextFactory(browserConfig);
16
- }
17
- if (browserConfig.cdpEndpoint) {
18
- console.log("[contextFactory] Using CdpContextFactory");
13
+ if (browserConfig.cdpEndpoint)
19
14
  return new CdpContextFactory(browserConfig);
20
- }
21
- if (browserConfig.isolated) {
22
- console.log("[contextFactory] Using IsolatedContextFactory");
15
+ if (browserConfig.isolated)
23
16
  return new IsolatedContextFactory(browserConfig);
24
- }
25
- if (browserConfig.browserAgent) {
26
- console.log("[contextFactory] Using BrowserServerContextFactory");
17
+ if (browserConfig.browserAgent)
27
18
  return new BrowserServerContextFactory(browserConfig);
28
- }
29
- console.log("[contextFactory] Using PersistentContextFactory");
30
19
  return new PersistentContextFactory(browserConfig);
31
20
  }
32
21
  class BaseContextFactory {
@@ -36,26 +25,17 @@ class BaseContextFactory {
36
25
  constructor(name, browserConfig) {
37
26
  this.name = name;
38
27
  this.browserConfig = browserConfig;
39
- console.log(`[BaseContextFactory] Created factory: ${name}`);
40
28
  }
41
29
  async _obtainBrowser() {
42
- console.log(`[BaseContextFactory._obtainBrowser] START (${this.name})`);
43
- console.log(`[BaseContextFactory._obtainBrowser] _browserPromise exists:`, !!this._browserPromise);
44
- if (this._browserPromise) {
45
- console.log(`[BaseContextFactory._obtainBrowser] Returning existing promise`);
30
+ if (this._browserPromise)
46
31
  return this._browserPromise;
47
- }
48
32
  testDebug(`obtain browser (${this.name})`);
49
- console.log(`[BaseContextFactory._obtainBrowser] Calling _doObtainBrowser...`);
50
33
  this._browserPromise = this._doObtainBrowser();
51
34
  void this._browserPromise.then((browser) => {
52
- console.log(`[BaseContextFactory._obtainBrowser] Browser obtained successfully`);
53
35
  browser.on("disconnected", () => {
54
- console.log(`[BaseContextFactory._obtainBrowser] Browser disconnected`);
55
36
  this._browserPromise = void 0;
56
37
  });
57
- }).catch((err) => {
58
- console.log(`[BaseContextFactory._obtainBrowser] Browser promise rejected:`, err.message);
38
+ }).catch(() => {
59
39
  this._browserPromise = void 0;
60
40
  });
61
41
  return this._browserPromise;
@@ -64,22 +44,15 @@ class BaseContextFactory {
64
44
  throw new Error("Not implemented");
65
45
  }
66
46
  async createContext() {
67
- console.log(`[BaseContextFactory.createContext] START (${this.name})`);
68
47
  testDebug(`create browser context (${this.name})`);
69
- console.log(`[BaseContextFactory.createContext] Calling _obtainBrowser...`);
70
48
  const browser = await this._obtainBrowser();
71
- console.log(`[BaseContextFactory.createContext] Got browser`);
72
- console.log(`[BaseContextFactory.createContext] Calling _doCreateContext...`);
73
49
  const browserContext = await this._doCreateContext(browser);
74
- console.log(`[BaseContextFactory.createContext] Got browserContext`);
75
- console.log(`[BaseContextFactory.createContext] END`);
76
50
  return { browserContext, close: () => this._closeBrowserContext(browserContext, browser) };
77
51
  }
78
52
  async _doCreateContext(browser) {
79
53
  throw new Error("Not implemented");
80
54
  }
81
55
  async _closeBrowserContext(browserContext, browser) {
82
- console.log(`[BaseContextFactory._closeBrowserContext] START (${this.name})`);
83
56
  testDebug(`close browser context (${this.name})`);
84
57
  if (browser.contexts().length === 1)
85
58
  this._browserPromise = void 0;
@@ -90,7 +63,6 @@ class BaseContextFactory {
90
63
  await browser.close().catch(() => {
91
64
  });
92
65
  }
93
- console.log(`[BaseContextFactory._closeBrowserContext] END`);
94
66
  }
95
67
  }
96
68
  class IsolatedContextFactory extends BaseContextFactory {
@@ -119,106 +91,10 @@ class CdpContextFactory extends BaseContextFactory {
119
91
  super("cdp", browserConfig);
120
92
  }
121
93
  async _doObtainBrowser() {
122
- console.log("[CdpContextFactory._doObtainBrowser] START");
123
- console.log("[CdpContextFactory._doObtainBrowser] cdpEndpoint:", this.browserConfig.cdpEndpoint);
124
- console.log("[CdpContextFactory._doObtainBrowser] cdpEndpoint type:", typeof this.browserConfig.cdpEndpoint);
125
- const endpoint = this.browserConfig.cdpEndpoint;
126
- const isCloudfareFetcher = typeof endpoint !== "string";
127
- console.log("[CdpContextFactory._doObtainBrowser] isCloudfareFetcher:", isCloudfareFetcher);
128
- try {
129
- let browser;
130
- if (isCloudfareFetcher) {
131
- const fetcher = endpoint;
132
- console.log("[CdpContextFactory._doObtainBrowser] Fetcher detected, using connect() method...");
133
- const connectUrls = [
134
- "https://cloudflare",
135
- "https://cloudflare/",
136
- "wss://cloudflare"
137
- ];
138
- let socket = null;
139
- let successUrl = "";
140
- for (const url of connectUrls) {
141
- try {
142
- console.log(`[CdpContextFactory._doObtainBrowser] Trying fetcher.connect("${url}")...`);
143
- const connection = await fetcher.connect(url);
144
- console.log("[CdpContextFactory._doObtainBrowser] connect() returned:", typeof connection);
145
- console.log("[CdpContextFactory._doObtainBrowser] connection keys:", Object.keys(connection || {}));
146
- console.log("[CdpContextFactory._doObtainBrowser] connection.socket:", typeof connection?.socket);
147
- if (connection && typeof connection === "object") {
148
- if ("accept" in connection && typeof connection.accept === "function") {
149
- console.log("[CdpContextFactory._doObtainBrowser] Connection has accept(), calling it...");
150
- connection.accept();
151
- }
152
- if ("send" in connection && "close" in connection) {
153
- socket = connection;
154
- successUrl = url;
155
- console.log("[CdpContextFactory._doObtainBrowser] Got WebSocket-like object directly");
156
- break;
157
- }
158
- if ("socket" in connection) {
159
- socket = connection.socket;
160
- successUrl = url;
161
- console.log("[CdpContextFactory._doObtainBrowser] Got socket from connection.socket");
162
- break;
163
- }
164
- if ("webSocket" in connection) {
165
- socket = connection.webSocket;
166
- if (socket && "accept" in socket) {
167
- socket.accept();
168
- }
169
- successUrl = url;
170
- console.log("[CdpContextFactory._doObtainBrowser] Got socket from connection.webSocket");
171
- break;
172
- }
173
- console.log("[CdpContextFactory._doObtainBrowser] Connection structure:", JSON.stringify(Object.getOwnPropertyNames(connection)));
174
- const proto = Object.getPrototypeOf(connection);
175
- if (proto) {
176
- console.log("[CdpContextFactory._doObtainBrowser] Connection prototype:", Object.getOwnPropertyNames(proto));
177
- }
178
- }
179
- } catch (e) {
180
- console.log(`[CdpContextFactory._doObtainBrowser] connect("${url}") error:`, e.message);
181
- }
182
- }
183
- if (!socket) {
184
- throw new Error("Could not establish WebSocket via Fetcher.connect(). Check logs above.");
185
- }
186
- console.log(`[CdpContextFactory._doObtainBrowser] WebSocket obtained from ${successUrl}`);
187
- console.log("[CdpContextFactory._doObtainBrowser] Socket type:", typeof socket);
188
- console.log("[CdpContextFactory._doObtainBrowser] Socket readyState:", socket.readyState);
189
- console.log("[CdpContextFactory._doObtainBrowser] Socket methods:", Object.getOwnPropertyNames(Object.getPrototypeOf(socket)));
190
- console.log("[CdpContextFactory._doObtainBrowser] playwright.chromium methods:", Object.keys(playwright.chromium));
191
- if (typeof playwright.chromium.connectOverCDPWithSocket === "function") {
192
- console.log("[CdpContextFactory._doObtainBrowser] Using connectOverCDPWithSocket...");
193
- browser = await playwright.chromium.connectOverCDPWithSocket(socket);
194
- } else if (typeof playwright.chromium.connectWithWebSocket === "function") {
195
- console.log("[CdpContextFactory._doObtainBrowser] Using connectWithWebSocket...");
196
- browser = await playwright.chromium.connectWithWebSocket(socket);
197
- } else {
198
- throw new Error(`WebSocket connection established but patchright needs a method to accept raw WebSocket for CDP. Available methods: ${Object.keys(playwright.chromium).join(", ")}`);
199
- }
200
- } else {
201
- console.log("[CdpContextFactory._doObtainBrowser] Using chromium.connectOverCDP() for CDP URL...");
202
- browser = await playwright.chromium.connectOverCDP(endpoint);
203
- }
204
- console.log("[CdpContextFactory._doObtainBrowser] Connection completed");
205
- return browser;
206
- } catch (err) {
207
- console.log("[CdpContextFactory._doObtainBrowser] ERROR:", err.message, err.stack);
208
- throw err;
209
- }
94
+ return playwright.chromium.connectOverCDP(this.browserConfig.cdpEndpoint);
210
95
  }
211
96
  async _doCreateContext(browser) {
212
- console.log("[CdpContextFactory._doCreateContext] START");
213
- console.log("[CdpContextFactory._doCreateContext] isolated:", this.browserConfig.isolated);
214
- try {
215
- const ctx = this.browserConfig.isolated ? await browser.newContext() : browser.contexts()[0];
216
- console.log("[CdpContextFactory._doCreateContext] Got context");
217
- return ctx;
218
- } catch (err) {
219
- console.log("[CdpContextFactory._doCreateContext] ERROR:", err.message, err.stack);
220
- throw err;
221
- }
97
+ return this.browserConfig.isolated ? await browser.newContext() : browser.contexts()[0];
222
98
  }
223
99
  }
224
100
  class RemoteContextFactory extends BaseContextFactory {
@@ -66,29 +66,9 @@ class Context {
66
66
  await this._currentTab.page.bringToFront();
67
67
  }
68
68
  async ensureTab() {
69
- console.log("[Context.ensureTab] START");
70
- console.log("[Context.ensureTab] _currentTab exists:", !!this._currentTab);
71
- console.log("[Context.ensureTab] Calling _ensureBrowserContext()...");
72
- let browserContext;
73
- try {
74
- const result = await this._ensureBrowserContext();
75
- browserContext = result.browserContext;
76
- console.log("[Context.ensureTab] _ensureBrowserContext() completed");
77
- } catch (err) {
78
- console.log("[Context.ensureTab] ERROR in _ensureBrowserContext():", err.message, err.stack);
79
- throw err;
80
- }
81
- if (!this._currentTab) {
82
- console.log("[Context.ensureTab] No current tab, creating new page...");
83
- try {
84
- await browserContext.newPage();
85
- console.log("[Context.ensureTab] New page created");
86
- } catch (err) {
87
- console.log("[Context.ensureTab] ERROR creating new page:", err.message, err.stack);
88
- throw err;
89
- }
90
- }
91
- console.log("[Context.ensureTab] END, returning tab");
69
+ const { browserContext } = await this._ensureBrowserContext();
70
+ if (!this._currentTab)
71
+ await browserContext.newPage();
92
72
  return this._currentTab;
93
73
  }
94
74
  async listTabsMarkdown() {
@@ -265,51 +245,29 @@ ${code.join("\n")}
265
245
  }
266
246
  }
267
247
  _ensureBrowserContext() {
268
- console.log("[Context._ensureBrowserContext] START");
269
- console.log("[Context._ensureBrowserContext] _browserContextPromise exists:", !!this._browserContextPromise);
270
248
  if (!this._browserContextPromise) {
271
- console.log("[Context._ensureBrowserContext] Creating new browser context promise...");
272
249
  this._browserContextPromise = this._setupBrowserContext();
273
- this._browserContextPromise.catch((err) => {
274
- console.log("[Context._ensureBrowserContext] Promise rejected:", err.message);
250
+ this._browserContextPromise.catch(() => {
275
251
  this._browserContextPromise = void 0;
276
252
  });
277
253
  }
278
- console.log("[Context._ensureBrowserContext] END, returning promise");
279
254
  return this._browserContextPromise;
280
255
  }
281
256
  async _setupBrowserContext() {
282
- console.log("[Context._setupBrowserContext] START");
283
- console.log("[Context._setupBrowserContext] Calling _browserContextFactory.createContext()...");
284
- let result;
285
- try {
286
- result = await this._browserContextFactory.createContext();
287
- console.log("[Context._setupBrowserContext] createContext() completed");
288
- } catch (err) {
289
- console.log("[Context._setupBrowserContext] ERROR in createContext():", err.message, err.stack);
290
- throw err;
291
- }
257
+ const result = await this._browserContextFactory.createContext();
292
258
  const { browserContext } = result;
293
- console.log("[Context._setupBrowserContext] Got browserContext");
294
- console.log("[Context._setupBrowserContext] Setting up request interception...");
295
259
  await this._setupRequestInterception(browserContext);
296
- console.log("[Context._setupBrowserContext] Request interception done");
297
- console.log("[Context._setupBrowserContext] Processing existing pages...");
298
260
  for (const page of browserContext.pages())
299
261
  this._onPageCreated(page);
300
262
  browserContext.on("page", (page) => this._onPageCreated(page));
301
- console.log("[Context._setupBrowserContext] Pages processed");
302
263
  if (this.config.saveTrace) {
303
- console.log("[Context._setupBrowserContext] Starting tracing...");
304
264
  await browserContext.tracing.start({
305
265
  name: "trace",
306
266
  screenshots: false,
307
267
  snapshots: true,
308
268
  sources: false
309
269
  });
310
- console.log("[Context._setupBrowserContext] Tracing started");
311
270
  }
312
- console.log("[Context._setupBrowserContext] END");
313
271
  return result;
314
272
  }
315
273
  }
@@ -46,40 +46,66 @@ class Tab {
46
46
  }));
47
47
  }
48
48
  async navigate(url) {
49
- console.log("[Tab.navigate] START, url:", url);
50
- console.log("[Tab.navigate] Clearing collected artifacts...");
49
+ const navStartTime = Date.now();
50
+ console.log(`[Tab.navigate] --------- TAB NAVIGATION START ---------`);
51
+ console.log(`[Tab.navigate] URL: ${url}`);
52
+ console.log(`[Tab.navigate] Current page URL: ${this.page.url()}`);
53
+ console.log(`[Tab.navigate] Page is closed: ${this.page.isClosed()}`);
54
+ console.log(`[Tab.navigate] Clearing collected artifacts...`);
51
55
  this._clearCollectedArtifacts();
52
- console.log("[Tab.navigate] Artifacts cleared");
53
- console.log("[Tab.navigate] Setting up download event listener...");
56
+ console.log(`[Tab.navigate] Artifacts cleared. Console messages: ${this._consoleMessages.length}, Requests: ${this._requests.size}`);
57
+ console.log(`[Tab.navigate] Setting up download event listener...`);
54
58
  const downloadEvent = callOnPageNoTrace(this.page, (page) => page.waitForEvent("download").catch(() => {
55
59
  }));
56
- console.log("[Tab.navigate] Download event listener ready");
60
+ console.log(`[Tab.navigate] Download event listener set up`);
61
+ console.log(`[Tab.navigate] Calling page.goto() with waitUntil: 'domcontentloaded'`);
62
+ const gotoStartTime = Date.now();
57
63
  try {
58
- console.log("[Tab.navigate] Calling page.goto()...");
59
- await this.page.goto(url, { waitUntil: "domcontentloaded" });
60
- console.log("[Tab.navigate] page.goto() completed successfully");
64
+ const response = await this.page.goto(url, { waitUntil: "domcontentloaded" });
65
+ const gotoTime = Date.now() - gotoStartTime;
66
+ console.log(`[Tab.navigate] page.goto() completed in ${gotoTime}ms`);
67
+ if (response) {
68
+ console.log(`[Tab.navigate] Response status: ${response.status()}`);
69
+ console.log(`[Tab.navigate] Response URL: ${response.url()}`);
70
+ console.log(`[Tab.navigate] Response headers:`, JSON.stringify(response.headers(), null, 2));
71
+ } else {
72
+ console.log(`[Tab.navigate] Response is null (might be same-document navigation or cached)`);
73
+ }
61
74
  } catch (_e) {
62
75
  const e = _e;
63
- console.log("[Tab.navigate] page.goto() threw error:", e.message);
76
+ const gotoTime = Date.now() - gotoStartTime;
77
+ console.log(`[Tab.navigate] page.goto() threw after ${gotoTime}ms`);
78
+ console.log(`[Tab.navigate] Error name: ${e.name}`);
79
+ console.log(`[Tab.navigate] Error message: ${e.message}`);
64
80
  const mightBeDownload = e.message.includes("net::ERR_ABORTED") || e.message.includes("Download is starting");
81
+ console.log(`[Tab.navigate] Might be download: ${mightBeDownload}`);
65
82
  if (!mightBeDownload) {
66
- console.log("[Tab.navigate] Not a download, re-throwing error");
83
+ console.error(`[Tab.navigate] ERROR: Not a download, rethrowing error`);
84
+ console.error(`[Tab.navigate] Error stack:`, e.stack);
67
85
  throw e;
68
86
  }
69
- console.log("[Tab.navigate] Might be download, waiting for download event...");
87
+ console.log(`[Tab.navigate] Waiting for potential download event (up to 1000ms)...`);
88
+ const downloadWaitStart = Date.now();
70
89
  const download = await Promise.race([
71
90
  downloadEvent,
72
91
  new Promise((resolve) => setTimeout(resolve, 1e3))
73
92
  ]);
93
+ console.log(`[Tab.navigate] Download wait completed in ${Date.now() - downloadWaitStart}ms`);
94
+ console.log(`[Tab.navigate] Download detected: ${!!download}`);
74
95
  if (!download) {
75
- console.log("[Tab.navigate] No download received, re-throwing error");
96
+ console.error(`[Tab.navigate] ERROR: No download detected, rethrowing original error`);
76
97
  throw e;
77
98
  }
78
- console.log("[Tab.navigate] Download detected");
99
+ console.log(`[Tab.navigate] Download detected, continuing without error`);
79
100
  }
80
- console.log("[Tab.navigate] Waiting for load state...");
101
+ console.log(`[Tab.navigate] Waiting for 'load' state (timeout: 5000ms)...`);
102
+ const loadWaitStart = Date.now();
81
103
  await this.waitForLoadState("load", { timeout: 5e3 });
82
- console.log("[Tab.navigate] END");
104
+ console.log(`[Tab.navigate] 'load' state wait completed in ${Date.now() - loadWaitStart}ms`);
105
+ console.log(`[Tab.navigate] Final page URL: ${this.page.url()}`);
106
+ console.log(`[Tab.navigate] Final page title: ${await this.page.title().catch(() => "N/A")}`);
107
+ console.log(`[Tab.navigate] Total navigation time: ${Date.now() - navStartTime}ms`);
108
+ console.log(`[Tab.navigate] --------- TAB NAVIGATION END ---------`);
83
109
  }
84
110
  hasSnapshot() {
85
111
  return !!this._snapshot;
@@ -1,9 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { defineTool } from './tool.js';
3
3
 
4
- const log = (msg, data) => {
5
- console.log(`[browser_navigate] ${msg}`, data !== void 0 ? JSON.stringify(data, null, 2) : "");
6
- };
7
4
  const navigate = (captureSnapshot) => defineTool({
8
5
  capability: "core",
9
6
  schema: {
@@ -16,39 +13,49 @@ const navigate = (captureSnapshot) => defineTool({
16
13
  type: "destructive"
17
14
  },
18
15
  handle: async (context, params) => {
19
- log("=== NAVIGATE HANDLER START ===");
20
- log("Params received:", params);
21
- log("captureSnapshot:", captureSnapshot);
22
- log("About to call context.ensureTab()...");
16
+ const startTime = Date.now();
17
+ console.log(`[browser_navigate] ========== NAVIGATION START ==========`);
18
+ console.log(`[browser_navigate] Target URL: ${params.url}`);
19
+ console.log(`[browser_navigate] Timestamp: ${(/* @__PURE__ */ new Date()).toISOString()}`);
20
+ console.log(`[browser_navigate] captureSnapshot setting: ${captureSnapshot}`);
21
+ console.log(`[browser_navigate] Ensuring tab is available...`);
22
+ const ensureTabStart = Date.now();
23
23
  let tab;
24
24
  try {
25
25
  tab = await context.ensureTab();
26
- log("context.ensureTab() completed successfully");
27
- log("Tab page URL:", tab.page?.url?.());
28
- } catch (err) {
29
- log("ERROR in context.ensureTab():", { message: err.message, stack: err.stack });
30
- throw err;
26
+ console.log(`[browser_navigate] Tab ensured successfully in ${Date.now() - ensureTabStart}ms`);
27
+ console.log(`[browser_navigate] Current page URL before navigation: ${tab.page.url()}`);
28
+ } catch (error) {
29
+ console.error(`[browser_navigate] ERROR: Failed to ensure tab after ${Date.now() - ensureTabStart}ms`);
30
+ console.error(`[browser_navigate] Error details:`, error);
31
+ throw error;
31
32
  }
32
- log(`About to call tab.navigate("${params.url}")...`);
33
+ console.log(`[browser_navigate] Starting navigation to: ${params.url}`);
34
+ const navigationStart = Date.now();
33
35
  try {
34
36
  await tab.navigate(params.url);
35
- log("tab.navigate() completed successfully");
36
- } catch (err) {
37
- log("ERROR in tab.navigate():", { message: err.message, stack: err.stack });
38
- throw err;
37
+ console.log(`[browser_navigate] Navigation completed successfully in ${Date.now() - navigationStart}ms`);
38
+ console.log(`[browser_navigate] Current page URL after navigation: ${tab.page.url()}`);
39
+ } catch (error) {
40
+ console.error(`[browser_navigate] ERROR: Navigation failed after ${Date.now() - navigationStart}ms`);
41
+ console.error(`[browser_navigate] Error type: ${error.constructor.name}`);
42
+ console.error(`[browser_navigate] Error message: ${error.message}`);
43
+ console.error(`[browser_navigate] Error stack:`, error.stack);
44
+ throw error;
39
45
  }
40
46
  const code = [
41
47
  `// Navigate to ${params.url}`,
42
48
  `await page.goto('${params.url}');`
43
49
  ];
44
- log("About to return result...");
45
- const result = {
50
+ const totalTime = Date.now() - startTime;
51
+ console.log(`[browser_navigate] Total execution time: ${totalTime}ms`);
52
+ console.log(`[browser_navigate] Returning with captureSnapshot: ${captureSnapshot}, waitForNetwork: false`);
53
+ console.log(`[browser_navigate] ========== NAVIGATION END ==========`);
54
+ return {
46
55
  code,
47
56
  captureSnapshot,
48
57
  waitForNetwork: false
49
58
  };
50
- log("=== NAVIGATE HANDLER END ===");
51
- return result;
52
59
  }
53
60
  });
54
61
  const goBack = (captureSnapshot) => defineTool({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@szymonrybczak/playwright-mcp",
3
- "version": "0.0.16",
3
+ "version": "0.0.17",
4
4
  "description": "Cloudflare Playwright Tools for MCP",
5
5
  "type": "module",
6
6
  "repository": {