@playwright/mcp 0.0.6 → 0.0.7
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 +73 -66
- package/lib/server.js +23 -0
- package/package.json +2 -2
package/lib/program.js
CHANGED
|
@@ -26,6 +26,7 @@ const commander_1 = require("commander");
|
|
|
26
26
|
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
27
27
|
const sse_js_1 = require("@modelcontextprotocol/sdk/server/sse.js");
|
|
28
28
|
const index_1 = require("./index");
|
|
29
|
+
const server_1 = require("./server");
|
|
29
30
|
const assert_1 = __importDefault(require("assert"));
|
|
30
31
|
const packageJSON = require('../package.json');
|
|
31
32
|
commander_1.program
|
|
@@ -40,85 +41,30 @@ commander_1.program
|
|
|
40
41
|
headless: !!options.headless,
|
|
41
42
|
channel: 'chrome',
|
|
42
43
|
};
|
|
43
|
-
const
|
|
44
|
-
|
|
44
|
+
const userDataDir = options.userDataDir ?? await createUserDataDir();
|
|
45
|
+
const serverList = new server_1.ServerList(() => (0, index_1.createServer)({
|
|
46
|
+
userDataDir,
|
|
45
47
|
launchOptions,
|
|
46
48
|
vision: !!options.vision,
|
|
47
|
-
});
|
|
48
|
-
setupExitWatchdog(
|
|
49
|
+
}));
|
|
50
|
+
setupExitWatchdog(serverList);
|
|
49
51
|
if (options.port) {
|
|
50
|
-
|
|
51
|
-
const httpServer = http_1.default.createServer(async (req, res) => {
|
|
52
|
-
if (req.method === 'POST') {
|
|
53
|
-
const host = req.headers.host ?? 'http://unknown';
|
|
54
|
-
const sessionId = new URL(host + req.url).searchParams.get('sessionId');
|
|
55
|
-
if (!sessionId) {
|
|
56
|
-
res.statusCode = 400;
|
|
57
|
-
res.end('Missing sessionId');
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
const transport = sessions.get(sessionId);
|
|
61
|
-
if (!transport) {
|
|
62
|
-
res.statusCode = 404;
|
|
63
|
-
res.end('Session not found');
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
await transport.handlePostMessage(req, res);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
else if (req.method === 'GET') {
|
|
70
|
-
const transport = new sse_js_1.SSEServerTransport('/sse', res);
|
|
71
|
-
sessions.set(transport.sessionId, transport);
|
|
72
|
-
res.on('close', () => {
|
|
73
|
-
sessions.delete(transport.sessionId);
|
|
74
|
-
});
|
|
75
|
-
await server.connect(transport);
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
res.statusCode = 405;
|
|
80
|
-
res.end('Method not allowed');
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
httpServer.listen(+options.port, () => {
|
|
84
|
-
const address = httpServer.address();
|
|
85
|
-
(0, assert_1.default)(address, 'Could not bind server socket');
|
|
86
|
-
let urlPrefixHumanReadable;
|
|
87
|
-
if (typeof address === 'string') {
|
|
88
|
-
urlPrefixHumanReadable = address;
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
const port = address.port;
|
|
92
|
-
let resolvedHost = address.family === 'IPv4' ? address.address : `[${address.address}]`;
|
|
93
|
-
if (resolvedHost === '0.0.0.0' || resolvedHost === '[::]')
|
|
94
|
-
resolvedHost = 'localhost';
|
|
95
|
-
urlPrefixHumanReadable = `http://${resolvedHost}:${port}`;
|
|
96
|
-
}
|
|
97
|
-
console.log(`Listening on ${urlPrefixHumanReadable}`);
|
|
98
|
-
console.log('Put this in your client config:');
|
|
99
|
-
console.log(JSON.stringify({
|
|
100
|
-
'mcpServers': {
|
|
101
|
-
'playwright': {
|
|
102
|
-
'url': `${urlPrefixHumanReadable}/sse`
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}, undefined, 2));
|
|
106
|
-
});
|
|
52
|
+
startSSEServer(+options.port, serverList);
|
|
107
53
|
}
|
|
108
54
|
else {
|
|
109
|
-
const
|
|
110
|
-
await server.connect(
|
|
55
|
+
const server = await serverList.create();
|
|
56
|
+
await server.connect(new stdio_js_1.StdioServerTransport());
|
|
111
57
|
}
|
|
112
58
|
});
|
|
113
|
-
function setupExitWatchdog(
|
|
59
|
+
function setupExitWatchdog(serverList) {
|
|
114
60
|
process.stdin.on('close', async () => {
|
|
115
61
|
setTimeout(() => process.exit(0), 15000);
|
|
116
|
-
await
|
|
62
|
+
await serverList.closeAll();
|
|
117
63
|
process.exit(0);
|
|
118
64
|
});
|
|
119
65
|
}
|
|
120
66
|
commander_1.program.parse(process.argv);
|
|
121
|
-
async function
|
|
67
|
+
async function createUserDataDir() {
|
|
122
68
|
let cacheDirectory;
|
|
123
69
|
if (process.platform === 'linux')
|
|
124
70
|
cacheDirectory = process.env.XDG_CACHE_HOME || path_1.default.join(os_1.default.homedir(), '.cache');
|
|
@@ -132,3 +78,64 @@ async function userDataDir() {
|
|
|
132
78
|
await fs_1.default.promises.mkdir(result, { recursive: true });
|
|
133
79
|
return result;
|
|
134
80
|
}
|
|
81
|
+
async function startSSEServer(port, serverList) {
|
|
82
|
+
const sessions = new Map();
|
|
83
|
+
const httpServer = http_1.default.createServer(async (req, res) => {
|
|
84
|
+
if (req.method === 'POST') {
|
|
85
|
+
const searchParams = new URL(`http://localhost${req.url}`).searchParams;
|
|
86
|
+
const sessionId = searchParams.get('sessionId');
|
|
87
|
+
if (!sessionId) {
|
|
88
|
+
res.statusCode = 400;
|
|
89
|
+
res.end('Missing sessionId');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const transport = sessions.get(sessionId);
|
|
93
|
+
if (!transport) {
|
|
94
|
+
res.statusCode = 404;
|
|
95
|
+
res.end('Session not found');
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
await transport.handlePostMessage(req, res);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
else if (req.method === 'GET') {
|
|
102
|
+
const transport = new sse_js_1.SSEServerTransport('/sse', res);
|
|
103
|
+
sessions.set(transport.sessionId, transport);
|
|
104
|
+
const server = await serverList.create();
|
|
105
|
+
res.on('close', () => {
|
|
106
|
+
sessions.delete(transport.sessionId);
|
|
107
|
+
serverList.close(server).catch(e => console.error(e));
|
|
108
|
+
});
|
|
109
|
+
await server.connect(transport);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
res.statusCode = 405;
|
|
114
|
+
res.end('Method not allowed');
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
httpServer.listen(port, () => {
|
|
118
|
+
const address = httpServer.address();
|
|
119
|
+
(0, assert_1.default)(address, 'Could not bind server socket');
|
|
120
|
+
let url;
|
|
121
|
+
if (typeof address === 'string') {
|
|
122
|
+
url = address;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
const resolvedPort = address.port;
|
|
126
|
+
let resolvedHost = address.family === 'IPv4' ? address.address : `[${address.address}]`;
|
|
127
|
+
if (resolvedHost === '0.0.0.0' || resolvedHost === '[::]')
|
|
128
|
+
resolvedHost = 'localhost';
|
|
129
|
+
url = `http://${resolvedHost}:${resolvedPort}`;
|
|
130
|
+
}
|
|
131
|
+
console.log(`Listening on ${url}`);
|
|
132
|
+
console.log('Put this in your client config:');
|
|
133
|
+
console.log(JSON.stringify({
|
|
134
|
+
'mcpServers': {
|
|
135
|
+
'playwright': {
|
|
136
|
+
'url': `${url}/sse`
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}, undefined, 2));
|
|
140
|
+
});
|
|
141
|
+
}
|
package/lib/server.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.ServerList = void 0;
|
|
18
19
|
exports.createServerWithTools = createServerWithTools;
|
|
19
20
|
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
20
21
|
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
@@ -67,3 +68,25 @@ function createServerWithTools(options) {
|
|
|
67
68
|
};
|
|
68
69
|
return server;
|
|
69
70
|
}
|
|
71
|
+
class ServerList {
|
|
72
|
+
_servers = [];
|
|
73
|
+
_serverFactory;
|
|
74
|
+
constructor(serverFactory) {
|
|
75
|
+
this._serverFactory = serverFactory;
|
|
76
|
+
}
|
|
77
|
+
async create() {
|
|
78
|
+
const server = this._serverFactory();
|
|
79
|
+
this._servers.push(server);
|
|
80
|
+
return server;
|
|
81
|
+
}
|
|
82
|
+
async close(server) {
|
|
83
|
+
const index = this._servers.indexOf(server);
|
|
84
|
+
if (index !== -1)
|
|
85
|
+
this._servers.splice(index, 1);
|
|
86
|
+
await server.close();
|
|
87
|
+
}
|
|
88
|
+
async closeAll() {
|
|
89
|
+
await Promise.all(this._servers.map(server => server.close()));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.ServerList = ServerList;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playwright/mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "Playwright Tools for MCP",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"watch": "tsc --watch",
|
|
21
21
|
"test": "playwright test",
|
|
22
22
|
"clean": "rm -rf lib",
|
|
23
|
-
"publish": "npm run clean && npm run build && npm run test && npm publish"
|
|
23
|
+
"npm-publish": "npm run clean && npm run build && npm run test && npm publish"
|
|
24
24
|
},
|
|
25
25
|
"exports": {
|
|
26
26
|
"./package.json": "./package.json",
|