@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 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
- \`quota_exceeded\` \u2192 render three options via your host's choice widget (\`ask_user_input_v0\` or \`AskUserQuestion\`) (smaller
6836
- count / wait until reset / upgrade).
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
- let filter = null;
16540
- try {
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.0";
31973
+ var VERSION = "0.31.1";
31875
31974
  var HELP = `
31876
31975
  leadbay-mcp ${VERSION} \u2014 Leadbay Model Context Protocol server
31877
31976
 
@@ -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
- \`quota_exceeded\` \u2192 render three options via your host's choice widget (\`ask_user_input_v0\` or \`AskUserQuestion\`) (smaller
9164
- count / wait until reset / upgrade).
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
- let filter = null;
18388
- try {
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.0" : "0.0.0-dev";
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 = {
@@ -1804,7 +1804,7 @@ var init_installer_gui = __esm({
1804
1804
  init_install_dxt();
1805
1805
  init_install_shared();
1806
1806
  init_oauth();
1807
- VERSION = true ? "0.31.0" : "0.0.0-dev";
1807
+ VERSION = true ? "0.31.1" : "0.0.0-dev";
1808
1808
  MESSAGES = {
1809
1809
  en: {
1810
1810
  installer: {
@@ -1067,7 +1067,7 @@ async function oauthLogin(opts) {
1067
1067
  }
1068
1068
 
1069
1069
  // installer/installer-gui.ts
1070
- var VERSION = true ? "0.31.0" : "0.0.0-dev";
1070
+ var VERSION = true ? "0.31.1" : "0.0.0-dev";
1071
1071
  var MESSAGES = {
1072
1072
  en: {
1073
1073
  installer: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leadbay/mcp",
3
- "version": "0.31.0",
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",