@leadbay/mcp 0.23.13 → 0.23.14
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 +6 -0
- package/dist/bin.js +54 -33
- package/dist/http-server.js +46 -28
- 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,11 @@
|
|
|
1
1
|
# Changelog — @leadbay/mcp
|
|
2
2
|
|
|
3
|
+
## 0.23.14 — 2026-07-03
|
|
4
|
+
|
|
5
|
+
Registry: publish the hosted remote endpoint so GitHub Copilot / VS Code can offer Leadbay as a one-click remote connector (product#3861).
|
|
6
|
+
|
|
7
|
+
- **`server.json` `remotes`** — the registry entry now advertises the hosted Streamable HTTP endpoint (`https://mcp.leadbay.app/mcp`) alongside the npm package. Landed in code in 0.23.13 but needs this version bump to actually re-publish to the MCP Registry (the registry won't re-publish an already-published version). Auth is standard MCP OAuth (discovered via `.well-known`), so no header/API-key declaration.
|
|
8
|
+
|
|
3
9
|
## 0.23.13 — 2026-07-03
|
|
4
10
|
|
|
5
11
|
Directory-submission schema fix.
|
package/dist/bin.js
CHANGED
|
@@ -5931,7 +5931,7 @@ WHEN NOT TO USE: as a substitute for leadbay_research_lead_by_id \u2014 that alr
|
|
|
5931
5931
|
|
|
5932
5932
|
## CREDIT COST \u2014 show the balance, discreetly
|
|
5933
5933
|
|
|
5934
|
-
Once \`all_done\` is true the result carries \`credits_remaining\` (the post-spend AI-credit balance). Don't make a fuss \u2014 no sentence, no callout. Just append ONE small italic line in parentheses at the very END of your reply: \`_(N credits remaining)_\`. If \`credits_remaining\` is null (billing unavailable), omit the line \u2014 don't print 0. Do NOT report a "credits used" figure for this run: the per-contact cost can't be scoped to this specific enrichment (a lead's contact list mixes in earlier runs), so any "X used" number would be misleading.
|
|
5934
|
+
Once \`all_done\` is true the result carries \`credits_remaining\` (the post-spend AI-credit balance). Don't make a fuss \u2014 no sentence, no callout. Just append ONE small italic line in parentheses at the very END of your reply: \`_(N credits remaining)_\`. If \`credits_remaining\` is null (billing unavailable), omit the line \u2014 don't print 0. If \`credits_remaining\` is the string \`"unlimited"\` (internal/unlimited account), also omit the line \u2014 there is no finite balance to show; do NOT print "unlimited". Do NOT report a "credits used" figure for this run: the per-contact cost can't be scoped to this specific enrichment (a lead's contact list mixes in earlier runs), so any "X used" number would be misleading.
|
|
5935
5935
|
`;
|
|
5936
5936
|
leadbay_bulk_qualify_leads = `Pick the next N unqualified leads in the active lens and qualify them (run AI rescore + web fetch). Pass \`wait_for_completion:false\` to return quickly with \`{status:'running', qualify_id}\`; poll leadbay_qualify_status with that id. With \`wait_for_completion\` omitted/true, the legacy behavior polls until the answers are populated or a budget is exhausted. Already-qualified leads (those with a non-null \`ai_agent_lead_score\`) are silently no-ops on the backend, so this composite paginates past them to find fresh candidates. On 429 mid-fanout, stops launching but keeps polling already-launched leads.
|
|
5937
5937
|
|
|
@@ -6411,7 +6411,7 @@ WHEN NOT TO USE: for bulk enrichment by job title across many leads \u2014 use l
|
|
|
6411
6411
|
|
|
6412
6412
|
## CREDIT COST \u2014 discreet
|
|
6413
6413
|
|
|
6414
|
-
This is a paid call. The result returns \`credits_remaining\` (billing.ai_credits, read before the spend). Don't make a fuss about credits: only flag the balance if it's low (e.g. \u2264 a few credits) so the user can decide. Otherwise append it quietly as a small italic parenthetical at the END of your reply \u2014 \`_(N credits remaining)_\`. Don't quote an exact per-contact cost (the rate is backend-only). The actual per-contact cost (enrichment.credits_used) appears on the contact via leadbay_get_contacts after enrichment. If \`credits_remaining\` is null, omit the line \u2014 don't assume zero.
|
|
6414
|
+
This is a paid call. The result returns \`credits_remaining\` (billing.ai_credits, read before the spend). Don't make a fuss about credits: only flag the balance if it's low (e.g. \u2264 a few credits) so the user can decide. Otherwise append it quietly as a small italic parenthetical at the END of your reply \u2014 \`_(N credits remaining)_\`. Don't quote an exact per-contact cost (the rate is backend-only). The actual per-contact cost (enrichment.credits_used) appears on the contact via leadbay_get_contacts after enrichment. If \`credits_remaining\` is null, omit the line \u2014 don't assume zero. If \`credits_remaining\` is the string \`"unlimited"\` (internal/unlimited account), also omit the line and proceed freely \u2014 do NOT print "unlimited" and never tell the user they're out of credits.
|
|
6415
6415
|
|
|
6416
6416
|
This tool MUTATES state. The caller (agent or human-in-the-loop) is responsible for confirming intent before invocation; the MCP server does not soft-prompt for confirmation. See \`annotations.destructiveHint\`.
|
|
6417
6417
|
`;
|
|
@@ -6425,9 +6425,9 @@ WHEN NOT TO USE: to enrich a single contact \u2014 that's leadbay_enrich_contact
|
|
|
6425
6425
|
|
|
6426
6426
|
Enrichment is the main PAID operation. Surface cost both before and after.
|
|
6427
6427
|
|
|
6428
|
-
**BEFORE (confirm before launching).** The discover / preview_only / dry_run modes return \`credits_remaining\` (the balance) and \`enrichable_contacts\` (the volume that would be enriched). Tell the user plainly: **"You have {credits_remaining} credits. This will enrich {enrichable_contacts} contacts."** then ask them to confirm before you launch the paid run. Route that confirmation through \`ask_user_input_v0\` ("Enrich {enrichable_contacts} contacts now?" \u2192 ["Yes, enrich", "No, cancel"]). Do NOT state an exact estimated cost \u2014 the per-contact credit rate lives backend-side and is not in the preview; show the balance and the count, never a fabricated "will cost N credits". If \`credits_remaining\` is null, billing is unavailable \u2014 say the balance is unknown, don't assume zero or unlimited.
|
|
6428
|
+
**BEFORE (confirm before launching).** The discover / preview_only / dry_run modes return \`credits_remaining\` (the balance) and \`enrichable_contacts\` (the volume that would be enriched). Tell the user plainly: **"You have {credits_remaining} credits. This will enrich {enrichable_contacts} contacts."** then ask them to confirm before you launch the paid run. Route that confirmation through \`ask_user_input_v0\` ("Enrich {enrichable_contacts} contacts now?" \u2192 ["Yes, enrich", "No, cancel"]). Do NOT state an exact estimated cost \u2014 the per-contact credit rate lives backend-side and is not in the preview; show the balance and the count, never a fabricated "will cost N credits". If \`credits_remaining\` is null, billing is unavailable \u2014 say the balance is unknown, don't assume zero or unlimited. If \`credits_remaining\` is the string \`"unlimited"\`, this is an internal/unlimited account: proceed freely and say NOTHING about credits \u2014 drop the "You have {credits_remaining} credits" sentence entirely (still name the count and still confirm before launching); do NOT announce "unlimited" and never tell the user they're out of credits.
|
|
6429
6429
|
|
|
6430
|
-
**AFTER (show the balance, discreetly).** Once the job finishes \u2014 poll \`leadbay_bulk_enrich_status\`, which returns \`credits_remaining\` (the post-spend balance). Don't make a fuss: append ONE small italic line in parentheses at the very END of your reply \u2014 \`_(N credits remaining)_\`. Omit it if \`credits_remaining\` is null. Do NOT report a "credits used" figure: per-run cost can't be scoped reliably (a lead's contacts mix earlier enrichments), so only the balance is shown.
|
|
6430
|
+
**AFTER (show the balance, discreetly).** Once the job finishes \u2014 poll \`leadbay_bulk_enrich_status\`, which returns \`credits_remaining\` (the post-spend balance). Don't make a fuss: append ONE small italic line in parentheses at the very END of your reply \u2014 \`_(N credits remaining)_\`. Omit it if \`credits_remaining\` is null. Omit it too if \`credits_remaining\` is the string \`"unlimited"\` (internal/unlimited account) \u2014 do NOT print \`_(unlimited credits remaining)_\` or otherwise announce unlimited. Do NOT report a "credits used" figure: per-run cost can't be scoped reliably (a lead's contacts mix earlier enrichments), so only the balance is shown.
|
|
6431
6431
|
|
|
6432
6432
|
## GATE \u2014 PREFER BUILT-IN HOST WIDGETS
|
|
6433
6433
|
|
|
@@ -9962,11 +9962,34 @@ var init_qualify_lead = __esm({
|
|
|
9962
9962
|
}
|
|
9963
9963
|
});
|
|
9964
9964
|
|
|
9965
|
+
// ../core/dist/composite/_credits-helpers.js
|
|
9966
|
+
function isUnlimitedAccount(me) {
|
|
9967
|
+
return me.email?.toLowerCase().trim().endsWith("@leadbay.ai") ?? false;
|
|
9968
|
+
}
|
|
9969
|
+
async function readCreditsRemaining(client, force = false) {
|
|
9970
|
+
try {
|
|
9971
|
+
const me = await client.resolveMe(force);
|
|
9972
|
+
if (isUnlimitedAccount(me))
|
|
9973
|
+
return UNLIMITED;
|
|
9974
|
+
return me.organization.billing?.ai_credits ?? null;
|
|
9975
|
+
} catch {
|
|
9976
|
+
return null;
|
|
9977
|
+
}
|
|
9978
|
+
}
|
|
9979
|
+
var UNLIMITED;
|
|
9980
|
+
var init_credits_helpers = __esm({
|
|
9981
|
+
"../core/dist/composite/_credits-helpers.js"() {
|
|
9982
|
+
"use strict";
|
|
9983
|
+
UNLIMITED = "unlimited";
|
|
9984
|
+
}
|
|
9985
|
+
});
|
|
9986
|
+
|
|
9965
9987
|
// ../core/dist/tools/enrich-contacts.js
|
|
9966
9988
|
var enrichContacts;
|
|
9967
9989
|
var init_enrich_contacts = __esm({
|
|
9968
9990
|
"../core/dist/tools/enrich-contacts.js"() {
|
|
9969
9991
|
"use strict";
|
|
9992
|
+
init_credits_helpers();
|
|
9970
9993
|
init_tool_descriptions_generated();
|
|
9971
9994
|
enrichContacts = {
|
|
9972
9995
|
name: "leadbay_enrich_contacts",
|
|
@@ -10011,9 +10034,13 @@ var init_enrich_contacts = __esm({
|
|
|
10011
10034
|
let creditsRemaining = null;
|
|
10012
10035
|
try {
|
|
10013
10036
|
const me = await client.request("GET", "/users/me");
|
|
10014
|
-
|
|
10015
|
-
|
|
10016
|
-
|
|
10037
|
+
if (isUnlimitedAccount(me)) {
|
|
10038
|
+
creditsRemaining = UNLIMITED;
|
|
10039
|
+
} else {
|
|
10040
|
+
creditsRemaining = me.organization.billing?.ai_credits ?? null;
|
|
10041
|
+
if (typeof creditsRemaining === "number" && creditsRemaining <= 0) {
|
|
10042
|
+
throw client.makeError("QUOTA_EXCEEDED", "No enrichment credits remaining", "Contact Leadbay support to extend your credit quota");
|
|
10043
|
+
}
|
|
10017
10044
|
}
|
|
10018
10045
|
} catch (e) {
|
|
10019
10046
|
if (e?.code === "QUOTA_EXCEEDED")
|
|
@@ -17421,6 +17448,7 @@ var init_account_status = __esm({
|
|
|
17421
17448
|
"../core/dist/composite/account-status.js"() {
|
|
17422
17449
|
"use strict";
|
|
17423
17450
|
init_agent_memory();
|
|
17451
|
+
init_credits_helpers();
|
|
17424
17452
|
init_tool_descriptions_generated();
|
|
17425
17453
|
accountStatus = {
|
|
17426
17454
|
name: "leadbay_account_status",
|
|
@@ -17458,7 +17486,11 @@ var init_account_status = __esm({
|
|
|
17458
17486
|
type: "boolean",
|
|
17459
17487
|
description: "True if Leadbay is mid-regenerating intelligence after a refine_prompt; new leads will reflect it shortly."
|
|
17460
17488
|
},
|
|
17461
|
-
plan: { type: ["string", "null"] }
|
|
17489
|
+
plan: { type: ["string", "null"] },
|
|
17490
|
+
unlimited_credits: {
|
|
17491
|
+
type: "boolean",
|
|
17492
|
+
description: "True for internal/unlimited accounts (billing disabled server-side). When true, DO NOT read the null `plan` or absent quota as 'no credits' \u2014 the account has unlimited credits; enrichment/qualify proceed freely. Stay SILENT on credits: don't announce 'unlimited', don't show a balance, and never tell the user they're out of credits. False for normal accounts, where the usual quota/credit story applies."
|
|
17493
|
+
}
|
|
17462
17494
|
}
|
|
17463
17495
|
},
|
|
17464
17496
|
last_requested_lens: {
|
|
@@ -17562,7 +17594,11 @@ var init_account_status = __esm({
|
|
|
17562
17594
|
name: me.organization.name,
|
|
17563
17595
|
ai_agent_enabled: me.organization.ai_agent_enabled ?? false,
|
|
17564
17596
|
computing_intelligence: me.organization.computing_intelligence ?? false,
|
|
17565
|
-
plan: quota?.plan ?? me.organization.quota_plan ?? null
|
|
17597
|
+
plan: quota?.plan ?? me.organization.quota_plan ?? null,
|
|
17598
|
+
// Internal/unlimited account (billing disabled). The backend zeroes
|
|
17599
|
+
// plan/quota for these, so without this flag the agent misreads
|
|
17600
|
+
// plan:null as "no credits" and refuses enrichment. product#3851.
|
|
17601
|
+
unlimited_credits: isUnlimitedAccount(me)
|
|
17566
17602
|
},
|
|
17567
17603
|
// Lens is withheld unless the user asked (lensAsked, above). When present,
|
|
17568
17604
|
// the id is normalized to the STRING form (my-lenses.ts) so it matches the
|
|
@@ -19976,21 +20012,6 @@ var init_qualify_status = __esm({
|
|
|
19976
20012
|
}
|
|
19977
20013
|
});
|
|
19978
20014
|
|
|
19979
|
-
// ../core/dist/composite/_credits-helpers.js
|
|
19980
|
-
async function readCreditsRemaining(client, force = false) {
|
|
19981
|
-
try {
|
|
19982
|
-
const me = await client.resolveMe(force);
|
|
19983
|
-
return me.organization.billing?.ai_credits ?? null;
|
|
19984
|
-
} catch {
|
|
19985
|
-
return null;
|
|
19986
|
-
}
|
|
19987
|
-
}
|
|
19988
|
-
var init_credits_helpers = __esm({
|
|
19989
|
-
"../core/dist/composite/_credits-helpers.js"() {
|
|
19990
|
-
"use strict";
|
|
19991
|
-
}
|
|
19992
|
-
});
|
|
19993
|
-
|
|
19994
20015
|
// ../core/dist/composite/enrich-titles.js
|
|
19995
20016
|
var DEFAULT_CANDIDATE_COUNT, enrichTitles;
|
|
19996
20017
|
var init_enrich_titles = __esm({
|
|
@@ -20081,8 +20102,8 @@ var init_enrich_titles = __esm({
|
|
|
20081
20102
|
description: "Count of enrichable contacts at preview time."
|
|
20082
20103
|
},
|
|
20083
20104
|
credits_remaining: {
|
|
20084
|
-
type: ["number", "null"],
|
|
20085
|
-
description:
|
|
20105
|
+
type: ["number", "string", "null"],
|
|
20106
|
+
description: `AI-credit balance BEFORE launching (billing.ai_credits). Present in discover / preview_only / dry_run modes. Pair with enrichable_contacts to tell the user 'you have N credits, this will enrich M contacts' \u2014 do NOT estimate an exact cost (the per-contact rate is backend-only). Null = billing unavailable. The string "unlimited" = an internal/unlimited account: proceed with enrichment freely and say NOTHING about credits (no balance, no 'unlimited', no 'no credits').`
|
|
20086
20107
|
},
|
|
20087
20108
|
selected_lead_count: {
|
|
20088
20109
|
type: "number",
|
|
@@ -20466,8 +20487,8 @@ var init_bulk_enrich_status = __esm({
|
|
|
20466
20487
|
description: "True when overall_progress.done === total AND no partial_failures."
|
|
20467
20488
|
},
|
|
20468
20489
|
credits_remaining: {
|
|
20469
|
-
type: ["number", "null"],
|
|
20470
|
-
description:
|
|
20490
|
+
type: ["number", "string", "null"],
|
|
20491
|
+
description: `AI-credit balance re-read after the spend (force-refreshed /users/me \u2192 billing.ai_credits). Present only when all_done. Null = billing unavailable (don't read as zero). The string "unlimited" = an internal/unlimited account: omit the _(N credits remaining)_ line entirely (there is no finite balance to show). NOTE: a per-run 'credits used' figure is intentionally NOT returned \u2014 getContacts can't scope cost to this bulk, so any sum would conflate historical enrichments.`
|
|
20471
20492
|
},
|
|
20472
20493
|
partial_failures: {
|
|
20473
20494
|
type: "array",
|
|
@@ -23260,7 +23281,7 @@ If \`pull_leads\` itself fails and you have no prior batch, then yes \u2014 retr
|
|
|
23260
23281
|
|
|
23261
23282
|
# PHASE 0 \u2014 STATE + AUDIENCE
|
|
23262
23283
|
|
|
23263
|
-
Call \`leadbay_account_status\` to see my remaining quota, my **enrichment credits**, and my **active lens**. Note \`web_fetch_remaining\` (qualification budget) and the enrichment credit balance \u2014 Phase 3 spends credits.
|
|
23284
|
+
Call \`leadbay_account_status\` to see my remaining quota, my **enrichment credits**, and my **active lens**. Note \`web_fetch_remaining\` (qualification budget) and the enrichment credit balance \u2014 Phase 3 spends credits. If \`organization.unlimited_credits\` is true, this is an internal/unlimited account: skip every credit-balance mention below and proceed with enrichment freely \u2014 never tell me I'm out of credits.
|
|
23264
23285
|
|
|
23265
23286
|
Resolve the audience:
|
|
23266
23287
|
|
|
@@ -23379,9 +23400,9 @@ Call \`leadbay_recall_ordered_titles({leadIds, lensId})\` and \`leadbay_enrich_t
|
|
|
23379
23400
|
|
|
23380
23401
|
Tell me what you swapped in one line ("dropped Corbett + RBS \u2014 ops-only; swapped in Acme + Globex which have Sales VPs"). The goal is a final cohort where EVERY lead has a real buyer to call.
|
|
23381
23402
|
|
|
23382
|
-
**Step C \u2014 show the spend + persona and confirm.** State the persona, the chosen titles, and "You have {credits_remaining} credits; this enriches {enrichable_contacts} contacts." Confirm via \`ask_user_input_v0\` ("Enrich these {enrichable_contacts} <persona> contacts now?" \u2192 ["Yes, enrich", "No, skip", "Change the persona/titles"]). Never launch a paid run without this. Enrich up to the campaign size (default 20) best persona-matching contacts.
|
|
23403
|
+
**Step C \u2014 show the spend + persona and confirm.** State the persona, the chosen titles, and "You have {credits_remaining} credits; this enriches {enrichable_contacts} contacts." Confirm via \`ask_user_input_v0\` ("Enrich these {enrichable_contacts} <persona> contacts now?" \u2192 ["Yes, enrich", "No, skip", "Change the persona/titles"]). Never launch a paid run without this. Enrich up to the campaign size (default 20) best persona-matching contacts. (Unlimited account \u2014 \`credits_remaining\` is \`"unlimited"\` or \`unlimited_credits\` was true: drop the "You have {credits_remaining} credits" clause and just state the persona + count; still confirm before launching.)
|
|
23383
23404
|
|
|
23384
|
-
**Step D \u2014 launch + poll.** On yes: \`leadbay_enrich_titles({leadIds, lensId, titles:[...chosen], email:true, phone:true})\` to launch, then poll \`leadbay_bulk_enrich_status\` until done (enrichment can take several minutes \u2014 keep polling, don't render an empty sheet prematurely). Append one small line at the very end of your reply: \`_(N credits remaining)_
|
|
23405
|
+
**Step D \u2014 launch + poll.** On yes: \`leadbay_enrich_titles({leadIds, lensId, titles:[...chosen], email:true, phone:true})\` to launch, then poll \`leadbay_bulk_enrich_status\` until done (enrichment can take several minutes \u2014 keep polling, don't render an empty sheet prematurely). Append one small line at the very end of your reply: \`_(N credits remaining)_\` \u2014 but omit it entirely when \`credits_remaining\` is \`"unlimited"\` (unlimited account has no finite balance to show).
|
|
23385
23406
|
|
|
23386
23407
|
If I skip enrichment, continue \u2014 the campaign can be enriched later from the call sheet.
|
|
23387
23408
|
|
|
@@ -23411,7 +23432,7 @@ Then STOP. Building a campaign is NOT outreaching \u2014 do not send anything an
|
|
|
23411
23432
|
- Enrichment targets MY buyer persona \u2014 the people who would actually buy what *I* sell, derived from my product/ICP \u2014 NOT generic seniority. For a sales/prospecting tool that means the revenue org (sales / BD / growth / marketing leaders); a Director of Operations, COO, or logistics manager is useless no matter how senior. Get the persona right or the campaign is worthless.
|
|
23412
23433
|
- Selection is DATA-DRIVEN (\`leadbay_recall_ordered_titles\` + \`leadbay_enrich_titles\` discovery) but FILTERED to the persona \u2014 never blindly repeat past-enriched or suggested titles that don't match who buys my product.
|
|
23413
23434
|
- The FINAL cohort must be all buyer-ready: drop/swap any lead with no enrichable buyer-persona contact (Phase 3 Step B.5) rather than shipping it empty. A campaign where half the leads have no buyer to call is a failed campaign. Pick a generous pool in Phase 2 so swaps are possible.
|
|
23414
|
-
- NEVER launch paid enrichment without showing \`credits_remaining\` + \`enrichable_contacts\`, naming the persona, and getting a yes.
|
|
23435
|
+
- NEVER launch paid enrichment without showing \`credits_remaining\` + \`enrichable_contacts\`, naming the persona, and getting a yes. (Exception on an unlimited account \u2014 \`credits_remaining\` is \`"unlimited"\` / \`unlimited_credits\` true: skip the \`credits_remaining\` display only; still show \`enrichable_contacts\`, name the persona, and get a yes.)
|
|
23415
23436
|
- Qualify / pick BEFORE \`leadbay_create_campaign\` \u2014 never seed a campaign with unvetted leads.
|
|
23416
23437
|
- Carry the captured \`lensId\` on every call. A lens shift loses the cohort.
|
|
23417
23438
|
- End at the rendered call sheet, then hand off to \`leadbay_work_campaign\`. Do NOT re-implement the calling / follow-up loop here, and do NOT call \`leadbay_report_outreach\`.
|
|
@@ -27812,7 +27833,7 @@ var OAUTH_BASE_URLS = {
|
|
|
27812
27833
|
fr: "https://staging.api.leadbay.app"
|
|
27813
27834
|
}
|
|
27814
27835
|
};
|
|
27815
|
-
var VERSION = "0.23.
|
|
27836
|
+
var VERSION = "0.23.14";
|
|
27816
27837
|
var HELP = `
|
|
27817
27838
|
leadbay-mcp ${VERSION} \u2014 Leadbay Model Context Protocol server
|
|
27818
27839
|
|
package/dist/http-server.js
CHANGED
|
@@ -75,7 +75,7 @@ If \`pull_leads\` itself fails and you have no prior batch, then yes \u2014 retr
|
|
|
75
75
|
|
|
76
76
|
# PHASE 0 \u2014 STATE + AUDIENCE
|
|
77
77
|
|
|
78
|
-
Call \`leadbay_account_status\` to see my remaining quota, my **enrichment credits**, and my **active lens**. Note \`web_fetch_remaining\` (qualification budget) and the enrichment credit balance \u2014 Phase 3 spends credits.
|
|
78
|
+
Call \`leadbay_account_status\` to see my remaining quota, my **enrichment credits**, and my **active lens**. Note \`web_fetch_remaining\` (qualification budget) and the enrichment credit balance \u2014 Phase 3 spends credits. If \`organization.unlimited_credits\` is true, this is an internal/unlimited account: skip every credit-balance mention below and proceed with enrichment freely \u2014 never tell me I'm out of credits.
|
|
79
79
|
|
|
80
80
|
Resolve the audience:
|
|
81
81
|
|
|
@@ -194,9 +194,9 @@ Call \`leadbay_recall_ordered_titles({leadIds, lensId})\` and \`leadbay_enrich_t
|
|
|
194
194
|
|
|
195
195
|
Tell me what you swapped in one line ("dropped Corbett + RBS \u2014 ops-only; swapped in Acme + Globex which have Sales VPs"). The goal is a final cohort where EVERY lead has a real buyer to call.
|
|
196
196
|
|
|
197
|
-
**Step C \u2014 show the spend + persona and confirm.** State the persona, the chosen titles, and "You have {credits_remaining} credits; this enriches {enrichable_contacts} contacts." Confirm via \`ask_user_input_v0\` ("Enrich these {enrichable_contacts} <persona> contacts now?" \u2192 ["Yes, enrich", "No, skip", "Change the persona/titles"]). Never launch a paid run without this. Enrich up to the campaign size (default 20) best persona-matching contacts.
|
|
197
|
+
**Step C \u2014 show the spend + persona and confirm.** State the persona, the chosen titles, and "You have {credits_remaining} credits; this enriches {enrichable_contacts} contacts." Confirm via \`ask_user_input_v0\` ("Enrich these {enrichable_contacts} <persona> contacts now?" \u2192 ["Yes, enrich", "No, skip", "Change the persona/titles"]). Never launch a paid run without this. Enrich up to the campaign size (default 20) best persona-matching contacts. (Unlimited account \u2014 \`credits_remaining\` is \`"unlimited"\` or \`unlimited_credits\` was true: drop the "You have {credits_remaining} credits" clause and just state the persona + count; still confirm before launching.)
|
|
198
198
|
|
|
199
|
-
**Step D \u2014 launch + poll.** On yes: \`leadbay_enrich_titles({leadIds, lensId, titles:[...chosen], email:true, phone:true})\` to launch, then poll \`leadbay_bulk_enrich_status\` until done (enrichment can take several minutes \u2014 keep polling, don't render an empty sheet prematurely). Append one small line at the very end of your reply: \`_(N credits remaining)_
|
|
199
|
+
**Step D \u2014 launch + poll.** On yes: \`leadbay_enrich_titles({leadIds, lensId, titles:[...chosen], email:true, phone:true})\` to launch, then poll \`leadbay_bulk_enrich_status\` until done (enrichment can take several minutes \u2014 keep polling, don't render an empty sheet prematurely). Append one small line at the very end of your reply: \`_(N credits remaining)_\` \u2014 but omit it entirely when \`credits_remaining\` is \`"unlimited"\` (unlimited account has no finite balance to show).
|
|
200
200
|
|
|
201
201
|
If I skip enrichment, continue \u2014 the campaign can be enriched later from the call sheet.
|
|
202
202
|
|
|
@@ -226,7 +226,7 @@ Then STOP. Building a campaign is NOT outreaching \u2014 do not send anything an
|
|
|
226
226
|
- Enrichment targets MY buyer persona \u2014 the people who would actually buy what *I* sell, derived from my product/ICP \u2014 NOT generic seniority. For a sales/prospecting tool that means the revenue org (sales / BD / growth / marketing leaders); a Director of Operations, COO, or logistics manager is useless no matter how senior. Get the persona right or the campaign is worthless.
|
|
227
227
|
- Selection is DATA-DRIVEN (\`leadbay_recall_ordered_titles\` + \`leadbay_enrich_titles\` discovery) but FILTERED to the persona \u2014 never blindly repeat past-enriched or suggested titles that don't match who buys my product.
|
|
228
228
|
- The FINAL cohort must be all buyer-ready: drop/swap any lead with no enrichable buyer-persona contact (Phase 3 Step B.5) rather than shipping it empty. A campaign where half the leads have no buyer to call is a failed campaign. Pick a generous pool in Phase 2 so swaps are possible.
|
|
229
|
-
- NEVER launch paid enrichment without showing \`credits_remaining\` + \`enrichable_contacts\`, naming the persona, and getting a yes.
|
|
229
|
+
- NEVER launch paid enrichment without showing \`credits_remaining\` + \`enrichable_contacts\`, naming the persona, and getting a yes. (Exception on an unlimited account \u2014 \`credits_remaining\` is \`"unlimited"\` / \`unlimited_credits\` true: skip the \`credits_remaining\` display only; still show \`enrichable_contacts\`, name the persona, and get a yes.)
|
|
230
230
|
- Qualify / pick BEFORE \`leadbay_create_campaign\` \u2014 never seed a campaign with unvetted leads.
|
|
231
231
|
- Carry the captured \`lensId\` on every call. A lens shift loses the cohort.
|
|
232
232
|
- End at the rendered call sheet, then hand off to \`leadbay_work_campaign\`. Do NOT re-implement the calling / follow-up loop here, and do NOT call \`leadbay_report_outreach\`.
|
|
@@ -7061,7 +7061,7 @@ WHEN NOT TO USE: as a substitute for leadbay_research_lead_by_id \u2014 that alr
|
|
|
7061
7061
|
|
|
7062
7062
|
## CREDIT COST \u2014 show the balance, discreetly
|
|
7063
7063
|
|
|
7064
|
-
Once \`all_done\` is true the result carries \`credits_remaining\` (the post-spend AI-credit balance). Don't make a fuss \u2014 no sentence, no callout. Just append ONE small italic line in parentheses at the very END of your reply: \`_(N credits remaining)_\`. If \`credits_remaining\` is null (billing unavailable), omit the line \u2014 don't print 0. Do NOT report a "credits used" figure for this run: the per-contact cost can't be scoped to this specific enrichment (a lead's contact list mixes in earlier runs), so any "X used" number would be misleading.
|
|
7064
|
+
Once \`all_done\` is true the result carries \`credits_remaining\` (the post-spend AI-credit balance). Don't make a fuss \u2014 no sentence, no callout. Just append ONE small italic line in parentheses at the very END of your reply: \`_(N credits remaining)_\`. If \`credits_remaining\` is null (billing unavailable), omit the line \u2014 don't print 0. If \`credits_remaining\` is the string \`"unlimited"\` (internal/unlimited account), also omit the line \u2014 there is no finite balance to show; do NOT print "unlimited". Do NOT report a "credits used" figure for this run: the per-contact cost can't be scoped to this specific enrichment (a lead's contact list mixes in earlier runs), so any "X used" number would be misleading.
|
|
7065
7065
|
`;
|
|
7066
7066
|
var leadbay_bulk_qualify_leads = `Pick the next N unqualified leads in the active lens and qualify them (run AI rescore + web fetch). Pass \`wait_for_completion:false\` to return quickly with \`{status:'running', qualify_id}\`; poll leadbay_qualify_status with that id. With \`wait_for_completion\` omitted/true, the legacy behavior polls until the answers are populated or a budget is exhausted. Already-qualified leads (those with a non-null \`ai_agent_lead_score\`) are silently no-ops on the backend, so this composite paginates past them to find fresh candidates. On 429 mid-fanout, stops launching but keeps polling already-launched leads.
|
|
7067
7067
|
|
|
@@ -7541,7 +7541,7 @@ WHEN NOT TO USE: for bulk enrichment by job title across many leads \u2014 use l
|
|
|
7541
7541
|
|
|
7542
7542
|
## CREDIT COST \u2014 discreet
|
|
7543
7543
|
|
|
7544
|
-
This is a paid call. The result returns \`credits_remaining\` (billing.ai_credits, read before the spend). Don't make a fuss about credits: only flag the balance if it's low (e.g. \u2264 a few credits) so the user can decide. Otherwise append it quietly as a small italic parenthetical at the END of your reply \u2014 \`_(N credits remaining)_\`. Don't quote an exact per-contact cost (the rate is backend-only). The actual per-contact cost (enrichment.credits_used) appears on the contact via leadbay_get_contacts after enrichment. If \`credits_remaining\` is null, omit the line \u2014 don't assume zero.
|
|
7544
|
+
This is a paid call. The result returns \`credits_remaining\` (billing.ai_credits, read before the spend). Don't make a fuss about credits: only flag the balance if it's low (e.g. \u2264 a few credits) so the user can decide. Otherwise append it quietly as a small italic parenthetical at the END of your reply \u2014 \`_(N credits remaining)_\`. Don't quote an exact per-contact cost (the rate is backend-only). The actual per-contact cost (enrichment.credits_used) appears on the contact via leadbay_get_contacts after enrichment. If \`credits_remaining\` is null, omit the line \u2014 don't assume zero. If \`credits_remaining\` is the string \`"unlimited"\` (internal/unlimited account), also omit the line and proceed freely \u2014 do NOT print "unlimited" and never tell the user they're out of credits.
|
|
7545
7545
|
|
|
7546
7546
|
This tool MUTATES state. The caller (agent or human-in-the-loop) is responsible for confirming intent before invocation; the MCP server does not soft-prompt for confirmation. See \`annotations.destructiveHint\`.
|
|
7547
7547
|
`;
|
|
@@ -7555,9 +7555,9 @@ WHEN NOT TO USE: to enrich a single contact \u2014 that's leadbay_enrich_contact
|
|
|
7555
7555
|
|
|
7556
7556
|
Enrichment is the main PAID operation. Surface cost both before and after.
|
|
7557
7557
|
|
|
7558
|
-
**BEFORE (confirm before launching).** The discover / preview_only / dry_run modes return \`credits_remaining\` (the balance) and \`enrichable_contacts\` (the volume that would be enriched). Tell the user plainly: **"You have {credits_remaining} credits. This will enrich {enrichable_contacts} contacts."** then ask them to confirm before you launch the paid run. Route that confirmation through \`ask_user_input_v0\` ("Enrich {enrichable_contacts} contacts now?" \u2192 ["Yes, enrich", "No, cancel"]). Do NOT state an exact estimated cost \u2014 the per-contact credit rate lives backend-side and is not in the preview; show the balance and the count, never a fabricated "will cost N credits". If \`credits_remaining\` is null, billing is unavailable \u2014 say the balance is unknown, don't assume zero or unlimited.
|
|
7558
|
+
**BEFORE (confirm before launching).** The discover / preview_only / dry_run modes return \`credits_remaining\` (the balance) and \`enrichable_contacts\` (the volume that would be enriched). Tell the user plainly: **"You have {credits_remaining} credits. This will enrich {enrichable_contacts} contacts."** then ask them to confirm before you launch the paid run. Route that confirmation through \`ask_user_input_v0\` ("Enrich {enrichable_contacts} contacts now?" \u2192 ["Yes, enrich", "No, cancel"]). Do NOT state an exact estimated cost \u2014 the per-contact credit rate lives backend-side and is not in the preview; show the balance and the count, never a fabricated "will cost N credits". If \`credits_remaining\` is null, billing is unavailable \u2014 say the balance is unknown, don't assume zero or unlimited. If \`credits_remaining\` is the string \`"unlimited"\`, this is an internal/unlimited account: proceed freely and say NOTHING about credits \u2014 drop the "You have {credits_remaining} credits" sentence entirely (still name the count and still confirm before launching); do NOT announce "unlimited" and never tell the user they're out of credits.
|
|
7559
7559
|
|
|
7560
|
-
**AFTER (show the balance, discreetly).** Once the job finishes \u2014 poll \`leadbay_bulk_enrich_status\`, which returns \`credits_remaining\` (the post-spend balance). Don't make a fuss: append ONE small italic line in parentheses at the very END of your reply \u2014 \`_(N credits remaining)_\`. Omit it if \`credits_remaining\` is null. Do NOT report a "credits used" figure: per-run cost can't be scoped reliably (a lead's contacts mix earlier enrichments), so only the balance is shown.
|
|
7560
|
+
**AFTER (show the balance, discreetly).** Once the job finishes \u2014 poll \`leadbay_bulk_enrich_status\`, which returns \`credits_remaining\` (the post-spend balance). Don't make a fuss: append ONE small italic line in parentheses at the very END of your reply \u2014 \`_(N credits remaining)_\`. Omit it if \`credits_remaining\` is null. Omit it too if \`credits_remaining\` is the string \`"unlimited"\` (internal/unlimited account) \u2014 do NOT print \`_(unlimited credits remaining)_\` or otherwise announce unlimited. Do NOT report a "credits used" figure: per-run cost can't be scoped reliably (a lead's contacts mix earlier enrichments), so only the balance is shown.
|
|
7561
7561
|
|
|
7562
7562
|
## GATE \u2014 PREFER BUILT-IN HOST WIDGETS
|
|
7563
7563
|
|
|
@@ -11033,6 +11033,22 @@ var qualifyLead = {
|
|
|
11033
11033
|
}
|
|
11034
11034
|
};
|
|
11035
11035
|
|
|
11036
|
+
// ../core/dist/composite/_credits-helpers.js
|
|
11037
|
+
var UNLIMITED = "unlimited";
|
|
11038
|
+
function isUnlimitedAccount(me) {
|
|
11039
|
+
return me.email?.toLowerCase().trim().endsWith("@leadbay.ai") ?? false;
|
|
11040
|
+
}
|
|
11041
|
+
async function readCreditsRemaining(client, force = false) {
|
|
11042
|
+
try {
|
|
11043
|
+
const me = await client.resolveMe(force);
|
|
11044
|
+
if (isUnlimitedAccount(me))
|
|
11045
|
+
return UNLIMITED;
|
|
11046
|
+
return me.organization.billing?.ai_credits ?? null;
|
|
11047
|
+
} catch {
|
|
11048
|
+
return null;
|
|
11049
|
+
}
|
|
11050
|
+
}
|
|
11051
|
+
|
|
11036
11052
|
// ../core/dist/tools/enrich-contacts.js
|
|
11037
11053
|
var enrichContacts = {
|
|
11038
11054
|
name: "leadbay_enrich_contacts",
|
|
@@ -11077,9 +11093,13 @@ var enrichContacts = {
|
|
|
11077
11093
|
let creditsRemaining = null;
|
|
11078
11094
|
try {
|
|
11079
11095
|
const me = await client.request("GET", "/users/me");
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
11096
|
+
if (isUnlimitedAccount(me)) {
|
|
11097
|
+
creditsRemaining = UNLIMITED;
|
|
11098
|
+
} else {
|
|
11099
|
+
creditsRemaining = me.organization.billing?.ai_credits ?? null;
|
|
11100
|
+
if (typeof creditsRemaining === "number" && creditsRemaining <= 0) {
|
|
11101
|
+
throw client.makeError("QUOTA_EXCEEDED", "No enrichment credits remaining", "Contact Leadbay support to extend your credit quota");
|
|
11102
|
+
}
|
|
11083
11103
|
}
|
|
11084
11104
|
} catch (e) {
|
|
11085
11105
|
if (e?.code === "QUOTA_EXCEEDED")
|
|
@@ -17975,7 +17995,11 @@ var accountStatus = {
|
|
|
17975
17995
|
type: "boolean",
|
|
17976
17996
|
description: "True if Leadbay is mid-regenerating intelligence after a refine_prompt; new leads will reflect it shortly."
|
|
17977
17997
|
},
|
|
17978
|
-
plan: { type: ["string", "null"] }
|
|
17998
|
+
plan: { type: ["string", "null"] },
|
|
17999
|
+
unlimited_credits: {
|
|
18000
|
+
type: "boolean",
|
|
18001
|
+
description: "True for internal/unlimited accounts (billing disabled server-side). When true, DO NOT read the null `plan` or absent quota as 'no credits' \u2014 the account has unlimited credits; enrichment/qualify proceed freely. Stay SILENT on credits: don't announce 'unlimited', don't show a balance, and never tell the user they're out of credits. False for normal accounts, where the usual quota/credit story applies."
|
|
18002
|
+
}
|
|
17979
18003
|
}
|
|
17980
18004
|
},
|
|
17981
18005
|
last_requested_lens: {
|
|
@@ -18079,7 +18103,11 @@ var accountStatus = {
|
|
|
18079
18103
|
name: me.organization.name,
|
|
18080
18104
|
ai_agent_enabled: me.organization.ai_agent_enabled ?? false,
|
|
18081
18105
|
computing_intelligence: me.organization.computing_intelligence ?? false,
|
|
18082
|
-
plan: quota?.plan ?? me.organization.quota_plan ?? null
|
|
18106
|
+
plan: quota?.plan ?? me.organization.quota_plan ?? null,
|
|
18107
|
+
// Internal/unlimited account (billing disabled). The backend zeroes
|
|
18108
|
+
// plan/quota for these, so without this flag the agent misreads
|
|
18109
|
+
// plan:null as "no credits" and refuses enrichment. product#3851.
|
|
18110
|
+
unlimited_credits: isUnlimitedAccount(me)
|
|
18083
18111
|
},
|
|
18084
18112
|
// Lens is withheld unless the user asked (lensAsked, above). When present,
|
|
18085
18113
|
// the id is normalized to the STRING form (my-lenses.ts) so it matches the
|
|
@@ -19835,16 +19863,6 @@ var qualifyStatus = {
|
|
|
19835
19863
|
}
|
|
19836
19864
|
};
|
|
19837
19865
|
|
|
19838
|
-
// ../core/dist/composite/_credits-helpers.js
|
|
19839
|
-
async function readCreditsRemaining(client, force = false) {
|
|
19840
|
-
try {
|
|
19841
|
-
const me = await client.resolveMe(force);
|
|
19842
|
-
return me.organization.billing?.ai_credits ?? null;
|
|
19843
|
-
} catch {
|
|
19844
|
-
return null;
|
|
19845
|
-
}
|
|
19846
|
-
}
|
|
19847
|
-
|
|
19848
19866
|
// ../core/dist/composite/enrich-titles.js
|
|
19849
19867
|
var DEFAULT_CANDIDATE_COUNT = 25;
|
|
19850
19868
|
var enrichTitles = {
|
|
@@ -19929,8 +19947,8 @@ var enrichTitles = {
|
|
|
19929
19947
|
description: "Count of enrichable contacts at preview time."
|
|
19930
19948
|
},
|
|
19931
19949
|
credits_remaining: {
|
|
19932
|
-
type: ["number", "null"],
|
|
19933
|
-
description:
|
|
19950
|
+
type: ["number", "string", "null"],
|
|
19951
|
+
description: `AI-credit balance BEFORE launching (billing.ai_credits). Present in discover / preview_only / dry_run modes. Pair with enrichable_contacts to tell the user 'you have N credits, this will enrich M contacts' \u2014 do NOT estimate an exact cost (the per-contact rate is backend-only). Null = billing unavailable. The string "unlimited" = an internal/unlimited account: proceed with enrichment freely and say NOTHING about credits (no balance, no 'unlimited', no 'no credits').`
|
|
19934
19952
|
},
|
|
19935
19953
|
selected_lead_count: {
|
|
19936
19954
|
type: "number",
|
|
@@ -20304,8 +20322,8 @@ var bulkEnrichStatus = {
|
|
|
20304
20322
|
description: "True when overall_progress.done === total AND no partial_failures."
|
|
20305
20323
|
},
|
|
20306
20324
|
credits_remaining: {
|
|
20307
|
-
type: ["number", "null"],
|
|
20308
|
-
description:
|
|
20325
|
+
type: ["number", "string", "null"],
|
|
20326
|
+
description: `AI-credit balance re-read after the spend (force-refreshed /users/me \u2192 billing.ai_credits). Present only when all_done. Null = billing unavailable (don't read as zero). The string "unlimited" = an internal/unlimited account: omit the _(N credits remaining)_ line entirely (there is no finite balance to show). NOTE: a per-run 'credits used' figure is intentionally NOT returned \u2014 getContacts can't scope cost to this bulk, so any sum would conflate historical enrichments.`
|
|
20309
20327
|
},
|
|
20310
20328
|
partial_failures: {
|
|
20311
20329
|
type: "array",
|
|
@@ -24067,7 +24085,7 @@ function parseWriteEnv(env = process.env) {
|
|
|
24067
24085
|
}
|
|
24068
24086
|
|
|
24069
24087
|
// src/http-server.ts
|
|
24070
|
-
var VERSION = true ? "0.23.
|
|
24088
|
+
var VERSION = true ? "0.23.14" : "0.0.0-dev";
|
|
24071
24089
|
var PORT = Number(process.env.PORT ?? 8080);
|
|
24072
24090
|
var HOST = process.env.HOST ?? "0.0.0.0";
|
|
24073
24091
|
var sseSessions = /* @__PURE__ */ new Map();
|
|
@@ -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.23.
|
|
1807
|
+
VERSION = true ? "0.23.14" : "0.0.0-dev";
|
|
1808
1808
|
MESSAGES = {
|
|
1809
1809
|
en: {
|
|
1810
1810
|
installer: {
|
package/dist/installer-gui.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leadbay/mcp",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.14",
|
|
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",
|