kibbutz-mcp 1.0.2 → 1.0.4
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/build/index.js +52 -20
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -9,14 +9,14 @@ import { spawn } from 'child_process';
|
|
|
9
9
|
import fs from 'fs';
|
|
10
10
|
import path from 'path';
|
|
11
11
|
let mcpSocket = null;
|
|
12
|
-
let
|
|
12
|
+
let pingSocket = null;
|
|
13
13
|
let token = null;
|
|
14
14
|
let port = null;
|
|
15
15
|
let wsPromise = null;
|
|
16
16
|
const map = new Map();
|
|
17
17
|
const server = new McpServer({
|
|
18
18
|
name: 'kibbutz-mcp',
|
|
19
|
-
version: '1.0.
|
|
19
|
+
version: '1.0.4',
|
|
20
20
|
});
|
|
21
21
|
function findChromeExecutable() {
|
|
22
22
|
const platform = process.platform;
|
|
@@ -65,7 +65,7 @@ const sendAndWaitForReply = async (message, args) => {
|
|
|
65
65
|
const timeout = setTimeout(() => {
|
|
66
66
|
wsPromise = null;
|
|
67
67
|
reject(new Error('Timeout waiting for MCP extension WebSocket connection'));
|
|
68
|
-
},
|
|
68
|
+
}, 2000);
|
|
69
69
|
wsPromise = () => {
|
|
70
70
|
clearTimeout(timeout);
|
|
71
71
|
resolve();
|
|
@@ -75,19 +75,15 @@ const sendAndWaitForReply = async (message, args) => {
|
|
|
75
75
|
}
|
|
76
76
|
catch (error) { }
|
|
77
77
|
}
|
|
78
|
-
if (!mcpSocket ||
|
|
78
|
+
if (!mcpSocket ||
|
|
79
|
+
!pingSocket ||
|
|
80
|
+
mcpSocket.readyState !== WebSocket.OPEN ||
|
|
81
|
+
pingSocket.readyState !== WebSocket.OPEN) {
|
|
79
82
|
return Promise.resolve({
|
|
80
83
|
content: [
|
|
81
84
|
{
|
|
82
85
|
type: 'text',
|
|
83
|
-
text: `Connection failed: The MCP server cannot reach the Chrome extension.
|
|
84
|
-
|
|
85
|
-
Please check the following:
|
|
86
|
-
1. Is the browser open?
|
|
87
|
-
2. Is the MCP switch inside the extension popup turned ON?
|
|
88
|
-
|
|
89
|
-
If the issue persists, copy and paste this URL into your browser address bar:
|
|
90
|
-
chrome-extension://bpfjmggaaiigpfahhmpmacfhlemnhhip/KIBBUTZ-MCP.html`,
|
|
86
|
+
text: `Connection failed: The MCP server cannot reach the Chrome extension. Open chrome-extension://bpfjmggaaiigpfahhmpmacfhlemnhhip/KIBBUTZ-MCP.html for troubleshooting.`,
|
|
91
87
|
},
|
|
92
88
|
],
|
|
93
89
|
isError: true,
|
|
@@ -145,6 +141,26 @@ server.registerTool('CLOSE_TABS_MCP', {
|
|
|
145
141
|
.describe('List of specific tab identifiers (integers) to close.'),
|
|
146
142
|
},
|
|
147
143
|
}, (args) => sendAndWaitForReply('CLOSE_TABS_MCP', args));
|
|
144
|
+
server.registerTool('PIN_TABS_MCP', {
|
|
145
|
+
title: 'Pin tabs',
|
|
146
|
+
description: 'Pin specific browser tabs using their unique IDs.',
|
|
147
|
+
inputSchema: {
|
|
148
|
+
tabIds: z
|
|
149
|
+
.array(z.number())
|
|
150
|
+
.min(1)
|
|
151
|
+
.describe('List of specific tab identifiers (integers) to pin.'),
|
|
152
|
+
},
|
|
153
|
+
}, (args) => sendAndWaitForReply('PIN_TABS_MCP', args));
|
|
154
|
+
server.registerTool('UNPIN_TABS_MCP', {
|
|
155
|
+
title: 'Unpin tabs',
|
|
156
|
+
description: 'Unpin specific browser tabs using their unique IDs.',
|
|
157
|
+
inputSchema: {
|
|
158
|
+
tabIds: z
|
|
159
|
+
.array(z.number())
|
|
160
|
+
.min(1)
|
|
161
|
+
.describe('List of specific tab identifiers (integers) to unpin.'),
|
|
162
|
+
},
|
|
163
|
+
}, (args) => sendAndWaitForReply('UNPIN_TABS_MCP', args));
|
|
148
164
|
server.registerTool('UNGROUP_TABS_MCP', {
|
|
149
165
|
title: 'Ungroup tabs',
|
|
150
166
|
description: 'Remove specific tabs from their assigned groups using their tab IDs. The tabs will become standalone.',
|
|
@@ -242,11 +258,19 @@ async function main() {
|
|
|
242
258
|
const wss = new WebSocketServer({ noServer: true });
|
|
243
259
|
const wssMcp = new WebSocketServer({ noServer: true });
|
|
244
260
|
wss.on('connection', (ws) => {
|
|
245
|
-
|
|
261
|
+
pingSocket = ws;
|
|
246
262
|
ws.on('error', console.error);
|
|
247
263
|
ws.on('close', () => {
|
|
248
|
-
if (
|
|
249
|
-
|
|
264
|
+
if (pingSocket === ws) {
|
|
265
|
+
pingSocket = null;
|
|
266
|
+
if (mcpSocket) {
|
|
267
|
+
wssMcp.clients.forEach((ws) => ws.terminate());
|
|
268
|
+
mcpSocket = null;
|
|
269
|
+
}
|
|
270
|
+
for (const { reject } of map.values()) {
|
|
271
|
+
reject(new Error('Extension WebSocket closed'));
|
|
272
|
+
}
|
|
273
|
+
map.clear();
|
|
250
274
|
}
|
|
251
275
|
});
|
|
252
276
|
});
|
|
@@ -277,24 +301,32 @@ async function main() {
|
|
|
277
301
|
});
|
|
278
302
|
});
|
|
279
303
|
httpServer.on('upgrade', (request, socket, head) => {
|
|
280
|
-
if (request.headers['sec-websocket-protocol'] !== token) {
|
|
281
|
-
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
|
282
|
-
socket.destroy();
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
304
|
const { pathname } = new URL(request.url || '', `wss:127.0.0.1:${port}`);
|
|
286
305
|
if (pathname === '/ping') {
|
|
306
|
+
if (request.headers['sec-websocket-protocol'] !== token) {
|
|
307
|
+
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
|
308
|
+
socket.destroy();
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
287
311
|
wss.clients.forEach((ws) => ws.terminate());
|
|
288
312
|
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
289
313
|
wss.emit('connection', ws, request);
|
|
290
314
|
});
|
|
291
315
|
}
|
|
292
316
|
else if (pathname === '/mcp') {
|
|
317
|
+
if (pingSocket?.readyState !== WebSocket.OPEN) {
|
|
318
|
+
socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
|
|
319
|
+
socket.destroy();
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
293
322
|
wssMcp.clients.forEach((ws) => ws.terminate());
|
|
294
323
|
wssMcp.handleUpgrade(request, socket, head, (ws) => {
|
|
295
324
|
wssMcp.emit('connection', ws, request);
|
|
296
325
|
});
|
|
297
326
|
}
|
|
327
|
+
else {
|
|
328
|
+
socket.destroy();
|
|
329
|
+
}
|
|
298
330
|
});
|
|
299
331
|
httpServer.listen(0, () => {
|
|
300
332
|
port = httpServer.address().port;
|