@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 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.bulk_progress)
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 an EXISTING contact \u2014 pass that contact's own \`contact_id\` plus first_name + last_name (required) and the fields to change
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\` (the \`id\` field from \`leadbay_research_lead_by_id\` or a contacts list) \u2014 **not** the parent lead 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: "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.",
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
- // Inbox of terminal bulk-progress notifications. Same shape the MCP
20883
- // server attaches to `_meta.notifications` on every tool response —
20884
- // duplicated here as a top-level field so the agent's daily-rhythm
20885
- // check-in (this composite) sees them without having to read _meta.
20886
- // Empty array when the WS listener isn't wired (OpenClaw, tests) OR
20887
- // when nothing has completed since the last ack.
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.1";
32975
+ var VERSION = "0.32.5";
32944
32976
  var HELP = `
32945
32977
  leadbay-mcp ${VERSION} \u2014 Leadbay Model Context Protocol server
32946
32978
 
@@ -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 an EXISTING contact \u2014 pass that contact's own \`contact_id\` plus first_name + last_name (required) and the fields to change
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\` (the \`id\` field from \`leadbay_research_lead_by_id\` or a contacts list) \u2014 **not** the parent lead 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: "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.",
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
- // Inbox of terminal bulk-progress notifications. Same shape the MCP
22534
- // server attaches to `_meta.notifications` on every tool response —
22535
- // duplicated here as a top-level field so the agent's daily-rhythm
22536
- // check-in (this composite) sees them without having to read _meta.
22537
- // Empty array when the WS listener isn't wired (OpenClaw, tests) OR
22538
- // when nothing has completed since the last ack.
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 DEFAULT_COUNT = 10;
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 ${DEFAULT_COUNT}, max ${MAX_COUNT})`
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 ?? DEFAULT_COUNT, MAX_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.1" : "0.0.0-dev";
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 = {
@@ -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.32.1" : "0.0.0-dev";
1807
+ VERSION = true ? "0.32.5" : "0.0.0-dev";
1808
1808
  MESSAGES = {
1809
1809
  en: {
1810
1810
  installer: {
@@ -1067,7 +1067,7 @@ async function oauthLogin(opts) {
1067
1067
  }
1068
1068
 
1069
1069
  // installer/installer-gui.ts
1070
- var VERSION = true ? "0.32.1" : "0.0.0-dev";
1070
+ var VERSION = true ? "0.32.5" : "0.0.0-dev";
1071
1071
  var MESSAGES = {
1072
1072
  en: {
1073
1073
  installer: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leadbay/mcp",
3
- "version": "0.32.1",
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",