kibbutz-mcp 1.0.1 → 1.0.3
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 +92 -64
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -8,13 +8,15 @@ import { z } from 'zod';
|
|
|
8
8
|
import { spawn } from 'child_process';
|
|
9
9
|
import fs from 'fs';
|
|
10
10
|
import path from 'path';
|
|
11
|
-
let
|
|
12
|
-
let
|
|
11
|
+
let mcpSocket = null;
|
|
12
|
+
let pingSocket = null;
|
|
13
13
|
let token = null;
|
|
14
|
+
let port = null;
|
|
15
|
+
let wsPromise = null;
|
|
14
16
|
const map = new Map();
|
|
15
17
|
const server = new McpServer({
|
|
16
18
|
name: 'kibbutz-mcp',
|
|
17
|
-
version: '1.0.
|
|
19
|
+
version: '1.0.3',
|
|
18
20
|
});
|
|
19
21
|
function findChromeExecutable() {
|
|
20
22
|
const platform = process.platform;
|
|
@@ -56,20 +58,32 @@ function openChromeWithExtension(wsPort) {
|
|
|
56
58
|
});
|
|
57
59
|
child.unref();
|
|
58
60
|
}
|
|
59
|
-
const sendAndWaitForReply = (message, args) => {
|
|
60
|
-
if (!
|
|
61
|
+
const sendAndWaitForReply = async (message, args) => {
|
|
62
|
+
if (!token) {
|
|
63
|
+
try {
|
|
64
|
+
await new Promise((resolve, reject) => {
|
|
65
|
+
const timeout = setTimeout(() => {
|
|
66
|
+
wsPromise = null;
|
|
67
|
+
reject(new Error('Timeout waiting for MCP extension WebSocket connection'));
|
|
68
|
+
}, 2000);
|
|
69
|
+
wsPromise = () => {
|
|
70
|
+
clearTimeout(timeout);
|
|
71
|
+
resolve();
|
|
72
|
+
};
|
|
73
|
+
openChromeWithExtension(port);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
catch (error) { }
|
|
77
|
+
}
|
|
78
|
+
if (!mcpSocket ||
|
|
79
|
+
!pingSocket ||
|
|
80
|
+
mcpSocket.readyState !== WebSocket.OPEN ||
|
|
81
|
+
pingSocket.readyState !== WebSocket.OPEN) {
|
|
61
82
|
return Promise.resolve({
|
|
62
83
|
content: [
|
|
63
84
|
{
|
|
64
85
|
type: 'text',
|
|
65
|
-
text: `Connection failed: The MCP server cannot reach the Chrome extension.
|
|
66
|
-
|
|
67
|
-
Please check the following:
|
|
68
|
-
1. Is the browser open?
|
|
69
|
-
2. Is the MCP switch inside the extension popup turned ON?
|
|
70
|
-
|
|
71
|
-
If the issue persists, copy and paste this URL into your browser address bar:
|
|
72
|
-
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.`,
|
|
73
87
|
},
|
|
74
88
|
],
|
|
75
89
|
isError: true,
|
|
@@ -86,7 +100,7 @@ chrome-extension://bpfjmggaaiigpfahhmpmacfhlemnhhip/kibbutz-mcp.html`,
|
|
|
86
100
|
},
|
|
87
101
|
reject,
|
|
88
102
|
});
|
|
89
|
-
|
|
103
|
+
mcpSocket?.send(JSON.stringify({
|
|
90
104
|
id,
|
|
91
105
|
message,
|
|
92
106
|
args,
|
|
@@ -220,30 +234,36 @@ server.registerTool('UPDATE_GROUP_MCP', {
|
|
|
220
234
|
},
|
|
221
235
|
}, (args) => sendAndWaitForReply('UPDATE_GROUP_MCP', args));
|
|
222
236
|
async function main() {
|
|
223
|
-
const httpServer = createServer(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
237
|
+
const httpServer = createServer();
|
|
238
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
239
|
+
const wssMcp = new WebSocketServer({ noServer: true });
|
|
240
|
+
wss.on('connection', (ws) => {
|
|
241
|
+
pingSocket = ws;
|
|
242
|
+
ws.on('error', console.error);
|
|
243
|
+
ws.on('close', () => {
|
|
244
|
+
if (pingSocket === ws) {
|
|
245
|
+
pingSocket = null;
|
|
246
|
+
if (mcpSocket) {
|
|
247
|
+
wssMcp.clients.forEach((ws) => ws.terminate());
|
|
248
|
+
mcpSocket = null;
|
|
249
|
+
}
|
|
250
|
+
for (const { reject } of map.values()) {
|
|
251
|
+
reject(new Error('Extension WebSocket closed'));
|
|
252
|
+
}
|
|
253
|
+
map.clear();
|
|
230
254
|
}
|
|
231
|
-
|
|
232
|
-
res.end(JSON.stringify({ verify }));
|
|
233
|
-
}
|
|
234
|
-
catch (e) {
|
|
235
|
-
console.error('Failed to parse URL:', req.url, e);
|
|
236
|
-
res.writeHead(404);
|
|
237
|
-
res.end(JSON.stringify({ verify: false }));
|
|
238
|
-
}
|
|
255
|
+
});
|
|
239
256
|
});
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
257
|
+
wssMcp.on('connection', (ws) => {
|
|
258
|
+
mcpSocket = ws;
|
|
259
|
+
if (wsPromise) {
|
|
260
|
+
wsPromise();
|
|
261
|
+
wsPromise = null;
|
|
262
|
+
}
|
|
243
263
|
ws.on('error', console.error);
|
|
244
264
|
ws.on('close', () => {
|
|
245
|
-
if (
|
|
246
|
-
|
|
265
|
+
if (mcpSocket === ws) {
|
|
266
|
+
mcpSocket = null;
|
|
247
267
|
}
|
|
248
268
|
for (const { reject } of map.values()) {
|
|
249
269
|
reject(new Error('Extension WebSocket closed'));
|
|
@@ -251,49 +271,57 @@ async function main() {
|
|
|
251
271
|
map.clear();
|
|
252
272
|
});
|
|
253
273
|
ws.on('message', (data) => {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const pending = map.get(response.id);
|
|
261
|
-
pending.resolve(response.result);
|
|
262
|
-
map.delete(response.id);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
catch (_e) {
|
|
266
|
-
console.error('Failed to parse message from extension:', data);
|
|
274
|
+
const text = data.toString();
|
|
275
|
+
const response = JSON.parse(text);
|
|
276
|
+
if (response.id && map.has(response.id)) {
|
|
277
|
+
const pending = map.get(response.id);
|
|
278
|
+
pending.resolve(response.result);
|
|
279
|
+
map.delete(response.id);
|
|
267
280
|
}
|
|
268
281
|
});
|
|
269
282
|
});
|
|
270
283
|
httpServer.on('upgrade', (request, socket, head) => {
|
|
271
|
-
|
|
272
|
-
|
|
284
|
+
const { pathname } = new URL(request.url || '', `wss:127.0.0.1:${port}`);
|
|
285
|
+
if (pathname === '/ping') {
|
|
286
|
+
if (request.headers['sec-websocket-protocol'] !== token) {
|
|
287
|
+
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
|
288
|
+
socket.destroy();
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
wss.clients.forEach((ws) => ws.terminate());
|
|
292
|
+
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
293
|
+
wss.emit('connection', ws, request);
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
else if (pathname === '/mcp') {
|
|
297
|
+
if (pingSocket?.readyState !== WebSocket.OPEN) {
|
|
298
|
+
socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
|
|
299
|
+
socket.destroy();
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
wssMcp.clients.forEach((ws) => ws.terminate());
|
|
303
|
+
wssMcp.handleUpgrade(request, socket, head, (ws) => {
|
|
304
|
+
wssMcp.emit('connection', ws, request);
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
273
308
|
socket.destroy();
|
|
274
|
-
return;
|
|
275
309
|
}
|
|
276
|
-
wss.clients.forEach((ws) => ws.terminate());
|
|
277
|
-
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
278
|
-
wss.emit('connection', ws, request);
|
|
279
|
-
});
|
|
280
310
|
});
|
|
281
311
|
httpServer.listen(0, () => {
|
|
282
|
-
|
|
283
|
-
openChromeWithExtension(port);
|
|
312
|
+
port = httpServer.address().port;
|
|
284
313
|
});
|
|
285
314
|
const transport = new StdioServerTransport();
|
|
286
315
|
await server.connect(transport);
|
|
287
316
|
process.stdin.on('end', () => {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
317
|
+
wss.clients.forEach((ws) => ws.terminate());
|
|
318
|
+
wssMcp.clients.forEach((ws) => ws.terminate());
|
|
319
|
+
});
|
|
320
|
+
process.on('SIGTERM', () => {
|
|
321
|
+
wss.clients.forEach((ws) => ws.terminate());
|
|
322
|
+
wssMcp.clients.forEach((ws) => ws.terminate());
|
|
294
323
|
});
|
|
295
324
|
}
|
|
296
|
-
main().catch((
|
|
297
|
-
console.error('Server error:', error);
|
|
325
|
+
main().catch(() => {
|
|
298
326
|
process.exit(1);
|
|
299
327
|
});
|