@leadbay/mcp 0.19.3 → 0.20.1
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 +9 -0
- package/dist/bin.js +83 -45
- package/dist/http-server.js +79 -43
- 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,14 @@
|
|
|
1
1
|
# Changelog — @leadbay/mcp
|
|
2
2
|
|
|
3
|
+
## 0.20.1 — 2026-06-15
|
|
4
|
+
|
|
5
|
+
- **Triage board stays the first next-step option on a poor-fit batch** (`leadbay_daily_check_in`): when today's batch is an ICP mismatch (every lead AI-scored off-profile), the agent was demoting the interactive triage board below "refine audience" in the NEXT STEPS widget — the plain ordering rule kept losing to the agent's own leverage judgment ("the whole batch is junk, so lead with fixing the lens"). The workflow contract requires the named artifact to be the FIRST option. The ordering rule now holds the triage board at position 1 even on a mismatched batch; the mismatch is surfaced in the prose nudge and offered as a *later* "refine the lens" option, never by displacing the artifact. Verified 5/5/5/5 across 3 consecutive eval runs on an all-off-ICP batch (the exact case that defeated the weaker rule).
|
|
6
|
+
|
|
7
|
+
## 0.20.0 — 2026-06-15
|
|
8
|
+
|
|
9
|
+
- **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.
|
|
10
|
+
- **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.
|
|
11
|
+
|
|
3
12
|
## 0.19.3 — 2026-06-15
|
|
4
13
|
|
|
5
14
|
- **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"]
|
|
@@ -22038,7 +22038,7 @@ Contact enrichment is offered in the NEXT STEPS widget below \u2014 do NOT emit
|
|
|
22038
22038
|
|
|
22039
22039
|
**REQUIRED OPTIONS \u2014 triggers and position rules:**
|
|
22040
22040
|
- **Recurring language** ("every day", "every morning", "I do this every", "remind me", "automate this", "recurring"): add "Schedule 'Daily prospecting check-in' as a recurring task" and place it **first**.
|
|
22041
|
-
- **\u22655 leads returned**: add "Build an interactive lead triage board for this batch" and place it **first** (or second if the scheduling offer above also applies).
|
|
22041
|
+
- **\u22655 leads returned**: add "Build an interactive lead triage board for this batch" and place it **first** (or second if the scheduling offer above also applies). This holds **even when the batch is a poor fit** (e.g. every lead AI-scored as off-ICP / a vertical mismatch): the triage board is still the first artifact option because the user asked to see and act on *this batch*. When the batch is a mismatch, ALSO offer "Refine the audience / lens so future batches fit better" \u2014 but as a *later* option, never displacing the triage board from first. Leading with audience-refinement instead of the artifact is a contract violation: surface the mismatch in your prose nudge, not by demoting the triage board.
|
|
22042
22042
|
|
|
22043
22043
|
## NEXT STEPS \u2014 after rendering the pull_leads table
|
|
22044
22044
|
|
|
@@ -22079,7 +22079,7 @@ Pick 2\u20133 items below based on what was actually observed in the response. T
|
|
|
22079
22079
|
If nothing in the menu applies cleanly, suggest only "pull next page" and "research a specific lead in depth" \u2014 never invent a tool that doesn't exist.
|
|
22080
22080
|
|
|
22081
22081
|
|
|
22082
|
-
**Final ordering check (do this before rendering):** Recurring offer \u2192 option 1; triage board \u2192 option 1 (or 2 if scheduling is also required). Swap if needed.
|
|
22082
|
+
**Final ordering check (do this before rendering):** Recurring offer \u2192 option 1; triage board \u2192 option 1 (or 2 if scheduling is also required). A poor-fit / mismatched batch does NOT change this \u2014 triage board stays first, refine-audience goes later in the list. Swap if needed.
|
|
22083
22083
|
|
|
22084
22084
|
# GATE \u2014 STOP
|
|
22085
22085
|
|
|
@@ -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);
|
|
@@ -24590,6 +24623,7 @@ function buildServer(client, opts = {}) {
|
|
|
24590
24623
|
endpoint: err._meta?.endpoint
|
|
24591
24624
|
});
|
|
24592
24625
|
}
|
|
24626
|
+
const httpStatus2 = err._meta?.http_status;
|
|
24593
24627
|
telemetry.captureToolCall({
|
|
24594
24628
|
tool: name,
|
|
24595
24629
|
ok: false,
|
|
@@ -24597,6 +24631,7 @@ function buildServer(client, opts = {}) {
|
|
|
24597
24631
|
format: "error-envelope",
|
|
24598
24632
|
bytes: errText.length,
|
|
24599
24633
|
error_code: code,
|
|
24634
|
+
...typeof httpStatus2 === "number" ? { http_status: httpStatus2 } : {},
|
|
24600
24635
|
triggered_by
|
|
24601
24636
|
});
|
|
24602
24637
|
if (COMPOSITE_FILE_TOOL_NAMES.has(name)) {
|
|
@@ -24605,7 +24640,8 @@ function buildServer(client, opts = {}) {
|
|
|
24605
24640
|
last_prompt: triggered_by ?? "",
|
|
24606
24641
|
ok: false,
|
|
24607
24642
|
duration_ms: errDur,
|
|
24608
|
-
error_code: code
|
|
24643
|
+
error_code: code,
|
|
24644
|
+
...typeof httpStatus2 === "number" ? { http_status: httpStatus2 } : {}
|
|
24609
24645
|
});
|
|
24610
24646
|
}
|
|
24611
24647
|
telemetry.captureException(err, buildBusinessCtx(name, err, triggered_by));
|
|
@@ -25461,8 +25497,10 @@ var UpdateStateStore = class {
|
|
|
25461
25497
|
if (typeof r.latest_known_version === "string") {
|
|
25462
25498
|
out.latest_known_version = r.latest_known_version;
|
|
25463
25499
|
}
|
|
25464
|
-
if (typeof r.
|
|
25465
|
-
out.
|
|
25500
|
+
if (typeof r.latest_known_install_url === "string") {
|
|
25501
|
+
out.latest_known_install_url = r.latest_known_install_url;
|
|
25502
|
+
} else if (typeof r.latest_known_mcpb_url === "string") {
|
|
25503
|
+
out.latest_known_install_url = r.latest_known_mcpb_url;
|
|
25466
25504
|
}
|
|
25467
25505
|
if (typeof r.latest_known_release_url === "string") {
|
|
25468
25506
|
out.latest_known_release_url = r.latest_known_release_url;
|
|
@@ -25890,7 +25928,7 @@ var OAUTH_BASE_URLS = {
|
|
|
25890
25928
|
fr: "https://staging.api.leadbay.app"
|
|
25891
25929
|
}
|
|
25892
25930
|
};
|
|
25893
|
-
var VERSION = "0.
|
|
25931
|
+
var VERSION = "0.20.1";
|
|
25894
25932
|
var HELP = `
|
|
25895
25933
|
leadbay-mcp ${VERSION} \u2014 Leadbay Model Context Protocol server
|
|
25896
25934
|
|
package/dist/http-server.js
CHANGED
|
@@ -180,7 +180,7 @@ Contact enrichment is offered in the NEXT STEPS widget below \u2014 do NOT emit
|
|
|
180
180
|
|
|
181
181
|
**REQUIRED OPTIONS \u2014 triggers and position rules:**
|
|
182
182
|
- **Recurring language** ("every day", "every morning", "I do this every", "remind me", "automate this", "recurring"): add "Schedule 'Daily prospecting check-in' as a recurring task" and place it **first**.
|
|
183
|
-
- **\u22655 leads returned**: add "Build an interactive lead triage board for this batch" and place it **first** (or second if the scheduling offer above also applies).
|
|
183
|
+
- **\u22655 leads returned**: add "Build an interactive lead triage board for this batch" and place it **first** (or second if the scheduling offer above also applies). This holds **even when the batch is a poor fit** (e.g. every lead AI-scored as off-ICP / a vertical mismatch): the triage board is still the first artifact option because the user asked to see and act on *this batch*. When the batch is a mismatch, ALSO offer "Refine the audience / lens so future batches fit better" \u2014 but as a *later* option, never displacing the triage board from first. Leading with audience-refinement instead of the artifact is a contract violation: surface the mismatch in your prose nudge, not by demoting the triage board.
|
|
184
184
|
|
|
185
185
|
## NEXT STEPS \u2014 after rendering the pull_leads table
|
|
186
186
|
|
|
@@ -221,7 +221,7 @@ Pick 2\u20133 items below based on what was actually observed in the response. T
|
|
|
221
221
|
If nothing in the menu applies cleanly, suggest only "pull next page" and "research a specific lead in depth" \u2014 never invent a tool that doesn't exist.
|
|
222
222
|
|
|
223
223
|
|
|
224
|
-
**Final ordering check (do this before rendering):** Recurring offer \u2192 option 1; triage board \u2192 option 1 (or 2 if scheduling is also required). Swap if needed.
|
|
224
|
+
**Final ordering check (do this before rendering):** Recurring offer \u2192 option 1; triage board \u2192 option 1 (or 2 if scheduling is also required). A poor-fit / mismatched batch does NOT change this \u2014 triage board stays first, refine-audience goes later in the list. Swap if needed.
|
|
225
225
|
|
|
226
226
|
# GATE \u2014 STOP
|
|
227
227
|
|
|
@@ -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);
|
|
@@ -22246,6 +22279,7 @@ function buildServer(client, opts = {}) {
|
|
|
22246
22279
|
endpoint: err._meta?.endpoint
|
|
22247
22280
|
});
|
|
22248
22281
|
}
|
|
22282
|
+
const httpStatus2 = err._meta?.http_status;
|
|
22249
22283
|
telemetry.captureToolCall({
|
|
22250
22284
|
tool: name,
|
|
22251
22285
|
ok: false,
|
|
@@ -22253,6 +22287,7 @@ function buildServer(client, opts = {}) {
|
|
|
22253
22287
|
format: "error-envelope",
|
|
22254
22288
|
bytes: errText.length,
|
|
22255
22289
|
error_code: code,
|
|
22290
|
+
...typeof httpStatus2 === "number" ? { http_status: httpStatus2 } : {},
|
|
22256
22291
|
triggered_by
|
|
22257
22292
|
});
|
|
22258
22293
|
if (COMPOSITE_FILE_TOOL_NAMES.has(name)) {
|
|
@@ -22261,7 +22296,8 @@ function buildServer(client, opts = {}) {
|
|
|
22261
22296
|
last_prompt: triggered_by ?? "",
|
|
22262
22297
|
ok: false,
|
|
22263
22298
|
duration_ms: errDur,
|
|
22264
|
-
error_code: code
|
|
22299
|
+
error_code: code,
|
|
22300
|
+
...typeof httpStatus2 === "number" ? { http_status: httpStatus2 } : {}
|
|
22265
22301
|
});
|
|
22266
22302
|
}
|
|
22267
22303
|
telemetry.captureException(err, buildBusinessCtx(name, err, triggered_by));
|
|
@@ -22403,7 +22439,7 @@ function parseWriteEnv(env = process.env) {
|
|
|
22403
22439
|
}
|
|
22404
22440
|
|
|
22405
22441
|
// src/http-server.ts
|
|
22406
|
-
var VERSION = true ? "0.
|
|
22442
|
+
var VERSION = true ? "0.20.1" : "0.0.0-dev";
|
|
22407
22443
|
var PORT = Number(process.env.PORT ?? 8080);
|
|
22408
22444
|
var HOST = process.env.HOST ?? "0.0.0.0";
|
|
22409
22445
|
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.1";
|
|
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.1";
|
|
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.1",
|
|
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",
|