playwright 1.55.1 → 1.56.1

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.
Files changed (105) hide show
  1. package/README.md +3 -3
  2. package/ThirdPartyNotices.txt +3 -3
  3. package/lib/agents/generateAgents.js +263 -0
  4. package/lib/agents/generator.md +102 -0
  5. package/lib/agents/healer.md +78 -0
  6. package/lib/agents/planner.md +135 -0
  7. package/lib/common/config.js +1 -1
  8. package/lib/common/expectBundle.js +3 -0
  9. package/lib/common/expectBundleImpl.js +51 -51
  10. package/lib/index.js +7 -8
  11. package/lib/isomorphic/testServerConnection.js +0 -7
  12. package/lib/isomorphic/testTree.js +35 -8
  13. package/lib/matchers/expect.js +8 -21
  14. package/lib/matchers/matcherHint.js +42 -18
  15. package/lib/matchers/matchers.js +12 -6
  16. package/lib/matchers/toBeTruthy.js +16 -14
  17. package/lib/matchers/toEqual.js +18 -13
  18. package/lib/matchers/toHaveURL.js +12 -27
  19. package/lib/matchers/toMatchAriaSnapshot.js +26 -30
  20. package/lib/matchers/toMatchSnapshot.js +15 -12
  21. package/lib/matchers/toMatchText.js +29 -35
  22. package/lib/mcp/browser/actions.d.js +16 -0
  23. package/lib/mcp/browser/browserContextFactory.js +296 -0
  24. package/lib/mcp/browser/browserServerBackend.js +76 -0
  25. package/lib/mcp/browser/codegen.js +66 -0
  26. package/lib/mcp/browser/config.js +383 -0
  27. package/lib/mcp/browser/context.js +284 -0
  28. package/lib/mcp/browser/response.js +228 -0
  29. package/lib/mcp/browser/sessionLog.js +160 -0
  30. package/lib/mcp/browser/tab.js +277 -0
  31. package/lib/mcp/browser/tools/common.js +63 -0
  32. package/lib/mcp/browser/tools/console.js +44 -0
  33. package/lib/mcp/browser/tools/dialogs.js +60 -0
  34. package/lib/mcp/browser/tools/evaluate.js +70 -0
  35. package/lib/mcp/browser/tools/files.js +58 -0
  36. package/lib/mcp/browser/tools/form.js +74 -0
  37. package/lib/mcp/browser/tools/install.js +69 -0
  38. package/lib/mcp/browser/tools/keyboard.js +85 -0
  39. package/lib/mcp/browser/tools/mouse.js +107 -0
  40. package/lib/mcp/browser/tools/navigate.js +62 -0
  41. package/lib/mcp/browser/tools/network.js +54 -0
  42. package/lib/mcp/browser/tools/pdf.js +59 -0
  43. package/lib/mcp/browser/tools/screenshot.js +88 -0
  44. package/lib/mcp/browser/tools/snapshot.js +182 -0
  45. package/lib/mcp/browser/tools/tabs.js +67 -0
  46. package/lib/mcp/browser/tools/tool.js +49 -0
  47. package/lib/mcp/browser/tools/tracing.js +74 -0
  48. package/lib/mcp/browser/tools/utils.js +100 -0
  49. package/lib/mcp/browser/tools/verify.js +154 -0
  50. package/lib/mcp/browser/tools/wait.js +63 -0
  51. package/lib/mcp/browser/tools.js +80 -0
  52. package/lib/mcp/browser/watchdog.js +44 -0
  53. package/lib/mcp/config.d.js +16 -0
  54. package/lib/mcp/extension/cdpRelay.js +351 -0
  55. package/lib/mcp/extension/extensionContextFactory.js +75 -0
  56. package/lib/mcp/extension/protocol.js +28 -0
  57. package/lib/mcp/index.js +61 -0
  58. package/lib/mcp/{tool.js → log.js} +12 -18
  59. package/lib/mcp/program.js +96 -0
  60. package/lib/mcp/{bundle.js → sdk/bundle.js} +24 -2
  61. package/lib/mcp/{exports.js → sdk/exports.js} +12 -10
  62. package/lib/mcp/{transport.js → sdk/http.js} +79 -60
  63. package/lib/mcp/sdk/mdb.js +208 -0
  64. package/lib/mcp/{proxyBackend.js → sdk/proxyBackend.js} +18 -13
  65. package/lib/mcp/sdk/server.js +190 -0
  66. package/lib/mcp/sdk/tool.js +51 -0
  67. package/lib/mcp/test/browserBackend.js +98 -0
  68. package/lib/mcp/test/generatorTools.js +122 -0
  69. package/lib/mcp/test/plannerTools.js +46 -0
  70. package/lib/mcp/test/seed.js +72 -0
  71. package/lib/mcp/test/streams.js +39 -0
  72. package/lib/mcp/test/testBackend.js +97 -0
  73. package/lib/mcp/test/testContext.js +176 -0
  74. package/lib/mcp/test/testTool.js +30 -0
  75. package/lib/mcp/test/testTools.js +115 -0
  76. package/lib/mcpBundleImpl.js +14 -67
  77. package/lib/plugins/webServerPlugin.js +2 -0
  78. package/lib/program.js +68 -0
  79. package/lib/reporters/base.js +15 -17
  80. package/lib/reporters/html.js +39 -26
  81. package/lib/reporters/list.js +8 -4
  82. package/lib/reporters/listModeReporter.js +6 -3
  83. package/lib/reporters/merge.js +3 -1
  84. package/lib/reporters/teleEmitter.js +3 -1
  85. package/lib/runner/dispatcher.js +9 -23
  86. package/lib/runner/failureTracker.js +12 -16
  87. package/lib/runner/loadUtils.js +39 -3
  88. package/lib/runner/projectUtils.js +8 -2
  89. package/lib/runner/tasks.js +18 -7
  90. package/lib/runner/testRunner.js +16 -28
  91. package/lib/runner/testServer.js +17 -23
  92. package/lib/runner/watchMode.js +1 -53
  93. package/lib/runner/workerHost.js +8 -10
  94. package/lib/transform/babelBundleImpl.js +10 -10
  95. package/lib/transform/compilationCache.js +22 -5
  96. package/lib/util.js +12 -16
  97. package/lib/utilsBundleImpl.js +1 -1
  98. package/lib/worker/fixtureRunner.js +15 -7
  99. package/lib/worker/testInfo.js +9 -24
  100. package/lib/worker/workerMain.js +12 -8
  101. package/package.json +7 -3
  102. package/types/test.d.ts +17 -8
  103. package/types/testReporter.d.ts +1 -1
  104. package/lib/mcp/server.js +0 -118
  105. /package/lib/mcp/{inProcessTransport.js → sdk/inProcessTransport.js} +0 -0
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
  var bundle_exports = {};
20
30
  __export(bundle_exports, {
@@ -23,23 +33,31 @@ __export(bundle_exports, {
23
33
  ListRootsRequestSchema: () => ListRootsRequestSchema,
24
34
  ListToolsRequestSchema: () => ListToolsRequestSchema,
25
35
  PingRequestSchema: () => PingRequestSchema,
36
+ ProgressNotificationSchema: () => ProgressNotificationSchema,
37
+ SSEClientTransport: () => SSEClientTransport,
26
38
  SSEServerTransport: () => SSEServerTransport,
27
39
  Server: () => Server,
40
+ StdioClientTransport: () => StdioClientTransport,
28
41
  StdioServerTransport: () => StdioServerTransport,
42
+ StreamableHTTPClientTransport: () => StreamableHTTPClientTransport,
29
43
  StreamableHTTPServerTransport: () => StreamableHTTPServerTransport,
30
44
  z: () => z,
31
45
  zodToJsonSchema: () => zodToJsonSchema
32
46
  });
33
47
  module.exports = __toCommonJS(bundle_exports);
34
- const bundle = require("./mcpBundleImpl");
35
- const zodToJsonSchema = require("./mcpBundleImpl").zodToJsonSchema;
48
+ var bundle = __toESM(require("../../mcpBundleImpl"));
49
+ const zodToJsonSchema = bundle.zodToJsonSchema;
36
50
  const Client = bundle.Client;
37
51
  const Server = bundle.Server;
52
+ const SSEClientTransport = bundle.SSEClientTransport;
38
53
  const SSEServerTransport = bundle.SSEServerTransport;
54
+ const StdioClientTransport = bundle.StdioClientTransport;
39
55
  const StdioServerTransport = bundle.StdioServerTransport;
40
56
  const StreamableHTTPServerTransport = bundle.StreamableHTTPServerTransport;
57
+ const StreamableHTTPClientTransport = bundle.StreamableHTTPClientTransport;
41
58
  const CallToolRequestSchema = bundle.CallToolRequestSchema;
42
59
  const ListRootsRequestSchema = bundle.ListRootsRequestSchema;
60
+ const ProgressNotificationSchema = bundle.ProgressNotificationSchema;
43
61
  const ListToolsRequestSchema = bundle.ListToolsRequestSchema;
44
62
  const PingRequestSchema = bundle.PingRequestSchema;
45
63
  const z = bundle.z;
@@ -50,9 +68,13 @@ const z = bundle.z;
50
68
  ListRootsRequestSchema,
51
69
  ListToolsRequestSchema,
52
70
  PingRequestSchema,
71
+ ProgressNotificationSchema,
72
+ SSEClientTransport,
53
73
  SSEServerTransport,
54
74
  Server,
75
+ StdioClientTransport,
55
76
  StdioServerTransport,
77
+ StreamableHTTPClientTransport,
56
78
  StreamableHTTPServerTransport,
57
79
  z,
58
80
  zodToJsonSchema
@@ -15,16 +15,18 @@ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "defau
15
15
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
16
16
  var exports_exports = {};
17
17
  module.exports = __toCommonJS(exports_exports);
18
- __reExport(exports_exports, require("./inProcessTransport.js"), module.exports);
19
- __reExport(exports_exports, require("./proxyBackend.js"), module.exports);
20
- __reExport(exports_exports, require("./server.js"), module.exports);
21
- __reExport(exports_exports, require("./tool.js"), module.exports);
22
- __reExport(exports_exports, require("./transport.js"), module.exports);
18
+ __reExport(exports_exports, require("./inProcessTransport"), module.exports);
19
+ __reExport(exports_exports, require("./proxyBackend"), module.exports);
20
+ __reExport(exports_exports, require("./server"), module.exports);
21
+ __reExport(exports_exports, require("./tool"), module.exports);
22
+ __reExport(exports_exports, require("./http"), module.exports);
23
+ __reExport(exports_exports, require("./mdb"), module.exports);
23
24
  // Annotate the CommonJS export names for ESM import in node:
24
25
  0 && (module.exports = {
25
- ...require("./inProcessTransport.js"),
26
- ...require("./proxyBackend.js"),
27
- ...require("./server.js"),
28
- ...require("./tool.js"),
29
- ...require("./transport.js")
26
+ ...require("./inProcessTransport"),
27
+ ...require("./proxyBackend"),
28
+ ...require("./server"),
29
+ ...require("./tool"),
30
+ ...require("./http"),
31
+ ...require("./mdb")
30
32
  });
@@ -26,29 +26,79 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  mod
27
27
  ));
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
- var transport_exports = {};
30
- __export(transport_exports, {
31
- start: () => start
29
+ var http_exports = {};
30
+ __export(http_exports, {
31
+ httpAddressToString: () => httpAddressToString,
32
+ installHttpTransport: () => installHttpTransport,
33
+ startHttpServer: () => startHttpServer
32
34
  });
33
- module.exports = __toCommonJS(transport_exports);
35
+ module.exports = __toCommonJS(http_exports);
34
36
  var import_assert = __toESM(require("assert"));
35
37
  var import_http = __toESM(require("http"));
36
38
  var import_crypto = __toESM(require("crypto"));
37
39
  var import_utilsBundle = require("playwright-core/lib/utilsBundle");
38
40
  var mcpBundle = __toESM(require("./bundle"));
39
41
  var mcpServer = __toESM(require("./server"));
40
- async function start(serverBackendFactory, options) {
41
- if (options.port !== void 0) {
42
- const httpServer = await startHttpServer(options);
43
- startHttpTransport(httpServer, serverBackendFactory);
44
- } else {
45
- await startStdioTransport(serverBackendFactory);
46
- }
42
+ const testDebug = (0, import_utilsBundle.debug)("pw:mcp:test");
43
+ async function startHttpServer(config, abortSignal) {
44
+ const { host, port } = config;
45
+ const httpServer = import_http.default.createServer();
46
+ decorateServer(httpServer);
47
+ await new Promise((resolve, reject) => {
48
+ httpServer.on("error", reject);
49
+ abortSignal?.addEventListener("abort", () => {
50
+ httpServer.close();
51
+ reject(new Error("Aborted"));
52
+ });
53
+ httpServer.listen(port, host, () => {
54
+ resolve();
55
+ httpServer.removeListener("error", reject);
56
+ });
57
+ });
58
+ return httpServer;
47
59
  }
48
- async function startStdioTransport(serverBackendFactory) {
49
- await mcpServer.connect(serverBackendFactory, new mcpBundle.StdioServerTransport(), false);
60
+ function httpAddressToString(address) {
61
+ (0, import_assert.default)(address, "Could not bind server socket");
62
+ if (typeof address === "string")
63
+ return address;
64
+ const resolvedPort = address.port;
65
+ let resolvedHost = address.family === "IPv4" ? address.address : `[${address.address}]`;
66
+ if (resolvedHost === "0.0.0.0" || resolvedHost === "[::]")
67
+ resolvedHost = "localhost";
68
+ return `http://${resolvedHost}:${resolvedPort}`;
69
+ }
70
+ async function installHttpTransport(httpServer, serverBackendFactory, allowedHosts) {
71
+ const url = httpAddressToString(httpServer.address());
72
+ const host = new URL(url).host;
73
+ allowedHosts = (allowedHosts || [host]).map((h) => h.toLowerCase());
74
+ const allowAnyHost = allowedHosts.includes("*");
75
+ const sseSessions = /* @__PURE__ */ new Map();
76
+ const streamableSessions = /* @__PURE__ */ new Map();
77
+ httpServer.on("request", async (req, res) => {
78
+ if (!allowAnyHost) {
79
+ const host2 = req.headers.host?.toLowerCase();
80
+ if (!host2) {
81
+ res.statusCode = 400;
82
+ return res.end("Missing host");
83
+ }
84
+ if (!allowedHosts.includes(host2)) {
85
+ res.statusCode = 403;
86
+ return res.end("Access is only allowed at " + allowedHosts.join(", "));
87
+ }
88
+ }
89
+ const url2 = new URL(`http://localhost${req.url}`);
90
+ if (url2.pathname === "/killkillkill" && req.method === "GET") {
91
+ res.statusCode = 200;
92
+ res.end("Killing process");
93
+ process.emit("SIGINT");
94
+ return;
95
+ }
96
+ if (url2.pathname.startsWith("/sse"))
97
+ await handleSSE(serverBackendFactory, req, res, url2, sseSessions);
98
+ else
99
+ await handleStreamable(serverBackendFactory, req, res, streamableSessions);
100
+ });
50
101
  }
51
- const testDebug = (0, import_utilsBundle.debug)("pw:mcp:test");
52
102
  async function handleSSE(serverBackendFactory, req, res, url, sessions) {
53
103
  if (req.method === "POST") {
54
104
  const sessionId = url.searchParams.get("sessionId");
@@ -108,54 +158,23 @@ async function handleStreamable(serverBackendFactory, req, res, sessions) {
108
158
  res.statusCode = 400;
109
159
  res.end("Invalid request");
110
160
  }
111
- function startHttpTransport(httpServer, serverBackendFactory) {
112
- const sseSessions = /* @__PURE__ */ new Map();
113
- const streamableSessions = /* @__PURE__ */ new Map();
114
- httpServer.on("request", async (req, res) => {
115
- const url2 = new URL(`http://localhost${req.url}`);
116
- if (url2.pathname.startsWith("/sse"))
117
- await handleSSE(serverBackendFactory, req, res, url2, sseSessions);
118
- else
119
- await handleStreamable(serverBackendFactory, req, res, streamableSessions);
161
+ function decorateServer(server) {
162
+ const sockets = /* @__PURE__ */ new Set();
163
+ server.on("connection", (socket) => {
164
+ sockets.add(socket);
165
+ socket.once("close", () => sockets.delete(socket));
120
166
  });
121
- const url = httpAddressToString(httpServer.address());
122
- const message = [
123
- `Listening on ${url}`,
124
- "Put this in your client config:",
125
- JSON.stringify({
126
- "mcpServers": {
127
- "playwright": {
128
- "url": `${url}/mcp`
129
- }
130
- }
131
- }, void 0, 2),
132
- "For legacy SSE transport support, you can use the /sse endpoint instead."
133
- ].join("\n");
134
- console.error(message);
135
- }
136
- async function startHttpServer(config) {
137
- const { host, port } = config;
138
- const httpServer = import_http.default.createServer();
139
- await new Promise((resolve, reject) => {
140
- httpServer.on("error", reject);
141
- httpServer.listen(port, host, () => {
142
- resolve();
143
- httpServer.removeListener("error", reject);
144
- });
145
- });
146
- return httpServer;
147
- }
148
- function httpAddressToString(address) {
149
- (0, import_assert.default)(address, "Could not bind server socket");
150
- if (typeof address === "string")
151
- return address;
152
- const resolvedPort = address.port;
153
- let resolvedHost = address.family === "IPv4" ? address.address : `[${address.address}]`;
154
- if (resolvedHost === "0.0.0.0" || resolvedHost === "[::]")
155
- resolvedHost = "localhost";
156
- return `http://${resolvedHost}:${resolvedPort}`;
167
+ const close = server.close;
168
+ server.close = (callback) => {
169
+ for (const socket of sockets)
170
+ socket.destroy();
171
+ sockets.clear();
172
+ return close.call(server, callback);
173
+ };
157
174
  }
158
175
  // Annotate the CommonJS export names for ESM import in node:
159
176
  0 && (module.exports = {
160
- start
177
+ httpAddressToString,
178
+ installHttpTransport,
179
+ startHttpServer
161
180
  });
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var mdb_exports = {};
30
+ __export(mdb_exports, {
31
+ MDBBackend: () => MDBBackend,
32
+ runMainBackend: () => runMainBackend,
33
+ runOnPauseBackendLoop: () => runOnPauseBackendLoop
34
+ });
35
+ module.exports = __toCommonJS(mdb_exports);
36
+ var import_utilsBundle = require("playwright-core/lib/utilsBundle");
37
+ var import_utils = require("playwright-core/lib/utils");
38
+ var import_tool = require("./tool");
39
+ var mcpBundle = __toESM(require("./bundle"));
40
+ var mcpServer = __toESM(require("./server"));
41
+ var mcpHttp = __toESM(require("./http"));
42
+ const mdbDebug = (0, import_utilsBundle.debug)("pw:mcp:mdb");
43
+ const errorsDebug = (0, import_utilsBundle.debug)("pw:mcp:errors");
44
+ const z = mcpBundle.z;
45
+ class MDBBackend {
46
+ constructor(mainBackend) {
47
+ this._progress = [];
48
+ this._mainBackend = mainBackend;
49
+ this._progressCallback = (params) => {
50
+ if (params.message)
51
+ this._progress.push({ type: "text", text: params.message });
52
+ };
53
+ }
54
+ async initialize(server, clientInfo) {
55
+ if (!this._clientInfo) {
56
+ this._clientInfo = clientInfo;
57
+ await this._mainBackend.initialize?.(server, clientInfo);
58
+ }
59
+ }
60
+ async listTools() {
61
+ return await this._mainBackend.listTools();
62
+ }
63
+ async callTool(name, args) {
64
+ if (name === pushToolsSchema.name) {
65
+ await this._createOnPauseClient(pushToolsSchema.inputSchema.parse(args || {}));
66
+ return { content: [{ type: "text", text: "Tools pushed" }] };
67
+ }
68
+ if (this._onPauseClient?.tools.find((tool) => tool.name === name)) {
69
+ const result2 = await this._onPauseClient.client.callTool({
70
+ name,
71
+ arguments: args
72
+ });
73
+ await this._mainBackend.afterCallTool?.(name, args, result2);
74
+ return result2;
75
+ }
76
+ await this._onPauseClient?.transport.terminateSession().catch(errorsDebug);
77
+ await this._onPauseClient?.client.close().catch(errorsDebug);
78
+ this._onPauseClient = void 0;
79
+ const resultPromise = new import_utils.ManualPromise();
80
+ const interruptPromise = new import_utils.ManualPromise();
81
+ this._interruptPromise = interruptPromise;
82
+ this._mainBackend.callTool(name, args, this._progressCallback).then((result2) => {
83
+ resultPromise.resolve(result2);
84
+ }).catch((e) => {
85
+ resultPromise.resolve({ content: [{ type: "text", text: String(e) }], isError: true });
86
+ });
87
+ const result = await Promise.race([interruptPromise, resultPromise]);
88
+ if (interruptPromise.isDone())
89
+ mdbDebug("client call intercepted", result);
90
+ else
91
+ mdbDebug("client call result", result);
92
+ result.content.unshift(...this._progress);
93
+ this._progress.length = 0;
94
+ return result;
95
+ }
96
+ async _createOnPauseClient(params) {
97
+ if (this._onPauseClient)
98
+ await this._onPauseClient.client.close().catch(errorsDebug);
99
+ this._onPauseClient = await this._createClient(params.mcpUrl);
100
+ this._interruptPromise?.resolve({
101
+ content: [{
102
+ type: "text",
103
+ text: params.introMessage || ""
104
+ }]
105
+ });
106
+ this._interruptPromise = void 0;
107
+ }
108
+ async _createClient(url) {
109
+ const client = new mcpBundle.Client({ name: "Interrupting client", version: "0.0.0" }, { capabilities: { roots: {} } });
110
+ client.setRequestHandler(mcpBundle.ListRootsRequestSchema, () => ({ roots: this._clientInfo?.roots || [] }));
111
+ client.setRequestHandler(mcpBundle.PingRequestSchema, () => ({}));
112
+ client.setNotificationHandler(mcpBundle.ProgressNotificationSchema, (notification) => {
113
+ if (notification.method === "notifications/progress") {
114
+ const { message } = notification.params;
115
+ if (message)
116
+ this._progress.push({ type: "text", text: message });
117
+ }
118
+ });
119
+ const transport = new mcpBundle.StreamableHTTPClientTransport(new URL(url));
120
+ await client.connect(transport);
121
+ const { tools } = await client.listTools();
122
+ return { client, tools, transport };
123
+ }
124
+ }
125
+ const pushToolsSchema = (0, import_tool.defineToolSchema)({
126
+ name: "mdb_push_tools",
127
+ title: "Push MCP tools to the tools stack",
128
+ description: "Push MCP tools to the tools stack",
129
+ inputSchema: z.object({
130
+ mcpUrl: z.string(),
131
+ introMessage: z.string().optional()
132
+ }),
133
+ type: "readOnly"
134
+ });
135
+ async function runMainBackend(backendFactory, options) {
136
+ const mdbBackend = new MDBBackend(backendFactory.create());
137
+ const factory = {
138
+ ...backendFactory,
139
+ create: () => mdbBackend
140
+ };
141
+ const url = await startAsHttp(factory, { port: options?.port || 0 });
142
+ process.env.PLAYWRIGHT_DEBUGGER_MCP = url;
143
+ if (options?.port !== void 0)
144
+ return url;
145
+ await mcpServer.connect(factory, new mcpBundle.StdioServerTransport(), false);
146
+ }
147
+ async function runOnPauseBackendLoop(backend, introMessage) {
148
+ const wrappedBackend = new ServerBackendWithCloseListener(backend);
149
+ const factory = {
150
+ name: "on-pause-backend",
151
+ nameInConfig: "on-pause-backend",
152
+ version: "0.0.0",
153
+ create: () => wrappedBackend
154
+ };
155
+ const httpServer = await mcpHttp.startHttpServer({ port: 0 });
156
+ await mcpHttp.installHttpTransport(httpServer, factory);
157
+ const url = mcpHttp.httpAddressToString(httpServer.address());
158
+ const client = new mcpBundle.Client({ name: "Pushing client", version: "0.0.0" });
159
+ client.setRequestHandler(mcpBundle.PingRequestSchema, () => ({}));
160
+ const transport = new mcpBundle.StreamableHTTPClientTransport(new URL(process.env.PLAYWRIGHT_DEBUGGER_MCP));
161
+ await client.connect(transport);
162
+ const pushToolsResult = await client.callTool({
163
+ name: pushToolsSchema.name,
164
+ arguments: {
165
+ mcpUrl: url,
166
+ introMessage
167
+ }
168
+ });
169
+ if (pushToolsResult.isError)
170
+ errorsDebug("Failed to push tools", pushToolsResult.content);
171
+ await transport.terminateSession();
172
+ await client.close();
173
+ await wrappedBackend.waitForClosed();
174
+ httpServer.close();
175
+ }
176
+ async function startAsHttp(backendFactory, options) {
177
+ const httpServer = await mcpHttp.startHttpServer(options);
178
+ await mcpHttp.installHttpTransport(httpServer, backendFactory);
179
+ return mcpHttp.httpAddressToString(httpServer.address());
180
+ }
181
+ class ServerBackendWithCloseListener {
182
+ constructor(backend) {
183
+ this._serverClosedPromise = new import_utils.ManualPromise();
184
+ this._backend = backend;
185
+ }
186
+ async initialize(server, clientInfo) {
187
+ await this._backend.initialize?.(server, clientInfo);
188
+ }
189
+ async listTools() {
190
+ return this._backend.listTools();
191
+ }
192
+ async callTool(name, args, progress) {
193
+ return this._backend.callTool(name, args, progress);
194
+ }
195
+ serverClosed(server) {
196
+ this._backend.serverClosed?.(server);
197
+ this._serverClosedPromise.resolve();
198
+ }
199
+ async waitForClosed() {
200
+ await this._serverClosedPromise;
201
+ }
202
+ }
203
+ // Annotate the CommonJS export names for ESM import in node:
204
+ 0 && (module.exports = {
205
+ MDBBackend,
206
+ runMainBackend,
207
+ runOnPauseBackendLoop
208
+ });
@@ -34,20 +34,18 @@ module.exports = __toCommonJS(proxyBackend_exports);
34
34
  var import_utilsBundle = require("playwright-core/lib/utilsBundle");
35
35
  var mcpBundle = __toESM(require("./bundle"));
36
36
  const errorsDebug = (0, import_utilsBundle.debug)("pw:mcp:errors");
37
+ const { z, zodToJsonSchema } = mcpBundle;
37
38
  class ProxyBackend {
38
- constructor(name, version, mcpProviders) {
39
- this._roots = [];
40
- this.name = name;
41
- this.version = version;
39
+ constructor(mcpProviders) {
42
40
  this._mcpProviders = mcpProviders;
43
41
  this._contextSwitchTool = this._defineContextSwitchTool();
44
42
  }
45
- async initialize(clientVersion, roots) {
46
- this._roots = roots;
47
- await this._setCurrentClient(this._mcpProviders[0]);
43
+ async initialize(server, clientInfo) {
44
+ this._clientInfo = clientInfo;
48
45
  }
49
46
  async listTools() {
50
- const response = await this._currentClient.listTools();
47
+ const currentClient = await this._ensureCurrentClient();
48
+ const response = await currentClient.listTools();
51
49
  if (this._mcpProviders.length === 1)
52
50
  return response.tools;
53
51
  return [
@@ -58,7 +56,8 @@ class ProxyBackend {
58
56
  async callTool(name, args) {
59
57
  if (name === this._contextSwitchTool.name)
60
58
  return this._callContextSwitchTool(args);
61
- return await this._currentClient.callTool({
59
+ const currentClient = await this._ensureCurrentClient();
60
+ return await currentClient.callTool({
62
61
  name,
63
62
  arguments: args
64
63
  });
@@ -91,8 +90,8 @@ Error: ${error}
91
90
  "Connect to a browser using one of the available methods:",
92
91
  ...this._mcpProviders.map((factory) => `- "${factory.name}": ${factory.description}`)
93
92
  ].join("\n"),
94
- inputSchema: mcpBundle.zodToJsonSchema(mcpBundle.z.object({
95
- name: mcpBundle.z.enum(this._mcpProviders.map((factory) => factory.name)).default(this._mcpProviders[0].name).describe("The method to use to connect to the browser")
93
+ inputSchema: zodToJsonSchema(z.object({
94
+ name: z.enum(this._mcpProviders.map((factory) => factory.name)).default(this._mcpProviders[0].name).describe("The method to use to connect to the browser")
96
95
  }), { strictUnions: true }),
97
96
  annotations: {
98
97
  title: "Connect to a browser context",
@@ -101,20 +100,26 @@ Error: ${error}
101
100
  }
102
101
  };
103
102
  }
103
+ async _ensureCurrentClient() {
104
+ if (this._currentClient)
105
+ return this._currentClient;
106
+ return await this._setCurrentClient(this._mcpProviders[0]);
107
+ }
104
108
  async _setCurrentClient(factory) {
105
109
  await this._currentClient?.close();
106
110
  this._currentClient = void 0;
107
- const client = new mcpBundle.Client({ name: this.name, version: this.version });
111
+ const client = new mcpBundle.Client({ name: "Playwright MCP Proxy", version: "0.0.0" });
108
112
  client.registerCapabilities({
109
113
  roots: {
110
114
  listRoots: true
111
115
  }
112
116
  });
113
- client.setRequestHandler(mcpBundle.ListRootsRequestSchema, () => ({ roots: this._roots }));
117
+ client.setRequestHandler(mcpBundle.ListRootsRequestSchema, () => ({ roots: this._clientInfo?.roots || [] }));
114
118
  client.setRequestHandler(mcpBundle.PingRequestSchema, () => ({}));
115
119
  const transport = await factory.connect();
116
120
  await client.connect(transport);
117
121
  this._currentClient = client;
122
+ return client;
118
123
  }
119
124
  }
120
125
  // Annotate the CommonJS export names for ESM import in node: