@leadbay/mcp 0.31.0 → 0.31.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/dist/bin.js +116 -17
- package/dist/http-server.js +115 -17
- package/dist/installer-electron.js +1 -1
- package/dist/installer-gui.js +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -6832,8 +6832,10 @@ Examples that should NOT invoke this tool (sound similar, route elsewhere):
|
|
|
6832
6832
|
|
|
6833
6833
|
\`queued\` \u2192 \u2705 "Queued <N> extra leads on lens <id>. Pull in ~30s." Do NOT
|
|
6834
6834
|
list \`accepted_seeds\`; they're internal.
|
|
6835
|
-
\`
|
|
6836
|
-
|
|
6835
|
+
\`no_candidates\` \u2192 \u26D4 surface \`reason.message\`, name the criteria, offer
|
|
6836
|
+
\`leadbay_adjust_audience\`. Never re-call this tool on that lens.
|
|
6837
|
+
\`quota_exceeded\` \u2192 three options via the host's choice widget
|
|
6838
|
+
(smaller count / wait for reset / upgrade).
|
|
6837
6839
|
\`refresh_in_progress\` \u2192 "lens is filling, retry in a minute".
|
|
6838
6840
|
\`no_valid_seeds\` \u2192 silently re-call \`leadbay_seed_candidates\`, retry once.
|
|
6839
6841
|
|
|
@@ -6853,6 +6855,9 @@ Queue an additive extra-refill on a lens \u2014 more leads on the same criteria,
|
|
|
6853
6855
|
- \`status: "quota_exceeded"\` \u2014 daily LENS_EXTRA_REFILL hit. Response carries \`quota: {used_today, resets_at}\` + a \`message\` to surface. **Render three options via your host's choice widget (\`ask_user_input_v0\` or \`AskUserQuestion\`)**: (1) smaller \`extra_count\`, (2) wait until \`resets_at\`, (3) upgrade plan (TIER1=150, TIER2=1000). Do NOT silently retry.
|
|
6854
6856
|
- \`status: "refresh_in_progress"\` \u2014 a refresh or extra-refill is already running. Tell the user to wait and call \`leadbay_pull_leads\` in ~30s.
|
|
6855
6857
|
- \`status: "no_valid_seeds"\` \u2014 seeds went stale. Silently re-call \`leadbay_seed_candidates\` and retry once; only surface to the user if the second attempt also fails.
|
|
6858
|
+
- \`status: "no_candidates"\` \u2014 **the refill was NOT queued.** The lens's candidate pool is empty, so a refill would report success, consume no quota and deliver nothing. \`reason\` carries the same \`{code, message, retryable, criteria?, narrow_locations?}\` shape \`leadbay_pull_leads\` returns in \`empty_reason\`, with \`retryable: false\`. **Stop. Do not re-call this tool on this lens** \u2014 the outcome cannot change until the audience changes. Surface \`reason.message\`, name the criteria in play, and offer \`leadbay_adjust_audience\` (or \`leadbay_pull_followups\` when \`reason.code\` is \`no_new_leads\` and the lens already holds leads).
|
|
6859
|
+
|
|
6860
|
+
**Extendability is checked before the write.** Every response carries \`available_count\` \u2014 how many leads a refill could still draw, read from the lens's own pool. \`0\` means the call was refused (\`no_candidates\`); \`null\` means the pool could not be read and the refill was queued anyway. An empty lens is NOT evidence of a broken refill: it is usually a lens that never had candidates. Reach for \`leadbay_adjust_audience\`, not another \`leadbay_extend_lens\`.
|
|
6856
6861
|
|
|
6857
6862
|
WHEN TO USE: when the user has a bigger appetite than the daily lens fill delivers \u2014 they want MORE of the same kind of leads, on demand. Canonical phrasings: "I want more leads on this lens", "extend the lens", "give me a bigger batch today". The \`leadbay_extend_my_lens\` prompt is the user-facing entry point that orchestrates the whole flow.
|
|
6858
6863
|
|
|
@@ -6893,6 +6898,8 @@ Pick the row matching the response \`status\`. Seed-picking is internal; do NOT
|
|
|
6893
6898
|
| \`quota_exceeded\` | "Upgrade plan for a higher daily limit" | (no call \u2014 direct user to contact account manager / sales) |
|
|
6894
6899
|
| \`refresh_in_progress\` | "Lens is already filling \u2014 pull leads in a minute" | \`leadbay_pull_leads()\` (after a short wait) |
|
|
6895
6900
|
| \`no_valid_seeds\` | (silent retry \u2014 re-call \`leadbay_seed_candidates\` then \`leadbay_extend_lens\`) | internal \u2014 only surface if the second attempt also fails |
|
|
6901
|
+
| \`no_candidates\` | "Widen the audience \u2014 this lens has nothing left to add" | \`leadbay_adjust_audience()\` \u2014 never \`leadbay_extend_lens\` again |
|
|
6902
|
+
| \`no_candidates\` (\`reason.code: no_new_leads\`) | "Work the leads already in the lens" | \`leadbay_pull_followups()\` |
|
|
6896
6903
|
|
|
6897
6904
|
If nothing matches cleanly, default to "pull leads now to see what's queued" \u2014 never invent a tool that doesn't exist.
|
|
6898
6905
|
`;
|
|
@@ -16522,6 +16529,21 @@ function narrowGeoSentence(narrow) {
|
|
|
16522
16529
|
return "";
|
|
16523
16530
|
return ` Its geography is pinned to ${names.join(", ")} \u2014 a city-scale area or smaller, which on an empty lens is almost always the criterion to relax first.`;
|
|
16524
16531
|
}
|
|
16532
|
+
async function readAudienceShape(client, lensId) {
|
|
16533
|
+
let filter = null;
|
|
16534
|
+
try {
|
|
16535
|
+
filter = await client.request("GET", `/lenses/${lensId}/filter`);
|
|
16536
|
+
} catch {
|
|
16537
|
+
}
|
|
16538
|
+
const criteria = criteriaOf(filter);
|
|
16539
|
+
const summary = summariseCriteria(criteria);
|
|
16540
|
+
const narrow = narrowLocationsOf(filter, criteria);
|
|
16541
|
+
return {
|
|
16542
|
+
...summary ? { criteria: summary } : {},
|
|
16543
|
+
...narrow.length > 0 ? { narrow_locations: narrow } : {},
|
|
16544
|
+
geoSentence: narrowGeoSentence(narrow)
|
|
16545
|
+
};
|
|
16546
|
+
}
|
|
16525
16547
|
async function diagnoseEmptyLens(client, lensId, computing) {
|
|
16526
16548
|
if (computing.wishlist || computing.scores) {
|
|
16527
16549
|
return {
|
|
@@ -16536,19 +16558,8 @@ async function diagnoseEmptyLens(client, lensId, computing) {
|
|
|
16536
16558
|
row = lenses.find((l) => String(l.id) === String(lensId));
|
|
16537
16559
|
} catch {
|
|
16538
16560
|
}
|
|
16539
|
-
|
|
16540
|
-
|
|
16541
|
-
filter = await client.request("GET", `/lenses/${lensId}/filter`);
|
|
16542
|
-
} catch {
|
|
16543
|
-
}
|
|
16544
|
-
const criteria = criteriaOf(filter);
|
|
16545
|
-
const summary = summariseCriteria(criteria);
|
|
16546
|
-
const narrow = narrowLocationsOf(filter, criteria);
|
|
16547
|
-
const geo = narrowGeoSentence(narrow);
|
|
16548
|
-
const extras = {
|
|
16549
|
-
...summary ? { criteria: summary } : {},
|
|
16550
|
-
...narrow.length > 0 ? { narrow_locations: narrow } : {}
|
|
16551
|
-
};
|
|
16561
|
+
const { geoSentence: geo, ...extras } = await readAudienceShape(client, lensId);
|
|
16562
|
+
const summary = extras.criteria;
|
|
16552
16563
|
if (row?.not_enough_lead_candidates) {
|
|
16553
16564
|
return {
|
|
16554
16565
|
code: "no_candidates",
|
|
@@ -24177,11 +24188,54 @@ async function readExtraRefillQuota(client) {
|
|
|
24177
24188
|
return { count: null, resets_at: null };
|
|
24178
24189
|
}
|
|
24179
24190
|
}
|
|
24191
|
+
async function readAvailablePool(client, lensId) {
|
|
24192
|
+
try {
|
|
24193
|
+
const preview = await client.request("GET", `/lenses/${lensId}/extra_refill_preview`);
|
|
24194
|
+
return typeof preview?.available_count === "number" ? preview.available_count : null;
|
|
24195
|
+
} catch {
|
|
24196
|
+
return null;
|
|
24197
|
+
}
|
|
24198
|
+
}
|
|
24199
|
+
async function readLensLeadTotal(client, lensId) {
|
|
24200
|
+
try {
|
|
24201
|
+
const page = await client.request("GET", `/lenses/${lensId}/leads/wishlist?count=1&page=0`);
|
|
24202
|
+
return typeof page?.pagination?.total === "number" ? page.pagination.total : null;
|
|
24203
|
+
} catch {
|
|
24204
|
+
return null;
|
|
24205
|
+
}
|
|
24206
|
+
}
|
|
24207
|
+
function noCandidatesReason(held, shape) {
|
|
24208
|
+
const { geoSentence, ...extras } = shape;
|
|
24209
|
+
const futile = " Extending again is futile \u2014 a refill on a lens with an empty candidate pool reports queued, consumes no quota and delivers nothing.";
|
|
24210
|
+
if (held === 0) {
|
|
24211
|
+
return {
|
|
24212
|
+
code: "audience_too_narrow",
|
|
24213
|
+
retryable: false,
|
|
24214
|
+
message: "This lens holds no leads and has none left to add: its criteria intersect to nothing." + geoSentence + futile + " Tell the user which criteria are in play and offer to widen the audience (leadbay_adjust_audience).",
|
|
24215
|
+
...extras
|
|
24216
|
+
};
|
|
24217
|
+
}
|
|
24218
|
+
if (held !== null) {
|
|
24219
|
+
return {
|
|
24220
|
+
code: "no_new_leads",
|
|
24221
|
+
retryable: false,
|
|
24222
|
+
message: `Every company matching this lens has already been delivered \u2014 all ${held} of them \u2014 so there is nothing left to add.` + futile + " Tell the user; offer to widen the audience (leadbay_adjust_audience) or work the leads already in the lens (leadbay_pull_followups).",
|
|
24223
|
+
...extras
|
|
24224
|
+
};
|
|
24225
|
+
}
|
|
24226
|
+
return {
|
|
24227
|
+
code: "no_candidates",
|
|
24228
|
+
retryable: false,
|
|
24229
|
+
message: "This lens has no candidates left to add." + geoSentence + futile + " Tell the user and offer to widen the audience (leadbay_adjust_audience).",
|
|
24230
|
+
...extras
|
|
24231
|
+
};
|
|
24232
|
+
}
|
|
24180
24233
|
var extendLens;
|
|
24181
24234
|
var init_extend_lens = __esm({
|
|
24182
24235
|
"../core/dist/composite/extend-lens.js"() {
|
|
24183
24236
|
"use strict";
|
|
24184
24237
|
init_tool_descriptions_generated();
|
|
24238
|
+
init_empty_lens_reason();
|
|
24185
24239
|
extendLens = {
|
|
24186
24240
|
name: "leadbay_extend_lens",
|
|
24187
24241
|
annotations: {
|
|
@@ -24218,7 +24272,7 @@ var init_extend_lens = __esm({
|
|
|
24218
24272
|
properties: {
|
|
24219
24273
|
status: {
|
|
24220
24274
|
type: "string",
|
|
24221
|
-
description: "queued | quota_exceeded | refresh_in_progress | no_valid_seeds"
|
|
24275
|
+
description: "queued | no_candidates | quota_exceeded | refresh_in_progress | no_valid_seeds"
|
|
24222
24276
|
},
|
|
24223
24277
|
lens: {
|
|
24224
24278
|
type: "object",
|
|
@@ -24233,6 +24287,35 @@ var init_extend_lens = __esm({
|
|
|
24233
24287
|
type: "string",
|
|
24234
24288
|
description: "Human-readable summary. On error statuses, this is the line to surface to the user."
|
|
24235
24289
|
},
|
|
24290
|
+
available_count: {
|
|
24291
|
+
type: ["number", "null"],
|
|
24292
|
+
description: "How many leads a refill on this lens could still draw, read from /extra_refill_preview before queueing. 0 means the refill was NOT queued (status=no_candidates). null means the pool could not be read and the refill was queued anyway."
|
|
24293
|
+
},
|
|
24294
|
+
reason: {
|
|
24295
|
+
type: "object",
|
|
24296
|
+
description: "Only present on status=no_candidates. Same shape and `code` vocabulary as leadbay_pull_leads' empty_reason, so one routing rule covers both tools.",
|
|
24297
|
+
properties: {
|
|
24298
|
+
code: {
|
|
24299
|
+
type: "string",
|
|
24300
|
+
description: "audience_too_narrow (lens holds nothing and its criteria intersect to nothing) | no_new_leads (everything matching has already been delivered) | no_candidates (pool is empty; which of the two could not be determined)"
|
|
24301
|
+
},
|
|
24302
|
+
message: { type: "string" },
|
|
24303
|
+
retryable: {
|
|
24304
|
+
type: "boolean",
|
|
24305
|
+
description: "Always false here. Re-calling leadbay_extend_lens cannot change the outcome \u2014 widen the audience instead."
|
|
24306
|
+
},
|
|
24307
|
+
criteria: {
|
|
24308
|
+
type: "object",
|
|
24309
|
+
description: "The criteria in play, so the agent can name them."
|
|
24310
|
+
},
|
|
24311
|
+
narrow_locations: {
|
|
24312
|
+
type: "array",
|
|
24313
|
+
description: "Include-locations that resolved to city-scale or smaller \u2014 the usual thing to relax first.",
|
|
24314
|
+
items: { type: "object" }
|
|
24315
|
+
}
|
|
24316
|
+
},
|
|
24317
|
+
required: ["code", "message", "retryable"]
|
|
24318
|
+
},
|
|
24236
24319
|
quota: {
|
|
24237
24320
|
type: "object",
|
|
24238
24321
|
description: "Only present on status=quota_exceeded. Shows the org's daily LENS_EXTRA_REFILL state.",
|
|
@@ -24246,6 +24329,21 @@ var init_extend_lens = __esm({
|
|
|
24246
24329
|
},
|
|
24247
24330
|
execute: async (client, params) => {
|
|
24248
24331
|
const lensId = params.lensId ?? await client.resolveDefaultLens();
|
|
24332
|
+
const availableCount = await readAvailablePool(client, lensId);
|
|
24333
|
+
if (availableCount === 0) {
|
|
24334
|
+
const [shape, held] = await Promise.all([
|
|
24335
|
+
readAudienceShape(client, lensId),
|
|
24336
|
+
readLensLeadTotal(client, lensId)
|
|
24337
|
+
]);
|
|
24338
|
+
const reason = noCandidatesReason(held, shape);
|
|
24339
|
+
return {
|
|
24340
|
+
status: "no_candidates",
|
|
24341
|
+
lens: { id: lensId },
|
|
24342
|
+
available_count: 0,
|
|
24343
|
+
reason,
|
|
24344
|
+
message: reason.message
|
|
24345
|
+
};
|
|
24346
|
+
}
|
|
24249
24347
|
const body = {
|
|
24250
24348
|
seed_lead_ids: params.seed_lead_ids ?? []
|
|
24251
24349
|
};
|
|
@@ -24257,6 +24355,7 @@ var init_extend_lens = __esm({
|
|
|
24257
24355
|
return {
|
|
24258
24356
|
status: "queued",
|
|
24259
24357
|
lens: { id: lensId },
|
|
24358
|
+
available_count: availableCount,
|
|
24260
24359
|
accepted_seeds: res.accepted_seeds,
|
|
24261
24360
|
message: "Extra refill queued. Leads stream in asynchronously \u2014 call leadbay_pull_leads in ~30s to see them."
|
|
24262
24361
|
};
|
|
@@ -31871,7 +31970,7 @@ var OAUTH_BASE_URLS = {
|
|
|
31871
31970
|
fr: "https://staging.api.leadbay.app"
|
|
31872
31971
|
}
|
|
31873
31972
|
};
|
|
31874
|
-
var VERSION = "0.31.
|
|
31973
|
+
var VERSION = "0.31.1";
|
|
31875
31974
|
var HELP = `
|
|
31876
31975
|
leadbay-mcp ${VERSION} \u2014 Leadbay Model Context Protocol server
|
|
31877
31976
|
|
package/dist/http-server.js
CHANGED
|
@@ -9160,8 +9160,10 @@ Examples that should NOT invoke this tool (sound similar, route elsewhere):
|
|
|
9160
9160
|
|
|
9161
9161
|
\`queued\` \u2192 \u2705 "Queued <N> extra leads on lens <id>. Pull in ~30s." Do NOT
|
|
9162
9162
|
list \`accepted_seeds\`; they're internal.
|
|
9163
|
-
\`
|
|
9164
|
-
|
|
9163
|
+
\`no_candidates\` \u2192 \u26D4 surface \`reason.message\`, name the criteria, offer
|
|
9164
|
+
\`leadbay_adjust_audience\`. Never re-call this tool on that lens.
|
|
9165
|
+
\`quota_exceeded\` \u2192 three options via the host's choice widget
|
|
9166
|
+
(smaller count / wait for reset / upgrade).
|
|
9165
9167
|
\`refresh_in_progress\` \u2192 "lens is filling, retry in a minute".
|
|
9166
9168
|
\`no_valid_seeds\` \u2192 silently re-call \`leadbay_seed_candidates\`, retry once.
|
|
9167
9169
|
|
|
@@ -9181,6 +9183,9 @@ Queue an additive extra-refill on a lens \u2014 more leads on the same criteria,
|
|
|
9181
9183
|
- \`status: "quota_exceeded"\` \u2014 daily LENS_EXTRA_REFILL hit. Response carries \`quota: {used_today, resets_at}\` + a \`message\` to surface. **Render three options via your host's choice widget (\`ask_user_input_v0\` or \`AskUserQuestion\`)**: (1) smaller \`extra_count\`, (2) wait until \`resets_at\`, (3) upgrade plan (TIER1=150, TIER2=1000). Do NOT silently retry.
|
|
9182
9184
|
- \`status: "refresh_in_progress"\` \u2014 a refresh or extra-refill is already running. Tell the user to wait and call \`leadbay_pull_leads\` in ~30s.
|
|
9183
9185
|
- \`status: "no_valid_seeds"\` \u2014 seeds went stale. Silently re-call \`leadbay_seed_candidates\` and retry once; only surface to the user if the second attempt also fails.
|
|
9186
|
+
- \`status: "no_candidates"\` \u2014 **the refill was NOT queued.** The lens's candidate pool is empty, so a refill would report success, consume no quota and deliver nothing. \`reason\` carries the same \`{code, message, retryable, criteria?, narrow_locations?}\` shape \`leadbay_pull_leads\` returns in \`empty_reason\`, with \`retryable: false\`. **Stop. Do not re-call this tool on this lens** \u2014 the outcome cannot change until the audience changes. Surface \`reason.message\`, name the criteria in play, and offer \`leadbay_adjust_audience\` (or \`leadbay_pull_followups\` when \`reason.code\` is \`no_new_leads\` and the lens already holds leads).
|
|
9187
|
+
|
|
9188
|
+
**Extendability is checked before the write.** Every response carries \`available_count\` \u2014 how many leads a refill could still draw, read from the lens's own pool. \`0\` means the call was refused (\`no_candidates\`); \`null\` means the pool could not be read and the refill was queued anyway. An empty lens is NOT evidence of a broken refill: it is usually a lens that never had candidates. Reach for \`leadbay_adjust_audience\`, not another \`leadbay_extend_lens\`.
|
|
9184
9189
|
|
|
9185
9190
|
WHEN TO USE: when the user has a bigger appetite than the daily lens fill delivers \u2014 they want MORE of the same kind of leads, on demand. Canonical phrasings: "I want more leads on this lens", "extend the lens", "give me a bigger batch today". The \`leadbay_extend_my_lens\` prompt is the user-facing entry point that orchestrates the whole flow.
|
|
9186
9191
|
|
|
@@ -9221,6 +9226,8 @@ Pick the row matching the response \`status\`. Seed-picking is internal; do NOT
|
|
|
9221
9226
|
| \`quota_exceeded\` | "Upgrade plan for a higher daily limit" | (no call \u2014 direct user to contact account manager / sales) |
|
|
9222
9227
|
| \`refresh_in_progress\` | "Lens is already filling \u2014 pull leads in a minute" | \`leadbay_pull_leads()\` (after a short wait) |
|
|
9223
9228
|
| \`no_valid_seeds\` | (silent retry \u2014 re-call \`leadbay_seed_candidates\` then \`leadbay_extend_lens\`) | internal \u2014 only surface if the second attempt also fails |
|
|
9229
|
+
| \`no_candidates\` | "Widen the audience \u2014 this lens has nothing left to add" | \`leadbay_adjust_audience()\` \u2014 never \`leadbay_extend_lens\` again |
|
|
9230
|
+
| \`no_candidates\` (\`reason.code: no_new_leads\`) | "Work the leads already in the lens" | \`leadbay_pull_followups()\` |
|
|
9224
9231
|
|
|
9225
9232
|
If nothing matches cleanly, default to "pull leads now to see what's queued" \u2014 never invent a tool that doesn't exist.
|
|
9226
9233
|
`;
|
|
@@ -18370,6 +18377,21 @@ function narrowGeoSentence(narrow) {
|
|
|
18370
18377
|
return "";
|
|
18371
18378
|
return ` Its geography is pinned to ${names.join(", ")} \u2014 a city-scale area or smaller, which on an empty lens is almost always the criterion to relax first.`;
|
|
18372
18379
|
}
|
|
18380
|
+
async function readAudienceShape(client, lensId) {
|
|
18381
|
+
let filter = null;
|
|
18382
|
+
try {
|
|
18383
|
+
filter = await client.request("GET", `/lenses/${lensId}/filter`);
|
|
18384
|
+
} catch {
|
|
18385
|
+
}
|
|
18386
|
+
const criteria = criteriaOf(filter);
|
|
18387
|
+
const summary = summariseCriteria(criteria);
|
|
18388
|
+
const narrow = narrowLocationsOf(filter, criteria);
|
|
18389
|
+
return {
|
|
18390
|
+
...summary ? { criteria: summary } : {},
|
|
18391
|
+
...narrow.length > 0 ? { narrow_locations: narrow } : {},
|
|
18392
|
+
geoSentence: narrowGeoSentence(narrow)
|
|
18393
|
+
};
|
|
18394
|
+
}
|
|
18373
18395
|
async function diagnoseEmptyLens(client, lensId, computing) {
|
|
18374
18396
|
if (computing.wishlist || computing.scores) {
|
|
18375
18397
|
return {
|
|
@@ -18384,19 +18406,8 @@ async function diagnoseEmptyLens(client, lensId, computing) {
|
|
|
18384
18406
|
row = lenses.find((l) => String(l.id) === String(lensId));
|
|
18385
18407
|
} catch {
|
|
18386
18408
|
}
|
|
18387
|
-
|
|
18388
|
-
|
|
18389
|
-
filter = await client.request("GET", `/lenses/${lensId}/filter`);
|
|
18390
|
-
} catch {
|
|
18391
|
-
}
|
|
18392
|
-
const criteria = criteriaOf(filter);
|
|
18393
|
-
const summary = summariseCriteria(criteria);
|
|
18394
|
-
const narrow = narrowLocationsOf(filter, criteria);
|
|
18395
|
-
const geo = narrowGeoSentence(narrow);
|
|
18396
|
-
const extras = {
|
|
18397
|
-
...summary ? { criteria: summary } : {},
|
|
18398
|
-
...narrow.length > 0 ? { narrow_locations: narrow } : {}
|
|
18399
|
-
};
|
|
18409
|
+
const { geoSentence: geo, ...extras } = await readAudienceShape(client, lensId);
|
|
18410
|
+
const summary = extras.criteria;
|
|
18400
18411
|
if (row?.not_enough_lead_candidates) {
|
|
18401
18412
|
return {
|
|
18402
18413
|
code: "no_candidates",
|
|
@@ -25140,6 +25151,48 @@ async function readExtraRefillQuota(client) {
|
|
|
25140
25151
|
return { count: null, resets_at: null };
|
|
25141
25152
|
}
|
|
25142
25153
|
}
|
|
25154
|
+
async function readAvailablePool(client, lensId) {
|
|
25155
|
+
try {
|
|
25156
|
+
const preview = await client.request("GET", `/lenses/${lensId}/extra_refill_preview`);
|
|
25157
|
+
return typeof preview?.available_count === "number" ? preview.available_count : null;
|
|
25158
|
+
} catch {
|
|
25159
|
+
return null;
|
|
25160
|
+
}
|
|
25161
|
+
}
|
|
25162
|
+
async function readLensLeadTotal(client, lensId) {
|
|
25163
|
+
try {
|
|
25164
|
+
const page = await client.request("GET", `/lenses/${lensId}/leads/wishlist?count=1&page=0`);
|
|
25165
|
+
return typeof page?.pagination?.total === "number" ? page.pagination.total : null;
|
|
25166
|
+
} catch {
|
|
25167
|
+
return null;
|
|
25168
|
+
}
|
|
25169
|
+
}
|
|
25170
|
+
function noCandidatesReason(held, shape) {
|
|
25171
|
+
const { geoSentence, ...extras } = shape;
|
|
25172
|
+
const futile = " Extending again is futile \u2014 a refill on a lens with an empty candidate pool reports queued, consumes no quota and delivers nothing.";
|
|
25173
|
+
if (held === 0) {
|
|
25174
|
+
return {
|
|
25175
|
+
code: "audience_too_narrow",
|
|
25176
|
+
retryable: false,
|
|
25177
|
+
message: "This lens holds no leads and has none left to add: its criteria intersect to nothing." + geoSentence + futile + " Tell the user which criteria are in play and offer to widen the audience (leadbay_adjust_audience).",
|
|
25178
|
+
...extras
|
|
25179
|
+
};
|
|
25180
|
+
}
|
|
25181
|
+
if (held !== null) {
|
|
25182
|
+
return {
|
|
25183
|
+
code: "no_new_leads",
|
|
25184
|
+
retryable: false,
|
|
25185
|
+
message: `Every company matching this lens has already been delivered \u2014 all ${held} of them \u2014 so there is nothing left to add.` + futile + " Tell the user; offer to widen the audience (leadbay_adjust_audience) or work the leads already in the lens (leadbay_pull_followups).",
|
|
25186
|
+
...extras
|
|
25187
|
+
};
|
|
25188
|
+
}
|
|
25189
|
+
return {
|
|
25190
|
+
code: "no_candidates",
|
|
25191
|
+
retryable: false,
|
|
25192
|
+
message: "This lens has no candidates left to add." + geoSentence + futile + " Tell the user and offer to widen the audience (leadbay_adjust_audience).",
|
|
25193
|
+
...extras
|
|
25194
|
+
};
|
|
25195
|
+
}
|
|
25143
25196
|
var extendLens = {
|
|
25144
25197
|
name: "leadbay_extend_lens",
|
|
25145
25198
|
annotations: {
|
|
@@ -25176,7 +25229,7 @@ var extendLens = {
|
|
|
25176
25229
|
properties: {
|
|
25177
25230
|
status: {
|
|
25178
25231
|
type: "string",
|
|
25179
|
-
description: "queued | quota_exceeded | refresh_in_progress | no_valid_seeds"
|
|
25232
|
+
description: "queued | no_candidates | quota_exceeded | refresh_in_progress | no_valid_seeds"
|
|
25180
25233
|
},
|
|
25181
25234
|
lens: {
|
|
25182
25235
|
type: "object",
|
|
@@ -25191,6 +25244,35 @@ var extendLens = {
|
|
|
25191
25244
|
type: "string",
|
|
25192
25245
|
description: "Human-readable summary. On error statuses, this is the line to surface to the user."
|
|
25193
25246
|
},
|
|
25247
|
+
available_count: {
|
|
25248
|
+
type: ["number", "null"],
|
|
25249
|
+
description: "How many leads a refill on this lens could still draw, read from /extra_refill_preview before queueing. 0 means the refill was NOT queued (status=no_candidates). null means the pool could not be read and the refill was queued anyway."
|
|
25250
|
+
},
|
|
25251
|
+
reason: {
|
|
25252
|
+
type: "object",
|
|
25253
|
+
description: "Only present on status=no_candidates. Same shape and `code` vocabulary as leadbay_pull_leads' empty_reason, so one routing rule covers both tools.",
|
|
25254
|
+
properties: {
|
|
25255
|
+
code: {
|
|
25256
|
+
type: "string",
|
|
25257
|
+
description: "audience_too_narrow (lens holds nothing and its criteria intersect to nothing) | no_new_leads (everything matching has already been delivered) | no_candidates (pool is empty; which of the two could not be determined)"
|
|
25258
|
+
},
|
|
25259
|
+
message: { type: "string" },
|
|
25260
|
+
retryable: {
|
|
25261
|
+
type: "boolean",
|
|
25262
|
+
description: "Always false here. Re-calling leadbay_extend_lens cannot change the outcome \u2014 widen the audience instead."
|
|
25263
|
+
},
|
|
25264
|
+
criteria: {
|
|
25265
|
+
type: "object",
|
|
25266
|
+
description: "The criteria in play, so the agent can name them."
|
|
25267
|
+
},
|
|
25268
|
+
narrow_locations: {
|
|
25269
|
+
type: "array",
|
|
25270
|
+
description: "Include-locations that resolved to city-scale or smaller \u2014 the usual thing to relax first.",
|
|
25271
|
+
items: { type: "object" }
|
|
25272
|
+
}
|
|
25273
|
+
},
|
|
25274
|
+
required: ["code", "message", "retryable"]
|
|
25275
|
+
},
|
|
25194
25276
|
quota: {
|
|
25195
25277
|
type: "object",
|
|
25196
25278
|
description: "Only present on status=quota_exceeded. Shows the org's daily LENS_EXTRA_REFILL state.",
|
|
@@ -25204,6 +25286,21 @@ var extendLens = {
|
|
|
25204
25286
|
},
|
|
25205
25287
|
execute: async (client, params) => {
|
|
25206
25288
|
const lensId = params.lensId ?? await client.resolveDefaultLens();
|
|
25289
|
+
const availableCount = await readAvailablePool(client, lensId);
|
|
25290
|
+
if (availableCount === 0) {
|
|
25291
|
+
const [shape, held] = await Promise.all([
|
|
25292
|
+
readAudienceShape(client, lensId),
|
|
25293
|
+
readLensLeadTotal(client, lensId)
|
|
25294
|
+
]);
|
|
25295
|
+
const reason = noCandidatesReason(held, shape);
|
|
25296
|
+
return {
|
|
25297
|
+
status: "no_candidates",
|
|
25298
|
+
lens: { id: lensId },
|
|
25299
|
+
available_count: 0,
|
|
25300
|
+
reason,
|
|
25301
|
+
message: reason.message
|
|
25302
|
+
};
|
|
25303
|
+
}
|
|
25207
25304
|
const body = {
|
|
25208
25305
|
seed_lead_ids: params.seed_lead_ids ?? []
|
|
25209
25306
|
};
|
|
@@ -25215,6 +25312,7 @@ var extendLens = {
|
|
|
25215
25312
|
return {
|
|
25216
25313
|
status: "queued",
|
|
25217
25314
|
lens: { id: lensId },
|
|
25315
|
+
available_count: availableCount,
|
|
25218
25316
|
accepted_seeds: res.accepted_seeds,
|
|
25219
25317
|
message: "Extra refill queued. Leads stream in asynchronously \u2014 call leadbay_pull_leads in ~30s to see them."
|
|
25220
25318
|
};
|
|
@@ -28441,7 +28539,7 @@ function parseWriteEnv(env = process.env) {
|
|
|
28441
28539
|
}
|
|
28442
28540
|
|
|
28443
28541
|
// src/http-server.ts
|
|
28444
|
-
var VERSION = true ? "0.31.
|
|
28542
|
+
var VERSION = true ? "0.31.1" : "0.0.0-dev";
|
|
28445
28543
|
var PORT = Number(process.env.PORT ?? 8080);
|
|
28446
28544
|
var HOST = process.env.HOST ?? "0.0.0.0";
|
|
28447
28545
|
var logger = {
|
package/dist/installer-gui.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leadbay/mcp",
|
|
3
|
-
"version": "0.31.
|
|
3
|
+
"version": "0.31.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",
|