@leadbay/mcp 0.17.2 → 0.17.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/CHANGELOG.md +10 -0
- package/README.md +38 -1
- package/dist/bin.js +840 -31
- package/dist/http-server.js +822 -30
- package/dist/installer-electron.js +145 -200
- package/dist/installer-gui.js +77 -109
- package/package.json +3 -4
package/dist/installer-gui.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
// installer/installer-gui.ts
|
|
4
4
|
import { createServer as createServer2 } from "http";
|
|
5
5
|
import { randomUUID } from "crypto";
|
|
6
|
-
import { realpathSync } from "fs";
|
|
7
6
|
import { resolve as resolvePath } from "path";
|
|
8
7
|
import { fileURLToPath } from "url";
|
|
9
8
|
|
|
@@ -874,7 +873,7 @@ async function oauthLogin(opts) {
|
|
|
874
873
|
}
|
|
875
874
|
|
|
876
875
|
// installer/installer-gui.ts
|
|
877
|
-
var VERSION = "0.17.
|
|
876
|
+
var VERSION = "0.17.3";
|
|
878
877
|
var PORT = Number(process.env.LEADBAY_INSTALLER_PORT ?? 0);
|
|
879
878
|
var sessions = /* @__PURE__ */ new Map();
|
|
880
879
|
var OAUTH_BASE_URLS = {
|
|
@@ -1043,7 +1042,7 @@ async function install(body) {
|
|
|
1043
1042
|
].join("\n");
|
|
1044
1043
|
return { ok: results.some((result) => result.ok), output: sanitizeOutput(output), results };
|
|
1045
1044
|
}
|
|
1046
|
-
async function streamInstall(url, res) {
|
|
1045
|
+
async function streamInstall(url, res, onDone) {
|
|
1047
1046
|
cleanupSessions();
|
|
1048
1047
|
res.writeHead(200, {
|
|
1049
1048
|
"content-type": "text/event-stream; charset=utf-8",
|
|
@@ -1055,16 +1054,23 @@ async function streamInstall(url, res) {
|
|
|
1055
1054
|
const includeWrite = url.searchParams.get("write") !== "0";
|
|
1056
1055
|
const telemetryEnabled = url.searchParams.get("telemetry") !== "0";
|
|
1057
1056
|
const emit = (level, message) => sendSse(res, { level, message: sanitizeOutput(message) });
|
|
1057
|
+
const abort = (msg) => {
|
|
1058
|
+
emit("done", msg);
|
|
1059
|
+
res.end();
|
|
1060
|
+
};
|
|
1061
|
+
const finish = (msg) => {
|
|
1062
|
+
emit("done", msg);
|
|
1063
|
+
res.end();
|
|
1064
|
+
onDone?.();
|
|
1065
|
+
};
|
|
1058
1066
|
if (!session) {
|
|
1059
1067
|
emit("error", "Login expired. Go back and sign in again.");
|
|
1060
|
-
|
|
1061
|
-
res.end();
|
|
1068
|
+
abort("Install stopped.");
|
|
1062
1069
|
return;
|
|
1063
1070
|
}
|
|
1064
1071
|
if (!clientIds.length) {
|
|
1065
1072
|
emit("error", "Select at least one agent.");
|
|
1066
|
-
|
|
1067
|
-
res.end();
|
|
1073
|
+
abort("Install stopped.");
|
|
1068
1074
|
return;
|
|
1069
1075
|
}
|
|
1070
1076
|
emit("info", `Connected to ${session.accountLabel}.`);
|
|
@@ -1075,8 +1081,7 @@ async function streamInstall(url, res) {
|
|
|
1075
1081
|
const selectedHasOnlyManualSetup = selected.length > 0 && selected.every(isManualSetupClient);
|
|
1076
1082
|
if (!selected.length) {
|
|
1077
1083
|
emit("error", "No selected agents were detected on this machine.");
|
|
1078
|
-
|
|
1079
|
-
res.end();
|
|
1084
|
+
abort("Install stopped.");
|
|
1080
1085
|
return;
|
|
1081
1086
|
}
|
|
1082
1087
|
let okCount = 0;
|
|
@@ -1090,11 +1095,16 @@ async function streamInstall(url, res) {
|
|
|
1090
1095
|
emit("error", `${result.label}: ${result.message}`);
|
|
1091
1096
|
}
|
|
1092
1097
|
}
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1098
|
+
const summary = selectedHasOnlyManualSetup ? "Manual ChatGPT setup instructions ready." : `${okCount}/${selected.length} agent(s) installed, updated, or prepared.`;
|
|
1099
|
+
const closing = selectedHasOnlyManualSetup ? "Follow the manual setup instructions shown above." : "Restart your MCP client(s) to pick up the new server.";
|
|
1100
|
+
emit(okCount > 0 ? "success" : "error", summary);
|
|
1101
|
+
if (okCount > 0) {
|
|
1102
|
+
finish(closing);
|
|
1103
|
+
} else {
|
|
1104
|
+
abort(closing);
|
|
1105
|
+
}
|
|
1096
1106
|
}
|
|
1097
|
-
async function streamUninstall(url, res) {
|
|
1107
|
+
async function streamUninstall(url, res, onDone) {
|
|
1098
1108
|
res.writeHead(200, {
|
|
1099
1109
|
"content-type": "text/event-stream; charset=utf-8",
|
|
1100
1110
|
"cache-control": "no-cache, no-transform",
|
|
@@ -1102,18 +1112,25 @@ async function streamUninstall(url, res) {
|
|
|
1102
1112
|
});
|
|
1103
1113
|
const clientIds = (url.searchParams.get("clients") ?? "").split(",").filter(Boolean);
|
|
1104
1114
|
const emit = (level, message) => sendSse(res, { level, message });
|
|
1115
|
+
const abort = (msg) => {
|
|
1116
|
+
emit("done", msg);
|
|
1117
|
+
res.end();
|
|
1118
|
+
};
|
|
1119
|
+
const finish = (msg) => {
|
|
1120
|
+
emit("done", msg);
|
|
1121
|
+
res.end();
|
|
1122
|
+
onDone?.();
|
|
1123
|
+
};
|
|
1105
1124
|
if (!clientIds.length) {
|
|
1106
1125
|
emit("error", "Select at least one agent.");
|
|
1107
|
-
|
|
1108
|
-
res.end();
|
|
1126
|
+
abort("Uninstall stopped.");
|
|
1109
1127
|
return;
|
|
1110
1128
|
}
|
|
1111
1129
|
const detected = await detectClients();
|
|
1112
1130
|
const selected = detected.filter((c) => clientIds.includes(c.id));
|
|
1113
1131
|
if (!selected.length) {
|
|
1114
1132
|
emit("error", "No selected agents were detected on this machine.");
|
|
1115
|
-
|
|
1116
|
-
res.end();
|
|
1133
|
+
abort("Uninstall stopped.");
|
|
1117
1134
|
return;
|
|
1118
1135
|
}
|
|
1119
1136
|
let okCount = 0;
|
|
@@ -1137,8 +1154,7 @@ async function streamUninstall(url, res) {
|
|
|
1137
1154
|
}
|
|
1138
1155
|
}
|
|
1139
1156
|
emit(okCount > 0 ? "success" : "error", `${okCount}/${selected.length} agent(s) removed.`);
|
|
1140
|
-
|
|
1141
|
-
res.end();
|
|
1157
|
+
finish("Restart your MCP client(s) to complete the removal.");
|
|
1142
1158
|
}
|
|
1143
1159
|
function pageUninstallHtml() {
|
|
1144
1160
|
return `<!doctype html>
|
|
@@ -1338,8 +1354,15 @@ async function openBrowser(url) {
|
|
|
1338
1354
|
|
|
1339
1355
|
`);
|
|
1340
1356
|
}
|
|
1341
|
-
|
|
1357
|
+
function makeGuiServer(options, pageContent, extraRoutes, logLabel) {
|
|
1342
1358
|
let expectedHost = `127.0.0.1:${(options.port ?? PORT) || 0}`;
|
|
1359
|
+
let resolveDone;
|
|
1360
|
+
const done = new Promise((r) => {
|
|
1361
|
+
resolveDone = r;
|
|
1362
|
+
});
|
|
1363
|
+
const onDone = () => setTimeout(() => {
|
|
1364
|
+
resolveDone();
|
|
1365
|
+
}, 1500);
|
|
1343
1366
|
const server = createServer2(async (req, res) => {
|
|
1344
1367
|
if (!isAllowedOrigin(req, expectedHost)) {
|
|
1345
1368
|
sendJson(res, 403, { ok: false, error: "forbidden" });
|
|
@@ -1347,37 +1370,18 @@ async function startInstallerGui(options = {}) {
|
|
|
1347
1370
|
}
|
|
1348
1371
|
try {
|
|
1349
1372
|
if (req.method === "GET" && req.url === "/") {
|
|
1350
|
-
const raw =
|
|
1373
|
+
const raw = pageContent();
|
|
1351
1374
|
res.writeHead(200, { "content-type": "text/html; charset=utf-8", "content-length": Buffer.byteLength(raw) });
|
|
1352
1375
|
res.end(raw);
|
|
1353
1376
|
return;
|
|
1354
1377
|
}
|
|
1355
|
-
if (
|
|
1356
|
-
sendJson(res, 200, {
|
|
1357
|
-
os: formatInstallOsLabel(),
|
|
1358
|
-
hostedMcpUrl: HOSTED_MCP_URL,
|
|
1359
|
-
clients: await clientsWithConfiguredStatus()
|
|
1360
|
-
});
|
|
1361
|
-
return;
|
|
1362
|
-
}
|
|
1363
|
-
if (req.method === "POST" && req.url === "/api/oauth-login") {
|
|
1364
|
-
sendJson(res, 200, await loginWithOAuth());
|
|
1365
|
-
return;
|
|
1366
|
-
}
|
|
1367
|
-
if (req.method === "POST" && req.url === "/api/install") {
|
|
1368
|
-
sendJson(res, 200, await install(await readJson(req)));
|
|
1369
|
-
return;
|
|
1370
|
-
}
|
|
1371
|
-
if (req.method === "GET" && req.url?.startsWith("/api/install-stream")) {
|
|
1372
|
-
await streamInstall(new URL(req.url, "http://127.0.0.1"), res);
|
|
1373
|
-
return;
|
|
1374
|
-
}
|
|
1378
|
+
if (await extraRoutes(req, res, onDone)) return;
|
|
1375
1379
|
sendJson(res, 404, { ok: false, error: "not found" });
|
|
1376
1380
|
} catch (err) {
|
|
1377
1381
|
sendJson(res, 500, { ok: false, error: err?.message ?? String(err) });
|
|
1378
1382
|
}
|
|
1379
1383
|
});
|
|
1380
|
-
return
|
|
1384
|
+
return new Promise((resolve, reject) => {
|
|
1381
1385
|
server.once("error", reject);
|
|
1382
1386
|
server.listen(options.port ?? PORT, "127.0.0.1", async () => {
|
|
1383
1387
|
server.off("error", reject);
|
|
@@ -1385,82 +1389,46 @@ async function startInstallerGui(options = {}) {
|
|
|
1385
1389
|
const port = typeof address === "object" && address ? address.port : options.port ?? PORT;
|
|
1386
1390
|
expectedHost = `127.0.0.1:${port}`;
|
|
1387
1391
|
const url = `http://127.0.0.1:${port}/`;
|
|
1388
|
-
process.stderr.write(`Leadbay MCP
|
|
1392
|
+
process.stderr.write(`Leadbay MCP ${logLabel} GUI: ${url}
|
|
1389
1393
|
`);
|
|
1390
1394
|
if (options.openBrowser !== false) await openBrowser(url).catch(() => void 0);
|
|
1391
|
-
resolve({ url, close: () => new Promise((
|
|
1395
|
+
resolve({ url, done, close: () => new Promise((res, rej) => server.close((e) => e ? rej(e) : res())) });
|
|
1392
1396
|
});
|
|
1393
1397
|
});
|
|
1394
1398
|
}
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
return;
|
|
1399
|
+
function startInstallerGui(options = {}) {
|
|
1400
|
+
return makeGuiServer(options, pageHtml, async (req, res, onDone) => {
|
|
1401
|
+
if (req.method === "GET" && req.url === "/api/status") {
|
|
1402
|
+
sendJson(res, 200, { os: formatInstallOsLabel(), hostedMcpUrl: HOSTED_MCP_URL, clients: await clientsWithConfiguredStatus() });
|
|
1403
|
+
return true;
|
|
1401
1404
|
}
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
res.writeHead(200, { "content-type": "text/html; charset=utf-8", "content-length": Buffer.byteLength(raw) });
|
|
1406
|
-
res.end(raw);
|
|
1407
|
-
return;
|
|
1408
|
-
}
|
|
1409
|
-
if (req.method === "GET" && req.url === "/api/status") {
|
|
1410
|
-
sendJson(res, 200, {
|
|
1411
|
-
os: formatInstallOsLabel(),
|
|
1412
|
-
clients: await clientsWithConfiguredStatus()
|
|
1413
|
-
});
|
|
1414
|
-
return;
|
|
1415
|
-
}
|
|
1416
|
-
if (req.method === "GET" && req.url?.startsWith("/api/uninstall-stream")) {
|
|
1417
|
-
await streamUninstall(new URL(req.url, "http://127.0.0.1"), res);
|
|
1418
|
-
return;
|
|
1419
|
-
}
|
|
1420
|
-
sendJson(res, 404, { ok: false, error: "not found" });
|
|
1421
|
-
} catch (err) {
|
|
1422
|
-
sendJson(res, 500, { ok: false, error: err?.message ?? String(err) });
|
|
1405
|
+
if (req.method === "POST" && req.url === "/api/oauth-login") {
|
|
1406
|
+
sendJson(res, 200, await loginWithOAuth());
|
|
1407
|
+
return true;
|
|
1423
1408
|
}
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
`);
|
|
1435
|
-
if (options.openBrowser !== false) await openBrowser(url).catch(() => void 0);
|
|
1436
|
-
resolve({ url, close: () => new Promise((closeResolve, closeReject) => server.close((err) => err ? closeReject(err) : closeResolve())) });
|
|
1437
|
-
});
|
|
1438
|
-
});
|
|
1439
|
-
}
|
|
1440
|
-
async function main() {
|
|
1441
|
-
const uninstall = process.argv.includes("--uninstall");
|
|
1442
|
-
const handle = uninstall ? await startUninstallerGui({ openBrowser: !process.argv.includes("--no-open") }) : await startInstallerGui({ openBrowser: !process.argv.includes("--no-open") });
|
|
1443
|
-
await new Promise((resolve) => {
|
|
1444
|
-
process.once("SIGINT", () => resolve());
|
|
1445
|
-
process.once("SIGTERM", () => resolve());
|
|
1446
|
-
});
|
|
1447
|
-
await handle.close().catch(() => void 0);
|
|
1409
|
+
if (req.method === "POST" && req.url === "/api/install") {
|
|
1410
|
+
sendJson(res, 200, await install(await readJson(req)));
|
|
1411
|
+
return true;
|
|
1412
|
+
}
|
|
1413
|
+
if (req.method === "GET" && req.url?.startsWith("/api/install-stream")) {
|
|
1414
|
+
await streamInstall(new URL(req.url, "http://127.0.0.1"), res, onDone);
|
|
1415
|
+
return true;
|
|
1416
|
+
}
|
|
1417
|
+
return false;
|
|
1418
|
+
}, "installer");
|
|
1448
1419
|
}
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1420
|
+
function startUninstallerGui(options = {}) {
|
|
1421
|
+
return makeGuiServer(options, pageUninstallHtml, async (req, res, onDone) => {
|
|
1422
|
+
if (req.method === "GET" && req.url === "/api/status") {
|
|
1423
|
+
sendJson(res, 200, { os: formatInstallOsLabel(), clients: await clientsWithConfiguredStatus() });
|
|
1424
|
+
return true;
|
|
1425
|
+
}
|
|
1426
|
+
if (req.method === "GET" && req.url?.startsWith("/api/uninstall-stream")) {
|
|
1427
|
+
await streamUninstall(new URL(req.url, "http://127.0.0.1"), res, onDone);
|
|
1428
|
+
return true;
|
|
1429
|
+
}
|
|
1455
1430
|
return false;
|
|
1456
|
-
}
|
|
1457
|
-
})();
|
|
1458
|
-
if (isEntrypoint) {
|
|
1459
|
-
main().catch((err) => {
|
|
1460
|
-
process.stderr.write(`leadbay-mcp-installer: ${err?.message ?? err}
|
|
1461
|
-
`);
|
|
1462
|
-
process.exit(1);
|
|
1463
|
-
});
|
|
1431
|
+
}, "uninstaller");
|
|
1464
1432
|
}
|
|
1465
1433
|
export {
|
|
1466
1434
|
install,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leadbay/mcp",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.3",
|
|
4
4
|
"mcpName": "io.github.leadbay/leadbay-mcp",
|
|
5
5
|
"description": "Model Context Protocol (MCP) server for Leadbay — AI lead discovery, qualification, and enrichment for Claude Desktop, Cursor, and Claude Code.",
|
|
6
6
|
"type": "module",
|
|
@@ -28,9 +28,8 @@
|
|
|
28
28
|
"test": "vitest run",
|
|
29
29
|
"test:smoke": "vitest run --config vitest.smoke.config.ts",
|
|
30
30
|
"prepublishOnly": "pnpm run build && pnpm run typecheck && pnpm run test",
|
|
31
|
-
"installer": "pnpm run build &&
|
|
32
|
-
"installer:gui": "pnpm run installer"
|
|
33
|
-
"installer:gui:web": "pnpm run build && node dist/installer-gui.js"
|
|
31
|
+
"installer": "pnpm run build && node dist/installer-electron.js",
|
|
32
|
+
"installer:gui": "pnpm run installer"
|
|
34
33
|
},
|
|
35
34
|
"dependencies": {
|
|
36
35
|
"@hono/node-server": "^1.13.7",
|