@leadbay/mcp 0.19.3 → 0.20.0
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 +5 -0
- package/dist/bin.js +77 -42
- package/dist/http-server.js +73 -40
- package/dist/installer-electron.js +1 -1
- package/dist/installer-gui.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog — @leadbay/mcp
|
|
2
2
|
|
|
3
|
+
## 0.20.0 — 2026-06-15
|
|
4
|
+
|
|
5
|
+
- **Proactive update proposal on a fresh session** (product#3742): the auto-update check already ran at boot, but the resulting proposal only reached the user if the agent happened to call `leadbay_account_status` — which a fresh session rarely does, so the "newer version available" prompt was effectively invisible. The cached `update_available` block now also rides along on `_meta.update_available` of the **first ordinary tool result** of a session while an upgrade is pending, gated once-per-version so it surfaces exactly once. `leadbay_account_status` keeps carrying it as a top-level field. The server-instruction paragraph now tells the agent to surface the `ask_user_input_v0` prompt whenever it sees the field on *any* response.
|
|
6
|
+
- **Installer asset is now `.dxt`, not `.mcpb`**: the release-asset picker prefers the `.dxt` bundle (falling back to `.mcpb` only when a release ships no `.dxt`). The field is renamed `mcpb_url` → `install_url` across `update_available`, the `leadbay_acknowledge_update` result, and the persisted update-state — with forward-migration of the legacy `latest_known_mcpb_url` key so existing users don't lose their cache.
|
|
7
|
+
|
|
3
8
|
## 0.19.3 — 2026-06-15
|
|
4
9
|
|
|
5
10
|
- **New tool `leadbay_send_feedback`**: delivers a user-authored message to the same destination as the web app's "Send feedback" form — the team's Sentry feedback inbox (the website form calls `Sentry.captureFeedback`; there is no Leadbay API endpoint, so the MCP reuses its already-initialized `@sentry/node`). User-initiated ("send feedback / report a bug / tell Leadbay…"), or offered on a tool error and sent only on explicit yes. Distinct from the silent, agent-detected, PostHog-only `leadbay_report_friction`: feedback is explicit, user-authored, and reaches the team's inbox. Honest delivery — if the Sentry transport isn't available it returns `sent:false`, never a false success; Sentry is flushed after capture so the event actually ships; identity is attached when it resolves (anonymous fallback rather than dropping the message). Write-gated (`LEADBAY_MCP_WRITE=1`) since it sends data outward.
|
package/dist/bin.js
CHANGED
|
@@ -16524,16 +16524,16 @@ var init_account_status = __esm({
|
|
|
16524
16524
|
properties: {
|
|
16525
16525
|
current_version: { type: "string" },
|
|
16526
16526
|
latest_version: { type: "string" },
|
|
16527
|
-
|
|
16527
|
+
install_url: {
|
|
16528
16528
|
type: "string",
|
|
16529
|
-
description: "Direct download URL for the
|
|
16529
|
+
description: "Direct download URL for the installer asset (.dxt, falling back to .mcpb)."
|
|
16530
16530
|
},
|
|
16531
16531
|
release_url: {
|
|
16532
16532
|
type: "string",
|
|
16533
16533
|
description: "GitHub release page (changelog)."
|
|
16534
16534
|
}
|
|
16535
16535
|
},
|
|
16536
|
-
required: ["current_version", "latest_version", "
|
|
16536
|
+
required: ["current_version", "latest_version", "install_url", "release_url"]
|
|
16537
16537
|
}
|
|
16538
16538
|
},
|
|
16539
16539
|
required: ["user", "organization"]
|
|
@@ -23584,10 +23584,13 @@ function initTelemetry(opts) {
|
|
|
23584
23584
|
|
|
23585
23585
|
// src/update-check.ts
|
|
23586
23586
|
var cachedInfo = null;
|
|
23587
|
-
var
|
|
23587
|
+
var inFlightCheck = null;
|
|
23588
23588
|
function getCachedUpdateInfo() {
|
|
23589
23589
|
return cachedInfo;
|
|
23590
23590
|
}
|
|
23591
|
+
function getInFlightCheck() {
|
|
23592
|
+
return inFlightCheck;
|
|
23593
|
+
}
|
|
23591
23594
|
var RELEASES_LATEST_URL = "https://api.github.com/repos/leadbay/leadclaw/releases/latest";
|
|
23592
23595
|
var CHECK_THROTTLE_MS = 24 * 60 * 60 * 1e3;
|
|
23593
23596
|
var FETCH_TIMEOUT_MS = 5e3;
|
|
@@ -23632,25 +23635,24 @@ function compareSemver(a, b) {
|
|
|
23632
23635
|
}
|
|
23633
23636
|
return 0;
|
|
23634
23637
|
}
|
|
23635
|
-
function
|
|
23638
|
+
function pickInstallAsset(rel) {
|
|
23636
23639
|
if (!Array.isArray(rel.assets)) return void 0;
|
|
23637
|
-
const mcpb = rel.assets.find(
|
|
23638
|
-
(a) => typeof a.name === "string" && a.name.endsWith(".mcpb")
|
|
23639
|
-
);
|
|
23640
|
-
if (mcpb?.browser_download_url) return mcpb.browser_download_url;
|
|
23641
23640
|
const dxt = rel.assets.find(
|
|
23642
23641
|
(a) => typeof a.name === "string" && a.name.endsWith(".dxt")
|
|
23643
23642
|
);
|
|
23644
|
-
|
|
23643
|
+
if (dxt?.browser_download_url) return dxt.browser_download_url;
|
|
23644
|
+
const mcpb = rel.assets.find(
|
|
23645
|
+
(a) => typeof a.name === "string" && a.name.endsWith(".mcpb")
|
|
23646
|
+
);
|
|
23647
|
+
return mcpb?.browser_download_url;
|
|
23645
23648
|
}
|
|
23646
|
-
|
|
23647
|
-
if (
|
|
23648
|
-
|
|
23649
|
-
|
|
23650
|
-
|
|
23651
|
-
|
|
23652
|
-
|
|
23653
|
-
}
|
|
23649
|
+
function checkForUpdate(opts) {
|
|
23650
|
+
if (inFlightCheck) return inFlightCheck;
|
|
23651
|
+
const p = doCheck(opts).finally(() => {
|
|
23652
|
+
if (inFlightCheck === p) inFlightCheck = null;
|
|
23653
|
+
});
|
|
23654
|
+
inFlightCheck = p;
|
|
23655
|
+
return p;
|
|
23654
23656
|
}
|
|
23655
23657
|
async function doCheck(opts) {
|
|
23656
23658
|
const now = opts.now ?? Date.now;
|
|
@@ -23659,11 +23661,11 @@ async function doCheck(opts) {
|
|
|
23659
23661
|
const currentVersion = opts.currentVersion;
|
|
23660
23662
|
const state = await opts.stateStore.read();
|
|
23661
23663
|
const within = now() - state.last_check_time < CHECK_THROTTLE_MS;
|
|
23662
|
-
if (!opts.force && within && state.latest_known_version && state.
|
|
23664
|
+
if (!opts.force && within && state.latest_known_version && state.latest_known_install_url && state.latest_known_release_url) {
|
|
23663
23665
|
const cached = buildInfoIfUpgrade(
|
|
23664
23666
|
currentVersion,
|
|
23665
23667
|
state.latest_known_version,
|
|
23666
|
-
state.
|
|
23668
|
+
state.latest_known_install_url,
|
|
23667
23669
|
state.latest_known_release_url,
|
|
23668
23670
|
state.suppressed_versions,
|
|
23669
23671
|
state.remind_until,
|
|
@@ -23717,18 +23719,18 @@ async function doCheck(opts) {
|
|
|
23717
23719
|
return null;
|
|
23718
23720
|
}
|
|
23719
23721
|
let latestVersion;
|
|
23720
|
-
let
|
|
23722
|
+
let installUrl;
|
|
23721
23723
|
let releaseUrl;
|
|
23722
23724
|
if (status === 200 && body) {
|
|
23723
23725
|
const parsed = body.tag_name ? parseTagName(body.tag_name) : null;
|
|
23724
23726
|
if (parsed) {
|
|
23725
23727
|
latestVersion = parsed;
|
|
23726
|
-
|
|
23728
|
+
installUrl = pickInstallAsset(body);
|
|
23727
23729
|
releaseUrl = body.html_url;
|
|
23728
23730
|
}
|
|
23729
23731
|
} else {
|
|
23730
23732
|
latestVersion = state.latest_known_version;
|
|
23731
|
-
|
|
23733
|
+
installUrl = state.latest_known_install_url;
|
|
23732
23734
|
releaseUrl = state.latest_known_release_url;
|
|
23733
23735
|
}
|
|
23734
23736
|
const persisted = await opts.stateStore.update((cur) => ({
|
|
@@ -23736,7 +23738,7 @@ async function doCheck(opts) {
|
|
|
23736
23738
|
last_check_time: now(),
|
|
23737
23739
|
etag: nextEtag,
|
|
23738
23740
|
latest_known_version: latestVersion ?? cur.latest_known_version,
|
|
23739
|
-
|
|
23741
|
+
latest_known_install_url: installUrl ?? cur.latest_known_install_url,
|
|
23740
23742
|
latest_known_release_url: releaseUrl ?? cur.latest_known_release_url
|
|
23741
23743
|
}));
|
|
23742
23744
|
opts.telemetry.captureUpdateCheck?.({
|
|
@@ -23746,7 +23748,7 @@ async function doCheck(opts) {
|
|
|
23746
23748
|
const info = buildInfoIfUpgrade(
|
|
23747
23749
|
currentVersion,
|
|
23748
23750
|
persisted.latest_known_version,
|
|
23749
|
-
persisted.
|
|
23751
|
+
persisted.latest_known_install_url,
|
|
23750
23752
|
persisted.latest_known_release_url,
|
|
23751
23753
|
persisted.suppressed_versions,
|
|
23752
23754
|
persisted.remind_until,
|
|
@@ -23755,15 +23757,15 @@ async function doCheck(opts) {
|
|
|
23755
23757
|
cachedInfo = info;
|
|
23756
23758
|
return info;
|
|
23757
23759
|
}
|
|
23758
|
-
function buildInfoIfUpgrade(currentVersion, latestVersion,
|
|
23759
|
-
if (!latestVersion || !
|
|
23760
|
+
function buildInfoIfUpgrade(currentVersion, latestVersion, installUrl, releaseUrl, suppressed, remindUntil, nowMs) {
|
|
23761
|
+
if (!latestVersion || !installUrl || !releaseUrl) return null;
|
|
23760
23762
|
if (compareSemver(latestVersion, currentVersion) <= 0) return null;
|
|
23761
23763
|
if (suppressed.includes(latestVersion)) return null;
|
|
23762
23764
|
if (remindUntil && remindUntil > nowMs) return null;
|
|
23763
23765
|
return {
|
|
23764
23766
|
current_version: currentVersion,
|
|
23765
23767
|
latest_version: latestVersion,
|
|
23766
|
-
|
|
23768
|
+
install_url: installUrl,
|
|
23767
23769
|
release_url: releaseUrl
|
|
23768
23770
|
};
|
|
23769
23771
|
}
|
|
@@ -23792,7 +23794,7 @@ async function recordRunningVersion(currentVersion, stateStore, telemetry) {
|
|
|
23792
23794
|
|
|
23793
23795
|
// src/update-tool.ts
|
|
23794
23796
|
var TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1e3;
|
|
23795
|
-
var DESCRIPTION = "Record the user's choice on an update prompt surfaced via `update_available` on leadbay_account_status. Pass `action: 'install' | 'remind_tomorrow' | 'skip'` and `version` (the `latest_version` from the prompt). On 'install', the server returns `{
|
|
23797
|
+
var DESCRIPTION = "Record the user's choice on an update prompt surfaced via `update_available` on leadbay_account_status. Pass `action: 'install' | 'remind_tomorrow' | 'skip'` and `version` (the `latest_version` from the prompt). On 'install', the server returns `{ install_url, release_url }` \u2014 show the user a clickable link to install_url (a `.dxt` bundle) so Claude Desktop's native installer opens it. On 'remind_tomorrow' the server suppresses the prompt for 24 hours. On 'skip' the version is suppressed permanently. Call this tool EXACTLY ONCE per prompt \u2014 do not loop, and do not call it speculatively when no update_available block is present.";
|
|
23796
23798
|
function buildAcknowledgeUpdateTool(opts) {
|
|
23797
23799
|
const now = opts.now ?? Date.now;
|
|
23798
23800
|
return {
|
|
@@ -23828,7 +23830,7 @@ function buildAcknowledgeUpdateTool(opts) {
|
|
|
23828
23830
|
action: { type: "string" },
|
|
23829
23831
|
version: { type: "string" },
|
|
23830
23832
|
message: { type: "string" },
|
|
23831
|
-
|
|
23833
|
+
install_url: { type: ["string", "null"] },
|
|
23832
23834
|
release_url: { type: ["string", "null"] }
|
|
23833
23835
|
},
|
|
23834
23836
|
required: ["ok", "action", "version", "message"]
|
|
@@ -23862,9 +23864,9 @@ function buildAcknowledgeUpdateTool(opts) {
|
|
|
23862
23864
|
ok: true,
|
|
23863
23865
|
action,
|
|
23864
23866
|
version,
|
|
23865
|
-
|
|
23867
|
+
install_url: state.latest_known_install_url ?? null,
|
|
23866
23868
|
release_url: state.latest_known_release_url ?? null,
|
|
23867
|
-
message: state.
|
|
23869
|
+
message: state.latest_known_install_url ? "Show the user the install_url (a .dxt bundle) as a clickable link \u2014 opening it in Claude Desktop runs the native installer." : "No installer URL is cached. Direct the user to the release_url to download manually."
|
|
23868
23870
|
};
|
|
23869
23871
|
}
|
|
23870
23872
|
if (action === "remind_tomorrow") {
|
|
@@ -23967,7 +23969,7 @@ function buildStartHereParagraph(has) {
|
|
|
23967
23969
|
}
|
|
23968
23970
|
function buildUpdateAvailableParagraph(has) {
|
|
23969
23971
|
if (!has("leadbay_acknowledge_update")) return null;
|
|
23970
|
-
return "MCP auto-update:
|
|
23972
|
+
return "MCP auto-update: an `update_available` field (`{ current_version, latest_version, install_url, release_url }`) means a newer MCP server release is published and the user has NOT suppressed it. It appears in TWO places: as a top-level field on `leadbay_account_status`, AND on `_meta.update_available` of the FIRST other tool result in a session while an update is pending (so a fresh session surfaces the proposal even without an account_status call). Whenever you see it on ANY response, surface a prompt via `ask_user_input_v0` with EXACTLY these three options: \"Install now\", \"Remind me tomorrow\", \"Skip this version\". Map the user's choice to `leadbay_acknowledge_update({ action: 'install' | 'remind_tomorrow' | 'skip', version: latest_version })`. On 'install', the tool returns `install_url` \u2014 render it as a clickable markdown link the user can open in Claude Desktop (the .dxt extension triggers the native installer). The user does NOT need to restart anything before clicking \u2014 the new server takes effect on the next MCP session. Prompt the user ONCE per session per version \u2014 don't re-prompt within the same chat after they've acknowledged.";
|
|
23971
23973
|
}
|
|
23972
23974
|
function buildRhythmParagraph(has) {
|
|
23973
23975
|
if (has("leadbay_report_outreach")) {
|
|
@@ -24262,16 +24264,47 @@ function buildServer(client, opts = {}) {
|
|
|
24262
24264
|
);
|
|
24263
24265
|
});
|
|
24264
24266
|
};
|
|
24265
|
-
const
|
|
24266
|
-
|
|
24267
|
+
const UPDATE_SURFACE_WAIT_MS = 1500;
|
|
24268
|
+
const maybeAttachUpdate = async (toolName, result) => {
|
|
24267
24269
|
if (!opts.updateStateStore) return;
|
|
24268
24270
|
if (result === null || typeof result !== "object" || Array.isArray(result)) {
|
|
24269
24271
|
return;
|
|
24270
24272
|
}
|
|
24271
|
-
|
|
24273
|
+
if (result.error === true) {
|
|
24274
|
+
return;
|
|
24275
|
+
}
|
|
24276
|
+
let info = getCachedUpdateInfo();
|
|
24277
|
+
if (!info) {
|
|
24278
|
+
const inflight = getInFlightCheck();
|
|
24279
|
+
if (inflight) {
|
|
24280
|
+
const settled = inflight.catch((err) => {
|
|
24281
|
+
opts.logger?.warn?.(
|
|
24282
|
+
`update_check.surface_await_failed ${err?.message ?? err}`
|
|
24283
|
+
);
|
|
24284
|
+
return null;
|
|
24285
|
+
});
|
|
24286
|
+
info = await Promise.race([
|
|
24287
|
+
settled,
|
|
24288
|
+
new Promise(
|
|
24289
|
+
(resolve) => setTimeout(() => resolve(null), UPDATE_SURFACE_WAIT_MS)
|
|
24290
|
+
)
|
|
24291
|
+
]);
|
|
24292
|
+
info = getCachedUpdateInfo() ?? info;
|
|
24293
|
+
}
|
|
24294
|
+
}
|
|
24272
24295
|
if (!info) return;
|
|
24273
|
-
|
|
24274
|
-
|
|
24296
|
+
const isAccountStatus = toolName === "leadbay_account_status";
|
|
24297
|
+
const alreadyPrompted = promptedVersionsThisSession.has(info.latest_version);
|
|
24298
|
+
if (!isAccountStatus && alreadyPrompted) return;
|
|
24299
|
+
if (isAccountStatus) {
|
|
24300
|
+
result.update_available = info;
|
|
24301
|
+
} else {
|
|
24302
|
+
const envelope = result;
|
|
24303
|
+
const target = envelope.__markdown_envelope === true && envelope.structured !== null && typeof envelope.structured === "object" && !Array.isArray(envelope.structured) ? envelope.structured : envelope;
|
|
24304
|
+
const existingMeta = target._meta && typeof target._meta === "object" && !Array.isArray(target._meta) ? target._meta : {};
|
|
24305
|
+
target._meta = { ...existingMeta, update_available: info };
|
|
24306
|
+
}
|
|
24307
|
+
if (!alreadyPrompted) {
|
|
24275
24308
|
promptedVersionsThisSession.add(info.latest_version);
|
|
24276
24309
|
telemetry.captureUpdatePrompted?.({
|
|
24277
24310
|
current_version: serverVersion,
|
|
@@ -24451,7 +24484,7 @@ function buildServer(client, opts = {}) {
|
|
|
24451
24484
|
// tool reports honestly when telemetry is off.
|
|
24452
24485
|
sendFeedback: (message, fbOpts) => telemetry.captureFeedback(message, fbOpts)
|
|
24453
24486
|
});
|
|
24454
|
-
maybeAttachUpdate(name, result);
|
|
24487
|
+
await maybeAttachUpdate(name, result);
|
|
24455
24488
|
maybeAttachNotifications(result);
|
|
24456
24489
|
if (result && typeof result === "object" && result.error === true) {
|
|
24457
24490
|
const envText = formatErrorForLLM(result);
|
|
@@ -25461,8 +25494,10 @@ var UpdateStateStore = class {
|
|
|
25461
25494
|
if (typeof r.latest_known_version === "string") {
|
|
25462
25495
|
out.latest_known_version = r.latest_known_version;
|
|
25463
25496
|
}
|
|
25464
|
-
if (typeof r.
|
|
25465
|
-
out.
|
|
25497
|
+
if (typeof r.latest_known_install_url === "string") {
|
|
25498
|
+
out.latest_known_install_url = r.latest_known_install_url;
|
|
25499
|
+
} else if (typeof r.latest_known_mcpb_url === "string") {
|
|
25500
|
+
out.latest_known_install_url = r.latest_known_mcpb_url;
|
|
25466
25501
|
}
|
|
25467
25502
|
if (typeof r.latest_known_release_url === "string") {
|
|
25468
25503
|
out.latest_known_release_url = r.latest_known_release_url;
|
|
@@ -25890,7 +25925,7 @@ var OAUTH_BASE_URLS = {
|
|
|
25890
25925
|
fr: "https://staging.api.leadbay.app"
|
|
25891
25926
|
}
|
|
25892
25927
|
};
|
|
25893
|
-
var VERSION = "0.
|
|
25928
|
+
var VERSION = "0.20.0";
|
|
25894
25929
|
var HELP = `
|
|
25895
25930
|
leadbay-mcp ${VERSION} \u2014 Leadbay Model Context Protocol server
|
|
25896
25931
|
|
package/dist/http-server.js
CHANGED
|
@@ -16779,16 +16779,16 @@ var accountStatus = {
|
|
|
16779
16779
|
properties: {
|
|
16780
16780
|
current_version: { type: "string" },
|
|
16781
16781
|
latest_version: { type: "string" },
|
|
16782
|
-
|
|
16782
|
+
install_url: {
|
|
16783
16783
|
type: "string",
|
|
16784
|
-
description: "Direct download URL for the
|
|
16784
|
+
description: "Direct download URL for the installer asset (.dxt, falling back to .mcpb)."
|
|
16785
16785
|
},
|
|
16786
16786
|
release_url: {
|
|
16787
16787
|
type: "string",
|
|
16788
16788
|
description: "GitHub release page (changelog)."
|
|
16789
16789
|
}
|
|
16790
16790
|
},
|
|
16791
|
-
required: ["current_version", "latest_version", "
|
|
16791
|
+
required: ["current_version", "latest_version", "install_url", "release_url"]
|
|
16792
16792
|
}
|
|
16793
16793
|
},
|
|
16794
16794
|
required: ["user", "organization"]
|
|
@@ -21262,10 +21262,13 @@ var NOOP_TELEMETRY = {
|
|
|
21262
21262
|
|
|
21263
21263
|
// src/update-check.ts
|
|
21264
21264
|
var cachedInfo = null;
|
|
21265
|
-
var
|
|
21265
|
+
var inFlightCheck = null;
|
|
21266
21266
|
function getCachedUpdateInfo() {
|
|
21267
21267
|
return cachedInfo;
|
|
21268
21268
|
}
|
|
21269
|
+
function getInFlightCheck() {
|
|
21270
|
+
return inFlightCheck;
|
|
21271
|
+
}
|
|
21269
21272
|
var RELEASES_LATEST_URL = "https://api.github.com/repos/leadbay/leadclaw/releases/latest";
|
|
21270
21273
|
var CHECK_THROTTLE_MS = 24 * 60 * 60 * 1e3;
|
|
21271
21274
|
var FETCH_TIMEOUT_MS = 5e3;
|
|
@@ -21310,25 +21313,24 @@ function compareSemver(a, b) {
|
|
|
21310
21313
|
}
|
|
21311
21314
|
return 0;
|
|
21312
21315
|
}
|
|
21313
|
-
function
|
|
21316
|
+
function pickInstallAsset(rel) {
|
|
21314
21317
|
if (!Array.isArray(rel.assets)) return void 0;
|
|
21315
|
-
const mcpb = rel.assets.find(
|
|
21316
|
-
(a) => typeof a.name === "string" && a.name.endsWith(".mcpb")
|
|
21317
|
-
);
|
|
21318
|
-
if (mcpb?.browser_download_url) return mcpb.browser_download_url;
|
|
21319
21318
|
const dxt = rel.assets.find(
|
|
21320
21319
|
(a) => typeof a.name === "string" && a.name.endsWith(".dxt")
|
|
21321
21320
|
);
|
|
21322
|
-
|
|
21321
|
+
if (dxt?.browser_download_url) return dxt.browser_download_url;
|
|
21322
|
+
const mcpb = rel.assets.find(
|
|
21323
|
+
(a) => typeof a.name === "string" && a.name.endsWith(".mcpb")
|
|
21324
|
+
);
|
|
21325
|
+
return mcpb?.browser_download_url;
|
|
21323
21326
|
}
|
|
21324
|
-
|
|
21325
|
-
if (
|
|
21326
|
-
|
|
21327
|
-
|
|
21328
|
-
|
|
21329
|
-
|
|
21330
|
-
|
|
21331
|
-
}
|
|
21327
|
+
function checkForUpdate(opts) {
|
|
21328
|
+
if (inFlightCheck) return inFlightCheck;
|
|
21329
|
+
const p = doCheck(opts).finally(() => {
|
|
21330
|
+
if (inFlightCheck === p) inFlightCheck = null;
|
|
21331
|
+
});
|
|
21332
|
+
inFlightCheck = p;
|
|
21333
|
+
return p;
|
|
21332
21334
|
}
|
|
21333
21335
|
async function doCheck(opts) {
|
|
21334
21336
|
const now = opts.now ?? Date.now;
|
|
@@ -21337,11 +21339,11 @@ async function doCheck(opts) {
|
|
|
21337
21339
|
const currentVersion = opts.currentVersion;
|
|
21338
21340
|
const state = await opts.stateStore.read();
|
|
21339
21341
|
const within = now() - state.last_check_time < CHECK_THROTTLE_MS;
|
|
21340
|
-
if (!opts.force && within && state.latest_known_version && state.
|
|
21342
|
+
if (!opts.force && within && state.latest_known_version && state.latest_known_install_url && state.latest_known_release_url) {
|
|
21341
21343
|
const cached = buildInfoIfUpgrade(
|
|
21342
21344
|
currentVersion,
|
|
21343
21345
|
state.latest_known_version,
|
|
21344
|
-
state.
|
|
21346
|
+
state.latest_known_install_url,
|
|
21345
21347
|
state.latest_known_release_url,
|
|
21346
21348
|
state.suppressed_versions,
|
|
21347
21349
|
state.remind_until,
|
|
@@ -21395,18 +21397,18 @@ async function doCheck(opts) {
|
|
|
21395
21397
|
return null;
|
|
21396
21398
|
}
|
|
21397
21399
|
let latestVersion;
|
|
21398
|
-
let
|
|
21400
|
+
let installUrl;
|
|
21399
21401
|
let releaseUrl;
|
|
21400
21402
|
if (status === 200 && body) {
|
|
21401
21403
|
const parsed = body.tag_name ? parseTagName(body.tag_name) : null;
|
|
21402
21404
|
if (parsed) {
|
|
21403
21405
|
latestVersion = parsed;
|
|
21404
|
-
|
|
21406
|
+
installUrl = pickInstallAsset(body);
|
|
21405
21407
|
releaseUrl = body.html_url;
|
|
21406
21408
|
}
|
|
21407
21409
|
} else {
|
|
21408
21410
|
latestVersion = state.latest_known_version;
|
|
21409
|
-
|
|
21411
|
+
installUrl = state.latest_known_install_url;
|
|
21410
21412
|
releaseUrl = state.latest_known_release_url;
|
|
21411
21413
|
}
|
|
21412
21414
|
const persisted = await opts.stateStore.update((cur) => ({
|
|
@@ -21414,7 +21416,7 @@ async function doCheck(opts) {
|
|
|
21414
21416
|
last_check_time: now(),
|
|
21415
21417
|
etag: nextEtag,
|
|
21416
21418
|
latest_known_version: latestVersion ?? cur.latest_known_version,
|
|
21417
|
-
|
|
21419
|
+
latest_known_install_url: installUrl ?? cur.latest_known_install_url,
|
|
21418
21420
|
latest_known_release_url: releaseUrl ?? cur.latest_known_release_url
|
|
21419
21421
|
}));
|
|
21420
21422
|
opts.telemetry.captureUpdateCheck?.({
|
|
@@ -21424,7 +21426,7 @@ async function doCheck(opts) {
|
|
|
21424
21426
|
const info = buildInfoIfUpgrade(
|
|
21425
21427
|
currentVersion,
|
|
21426
21428
|
persisted.latest_known_version,
|
|
21427
|
-
persisted.
|
|
21429
|
+
persisted.latest_known_install_url,
|
|
21428
21430
|
persisted.latest_known_release_url,
|
|
21429
21431
|
persisted.suppressed_versions,
|
|
21430
21432
|
persisted.remind_until,
|
|
@@ -21433,22 +21435,22 @@ async function doCheck(opts) {
|
|
|
21433
21435
|
cachedInfo = info;
|
|
21434
21436
|
return info;
|
|
21435
21437
|
}
|
|
21436
|
-
function buildInfoIfUpgrade(currentVersion, latestVersion,
|
|
21437
|
-
if (!latestVersion || !
|
|
21438
|
+
function buildInfoIfUpgrade(currentVersion, latestVersion, installUrl, releaseUrl, suppressed, remindUntil, nowMs) {
|
|
21439
|
+
if (!latestVersion || !installUrl || !releaseUrl) return null;
|
|
21438
21440
|
if (compareSemver(latestVersion, currentVersion) <= 0) return null;
|
|
21439
21441
|
if (suppressed.includes(latestVersion)) return null;
|
|
21440
21442
|
if (remindUntil && remindUntil > nowMs) return null;
|
|
21441
21443
|
return {
|
|
21442
21444
|
current_version: currentVersion,
|
|
21443
21445
|
latest_version: latestVersion,
|
|
21444
|
-
|
|
21446
|
+
install_url: installUrl,
|
|
21445
21447
|
release_url: releaseUrl
|
|
21446
21448
|
};
|
|
21447
21449
|
}
|
|
21448
21450
|
|
|
21449
21451
|
// src/update-tool.ts
|
|
21450
21452
|
var TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1e3;
|
|
21451
|
-
var DESCRIPTION = "Record the user's choice on an update prompt surfaced via `update_available` on leadbay_account_status. Pass `action: 'install' | 'remind_tomorrow' | 'skip'` and `version` (the `latest_version` from the prompt). On 'install', the server returns `{
|
|
21453
|
+
var DESCRIPTION = "Record the user's choice on an update prompt surfaced via `update_available` on leadbay_account_status. Pass `action: 'install' | 'remind_tomorrow' | 'skip'` and `version` (the `latest_version` from the prompt). On 'install', the server returns `{ install_url, release_url }` \u2014 show the user a clickable link to install_url (a `.dxt` bundle) so Claude Desktop's native installer opens it. On 'remind_tomorrow' the server suppresses the prompt for 24 hours. On 'skip' the version is suppressed permanently. Call this tool EXACTLY ONCE per prompt \u2014 do not loop, and do not call it speculatively when no update_available block is present.";
|
|
21452
21454
|
function buildAcknowledgeUpdateTool(opts) {
|
|
21453
21455
|
const now = opts.now ?? Date.now;
|
|
21454
21456
|
return {
|
|
@@ -21484,7 +21486,7 @@ function buildAcknowledgeUpdateTool(opts) {
|
|
|
21484
21486
|
action: { type: "string" },
|
|
21485
21487
|
version: { type: "string" },
|
|
21486
21488
|
message: { type: "string" },
|
|
21487
|
-
|
|
21489
|
+
install_url: { type: ["string", "null"] },
|
|
21488
21490
|
release_url: { type: ["string", "null"] }
|
|
21489
21491
|
},
|
|
21490
21492
|
required: ["ok", "action", "version", "message"]
|
|
@@ -21518,9 +21520,9 @@ function buildAcknowledgeUpdateTool(opts) {
|
|
|
21518
21520
|
ok: true,
|
|
21519
21521
|
action,
|
|
21520
21522
|
version,
|
|
21521
|
-
|
|
21523
|
+
install_url: state.latest_known_install_url ?? null,
|
|
21522
21524
|
release_url: state.latest_known_release_url ?? null,
|
|
21523
|
-
message: state.
|
|
21525
|
+
message: state.latest_known_install_url ? "Show the user the install_url (a .dxt bundle) as a clickable link \u2014 opening it in Claude Desktop runs the native installer." : "No installer URL is cached. Direct the user to the release_url to download manually."
|
|
21524
21526
|
};
|
|
21525
21527
|
}
|
|
21526
21528
|
if (action === "remind_tomorrow") {
|
|
@@ -21623,7 +21625,7 @@ function buildStartHereParagraph(has) {
|
|
|
21623
21625
|
}
|
|
21624
21626
|
function buildUpdateAvailableParagraph(has) {
|
|
21625
21627
|
if (!has("leadbay_acknowledge_update")) return null;
|
|
21626
|
-
return "MCP auto-update:
|
|
21628
|
+
return "MCP auto-update: an `update_available` field (`{ current_version, latest_version, install_url, release_url }`) means a newer MCP server release is published and the user has NOT suppressed it. It appears in TWO places: as a top-level field on `leadbay_account_status`, AND on `_meta.update_available` of the FIRST other tool result in a session while an update is pending (so a fresh session surfaces the proposal even without an account_status call). Whenever you see it on ANY response, surface a prompt via `ask_user_input_v0` with EXACTLY these three options: \"Install now\", \"Remind me tomorrow\", \"Skip this version\". Map the user's choice to `leadbay_acknowledge_update({ action: 'install' | 'remind_tomorrow' | 'skip', version: latest_version })`. On 'install', the tool returns `install_url` \u2014 render it as a clickable markdown link the user can open in Claude Desktop (the .dxt extension triggers the native installer). The user does NOT need to restart anything before clicking \u2014 the new server takes effect on the next MCP session. Prompt the user ONCE per session per version \u2014 don't re-prompt within the same chat after they've acknowledged.";
|
|
21627
21629
|
}
|
|
21628
21630
|
function buildRhythmParagraph(has) {
|
|
21629
21631
|
if (has("leadbay_report_outreach")) {
|
|
@@ -21918,16 +21920,47 @@ function buildServer(client, opts = {}) {
|
|
|
21918
21920
|
);
|
|
21919
21921
|
});
|
|
21920
21922
|
};
|
|
21921
|
-
const
|
|
21922
|
-
|
|
21923
|
+
const UPDATE_SURFACE_WAIT_MS = 1500;
|
|
21924
|
+
const maybeAttachUpdate = async (toolName, result) => {
|
|
21923
21925
|
if (!opts.updateStateStore) return;
|
|
21924
21926
|
if (result === null || typeof result !== "object" || Array.isArray(result)) {
|
|
21925
21927
|
return;
|
|
21926
21928
|
}
|
|
21927
|
-
|
|
21929
|
+
if (result.error === true) {
|
|
21930
|
+
return;
|
|
21931
|
+
}
|
|
21932
|
+
let info = getCachedUpdateInfo();
|
|
21933
|
+
if (!info) {
|
|
21934
|
+
const inflight = getInFlightCheck();
|
|
21935
|
+
if (inflight) {
|
|
21936
|
+
const settled = inflight.catch((err) => {
|
|
21937
|
+
opts.logger?.warn?.(
|
|
21938
|
+
`update_check.surface_await_failed ${err?.message ?? err}`
|
|
21939
|
+
);
|
|
21940
|
+
return null;
|
|
21941
|
+
});
|
|
21942
|
+
info = await Promise.race([
|
|
21943
|
+
settled,
|
|
21944
|
+
new Promise(
|
|
21945
|
+
(resolve) => setTimeout(() => resolve(null), UPDATE_SURFACE_WAIT_MS)
|
|
21946
|
+
)
|
|
21947
|
+
]);
|
|
21948
|
+
info = getCachedUpdateInfo() ?? info;
|
|
21949
|
+
}
|
|
21950
|
+
}
|
|
21928
21951
|
if (!info) return;
|
|
21929
|
-
|
|
21930
|
-
|
|
21952
|
+
const isAccountStatus = toolName === "leadbay_account_status";
|
|
21953
|
+
const alreadyPrompted = promptedVersionsThisSession.has(info.latest_version);
|
|
21954
|
+
if (!isAccountStatus && alreadyPrompted) return;
|
|
21955
|
+
if (isAccountStatus) {
|
|
21956
|
+
result.update_available = info;
|
|
21957
|
+
} else {
|
|
21958
|
+
const envelope = result;
|
|
21959
|
+
const target = envelope.__markdown_envelope === true && envelope.structured !== null && typeof envelope.structured === "object" && !Array.isArray(envelope.structured) ? envelope.structured : envelope;
|
|
21960
|
+
const existingMeta = target._meta && typeof target._meta === "object" && !Array.isArray(target._meta) ? target._meta : {};
|
|
21961
|
+
target._meta = { ...existingMeta, update_available: info };
|
|
21962
|
+
}
|
|
21963
|
+
if (!alreadyPrompted) {
|
|
21931
21964
|
promptedVersionsThisSession.add(info.latest_version);
|
|
21932
21965
|
telemetry.captureUpdatePrompted?.({
|
|
21933
21966
|
current_version: serverVersion,
|
|
@@ -22107,7 +22140,7 @@ function buildServer(client, opts = {}) {
|
|
|
22107
22140
|
// tool reports honestly when telemetry is off.
|
|
22108
22141
|
sendFeedback: (message, fbOpts) => telemetry.captureFeedback(message, fbOpts)
|
|
22109
22142
|
});
|
|
22110
|
-
maybeAttachUpdate(name, result);
|
|
22143
|
+
await maybeAttachUpdate(name, result);
|
|
22111
22144
|
maybeAttachNotifications(result);
|
|
22112
22145
|
if (result && typeof result === "object" && result.error === true) {
|
|
22113
22146
|
const envText = formatErrorForLLM(result);
|
|
@@ -22403,7 +22436,7 @@ function parseWriteEnv(env = process.env) {
|
|
|
22403
22436
|
}
|
|
22404
22437
|
|
|
22405
22438
|
// src/http-server.ts
|
|
22406
|
-
var VERSION = true ? "0.
|
|
22439
|
+
var VERSION = true ? "0.20.0" : "0.0.0-dev";
|
|
22407
22440
|
var PORT = Number(process.env.PORT ?? 8080);
|
|
22408
22441
|
var HOST = process.env.HOST ?? "0.0.0.0";
|
|
22409
22442
|
var sseSessions = /* @__PURE__ */ new Map();
|
|
@@ -1466,7 +1466,7 @@ var init_installer_gui = __esm({
|
|
|
1466
1466
|
init_install_dxt();
|
|
1467
1467
|
init_install_shared();
|
|
1468
1468
|
init_oauth();
|
|
1469
|
-
VERSION = "0.
|
|
1469
|
+
VERSION = "0.20.0";
|
|
1470
1470
|
PORT = Number(process.env.LEADBAY_INSTALLER_PORT ?? 0);
|
|
1471
1471
|
sessions = /* @__PURE__ */ new Map();
|
|
1472
1472
|
OAUTH_BASE_URLS = {
|
package/dist/installer-gui.js
CHANGED
|
@@ -873,7 +873,7 @@ async function oauthLogin(opts) {
|
|
|
873
873
|
}
|
|
874
874
|
|
|
875
875
|
// installer/installer-gui.ts
|
|
876
|
-
var VERSION = "0.
|
|
876
|
+
var VERSION = "0.20.0";
|
|
877
877
|
var PORT = Number(process.env.LEADBAY_INSTALLER_PORT ?? 0);
|
|
878
878
|
var sessions = /* @__PURE__ */ new Map();
|
|
879
879
|
var OAUTH_BASE_URLS = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leadbay/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
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",
|