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