@leadbay/mcp 0.33.3 → 0.33.4
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 +39 -0
- package/README.md +1 -1
- package/dist/bin.js +45 -14
- package/dist/http-server.js +45 -14
- 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,44 @@
|
|
|
1
1
|
# Changelog — @leadbay/mcp
|
|
2
2
|
|
|
3
|
+
## 0.33.4 — 2026-09-02
|
|
4
|
+
|
|
5
|
+
`leadbay_enrich_contacts` moves from `granularWriteTools` to
|
|
6
|
+
`compositeWriteTools` (product#4050).
|
|
7
|
+
|
|
8
|
+
It is the only tool that enriches ONE chosen person — `leadId` + `contactId`,
|
|
9
|
+
paid-candidate path first, org-contact path on `NOT_FOUND` — but it sat behind
|
|
10
|
+
`LEADBAY_MCP_ADVANCED=1`, which hosted never sets. No granular tool has been
|
|
11
|
+
called from a hosted IP in 30 days. The daily check-in prompt and the pull-leads
|
|
12
|
+
NEXT STEPS table already told the agent to call it, so on hosted they named a
|
|
13
|
+
tool that was not registered. `zoe+dogfood@leadbay.ai`'s scheduled agent
|
|
14
|
+
reached for `leadbay_pin_contact` instead (41 `contact not found` over 12 days;
|
|
15
|
+
see 0.33.3). Same registration pattern as add/remove/pin/unpin/update_contact
|
|
16
|
+
and set_lead_status: granular-shaped, lives in `tools/`, default write surface,
|
|
17
|
+
still hidden by `LEADBAY_MCP_WRITE=0`. Not in `COMPOSITE_FILE_TOOL_NAMES`, so
|
|
18
|
+
`_triggered_by` stays optional.
|
|
19
|
+
|
|
20
|
+
What moved with it, so the tool works on the surface it now lands on:
|
|
21
|
+
|
|
22
|
+
- **The result hint names a read that exists there.** It said "re-check
|
|
23
|
+
`leadbay_get_contacts`", which is advanced-only. It now says re-read the
|
|
24
|
+
lead's contacts via `leadbay_research_lead_by_id` (or `get_contacts` where
|
|
25
|
+
exposed).
|
|
26
|
+
- **The description carries routing.** `routing` + `rendering_hint`
|
|
27
|
+
frontmatter, added to `TOOLS_WITH_ROUTING`; the `prefer_when` states that a
|
|
28
|
+
`source:"paid"` candidate id from `research_lead_by_id` is valid input and
|
|
29
|
+
that pinning does not enrich anyone. Cross-routes to `enrich_titles`
|
|
30
|
+
(by title, many leads), `pin_contact`, `prepare_outreach`.
|
|
31
|
+
- **The pin/unpin 404 hint and `heuristics/pinnable-contacts` name it** as the
|
|
32
|
+
direct route alongside `enrich_titles` and `add_contact`. `enrich_titles`'s
|
|
33
|
+
WHEN NOT TO USE drops the "(granular)" label.
|
|
34
|
+
|
|
35
|
+
Tests: `packages/mcp/test/enrich-contacts-default-surface.test.ts` drives the
|
|
36
|
+
real MCP `tools/list` — present with `includeWrite:true, includeAdvanced:false`,
|
|
37
|
+
absent with `includeWrite:false`, and every `leadbay_*` the description and
|
|
38
|
+
the call hint name is itself registered on that surface.
|
|
39
|
+
`packages/core/test/unit/tools/pin-contact-hint-names-enrich-contacts.test.ts`
|
|
40
|
+
pins the hint.
|
|
41
|
+
|
|
3
42
|
## 0.33.3 — 2026-09-02
|
|
4
43
|
|
|
5
44
|
`leadbay_pin_contact` failed 43 of its 48 production calls.
|
package/README.md
CHANGED
|
@@ -634,7 +634,7 @@ Use `dry_run: true` to validate domain formatting and wizard reachability withou
|
|
|
634
634
|
|
|
635
635
|
- Tokens live only in your MCP client's config file — they never traverse the network except to `api-{region}.leadbay.app`.
|
|
636
636
|
- The `leadbay_login` tool from the OpenClaw adapter is **not** registered on MCP: exposing a credential-taking tool to an LLM is a prompt-injection risk. Use the token path above.
|
|
637
|
-
- The `leadbay_add_note` tool is a write action flagged `optional: true`. If your client supports per-tool opt-in, leave it disabled until you need it. `leadbay_enrich_contacts` is
|
|
637
|
+
- The `leadbay_add_note` tool is a write action flagged `optional: true`. If your client supports per-tool opt-in, leave it disabled until you need it. `leadbay_enrich_contacts` (enrich one named contact) is on the default write surface since 0.33.4; it was advanced-only before.
|
|
638
638
|
|
|
639
639
|
### Privacy & telemetry
|
|
640
640
|
|
package/dist/bin.js
CHANGED
|
@@ -2282,15 +2282,41 @@ WHEN NOT TO USE: as a default \u2014 answering with even a free-text reason give
|
|
|
2282
2282
|
|
|
2283
2283
|
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\`.
|
|
2284
2284
|
`;
|
|
2285
|
-
leadbay_enrich_contacts =
|
|
2285
|
+
leadbay_enrich_contacts = `## WHEN TO USE
|
|
2286
2286
|
|
|
2287
|
-
|
|
2287
|
+
Trigger phrases: "enrich this contact", "get this person's email", "find <name>'s phone number", "reveal the email of <name> at <company>", "enrich the managing director, not the president", "enrichir le DG \xE0 la place du pr\xE9sident".
|
|
2288
2288
|
|
|
2289
|
-
|
|
2289
|
+
Do NOT use for: "enrich the CEOs / a job title across my leads" \u2192 \`leadbay_enrich_titles\`; "pin / mark this person as the priority contact" \u2192 \`leadbay_pin_contact\`; "draft an email / prepare outreach for this lead" \u2192 \`leadbay_prepare_outreach\`.
|
|
2290
|
+
|
|
2291
|
+
Prefer when: the user names ONE person on ONE company \u2014 pass the lead id + that contact's own id. A \`source:"paid"\` candidate id from leadbay_research_lead_by_id is valid input. Pinning does not enrich anyone.
|
|
2292
|
+
|
|
2293
|
+
Examples that SHOULD invoke this tool:
|
|
2294
|
+
- "Get me Jane Doe's email at Acme."
|
|
2295
|
+
- "Enrich the managing director at Cromology, not the president."
|
|
2296
|
+
- "Reveal the phone number for this contact."
|
|
2297
|
+
|
|
2298
|
+
Examples that should NOT invoke this tool (sound similar, route elsewhere):
|
|
2299
|
+
- "Enrich the CEOs across my top 10 leads."
|
|
2300
|
+
- "Pin Jane Doe as the main contact on Acme."
|
|
2301
|
+
- "Draft an outreach email for Acme."
|
|
2302
|
+
|
|
2303
|
+
## RENDER (quick)
|
|
2304
|
+
|
|
2305
|
+
One line: "Enrichment started for <name> (<email / phone>)". Then re-read the
|
|
2306
|
+
lead's contacts every ~30s via leadbay_research_lead_by_id and report when
|
|
2307
|
+
the requested channel has landed. Never print a credits figure.
|
|
2308
|
+
|
|
2309
|
+
---
|
|
2310
|
+
|
|
2311
|
+
Order email and/or phone enrichment for a specific contact. Tries the paid-contact path and falls back to the org-contact path on NOT_FOUND. Each email reveal and each phone reveal consumes QUOTA. Both \`email\` and \`phone\` default to \`true\` \u2014 a bare call enriches both channels.
|
|
2312
|
+
|
|
2313
|
+
WHEN TO USE: when the user has already picked WHO they want on a company and you hold that person's \`contact_id\` \u2014 from \`leadbay_research_lead_by_id\` (\`reachable\` / \`candidates\` lists) or \`leadbay_get_contacts\` where exposed. A \`source: "paid"\` candidate id is the normal input here: it is the person Leadbay suggested but has not resolved yet, and this tool is what resolves them. What to expect on the re-read depends on which id you passed. **Paid candidate:** the candidate row never carries email or phone; when the provider finds a channel, the person appears as a \`source: "org"\` row (new, or merged into an existing org contact for the same person) with a DIFFERENT id, and that row holds the channel and is pinnable. If nothing is found, the candidate row reads \`enrichment_done: true\` with no channel and no org row is created. **Org contact** (\`source: "org"\`, e.g. adding phone to someone who already has an email): the SAME row is updated in place, same id.
|
|
2314
|
+
|
|
2315
|
+
WHEN NOT TO USE: for bulk enrichment by job title across many leads \u2014 use leadbay_enrich_titles, which handles the selection lifecycle and returns a clean preview/launch flow. Not to mark someone as the priority contact \u2014 that is leadbay_pin_contact, and pinning does not enrich anyone.
|
|
2290
2316
|
|
|
2291
2317
|
## QUOTA, NOT CREDITS
|
|
2292
2318
|
|
|
2293
|
-
Enrichment is gated by QUOTA (the per-window allowance in \`leadbay_account_status\`), not a credit balance. **Never pre-refuse because a credit number looks low or zero** \u2014 a freemium/fresh account with quota left can enrich even when its credit counter reads 0. The reveal either fits the remaining quota or the backend returns 429 (\`quota_exceeded\`); only THEN surface the exhausted window + wait-or-top-up choice. The \`credits_remaining\` field on the result is **advisory internal context only \u2014 do NOT display it**. Because it can read \`0\` on an account that still has quota, printing \`_(N credits remaining)_\` would falsely tell the user they're out. Do not render a credits balance at all; if the user asks where they stand, call \`leadbay_account_status\` and show the quota gauge instead. The actual per-contact cost (\`enrichment.credits_used\`) appears on the contact
|
|
2319
|
+
Enrichment is gated by QUOTA (the per-window allowance in \`leadbay_account_status\`), not a credit balance. **Never pre-refuse because a credit number looks low or zero** \u2014 a freemium/fresh account with quota left can enrich even when its credit counter reads 0. The reveal either fits the remaining quota or the backend returns 429 (\`quota_exceeded\`); only THEN surface the exhausted window + wait-or-top-up choice. The \`credits_remaining\` field on the result is **advisory internal context only \u2014 do NOT display it**. Because it can read \`0\` on an account that still has quota, printing \`_(N credits remaining)_\` would falsely tell the user they're out. Do not render a credits balance at all; if the user asks where they stand, call \`leadbay_account_status\` and show the quota gauge instead. The actual per-contact cost (\`enrichment.credits_used\`) appears on the contact after enrichment.
|
|
2294
2320
|
|
|
2295
2321
|
**Channels: when the user asks to enrich a contact without naming a channel, confirm scope via \`ask_user_input_v0\`** \u2014 \`"Enrich email only, or email + phone? (phone uses more quota)"\` \u2192 \`["Email only", "Email + phone"]\` \u2014 then pass the chosen \`email\`/\`phone\` flags. Skip the question only if they already said which channel(s) they want.
|
|
2296
2322
|
|
|
@@ -2300,7 +2326,7 @@ This tool MUTATES state. The caller (agent or human-in-the-loop) is responsible
|
|
|
2300
2326
|
|
|
2301
2327
|
WHEN TO USE: as the agent's go-to enrichment entry point, immediately before proposing outreach.
|
|
2302
2328
|
|
|
2303
|
-
WHEN NOT TO USE: to enrich a single contact \u2014 that's leadbay_enrich_contacts
|
|
2329
|
+
WHEN NOT TO USE: to enrich a single named contact \u2014 that's leadbay_enrich_contacts. Speculatively, before the user has committed to outreaching \u2014 enrichment consumes quota. **NOT to add "titles" or "LinkedIn" to a list** \u2014 a contact's \`job_title\` and \`linkedin_page\` already ride on the contact record; they are FREE and need no enrichment. If the user asks for "title and LinkedIn only", read those fields directly (e.g. leadbay_get_contacts / leadbay_research_lead_by_id); do NOT launch a job here. This tool is strictly the email / phone reveal, which consumes quota.
|
|
2304
2330
|
|
|
2305
2331
|
## ENRICHMENT CONSUMES QUOTA \u2014 the model to reason with
|
|
2306
2332
|
|
|
@@ -3719,10 +3745,10 @@ Passing a \`source: "paid"\` id here returns **\`contact not found\`**. That is
|
|
|
3719
3745
|
|
|
3720
3746
|
To pin someone who is currently only a candidate, first make them an org contact:
|
|
3721
3747
|
|
|
3722
|
-
- \`leadbay_enrich_titles\` (or \`leadbay_prepare_outreach\` with \`enrich: true\`)
|
|
3748
|
+
- \`leadbay_enrich_contacts\` with the lead id + this candidate's id enriches exactly this person. \`leadbay_enrich_titles\` (or \`leadbay_prepare_outreach\` with \`enrich: true\`) does the same by job title. When the provider finds an email or phone, each writes a NEW org contact for that person (or merges into an existing one). It has a **different \`id\`** from the paid candidate, so re-read the contacts list afterwards and pin the \`source: "org"\` row. If nothing was found, no org contact exists and there is nothing to pin.
|
|
3723
3749
|
- Or add them directly with \`leadbay_add_contact\`, which returns the new org contact's \`id\` \u2014 that id is pinnable immediately.
|
|
3724
3750
|
|
|
3725
|
-
**Pinning does not steer enrichment.** It only marks who the priority is on a company the user already has.
|
|
3751
|
+
**Pinning does not steer enrichment.** It only marks who the priority is on a company the user already has. "Enrich the Directeur G\xE9n\xE9ral rather than the Pr\xE9sident" is \`leadbay_enrich_contacts\` with that person's id (or \`leadbay_enrich_titles\` with the wanted title) \u2014 not a pin. Pinning first and enriching after changes nothing about who gets enriched.
|
|
3726
3752
|
|
|
3727
3753
|
|
|
3728
3754
|
Backend: \`POST /contacts/{contact_id}/pin\` \u2192 204. Idempotent. The inverse is \`leadbay_unpin_contact\`.
|
|
@@ -5704,10 +5730,10 @@ Passing a \`source: "paid"\` id here returns **\`contact not found\`**. That is
|
|
|
5704
5730
|
|
|
5705
5731
|
To pin someone who is currently only a candidate, first make them an org contact:
|
|
5706
5732
|
|
|
5707
|
-
- \`leadbay_enrich_titles\` (or \`leadbay_prepare_outreach\` with \`enrich: true\`)
|
|
5733
|
+
- \`leadbay_enrich_contacts\` with the lead id + this candidate's id enriches exactly this person. \`leadbay_enrich_titles\` (or \`leadbay_prepare_outreach\` with \`enrich: true\`) does the same by job title. When the provider finds an email or phone, each writes a NEW org contact for that person (or merges into an existing one). It has a **different \`id\`** from the paid candidate, so re-read the contacts list afterwards and pin the \`source: "org"\` row. If nothing was found, no org contact exists and there is nothing to pin.
|
|
5708
5734
|
- Or add them directly with \`leadbay_add_contact\`, which returns the new org contact's \`id\` \u2014 that id is pinnable immediately.
|
|
5709
5735
|
|
|
5710
|
-
**Pinning does not steer enrichment.** It only marks who the priority is on a company the user already has.
|
|
5736
|
+
**Pinning does not steer enrichment.** It only marks who the priority is on a company the user already has. "Enrich the Directeur G\xE9n\xE9ral rather than the Pr\xE9sident" is \`leadbay_enrich_contacts\` with that person's id (or \`leadbay_enrich_titles\` with the wanted title) \u2014 not a pin. Pinning first and enriching after changes nothing about who gets enriched.
|
|
5711
5737
|
|
|
5712
5738
|
|
|
5713
5739
|
A \`source: "org"\` contact that was never pinned is a no-op here, not an error \u2014 the backend answers 204 either way. Check \`pinned\` on the contact before calling if you need to tell the user whether anything actually changed.
|
|
@@ -6606,7 +6632,7 @@ var init_enrich_contacts = __esm({
|
|
|
6606
6632
|
email_requested: email,
|
|
6607
6633
|
phone_requested: phone,
|
|
6608
6634
|
credits_remaining: creditsRemaining,
|
|
6609
|
-
hint: "Enrichment started (runs async). Re-
|
|
6635
|
+
hint: "Enrichment started (runs async). Re-read the lead's contacts every ~30s (leadbay_research_lead_by_id; or leadbay_get_contacts where exposed) and treat the contact as done only when the REQUESTED channel actually landed \u2014 " + [email ? "`email` present" : null, phone ? "`phone_number` present" : null].filter(Boolean).join(" AND ") + " \u2014 on the person's row: for a `source:\"paid\"` candidate that is a NEW `source:\"org\"` row with a different id (the candidate row itself never carries a channel and only flips enrichment_done; done with no org row means nothing was found), for a `source:\"org\"` contact it is the same row (contact.enrichment.done alone is NOT enough: a contact previously enriched for the other channel already shows done:true, so a phone reveal can be reported before phone_number arrives). Then report the result \u2014 don't end your turn leaving it for the user to reprompt. Bound the wait: if the requested field hasn't landed after ~90s\u20132min of spaced re-checks, it likely can't be resolved \u2014 report that plainly (naming the channel that didn't resolve) rather than polling forever. EXCEPTION: if the user explicitly asked NOT to wait (background/'I'll check later'), don't run this loop \u2014 hand back the leadId + contactId and tell them to ask again / re-check later."
|
|
6610
6636
|
};
|
|
6611
6637
|
}
|
|
6612
6638
|
};
|
|
@@ -11838,7 +11864,7 @@ var init_pin_contact = __esm({
|
|
|
11838
11864
|
"../core/dist/tools/pin-contact.js"() {
|
|
11839
11865
|
"use strict";
|
|
11840
11866
|
init_tool_descriptions_generated();
|
|
11841
|
-
NOT_PINNABLE_HINT = 'This contact id is not in your organization\'s contact directory, so it cannot be pinned or unpinned. Almost always it is a `source: "paid"` enrichment candidate from leadbay_research_lead_by_id\'s `candidates` list; only `source: "org"` contacts are pinnable. The id is not wrong and the tool is not broken, so do NOT retry it. To act on this person, enrich them by job title with leadbay_enrich_titles, or add them with leadbay_add_contact \u2014
|
|
11867
|
+
NOT_PINNABLE_HINT = 'This contact id is not in your organization\'s contact directory, so it cannot be pinned or unpinned. Almost always it is a `source: "paid"` enrichment candidate from leadbay_research_lead_by_id\'s `candidates` list; only `source: "org"` contacts are pinnable. The id is not wrong and the tool is not broken, so do NOT retry it. To act on this person, enrich them directly with leadbay_enrich_contacts (the lead id + this contact id), enrich by job title with leadbay_enrich_titles, or add them with leadbay_add_contact \u2014 each produces a NEW org contact with a different id, which is pinnable. Note that pinning does not decide who gets enriched: leadbay_enrich_titles selects people by job title, leadbay_enrich_contacts by the contact id you pass.';
|
|
11842
11868
|
pinContact = {
|
|
11843
11869
|
name: "leadbay_pin_contact",
|
|
11844
11870
|
description: leadbay_pin_contact,
|
|
@@ -21916,7 +21942,6 @@ var init_dist = __esm({
|
|
|
21916
21942
|
];
|
|
21917
21943
|
granularWriteTools = [
|
|
21918
21944
|
qualifyLead,
|
|
21919
|
-
enrichContacts,
|
|
21920
21945
|
addNote,
|
|
21921
21946
|
selectLeads,
|
|
21922
21947
|
deselectLeads,
|
|
@@ -22068,6 +22093,12 @@ var init_dist = __esm({
|
|
|
22068
22093
|
pinContact,
|
|
22069
22094
|
unpinContact,
|
|
22070
22095
|
updateContact,
|
|
22096
|
+
// enrichContacts is granular-shaped (one POST per contact) but registered
|
|
22097
|
+
// HERE, not in granularWriteTools: it is the only tool that enriches ONE
|
|
22098
|
+
// chosen person (leadId + contactId) rather than a job title, and hosted
|
|
22099
|
+
// never sets LEADBAY_MCP_ADVANCED, so behind that gate no hosted agent could
|
|
22100
|
+
// act on a person it had already identified (product#4050).
|
|
22101
|
+
enrichContacts,
|
|
22071
22102
|
// createCustomField is granular-shaped but file-import prompts depend on it
|
|
22072
22103
|
// to preserve source-system links without requiring advanced-tool exposure.
|
|
22073
22104
|
createCustomField,
|
|
@@ -25695,7 +25726,7 @@ var FRICTION = `Problem reports: when the user asks you to report a Leadbay prob
|
|
|
25695
25726
|
var MENTAL_MODEL = `How Leadbay works (mental model): Leadbay is a sales inbox, not a queryable database. Each day the user logs back in, a fresh batch of leads is delivered. Batch size is paced by how many leads the user has actually acted on recently \u2014 some workflows produce a big stream of smaller prospects, others a narrow stream of bigger ones. Pulling more won't produce more; the user acting on leads (outreach, skips, saves) does.`;
|
|
25696
25727
|
var QUOTA_TOPUP = `Quota & top-ups: when a tool returns QUOTA_EXCEEDED / 429, the user has TWO options \u2014 wait for the window reset (daily / weekly / monthly resets shown in leadbay_account_status), OR top up AI credits (top-ups clear the throttle IMMEDIATELY \u2014 they are not subject to the same window). Always offer BOTH options; default-recommending 'wait until tomorrow' is wrong when a 30-second top-up unblocks the same call. If the host exposes leadbay_create_topup_link, OFFER it on every quota wall: 'Want me to generate a top-up link?' \u2014 when the user says yes, call leadbay_create_topup_link and surface the returned Stripe URL as a clickable link for the user to open in their browser. (Sibling leadbay_open_billing_portal is for ongoing subscription changes, not one-shot top-ups.) AFTER the user has topped up: do NOT keep refusing operations. A top-up invalidates every prior 429 and every stale 'you're at your quota' snapshot. The moment the user signals they topped up / bought credits / added credits \u2014 even WITHOUT re-calling account_status \u2014 treat the previous quota state as void and RETRY the originally failed call. (Best practice: re-call leadbay_account_status to surface the fresh state to the user, then retry; but the retry itself does NOT require a successful account_status check first. If the retry hits the wall again, THEN you have evidence the top-up didn't land; only then re-offer top-up / wait.) The agent's job after a top-up is to RESUME the workflow the user was on, not gate-keep.
|
|
25697
25728
|
|
|
25698
|
-
Show the refreshed quota AFTER a paid action has actually COMPLETED: when leadbay_bulk_enrich_status reports the job done \u2014 all_done, OR a plateau you've decided is terminal (overall_progress.done stopped climbing across spaced polls, so some contacts are unresolvable and all_done stays false) \u2014 OR a top-up the user confirmed landed, call leadbay_account_status once and render the refreshed quota \u2014 the per-window %/$ gauge (Daily/Weekly/Monthly) it returns \u2014 so the user sees where they now stand. Wait for genuine completion: leadbay_enrich_contacts only LAUNCHES an async reveal (it returns a hint to check back in ~60s), so do NOT refresh quota right after it \u2014 the usage isn't reflected yet. For that single-contact flow, refresh only once leadbay_get_contacts shows the REQUESTED channel actually landed \u2014 the requested email and/or phone_number present \u2014 NOT enrichment.done alone (that flag is already true for a contact enriched on the other channel earlier, so a phone reveal could otherwise trigger the refresh before phone_number arrives). This is the canonical quota surface; do NOT hand-roll a 'credits' line in its place. Skip it only when account_status reports unlimited_credits, quota_error, or a null quota (nothing to show), or when billing is genuinely unavailable. Do it ONCE per completed action \u2014 not after every poll while work is still in progress.`;
|
|
25729
|
+
Show the refreshed quota AFTER a paid action has actually COMPLETED: when leadbay_bulk_enrich_status reports the job done \u2014 all_done, OR a plateau you've decided is terminal (overall_progress.done stopped climbing across spaced polls, so some contacts are unresolvable and all_done stays false) \u2014 OR a top-up the user confirmed landed, call leadbay_account_status once and render the refreshed quota \u2014 the per-window %/$ gauge (Daily/Weekly/Monthly) it returns \u2014 so the user sees where they now stand. Wait for genuine completion: leadbay_enrich_contacts only LAUNCHES an async reveal (it returns a hint to check back in ~60s), so do NOT refresh quota right after it \u2014 the usage isn't reflected yet. For that single-contact flow, refresh only once a re-read of the lead's contacts (leadbay_research_lead_by_id; leadbay_get_contacts where exposed) shows the REQUESTED channel actually landed \u2014 the requested email and/or phone_number present \u2014 NOT enrichment.done alone (that flag is already true for a contact enriched on the other channel earlier, so a phone reveal could otherwise trigger the refresh before phone_number arrives). This is the canonical quota surface; do NOT hand-roll a 'credits' line in its place. Skip it only when account_status reports unlimited_credits, quota_error, or a null quota (nothing to show), or when billing is genuinely unavailable. Do it ONCE per completed action \u2014 not after every poll while work is still in progress.`;
|
|
25699
25730
|
var TRANSIENT_401 = `Transient 401s (DO NOT narrate as an auth problem): Leadbay bearer tokens do NOT expire on a timer, and the client already auto-retried this call once before surfacing it. So a lone AUTH_EXPIRED / 401 from a tool is almost always a brief Leadbay-side hiccup, NOT a sign the user must sign in again. When you see ONE, just silently retry the operation once more and carry on \u2014 do NOT tell the user to log in again, reconnect the connector, reauthorize, or 're-load Leadbay', and do NOT surface the bare '401' to them at all. Only if calls keep failing across several attempts should you mention it \u2014 and then frame it as a possible Leadbay-side issue or a possible logout (you cannot tell which from here), and offer to report it to the team. Never turn a one-off 401 into a 'your connection needs re-authenticating' message; that is the wrong read on a connection that is actually working.`;
|
|
25700
25731
|
var TRIGGERED_BY = `Trigger provenance (MANDATORY): every Leadbay composite-tool call MUST carry a non-empty \`_triggered_by\` argument \u2014 the verbatim slice of the user's most recent message that this call is acting upon. Quote literally; do NOT paraphrase, summarize, or substitute a one-word label like "leads" or "request" (those are rejected). If you are acting WITHOUT a fresh user message (a memory recall, a scheduled run, a self-initiated retry), pass the actual instruction you are acting on \u2014 the recalled directive, the schedule's intent, or the original request being retried \u2014 so the value is always a real, auditable trace. Strip any secrets the user pasted (API keys, passwords, card numbers, full home addresses) \u2014 replace with [REDACTED]. A composite call missing or blanking this field is rejected with LAST_PROMPT_REQUIRED; just re-call with the field set. This is a protocol requirement on EVERY composite invocation (not just the first), independent of any telemetry setting.`;
|
|
25701
25732
|
var VERIFICATION = `After every email, call, message, or meeting with a lead's contact, you MUST call leadbay_report_outreach with verification={source, ref} (gmail_message_id from the Gmail send, calendar_event_id from a booking, or user_confirmed='<the user's literal confirmation>'). Skipping or fabricating verification poisons the human team's pipeline.`;
|
|
@@ -27971,7 +28002,7 @@ var OAUTH_BASE_URLS = {
|
|
|
27971
28002
|
fr: "https://staging.api.leadbay.app"
|
|
27972
28003
|
}
|
|
27973
28004
|
};
|
|
27974
|
-
var VERSION = "0.33.
|
|
28005
|
+
var VERSION = "0.33.4";
|
|
27975
28006
|
var HELP = `
|
|
27976
28007
|
leadbay-mcp ${VERSION} \u2014 Leadbay Model Context Protocol server
|
|
27977
28008
|
|
package/dist/http-server.js
CHANGED
|
@@ -4873,15 +4873,41 @@ WHEN NOT TO USE: as a default \u2014 answering with even a free-text reason give
|
|
|
4873
4873
|
|
|
4874
4874
|
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\`.
|
|
4875
4875
|
`;
|
|
4876
|
-
var leadbay_enrich_contacts =
|
|
4876
|
+
var leadbay_enrich_contacts = `## WHEN TO USE
|
|
4877
4877
|
|
|
4878
|
-
|
|
4878
|
+
Trigger phrases: "enrich this contact", "get this person's email", "find <name>'s phone number", "reveal the email of <name> at <company>", "enrich the managing director, not the president", "enrichir le DG \xE0 la place du pr\xE9sident".
|
|
4879
4879
|
|
|
4880
|
-
|
|
4880
|
+
Do NOT use for: "enrich the CEOs / a job title across my leads" \u2192 \`leadbay_enrich_titles\`; "pin / mark this person as the priority contact" \u2192 \`leadbay_pin_contact\`; "draft an email / prepare outreach for this lead" \u2192 \`leadbay_prepare_outreach\`.
|
|
4881
|
+
|
|
4882
|
+
Prefer when: the user names ONE person on ONE company \u2014 pass the lead id + that contact's own id. A \`source:"paid"\` candidate id from leadbay_research_lead_by_id is valid input. Pinning does not enrich anyone.
|
|
4883
|
+
|
|
4884
|
+
Examples that SHOULD invoke this tool:
|
|
4885
|
+
- "Get me Jane Doe's email at Acme."
|
|
4886
|
+
- "Enrich the managing director at Cromology, not the president."
|
|
4887
|
+
- "Reveal the phone number for this contact."
|
|
4888
|
+
|
|
4889
|
+
Examples that should NOT invoke this tool (sound similar, route elsewhere):
|
|
4890
|
+
- "Enrich the CEOs across my top 10 leads."
|
|
4891
|
+
- "Pin Jane Doe as the main contact on Acme."
|
|
4892
|
+
- "Draft an outreach email for Acme."
|
|
4893
|
+
|
|
4894
|
+
## RENDER (quick)
|
|
4895
|
+
|
|
4896
|
+
One line: "Enrichment started for <name> (<email / phone>)". Then re-read the
|
|
4897
|
+
lead's contacts every ~30s via leadbay_research_lead_by_id and report when
|
|
4898
|
+
the requested channel has landed. Never print a credits figure.
|
|
4899
|
+
|
|
4900
|
+
---
|
|
4901
|
+
|
|
4902
|
+
Order email and/or phone enrichment for a specific contact. Tries the paid-contact path and falls back to the org-contact path on NOT_FOUND. Each email reveal and each phone reveal consumes QUOTA. Both \`email\` and \`phone\` default to \`true\` \u2014 a bare call enriches both channels.
|
|
4903
|
+
|
|
4904
|
+
WHEN TO USE: when the user has already picked WHO they want on a company and you hold that person's \`contact_id\` \u2014 from \`leadbay_research_lead_by_id\` (\`reachable\` / \`candidates\` lists) or \`leadbay_get_contacts\` where exposed. A \`source: "paid"\` candidate id is the normal input here: it is the person Leadbay suggested but has not resolved yet, and this tool is what resolves them. What to expect on the re-read depends on which id you passed. **Paid candidate:** the candidate row never carries email or phone; when the provider finds a channel, the person appears as a \`source: "org"\` row (new, or merged into an existing org contact for the same person) with a DIFFERENT id, and that row holds the channel and is pinnable. If nothing is found, the candidate row reads \`enrichment_done: true\` with no channel and no org row is created. **Org contact** (\`source: "org"\`, e.g. adding phone to someone who already has an email): the SAME row is updated in place, same id.
|
|
4905
|
+
|
|
4906
|
+
WHEN NOT TO USE: for bulk enrichment by job title across many leads \u2014 use leadbay_enrich_titles, which handles the selection lifecycle and returns a clean preview/launch flow. Not to mark someone as the priority contact \u2014 that is leadbay_pin_contact, and pinning does not enrich anyone.
|
|
4881
4907
|
|
|
4882
4908
|
## QUOTA, NOT CREDITS
|
|
4883
4909
|
|
|
4884
|
-
Enrichment is gated by QUOTA (the per-window allowance in \`leadbay_account_status\`), not a credit balance. **Never pre-refuse because a credit number looks low or zero** \u2014 a freemium/fresh account with quota left can enrich even when its credit counter reads 0. The reveal either fits the remaining quota or the backend returns 429 (\`quota_exceeded\`); only THEN surface the exhausted window + wait-or-top-up choice. The \`credits_remaining\` field on the result is **advisory internal context only \u2014 do NOT display it**. Because it can read \`0\` on an account that still has quota, printing \`_(N credits remaining)_\` would falsely tell the user they're out. Do not render a credits balance at all; if the user asks where they stand, call \`leadbay_account_status\` and show the quota gauge instead. The actual per-contact cost (\`enrichment.credits_used\`) appears on the contact
|
|
4910
|
+
Enrichment is gated by QUOTA (the per-window allowance in \`leadbay_account_status\`), not a credit balance. **Never pre-refuse because a credit number looks low or zero** \u2014 a freemium/fresh account with quota left can enrich even when its credit counter reads 0. The reveal either fits the remaining quota or the backend returns 429 (\`quota_exceeded\`); only THEN surface the exhausted window + wait-or-top-up choice. The \`credits_remaining\` field on the result is **advisory internal context only \u2014 do NOT display it**. Because it can read \`0\` on an account that still has quota, printing \`_(N credits remaining)_\` would falsely tell the user they're out. Do not render a credits balance at all; if the user asks where they stand, call \`leadbay_account_status\` and show the quota gauge instead. The actual per-contact cost (\`enrichment.credits_used\`) appears on the contact after enrichment.
|
|
4885
4911
|
|
|
4886
4912
|
**Channels: when the user asks to enrich a contact without naming a channel, confirm scope via \`ask_user_input_v0\`** \u2014 \`"Enrich email only, or email + phone? (phone uses more quota)"\` \u2192 \`["Email only", "Email + phone"]\` \u2014 then pass the chosen \`email\`/\`phone\` flags. Skip the question only if they already said which channel(s) they want.
|
|
4887
4913
|
|
|
@@ -4891,7 +4917,7 @@ var leadbay_enrich_titles = `Order contact enrichments by job title across many
|
|
|
4891
4917
|
|
|
4892
4918
|
WHEN TO USE: as the agent's go-to enrichment entry point, immediately before proposing outreach.
|
|
4893
4919
|
|
|
4894
|
-
WHEN NOT TO USE: to enrich a single contact \u2014 that's leadbay_enrich_contacts
|
|
4920
|
+
WHEN NOT TO USE: to enrich a single named contact \u2014 that's leadbay_enrich_contacts. Speculatively, before the user has committed to outreaching \u2014 enrichment consumes quota. **NOT to add "titles" or "LinkedIn" to a list** \u2014 a contact's \`job_title\` and \`linkedin_page\` already ride on the contact record; they are FREE and need no enrichment. If the user asks for "title and LinkedIn only", read those fields directly (e.g. leadbay_get_contacts / leadbay_research_lead_by_id); do NOT launch a job here. This tool is strictly the email / phone reveal, which consumes quota.
|
|
4895
4921
|
|
|
4896
4922
|
## ENRICHMENT CONSUMES QUOTA \u2014 the model to reason with
|
|
4897
4923
|
|
|
@@ -6310,10 +6336,10 @@ Passing a \`source: "paid"\` id here returns **\`contact not found\`**. That is
|
|
|
6310
6336
|
|
|
6311
6337
|
To pin someone who is currently only a candidate, first make them an org contact:
|
|
6312
6338
|
|
|
6313
|
-
- \`leadbay_enrich_titles\` (or \`leadbay_prepare_outreach\` with \`enrich: true\`)
|
|
6339
|
+
- \`leadbay_enrich_contacts\` with the lead id + this candidate's id enriches exactly this person. \`leadbay_enrich_titles\` (or \`leadbay_prepare_outreach\` with \`enrich: true\`) does the same by job title. When the provider finds an email or phone, each writes a NEW org contact for that person (or merges into an existing one). It has a **different \`id\`** from the paid candidate, so re-read the contacts list afterwards and pin the \`source: "org"\` row. If nothing was found, no org contact exists and there is nothing to pin.
|
|
6314
6340
|
- Or add them directly with \`leadbay_add_contact\`, which returns the new org contact's \`id\` \u2014 that id is pinnable immediately.
|
|
6315
6341
|
|
|
6316
|
-
**Pinning does not steer enrichment.** It only marks who the priority is on a company the user already has.
|
|
6342
|
+
**Pinning does not steer enrichment.** It only marks who the priority is on a company the user already has. "Enrich the Directeur G\xE9n\xE9ral rather than the Pr\xE9sident" is \`leadbay_enrich_contacts\` with that person's id (or \`leadbay_enrich_titles\` with the wanted title) \u2014 not a pin. Pinning first and enriching after changes nothing about who gets enriched.
|
|
6317
6343
|
|
|
6318
6344
|
|
|
6319
6345
|
Backend: \`POST /contacts/{contact_id}/pin\` \u2192 204. Idempotent. The inverse is \`leadbay_unpin_contact\`.
|
|
@@ -8295,10 +8321,10 @@ Passing a \`source: "paid"\` id here returns **\`contact not found\`**. That is
|
|
|
8295
8321
|
|
|
8296
8322
|
To pin someone who is currently only a candidate, first make them an org contact:
|
|
8297
8323
|
|
|
8298
|
-
- \`leadbay_enrich_titles\` (or \`leadbay_prepare_outreach\` with \`enrich: true\`)
|
|
8324
|
+
- \`leadbay_enrich_contacts\` with the lead id + this candidate's id enriches exactly this person. \`leadbay_enrich_titles\` (or \`leadbay_prepare_outreach\` with \`enrich: true\`) does the same by job title. When the provider finds an email or phone, each writes a NEW org contact for that person (or merges into an existing one). It has a **different \`id\`** from the paid candidate, so re-read the contacts list afterwards and pin the \`source: "org"\` row. If nothing was found, no org contact exists and there is nothing to pin.
|
|
8299
8325
|
- Or add them directly with \`leadbay_add_contact\`, which returns the new org contact's \`id\` \u2014 that id is pinnable immediately.
|
|
8300
8326
|
|
|
8301
|
-
**Pinning does not steer enrichment.** It only marks who the priority is on a company the user already has.
|
|
8327
|
+
**Pinning does not steer enrichment.** It only marks who the priority is on a company the user already has. "Enrich the Directeur G\xE9n\xE9ral rather than the Pr\xE9sident" is \`leadbay_enrich_contacts\` with that person's id (or \`leadbay_enrich_titles\` with the wanted title) \u2014 not a pin. Pinning first and enriching after changes nothing about who gets enriched.
|
|
8302
8328
|
|
|
8303
8329
|
|
|
8304
8330
|
A \`source: "org"\` contact that was never pinned is a no-op here, not an error \u2014 the backend answers 204 either way. Check \`pinned\` on the contact before calling if you need to tell the user whether anything actually changed.
|
|
@@ -9126,7 +9152,7 @@ var enrichContacts = {
|
|
|
9126
9152
|
email_requested: email,
|
|
9127
9153
|
phone_requested: phone,
|
|
9128
9154
|
credits_remaining: creditsRemaining,
|
|
9129
|
-
hint: "Enrichment started (runs async). Re-
|
|
9155
|
+
hint: "Enrichment started (runs async). Re-read the lead's contacts every ~30s (leadbay_research_lead_by_id; or leadbay_get_contacts where exposed) and treat the contact as done only when the REQUESTED channel actually landed \u2014 " + [email ? "`email` present" : null, phone ? "`phone_number` present" : null].filter(Boolean).join(" AND ") + " \u2014 on the person's row: for a `source:\"paid\"` candidate that is a NEW `source:\"org\"` row with a different id (the candidate row itself never carries a channel and only flips enrichment_done; done with no org row means nothing was found), for a `source:\"org\"` contact it is the same row (contact.enrichment.done alone is NOT enough: a contact previously enriched for the other channel already shows done:true, so a phone reveal can be reported before phone_number arrives). Then report the result \u2014 don't end your turn leaving it for the user to reprompt. Bound the wait: if the requested field hasn't landed after ~90s\u20132min of spaced re-checks, it likely can't be resolved \u2014 report that plainly (naming the channel that didn't resolve) rather than polling forever. EXCEPTION: if the user explicitly asked NOT to wait (background/'I'll check later'), don't run this loop \u2014 hand back the leadId + contactId and tell them to ask again / re-check later."
|
|
9130
9156
|
};
|
|
9131
9157
|
}
|
|
9132
9158
|
};
|
|
@@ -13979,7 +14005,7 @@ var removeContact = {
|
|
|
13979
14005
|
};
|
|
13980
14006
|
|
|
13981
14007
|
// ../core/dist/tools/pin-contact.js
|
|
13982
|
-
var NOT_PINNABLE_HINT = 'This contact id is not in your organization\'s contact directory, so it cannot be pinned or unpinned. Almost always it is a `source: "paid"` enrichment candidate from leadbay_research_lead_by_id\'s `candidates` list; only `source: "org"` contacts are pinnable. The id is not wrong and the tool is not broken, so do NOT retry it. To act on this person, enrich them by job title with leadbay_enrich_titles, or add them with leadbay_add_contact \u2014
|
|
14008
|
+
var NOT_PINNABLE_HINT = 'This contact id is not in your organization\'s contact directory, so it cannot be pinned or unpinned. Almost always it is a `source: "paid"` enrichment candidate from leadbay_research_lead_by_id\'s `candidates` list; only `source: "org"` contacts are pinnable. The id is not wrong and the tool is not broken, so do NOT retry it. To act on this person, enrich them directly with leadbay_enrich_contacts (the lead id + this contact id), enrich by job title with leadbay_enrich_titles, or add them with leadbay_add_contact \u2014 each produces a NEW org contact with a different id, which is pinnable. Note that pinning does not decide who gets enriched: leadbay_enrich_titles selects people by job title, leadbay_enrich_contacts by the contact id you pass.';
|
|
13983
14009
|
var pinContact = {
|
|
13984
14010
|
name: "leadbay_pin_contact",
|
|
13985
14011
|
description: leadbay_pin_contact,
|
|
@@ -22831,7 +22857,6 @@ var granularReadTools = [
|
|
|
22831
22857
|
];
|
|
22832
22858
|
var granularWriteTools = [
|
|
22833
22859
|
qualifyLead,
|
|
22834
|
-
enrichContacts,
|
|
22835
22860
|
addNote,
|
|
22836
22861
|
selectLeads,
|
|
22837
22862
|
deselectLeads,
|
|
@@ -22983,6 +23008,12 @@ var compositeWriteTools = [
|
|
|
22983
23008
|
pinContact,
|
|
22984
23009
|
unpinContact,
|
|
22985
23010
|
updateContact,
|
|
23011
|
+
// enrichContacts is granular-shaped (one POST per contact) but registered
|
|
23012
|
+
// HERE, not in granularWriteTools: it is the only tool that enriches ONE
|
|
23013
|
+
// chosen person (leadId + contactId) rather than a job title, and hosted
|
|
23014
|
+
// never sets LEADBAY_MCP_ADVANCED, so behind that gate no hosted agent could
|
|
23015
|
+
// act on a person it had already identified (product#4050).
|
|
23016
|
+
enrichContacts,
|
|
22986
23017
|
// createCustomField is granular-shaped but file-import prompts depend on it
|
|
22987
23018
|
// to preserve source-system links without requiring advanced-tool exposure.
|
|
22988
23019
|
createCustomField,
|
|
@@ -23716,7 +23747,7 @@ var FRICTION = `Problem reports: when the user asks you to report a Leadbay prob
|
|
|
23716
23747
|
var MENTAL_MODEL = `How Leadbay works (mental model): Leadbay is a sales inbox, not a queryable database. Each day the user logs back in, a fresh batch of leads is delivered. Batch size is paced by how many leads the user has actually acted on recently \u2014 some workflows produce a big stream of smaller prospects, others a narrow stream of bigger ones. Pulling more won't produce more; the user acting on leads (outreach, skips, saves) does.`;
|
|
23717
23748
|
var QUOTA_TOPUP = `Quota & top-ups: when a tool returns QUOTA_EXCEEDED / 429, the user has TWO options \u2014 wait for the window reset (daily / weekly / monthly resets shown in leadbay_account_status), OR top up AI credits (top-ups clear the throttle IMMEDIATELY \u2014 they are not subject to the same window). Always offer BOTH options; default-recommending 'wait until tomorrow' is wrong when a 30-second top-up unblocks the same call. If the host exposes leadbay_create_topup_link, OFFER it on every quota wall: 'Want me to generate a top-up link?' \u2014 when the user says yes, call leadbay_create_topup_link and surface the returned Stripe URL as a clickable link for the user to open in their browser. (Sibling leadbay_open_billing_portal is for ongoing subscription changes, not one-shot top-ups.) AFTER the user has topped up: do NOT keep refusing operations. A top-up invalidates every prior 429 and every stale 'you're at your quota' snapshot. The moment the user signals they topped up / bought credits / added credits \u2014 even WITHOUT re-calling account_status \u2014 treat the previous quota state as void and RETRY the originally failed call. (Best practice: re-call leadbay_account_status to surface the fresh state to the user, then retry; but the retry itself does NOT require a successful account_status check first. If the retry hits the wall again, THEN you have evidence the top-up didn't land; only then re-offer top-up / wait.) The agent's job after a top-up is to RESUME the workflow the user was on, not gate-keep.
|
|
23718
23749
|
|
|
23719
|
-
Show the refreshed quota AFTER a paid action has actually COMPLETED: when leadbay_bulk_enrich_status reports the job done \u2014 all_done, OR a plateau you've decided is terminal (overall_progress.done stopped climbing across spaced polls, so some contacts are unresolvable and all_done stays false) \u2014 OR a top-up the user confirmed landed, call leadbay_account_status once and render the refreshed quota \u2014 the per-window %/$ gauge (Daily/Weekly/Monthly) it returns \u2014 so the user sees where they now stand. Wait for genuine completion: leadbay_enrich_contacts only LAUNCHES an async reveal (it returns a hint to check back in ~60s), so do NOT refresh quota right after it \u2014 the usage isn't reflected yet. For that single-contact flow, refresh only once leadbay_get_contacts shows the REQUESTED channel actually landed \u2014 the requested email and/or phone_number present \u2014 NOT enrichment.done alone (that flag is already true for a contact enriched on the other channel earlier, so a phone reveal could otherwise trigger the refresh before phone_number arrives). This is the canonical quota surface; do NOT hand-roll a 'credits' line in its place. Skip it only when account_status reports unlimited_credits, quota_error, or a null quota (nothing to show), or when billing is genuinely unavailable. Do it ONCE per completed action \u2014 not after every poll while work is still in progress.`;
|
|
23750
|
+
Show the refreshed quota AFTER a paid action has actually COMPLETED: when leadbay_bulk_enrich_status reports the job done \u2014 all_done, OR a plateau you've decided is terminal (overall_progress.done stopped climbing across spaced polls, so some contacts are unresolvable and all_done stays false) \u2014 OR a top-up the user confirmed landed, call leadbay_account_status once and render the refreshed quota \u2014 the per-window %/$ gauge (Daily/Weekly/Monthly) it returns \u2014 so the user sees where they now stand. Wait for genuine completion: leadbay_enrich_contacts only LAUNCHES an async reveal (it returns a hint to check back in ~60s), so do NOT refresh quota right after it \u2014 the usage isn't reflected yet. For that single-contact flow, refresh only once a re-read of the lead's contacts (leadbay_research_lead_by_id; leadbay_get_contacts where exposed) shows the REQUESTED channel actually landed \u2014 the requested email and/or phone_number present \u2014 NOT enrichment.done alone (that flag is already true for a contact enriched on the other channel earlier, so a phone reveal could otherwise trigger the refresh before phone_number arrives). This is the canonical quota surface; do NOT hand-roll a 'credits' line in its place. Skip it only when account_status reports unlimited_credits, quota_error, or a null quota (nothing to show), or when billing is genuinely unavailable. Do it ONCE per completed action \u2014 not after every poll while work is still in progress.`;
|
|
23720
23751
|
var TRANSIENT_401 = `Transient 401s (DO NOT narrate as an auth problem): Leadbay bearer tokens do NOT expire on a timer, and the client already auto-retried this call once before surfacing it. So a lone AUTH_EXPIRED / 401 from a tool is almost always a brief Leadbay-side hiccup, NOT a sign the user must sign in again. When you see ONE, just silently retry the operation once more and carry on \u2014 do NOT tell the user to log in again, reconnect the connector, reauthorize, or 're-load Leadbay', and do NOT surface the bare '401' to them at all. Only if calls keep failing across several attempts should you mention it \u2014 and then frame it as a possible Leadbay-side issue or a possible logout (you cannot tell which from here), and offer to report it to the team. Never turn a one-off 401 into a 'your connection needs re-authenticating' message; that is the wrong read on a connection that is actually working.`;
|
|
23721
23752
|
var TRIGGERED_BY = `Trigger provenance (MANDATORY): every Leadbay composite-tool call MUST carry a non-empty \`_triggered_by\` argument \u2014 the verbatim slice of the user's most recent message that this call is acting upon. Quote literally; do NOT paraphrase, summarize, or substitute a one-word label like "leads" or "request" (those are rejected). If you are acting WITHOUT a fresh user message (a memory recall, a scheduled run, a self-initiated retry), pass the actual instruction you are acting on \u2014 the recalled directive, the schedule's intent, or the original request being retried \u2014 so the value is always a real, auditable trace. Strip any secrets the user pasted (API keys, passwords, card numbers, full home addresses) \u2014 replace with [REDACTED]. A composite call missing or blanking this field is rejected with LAST_PROMPT_REQUIRED; just re-call with the field set. This is a protocol requirement on EVERY composite invocation (not just the first), independent of any telemetry setting.`;
|
|
23722
23753
|
var VERIFICATION = `After every email, call, message, or meeting with a lead's contact, you MUST call leadbay_report_outreach with verification={source, ref} (gmail_message_id from the Gmail send, calendar_event_id from a booking, or user_confirmed='<the user's literal confirmation>'). Skipping or fabricating verification poisons the human team's pipeline.`;
|
|
@@ -24737,7 +24768,7 @@ function parseWriteEnv(env = process.env) {
|
|
|
24737
24768
|
}
|
|
24738
24769
|
|
|
24739
24770
|
// src/http-server.ts
|
|
24740
|
-
var VERSION = true ? "0.33.
|
|
24771
|
+
var VERSION = true ? "0.33.4" : "0.0.0-dev";
|
|
24741
24772
|
var PORT = Number(process.env.PORT ?? 8080);
|
|
24742
24773
|
var HOST = process.env.HOST ?? "0.0.0.0";
|
|
24743
24774
|
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.33.
|
|
3
|
+
"version": "0.33.4",
|
|
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",
|