@leadbay/mcp 0.32.1 → 0.32.5
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 +56 -0
- package/dist/bin.js +49 -17
- package/dist/http-server.js +100 -15
- 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,61 @@
|
|
|
1
1
|
# Changelog — @leadbay/mcp
|
|
2
2
|
|
|
3
|
+
## 0.32.5 — 2026-09-01
|
|
4
|
+
|
|
5
|
+
`leadbay_update_contact` returned `NOT_FOUND` / 404 on 100% of the calls ever
|
|
6
|
+
made to it (product#3997). Not a missing route and not a backend bug.
|
|
7
|
+
|
|
8
|
+
Leadbay holds contacts in two id namespaces — `org_contacts` (the org's own
|
|
9
|
+
directory) and `paid_contacts` (enrichment results) — with separate models,
|
|
10
|
+
separate DAOs and separate ids. `POST /contacts/{id}/update` resolves
|
|
11
|
+
`orgContacts.findById` only, so a paid id can only ever 404.
|
|
12
|
+
|
|
13
|
+
`research_lead_by_id` merges `/leads/{id}/enrich/contacts` and
|
|
14
|
+
`/leads/{id}/contacts` into `contacts.reachable` / `contacts.candidates`, split
|
|
15
|
+
by **whether the person is messagable right now, not by which endpoint they came
|
|
16
|
+
from**. That split is right for outreach and wrong for identity: an enriched
|
|
17
|
+
paid contact sits in the same list as an org contact, and the agent had nothing
|
|
18
|
+
to distinguish them by. Roughly half the ids we handed out were unusable by a
|
|
19
|
+
tool the server instructions tell the agent to call after every outreach.
|
|
20
|
+
|
|
21
|
+
- The `source` field (`"org"` / `"paid"`) was already on the wire but undeclared
|
|
22
|
+
and unexplained. It is now documented in `research_lead_by_id`'s output schema
|
|
23
|
+
as the thing that decides whether an id can be passed to
|
|
24
|
+
`leadbay_update_contact`.
|
|
25
|
+
- `leadbay_update_contact`'s description states which namespace it accepts,
|
|
26
|
+
what a 404 means, and routes a correction to a paid contact through
|
|
27
|
+
`leadbay_add_contact` instead — the enrichment row is a provider's answer and
|
|
28
|
+
is not ours to edit.
|
|
29
|
+
- New: `research-contact-source-provenance.test.ts`, covering the case the
|
|
30
|
+
reachability split hides — an enriched paid contact and an org contact in the
|
|
31
|
+
SAME `reachable` list, distinguished only by `source`.
|
|
32
|
+
## 0.32.4 — 2026-09-01
|
|
33
|
+
|
|
34
|
+
`leadbay_account_status.notifications` was permanently `[]` on the hosted server
|
|
35
|
+
(product#4009). Same shape as product#4005: `buildServerFromClient` builds every
|
|
36
|
+
hosted server without a `notificationsInbox`, `BuildServerOptions` makes it
|
|
37
|
+
optional, so it compiled and failed silently.
|
|
38
|
+
|
|
39
|
+
**Not fixed by wiring the inbox, because the inbox cannot exist there.** It is
|
|
40
|
+
fed by a WS listener whose ticket (`GET /auth/ws`) is per-account, which is
|
|
41
|
+
meaningless on a multi-tenant process, and the streamable transport builds a
|
|
42
|
+
fresh `Server` per request so nothing would survive to be cached. Porting it
|
|
43
|
+
would be the same mistake in a third place.
|
|
44
|
+
|
|
45
|
+
`GET /notifications` is already per-account and already durable. On hosted the
|
|
46
|
+
ledger IS the inbox, so `account_status` reads it on demand when no inbox is
|
|
47
|
+
wired. Stdio is unchanged and still free — the WS listener already has the
|
|
48
|
+
answer, and the ledger is not called.
|
|
49
|
+
|
|
50
|
+
- `fetchTerminalNotifications(client)` in `notifications/catch-up.ts` returns
|
|
51
|
+
terminal, unseen entries directly. `isTerminalUnseen` is extracted so inbox
|
|
52
|
+
seeding and the inbox-less read cannot disagree about what counts.
|
|
53
|
+
- A notifications failure resolves to `[]` rather than throwing. `account_status`
|
|
54
|
+
is the daily entry point; a ledger hiccup must not take the check-in down.
|
|
55
|
+
- **Deliberately not done: `_meta.notifications` still does not ride hosted tool
|
|
56
|
+
responses.** Reviving it would mean a `GET /notifications` per tool call to
|
|
57
|
+
decorate every response. The cost lands on the check-in entry point only,
|
|
58
|
+
which is where the daily-rhythm channel is actually read.
|
|
3
59
|
## 0.32.0 — 2026-09-01
|
|
4
60
|
|
|
5
61
|
A poll-budget timeout stops being an error (product#4007). The import wizard's
|
package/dist/bin.js
CHANGED
|
@@ -5568,6 +5568,28 @@ var init_inbox = __esm({
|
|
|
5568
5568
|
});
|
|
5569
5569
|
|
|
5570
5570
|
// ../core/dist/notifications/catch-up.js
|
|
5571
|
+
function isTerminalUnseen(n) {
|
|
5572
|
+
if (!n.bulk_progress)
|
|
5573
|
+
return false;
|
|
5574
|
+
if (n.in_progress)
|
|
5575
|
+
return false;
|
|
5576
|
+
if (n.first_seen_at)
|
|
5577
|
+
return false;
|
|
5578
|
+
return true;
|
|
5579
|
+
}
|
|
5580
|
+
async function fetchTerminalNotifications(client, opts = {}) {
|
|
5581
|
+
try {
|
|
5582
|
+
const page = await client.listNotifications({
|
|
5583
|
+
archived: false,
|
|
5584
|
+
page: 0,
|
|
5585
|
+
count: opts.count ?? DEFAULT_COUNT
|
|
5586
|
+
});
|
|
5587
|
+
return page.items.filter(isTerminalUnseen).map(toInboxEntry);
|
|
5588
|
+
} catch (err) {
|
|
5589
|
+
opts.logger?.warn?.(`notifications.fetch_terminal failed: ${err?.message ?? err?.code ?? err}`);
|
|
5590
|
+
return [];
|
|
5591
|
+
}
|
|
5592
|
+
}
|
|
5571
5593
|
async function catchUpNotifications(client, inbox, opts = {}) {
|
|
5572
5594
|
const count = opts.count ?? DEFAULT_COUNT;
|
|
5573
5595
|
let added = 0;
|
|
@@ -5578,11 +5600,7 @@ async function catchUpNotifications(client, inbox, opts = {}) {
|
|
|
5578
5600
|
count
|
|
5579
5601
|
});
|
|
5580
5602
|
for (const n of page.items) {
|
|
5581
|
-
if (!n
|
|
5582
|
-
continue;
|
|
5583
|
-
if (n.in_progress)
|
|
5584
|
-
continue;
|
|
5585
|
-
if (n.first_seen_at)
|
|
5603
|
+
if (!isTerminalUnseen(n))
|
|
5586
5604
|
continue;
|
|
5587
5605
|
const sizeBefore = inbox.size();
|
|
5588
5606
|
inbox.record(n);
|
|
@@ -5599,6 +5617,7 @@ var DEFAULT_COUNT;
|
|
|
5599
5617
|
var init_catch_up = __esm({
|
|
5600
5618
|
"../core/dist/notifications/catch-up.js"() {
|
|
5601
5619
|
"use strict";
|
|
5620
|
+
init_revise_hint();
|
|
5602
5621
|
DEFAULT_COUNT = 50;
|
|
5603
5622
|
}
|
|
5604
5623
|
});
|
|
@@ -10270,9 +10289,9 @@ Trigger phrases: "update this contact", "fix this contact's title", "change thei
|
|
|
10270
10289
|
|
|
10271
10290
|
**Memory:** recall + capture via \`leadbay_agent_memory_*\` tools.
|
|
10272
10291
|
|
|
10273
|
-
Do NOT use for: "add a new contact to this company" \u2192 \`leadbay_add_contact\`; "remove / delete this contact" \u2192 \`leadbay_remove_contact\`; "get email/phone for a contact (enrichment)" \u2192 \`leadbay_enrich_titles\`.
|
|
10292
|
+
Do NOT use for: "add a new contact to this company" \u2192 \`leadbay_add_contact\`; "remove / delete this contact" \u2192 \`leadbay_remove_contact\`; "get email/phone for a contact (enrichment)" \u2192 \`leadbay_enrich_titles\`; "fix an enriched contact's details" \u2192 \`leadbay_add_contact\`.
|
|
10274
10293
|
|
|
10275
|
-
Prefer when: user wants to change details on
|
|
10294
|
+
Prefer when: user wants to change details on a contact that is in their own directory (\`source: "org"\`) \u2014 pass that contact's own \`contact_id\` plus first_name + last_name (required) and the fields to change
|
|
10276
10295
|
|
|
10277
10296
|
Examples that SHOULD invoke this tool:
|
|
10278
10297
|
- "Update Jane's title to SVP Engineering."
|
|
@@ -10292,7 +10311,18 @@ One-line confirmation naming the contact and what changed. No table.
|
|
|
10292
10311
|
|
|
10293
10312
|
Edit an existing contact in place \u2014 change their \`job_title\`, \`linkedin_page\`, \`email\`, \`phone_number\`, or name.
|
|
10294
10313
|
|
|
10295
|
-
Pass the contact's **own** \`contact_id\`
|
|
10314
|
+
Pass the contact's **own** \`contact_id\` \u2014 **not** the parent lead id.
|
|
10315
|
+
|
|
10316
|
+
**Only your organization's own directory contacts can be edited.** Leadbay holds contacts in two separate id namespaces and this endpoint resolves one of them:
|
|
10317
|
+
|
|
10318
|
+
| \`source\` on the contact | What it is | Editable here |
|
|
10319
|
+
|---|---|---|
|
|
10320
|
+
| \`"org"\` | A row in your organization's contact directory \u2014 added by you or your team, or promoted from an import | **yes** |
|
|
10321
|
+
| \`"paid"\` | An enrichment result bought from a data provider | **no** \u2014 returns \`NOT_FOUND\` / 404 |
|
|
10322
|
+
|
|
10323
|
+
\`leadbay_research_lead_by_id\` returns both, merged into \`contacts.reachable\` / \`contacts.candidates\` and split by whether the person is messagable right now \u2014 **not** by which namespace they came from. So read \`source\` on the contact before calling this. If it is \`"paid"\` and the user wants different details on record, add the corrected person with \`leadbay_add_contact\` instead; the enrichment row is a provider's answer and is not ours to edit.
|
|
10324
|
+
|
|
10325
|
+
A 404 from this tool almost always means a \`"paid"\` id was passed. Re-read the contact, check \`source\`, and do not retry the same id.
|
|
10296
10326
|
|
|
10297
10327
|
**\`first_name\` + \`last_name\` are required even on an edit.** The backend validates the full contact identity and rejects a partial body (\`invalid contact\`). So pass the contact's *current* first/last name even when you're only changing the title \u2014 read the current values via \`leadbay_research_lead_by_id\` first if you don't have them.
|
|
10298
10328
|
|
|
@@ -19122,7 +19152,7 @@ var init_research_lead_by_id = __esm({
|
|
|
19122
19152
|
},
|
|
19123
19153
|
contacts: {
|
|
19124
19154
|
type: "object",
|
|
19125
|
-
description:
|
|
19155
|
+
description: 'Two-tier contact set, partitioned by reachability \u2014 agent-friendly framing of the backend\'s paid-vs-org split. `reachable`: contacts with an email or phone right now (org-directory entries that ship with channels, PLUS paid contacts whose enrichment has completed). The agent can message these without buying enrichment. `candidates`: paid-contact entries WITHOUT resolved channels yet \u2014 typically LinkedIn URL only, `enrichment_done: false`. The agent must call leadbay_enrich_titles (or leadbay_prepare_outreach with enrich:true) before these become messagable. Every contact in both lists carries `source`: `"org"` means it is a row in your organization\'s own contact directory, `"paid"` means it came from enrichment. The two are separate id namespaces on the backend, so only a `source:"org"` id can be passed to leadbay_update_contact / leadbay_remove_contact \u2014 a `"paid"` id returns NOT_FOUND there.',
|
|
19126
19156
|
properties: {
|
|
19127
19157
|
reachable: { type: "array", items: { type: "object" } },
|
|
19128
19158
|
candidates: { type: "array", items: { type: "object" } }
|
|
@@ -20718,6 +20748,7 @@ var init_account_status = __esm({
|
|
|
20718
20748
|
"../core/dist/composite/account-status.js"() {
|
|
20719
20749
|
"use strict";
|
|
20720
20750
|
init_agent_memory();
|
|
20751
|
+
init_catch_up();
|
|
20721
20752
|
init_credits_helpers();
|
|
20722
20753
|
init_tool_descriptions_generated();
|
|
20723
20754
|
accountStatus = {
|
|
@@ -20839,6 +20870,8 @@ var init_account_status = __esm({
|
|
|
20839
20870
|
ctx?.logger?.warn?.(`account_status: quota_status failed: ${err?.message ?? err?.code ?? err}`);
|
|
20840
20871
|
}
|
|
20841
20872
|
}
|
|
20873
|
+
const inbox = ctx?.notificationsInbox;
|
|
20874
|
+
const notifications = inbox ? inbox.list() : await fetchTerminalNotifications(client, { logger: ctx?.logger });
|
|
20842
20875
|
const lensId = me.last_requested_lens ?? null;
|
|
20843
20876
|
const lensAsked = typeof ctx?.triggered_by === "string" && /\b(lens|lenses|audience|targeting|segment|filter)\b/i.test(ctx.triggered_by);
|
|
20844
20877
|
let last_requested_lens_name = null;
|
|
@@ -20879,13 +20912,12 @@ var init_account_status = __esm({
|
|
|
20879
20912
|
// on /me are intentionally NOT surfaced — they're defunct (see
|
|
20880
20913
|
// SHAPE-DRIFT.md probe round 4).
|
|
20881
20914
|
quota,
|
|
20882
|
-
//
|
|
20883
|
-
//
|
|
20884
|
-
//
|
|
20885
|
-
//
|
|
20886
|
-
//
|
|
20887
|
-
|
|
20888
|
-
notifications: ctx?.notificationsInbox?.list() ?? [],
|
|
20915
|
+
// Terminal bulk-progress notifications. Same shape the MCP server
|
|
20916
|
+
// attaches to `_meta.notifications` on every tool response — carried
|
|
20917
|
+
// here as a top-level field so the agent's daily-rhythm check-in (this
|
|
20918
|
+
// composite) sees them without reading _meta. See the read above for
|
|
20919
|
+
// why hosted takes a different path to the same list.
|
|
20920
|
+
notifications,
|
|
20889
20921
|
// Non-null ONLY when the quota_status call failed. The agent must treat
|
|
20890
20922
|
// this as "could not read quota" — NOT as zero usage, and NOT as a broken
|
|
20891
20923
|
// login (the token just authenticated /users/me above). product#3761.
|
|
@@ -32940,7 +32972,7 @@ var OAUTH_BASE_URLS = {
|
|
|
32940
32972
|
fr: "https://staging.api.leadbay.app"
|
|
32941
32973
|
}
|
|
32942
32974
|
};
|
|
32943
|
-
var VERSION = "0.32.
|
|
32975
|
+
var VERSION = "0.32.5";
|
|
32944
32976
|
var HELP = `
|
|
32945
32977
|
leadbay-mcp ${VERSION} \u2014 Leadbay Model Context Protocol server
|
|
32946
32978
|
|
package/dist/http-server.js
CHANGED
|
@@ -8101,9 +8101,82 @@ var COMPOSITE_FILE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
8101
8101
|
"leadbay_tour_plan"
|
|
8102
8102
|
]);
|
|
8103
8103
|
|
|
8104
|
+
// ../core/dist/notifications/revise-hint.js
|
|
8105
|
+
var HINT_BULK_ENRICH = "Contact enrichment just finished. Revise any prior output that named these leads' contacts (outreach drafts, contact lists, recommended-lead lists with contact_count, NEXT STEPS asking the user to wait for emails / phones). Re-fetch contacts via leadbay_get_contacts for the affected leads.";
|
|
8106
|
+
var HINT_BULK_QUALIFY = "Lead qualification just finished. Revise any prior lead list / ranking / outreach shortlist that depended on ai_agent_lead_score for these leads \u2014 today's leads, top-of-inbox, followups maps, prepare-outreach shortlists. Re-pull qualification answers via leadbay_research_lead_by_id or re-rank via leadbay_pull_leads.";
|
|
8107
|
+
var HINT_IMPORT = "CSV / CRM import just finished. Revise any prior output that referenced 'leads available' before the import landed \u2014 lead lists pulled from the affected lens, 'what's new today', followup planning. Re-pull the affected lens via leadbay_pull_leads / leadbay_pull_followups.";
|
|
8108
|
+
var HINT_OTHER = "Background work just completed. If you referenced its subject in prior output, re-fetch the affected data and revise.";
|
|
8109
|
+
function reviseHintFor(kind) {
|
|
8110
|
+
switch (kind) {
|
|
8111
|
+
case "bulk_enrich":
|
|
8112
|
+
return HINT_BULK_ENRICH;
|
|
8113
|
+
case "bulk_qualify":
|
|
8114
|
+
return HINT_BULK_QUALIFY;
|
|
8115
|
+
case "import":
|
|
8116
|
+
return HINT_IMPORT;
|
|
8117
|
+
default:
|
|
8118
|
+
return HINT_OTHER;
|
|
8119
|
+
}
|
|
8120
|
+
}
|
|
8121
|
+
function inferKind(n) {
|
|
8122
|
+
if (n.links.some((l) => l.type === "bulk_enrichment"))
|
|
8123
|
+
return "bulk_enrich";
|
|
8124
|
+
if (n.file_import_id)
|
|
8125
|
+
return "import";
|
|
8126
|
+
if (n.bulk_progress)
|
|
8127
|
+
return "bulk_qualify";
|
|
8128
|
+
return "other";
|
|
8129
|
+
}
|
|
8130
|
+
function anchorIdFor(n, kind) {
|
|
8131
|
+
if (kind === "bulk_enrich") {
|
|
8132
|
+
const link = n.links.find((l) => l.type === "bulk_enrichment");
|
|
8133
|
+
return link ? String(link.id) : null;
|
|
8134
|
+
}
|
|
8135
|
+
if (kind === "import")
|
|
8136
|
+
return n.file_import_id;
|
|
8137
|
+
return null;
|
|
8138
|
+
}
|
|
8139
|
+
function toInboxEntry(n) {
|
|
8140
|
+
const kind = inferKind(n);
|
|
8141
|
+
return {
|
|
8142
|
+
notification_id: n.id,
|
|
8143
|
+
kind,
|
|
8144
|
+
anchor_id: anchorIdFor(n, kind),
|
|
8145
|
+
title: n.title,
|
|
8146
|
+
bulk_progress: n.bulk_progress,
|
|
8147
|
+
completed_at: n.updated_at,
|
|
8148
|
+
revise_hint: reviseHintFor(kind)
|
|
8149
|
+
};
|
|
8150
|
+
}
|
|
8151
|
+
|
|
8104
8152
|
// ../core/dist/notifications/inbox.js
|
|
8105
8153
|
var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
8106
8154
|
|
|
8155
|
+
// ../core/dist/notifications/catch-up.js
|
|
8156
|
+
var DEFAULT_COUNT = 50;
|
|
8157
|
+
function isTerminalUnseen(n) {
|
|
8158
|
+
if (!n.bulk_progress)
|
|
8159
|
+
return false;
|
|
8160
|
+
if (n.in_progress)
|
|
8161
|
+
return false;
|
|
8162
|
+
if (n.first_seen_at)
|
|
8163
|
+
return false;
|
|
8164
|
+
return true;
|
|
8165
|
+
}
|
|
8166
|
+
async function fetchTerminalNotifications(client, opts = {}) {
|
|
8167
|
+
try {
|
|
8168
|
+
const page = await client.listNotifications({
|
|
8169
|
+
archived: false,
|
|
8170
|
+
page: 0,
|
|
8171
|
+
count: opts.count ?? DEFAULT_COUNT
|
|
8172
|
+
});
|
|
8173
|
+
return page.items.filter(isTerminalUnseen).map(toInboxEntry);
|
|
8174
|
+
} catch (err) {
|
|
8175
|
+
opts.logger?.warn?.(`notifications.fetch_terminal failed: ${err?.message ?? err?.code ?? err}`);
|
|
8176
|
+
return [];
|
|
8177
|
+
}
|
|
8178
|
+
}
|
|
8179
|
+
|
|
8107
8180
|
// ../core/dist/tool-descriptions.generated.js
|
|
8108
8181
|
var leadbay_account_history = `## WHEN TO USE
|
|
8109
8182
|
|
|
@@ -12609,9 +12682,9 @@ Trigger phrases: "update this contact", "fix this contact's title", "change thei
|
|
|
12609
12682
|
|
|
12610
12683
|
**Memory:** recall + capture via \`leadbay_agent_memory_*\` tools.
|
|
12611
12684
|
|
|
12612
|
-
Do NOT use for: "add a new contact to this company" \u2192 \`leadbay_add_contact\`; "remove / delete this contact" \u2192 \`leadbay_remove_contact\`; "get email/phone for a contact (enrichment)" \u2192 \`leadbay_enrich_titles\`.
|
|
12685
|
+
Do NOT use for: "add a new contact to this company" \u2192 \`leadbay_add_contact\`; "remove / delete this contact" \u2192 \`leadbay_remove_contact\`; "get email/phone for a contact (enrichment)" \u2192 \`leadbay_enrich_titles\`; "fix an enriched contact's details" \u2192 \`leadbay_add_contact\`.
|
|
12613
12686
|
|
|
12614
|
-
Prefer when: user wants to change details on
|
|
12687
|
+
Prefer when: user wants to change details on a contact that is in their own directory (\`source: "org"\`) \u2014 pass that contact's own \`contact_id\` plus first_name + last_name (required) and the fields to change
|
|
12615
12688
|
|
|
12616
12689
|
Examples that SHOULD invoke this tool:
|
|
12617
12690
|
- "Update Jane's title to SVP Engineering."
|
|
@@ -12631,7 +12704,18 @@ One-line confirmation naming the contact and what changed. No table.
|
|
|
12631
12704
|
|
|
12632
12705
|
Edit an existing contact in place \u2014 change their \`job_title\`, \`linkedin_page\`, \`email\`, \`phone_number\`, or name.
|
|
12633
12706
|
|
|
12634
|
-
Pass the contact's **own** \`contact_id\`
|
|
12707
|
+
Pass the contact's **own** \`contact_id\` \u2014 **not** the parent lead id.
|
|
12708
|
+
|
|
12709
|
+
**Only your organization's own directory contacts can be edited.** Leadbay holds contacts in two separate id namespaces and this endpoint resolves one of them:
|
|
12710
|
+
|
|
12711
|
+
| \`source\` on the contact | What it is | Editable here |
|
|
12712
|
+
|---|---|---|
|
|
12713
|
+
| \`"org"\` | A row in your organization's contact directory \u2014 added by you or your team, or promoted from an import | **yes** |
|
|
12714
|
+
| \`"paid"\` | An enrichment result bought from a data provider | **no** \u2014 returns \`NOT_FOUND\` / 404 |
|
|
12715
|
+
|
|
12716
|
+
\`leadbay_research_lead_by_id\` returns both, merged into \`contacts.reachable\` / \`contacts.candidates\` and split by whether the person is messagable right now \u2014 **not** by which namespace they came from. So read \`source\` on the contact before calling this. If it is \`"paid"\` and the user wants different details on record, add the corrected person with \`leadbay_add_contact\` instead; the enrichment row is a provider's answer and is not ours to edit.
|
|
12717
|
+
|
|
12718
|
+
A 404 from this tool almost always means a \`"paid"\` id was passed. Re-read the contact, check \`source\`, and do not retry the same id.
|
|
12635
12719
|
|
|
12636
12720
|
**\`first_name\` + \`last_name\` are required even on an edit.** The backend validates the full contact identity and rejects a partial body (\`invalid contact\`). So pass the contact's *current* first/last name even when you're only changing the title \u2014 read the current values via \`leadbay_research_lead_by_id\` first if you don't have them.
|
|
12637
12721
|
|
|
@@ -20848,7 +20932,7 @@ var researchLeadById = {
|
|
|
20848
20932
|
},
|
|
20849
20933
|
contacts: {
|
|
20850
20934
|
type: "object",
|
|
20851
|
-
description:
|
|
20935
|
+
description: 'Two-tier contact set, partitioned by reachability \u2014 agent-friendly framing of the backend\'s paid-vs-org split. `reachable`: contacts with an email or phone right now (org-directory entries that ship with channels, PLUS paid contacts whose enrichment has completed). The agent can message these without buying enrichment. `candidates`: paid-contact entries WITHOUT resolved channels yet \u2014 typically LinkedIn URL only, `enrichment_done: false`. The agent must call leadbay_enrich_titles (or leadbay_prepare_outreach with enrich:true) before these become messagable. Every contact in both lists carries `source`: `"org"` means it is a row in your organization\'s own contact directory, `"paid"` means it came from enrichment. The two are separate id namespaces on the backend, so only a `source:"org"` id can be passed to leadbay_update_contact / leadbay_remove_contact \u2014 a `"paid"` id returns NOT_FOUND there.',
|
|
20852
20936
|
properties: {
|
|
20853
20937
|
reachable: { type: "array", items: { type: "object" } },
|
|
20854
20938
|
candidates: { type: "array", items: { type: "object" } }
|
|
@@ -22490,6 +22574,8 @@ var accountStatus = {
|
|
|
22490
22574
|
ctx?.logger?.warn?.(`account_status: quota_status failed: ${err?.message ?? err?.code ?? err}`);
|
|
22491
22575
|
}
|
|
22492
22576
|
}
|
|
22577
|
+
const inbox = ctx?.notificationsInbox;
|
|
22578
|
+
const notifications = inbox ? inbox.list() : await fetchTerminalNotifications(client, { logger: ctx?.logger });
|
|
22493
22579
|
const lensId = me.last_requested_lens ?? null;
|
|
22494
22580
|
const lensAsked = typeof ctx?.triggered_by === "string" && /\b(lens|lenses|audience|targeting|segment|filter)\b/i.test(ctx.triggered_by);
|
|
22495
22581
|
let last_requested_lens_name = null;
|
|
@@ -22530,13 +22616,12 @@ var accountStatus = {
|
|
|
22530
22616
|
// on /me are intentionally NOT surfaced — they're defunct (see
|
|
22531
22617
|
// SHAPE-DRIFT.md probe round 4).
|
|
22532
22618
|
quota,
|
|
22533
|
-
//
|
|
22534
|
-
//
|
|
22535
|
-
//
|
|
22536
|
-
//
|
|
22537
|
-
//
|
|
22538
|
-
|
|
22539
|
-
notifications: ctx?.notificationsInbox?.list() ?? [],
|
|
22619
|
+
// Terminal bulk-progress notifications. Same shape the MCP server
|
|
22620
|
+
// attaches to `_meta.notifications` on every tool response — carried
|
|
22621
|
+
// here as a top-level field so the agent's daily-rhythm check-in (this
|
|
22622
|
+
// composite) sees them without reading _meta. See the read above for
|
|
22623
|
+
// why hosted takes a different path to the same list.
|
|
22624
|
+
notifications,
|
|
22540
22625
|
// Non-null ONLY when the quota_status call failed. The agent must treat
|
|
22541
22626
|
// this as "could not read quota" — NOT as zero usage, and NOT as a broken
|
|
22542
22627
|
// login (the token just authenticated /users/me above). product#3761.
|
|
@@ -22550,7 +22635,7 @@ var accountStatus = {
|
|
|
22550
22635
|
|
|
22551
22636
|
// ../core/dist/composite/bulk-qualify-leads.js
|
|
22552
22637
|
var PAGE_SIZE = 50;
|
|
22553
|
-
var
|
|
22638
|
+
var DEFAULT_COUNT2 = 10;
|
|
22554
22639
|
var MAX_COUNT = 25;
|
|
22555
22640
|
var DEFAULT_PER_LEAD_BUDGET_MS = 9e4;
|
|
22556
22641
|
var DEFAULT_TOTAL_BUDGET_MS2 = 5 * 6e4;
|
|
@@ -22598,7 +22683,7 @@ var bulkQualifyLeads = {
|
|
|
22598
22683
|
properties: {
|
|
22599
22684
|
count: {
|
|
22600
22685
|
type: "number",
|
|
22601
|
-
description: `How many fresh leads to qualify (default ${
|
|
22686
|
+
description: `How many fresh leads to qualify (default ${DEFAULT_COUNT2}, max ${MAX_COUNT})`
|
|
22602
22687
|
},
|
|
22603
22688
|
leadIds: {
|
|
22604
22689
|
type: "array",
|
|
@@ -22689,7 +22774,7 @@ var bulkQualifyLeads = {
|
|
|
22689
22774
|
]
|
|
22690
22775
|
},
|
|
22691
22776
|
execute: async (client, params, ctx) => {
|
|
22692
|
-
const wantCount = Math.min(params.count ??
|
|
22777
|
+
const wantCount = Math.min(params.count ?? DEFAULT_COUNT2, MAX_COUNT);
|
|
22693
22778
|
const perLeadBudget = params.per_lead_budget_ms ?? DEFAULT_PER_LEAD_BUDGET_MS;
|
|
22694
22779
|
const totalBudget = params.total_budget_ms ?? DEFAULT_TOTAL_BUDGET_MS2;
|
|
22695
22780
|
const totalDeadline = Date.now() + totalBudget;
|
|
@@ -29473,7 +29558,7 @@ function parseWriteEnv(env = process.env) {
|
|
|
29473
29558
|
}
|
|
29474
29559
|
|
|
29475
29560
|
// src/http-server.ts
|
|
29476
|
-
var VERSION = true ? "0.32.
|
|
29561
|
+
var VERSION = true ? "0.32.5" : "0.0.0-dev";
|
|
29477
29562
|
var PORT = Number(process.env.PORT ?? 8080);
|
|
29478
29563
|
var HOST = process.env.HOST ?? "0.0.0.0";
|
|
29479
29564
|
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.32.
|
|
3
|
+
"version": "0.32.5",
|
|
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",
|