@leadbay/mcp 0.39.7 → 0.39.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +48 -23
- package/dist/http-server.js +47 -22
- package/dist/installer-electron.js +1 -1
- package/dist/installer-gui.js +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -2996,7 +2996,7 @@ Pick the 2-3 options that match what actually happened \u2014 never all seven:
|
|
|
2996
2996
|
|
|
2997
2997
|
| Observation | Suggest | Calls |
|
|
2998
2998
|
|---|---|---|
|
|
2999
|
-
| Job still running (\`still_running: true\`) | "Check on it in ~1 min" | leadbay_lead_job_status(job_id, wait_seconds:
|
|
2999
|
+
| Job still running (\`still_running: true\`) | "Check on it in ~1 min" | leadbay_lead_job_status(job_id, wait_seconds: 45) |
|
|
3000
3000
|
| Free run delivered on-profile leads | "Qualify these N against your criteria (uses quota \u2014 \`dry_run\` first)" | leadbay_qualify_leads(prior_deliveries: {job_id}) |
|
|
3001
3001
|
| Delivered leads look right | "Draft outreach for the top ones" | leadbay_prepare_outreach |
|
|
3002
3002
|
| Delivered 0 or off-profile | "Reshape the example and retry" (name the fix from funnel + scope_notes) | leadbay_find_new_leads (NEW request_id) |
|
|
@@ -3992,7 +3992,7 @@ Trigger phrases: "is the lead search done", "any results yet on that job", "chec
|
|
|
3992
3992
|
|
|
3993
3993
|
Do NOT use for: "is the enrichment done" \u2192 \`leadbay_bulk_enrich_status\`; "is my import done" \u2192 \`leadbay_import_status\`; "is the top-N qualification done" \u2192 \`leadbay_qualify_status\`.
|
|
3994
3994
|
|
|
3995
|
-
Prefer when: a find_new_leads / qualify_leads result carried next_poll \u2014 pass its job_id; use wait_seconds
|
|
3995
|
+
Prefer when: a find_new_leads / qualify_leads result carried next_poll \u2014 pass its job_id; use wait_seconds 45 when the user asked to wait for results.
|
|
3996
3996
|
|
|
3997
3997
|
Examples that SHOULD invoke this tool:
|
|
3998
3998
|
- "Any leads yet from that search you started?"
|
|
@@ -4019,7 +4019,7 @@ usage counters (never render them), and the \`explain\` block
|
|
|
4019
4019
|
(basis, seed strategy, scope notes). Items are immutable once emitted \u2014
|
|
4020
4020
|
polling never re-reads live data, so numbers only ever grow.
|
|
4021
4021
|
|
|
4022
|
-
\`wait_seconds: 0\` (default) answers instantly; set
|
|
4022
|
+
\`wait_seconds: 0\` (default) answers instantly; set 45 (the maximum) to block-wait for
|
|
4023
4023
|
completion when the user asked for results "in this reply". \`since\` (from a
|
|
4024
4024
|
prior poll's \`next_since\`) pages only the new items. Jobs terminalize
|
|
4025
4025
|
server-side: past the 30-min wall clock a job reads \`completed_partial\`
|
|
@@ -4201,7 +4201,7 @@ is a status tool, keep it terse:
|
|
|
4201
4201
|
|
|
4202
4202
|
| Observation | Suggest | Calls |
|
|
4203
4203
|
|---|---|---|
|
|
4204
|
-
| Still running | "Keep waiting (~1 min) or leave it \u2014 results are kept 30 days" | leadbay_lead_job_status(job_id, wait_seconds:
|
|
4204
|
+
| Still running | "Keep waiting (~1 min) or leave it \u2014 results are kept 30 days" | leadbay_lead_job_status(job_id, wait_seconds: 45) |
|
|
4205
4205
|
| Terminal (completed / partial / failed) | Render the delivery per the RENDERING block, then offer the matching find_new_leads / qualify_leads NEXT STEPS | \u2014 |
|
|
4206
4206
|
| \`expired\` (past the 30-day window) | "Re-read the delivered leads from your delivery ledger" \u2014 there is nothing left to render: the job terminalized and its items are no longer listed, so do NOT present an empty delivery as a result | leadbay_qualify_leads(prior_deliveries: {job_id}) |
|
|
4207
4207
|
`;
|
|
@@ -5541,7 +5541,7 @@ Pick the 2-3 options that match what actually happened:
|
|
|
5541
5541
|
|
|
5542
5542
|
| Observation | Suggest | Calls |
|
|
5543
5543
|
|---|---|---|
|
|
5544
|
-
| Job still running | "Check on it in ~1 min" | leadbay_lead_job_status(job_id, wait_seconds:
|
|
5544
|
+
| Job still running | "Check on it in ~1 min" | leadbay_lead_job_status(job_id, wait_seconds: 45) |
|
|
5545
5545
|
| Fit leads with contacts delivered | "Draft outreach for the qualified ones" | leadbay_prepare_outreach |
|
|
5546
5546
|
| Items skipped \`not_in_universe\` | "Import those companies first, then re-qualify" | leadbay_import_leads \u2192 leadbay_qualify_leads |
|
|
5547
5547
|
| Items skipped \`low_confidence_identity\` | "Pick the right match" (show \`resolution.alternatives\`) | leadbay_qualify_leads with the chosen lead_id |
|
|
@@ -7794,7 +7794,7 @@ Pick the 2-3 options that match what actually happened \u2014 never all seven:
|
|
|
7794
7794
|
|
|
7795
7795
|
| Observation | Suggest | Calls |
|
|
7796
7796
|
|---|---|---|
|
|
7797
|
-
| Job still running (\`still_running: true\`) | "Check on it in ~1 min" | leadbay_lead_job_status(job_id, wait_seconds:
|
|
7797
|
+
| Job still running (\`still_running: true\`) | "Check on it in ~1 min" | leadbay_lead_job_status(job_id, wait_seconds: 45) |
|
|
7798
7798
|
| Free run delivered on-profile leads | "Qualify these N against your criteria (uses quota \u2014 \`dry_run\` first)" | leadbay_qualify_leads(prior_deliveries: {job_id}) |
|
|
7799
7799
|
| Delivered leads look right | "Draft outreach for the top ones" | leadbay_prepare_outreach |
|
|
7800
7800
|
| Delivered 0 or off-profile | "Reshape the example and retry" (name the fix from funnel + scope_notes) | leadbay_find_new_leads (NEW request_id) |
|
|
@@ -21557,6 +21557,21 @@ function mergeFilter(current, toAddSectors, toExcludeSectors, sizes, toAddLocati
|
|
|
21557
21557
|
const items = current?.lens_filter?.items ?? [];
|
|
21558
21558
|
const item = items[0] ?? { criteria: [] };
|
|
21559
21559
|
const criteria = item.criteria ? [...item.criteria] : [];
|
|
21560
|
+
const dropIds = (type, fromExcluded, ids) => {
|
|
21561
|
+
if (ids.length === 0)
|
|
21562
|
+
return;
|
|
21563
|
+
const idx = criteria.findIndex((c) => c.type === type && !!c.is_excluded === fromExcluded);
|
|
21564
|
+
if (idx < 0)
|
|
21565
|
+
return;
|
|
21566
|
+
const cur = criteria[idx];
|
|
21567
|
+
const key = type === "sector_ids" ? "sectors" : "locations";
|
|
21568
|
+
const held = cur[key] ?? [];
|
|
21569
|
+
const kept = held.filter((s) => !ids.includes(s));
|
|
21570
|
+
if (kept.length === 0)
|
|
21571
|
+
criteria.splice(idx, 1);
|
|
21572
|
+
else
|
|
21573
|
+
criteria[idx] = { ...cur, [key]: kept };
|
|
21574
|
+
};
|
|
21560
21575
|
if (toAddSectors.length > 0) {
|
|
21561
21576
|
const idx = criteria.findIndex((c) => c.type === "sector_ids" && !c.is_excluded);
|
|
21562
21577
|
if (idx >= 0) {
|
|
@@ -21571,6 +21586,7 @@ function mergeFilter(current, toAddSectors, toExcludeSectors, sizes, toAddLocati
|
|
|
21571
21586
|
});
|
|
21572
21587
|
}
|
|
21573
21588
|
}
|
|
21589
|
+
dropIds("sector_ids", true, toAddSectors);
|
|
21574
21590
|
if (toExcludeSectors.length > 0) {
|
|
21575
21591
|
const idx = criteria.findIndex((c) => c.type === "sector_ids" && c.is_excluded);
|
|
21576
21592
|
if (idx >= 0) {
|
|
@@ -21585,6 +21601,7 @@ function mergeFilter(current, toAddSectors, toExcludeSectors, sizes, toAddLocati
|
|
|
21585
21601
|
});
|
|
21586
21602
|
}
|
|
21587
21603
|
}
|
|
21604
|
+
dropIds("sector_ids", false, toExcludeSectors);
|
|
21588
21605
|
if (toAddLocations.length > 0) {
|
|
21589
21606
|
const idx = criteria.findIndex((c) => c.type === "location_ids" && !c.is_excluded);
|
|
21590
21607
|
if (idx >= 0) {
|
|
@@ -21599,6 +21616,7 @@ function mergeFilter(current, toAddSectors, toExcludeSectors, sizes, toAddLocati
|
|
|
21599
21616
|
});
|
|
21600
21617
|
}
|
|
21601
21618
|
}
|
|
21619
|
+
dropIds("location_ids", true, toAddLocations);
|
|
21602
21620
|
if (toExcludeLocations.length > 0) {
|
|
21603
21621
|
const idx = criteria.findIndex((c) => c.type === "location_ids" && c.is_excluded);
|
|
21604
21622
|
if (idx >= 0) {
|
|
@@ -21613,6 +21631,7 @@ function mergeFilter(current, toAddSectors, toExcludeSectors, sizes, toAddLocati
|
|
|
21613
21631
|
});
|
|
21614
21632
|
}
|
|
21615
21633
|
}
|
|
21634
|
+
dropIds("location_ids", false, toExcludeLocations);
|
|
21616
21635
|
if (sizes && sizes.length > 0) {
|
|
21617
21636
|
const normalizedSizes = sizes.map((s) => ({
|
|
21618
21637
|
min: s.min ?? 0,
|
|
@@ -22773,12 +22792,15 @@ function normalizeSearchFilters(filters) {
|
|
|
22773
22792
|
}
|
|
22774
22793
|
return out;
|
|
22775
22794
|
}
|
|
22776
|
-
function clampWaitSeconds(requested, fallback) {
|
|
22777
|
-
|
|
22778
|
-
|
|
22779
|
-
|
|
22795
|
+
function clampWaitSeconds(requested, fallback, startedAtMs) {
|
|
22796
|
+
const asked = requested == null || Number.isNaN(requested) ? fallback : requested;
|
|
22797
|
+
const bounded = Math.min(Math.max(asked, 0), MAX_WAIT_SECONDS);
|
|
22798
|
+
if (startedAtMs == null)
|
|
22799
|
+
return bounded;
|
|
22800
|
+
const spentSeconds = (Date.now() - startedAtMs) / 1e3;
|
|
22801
|
+
return Math.max(0, Math.min(bounded, MAX_WAIT_SECONDS - spentSeconds));
|
|
22780
22802
|
}
|
|
22781
|
-
var TERMINAL_JOB_STATES, MCP_JOB_POLL, SNAPSHOT_TIMEOUT_MS, PAGE_LIMIT, MAX_JOB_ITEMS, MIN_PAGES, maxPagesFor, JOB_ID_CHARSET, MAX_JOB_ID_LENGTH, UUID_RE2, LEAD_REASON_CHARS, SIGNAL_CHARS, MAX_SIGNALS, MAX_ALTERNATIVE_CONTACTS, RESULT_CHARS, LEAN_ROW_CHARS, COUNTRY_ALIASES, SUBNATIONAL_EXEMPTIONS, ALL_EXEMPTIONS, COUNTRY_LOCATION_VALUES, MAX_EXCLUDE_LEAD_IDS, MAX_LEAD_REFS;
|
|
22803
|
+
var TERMINAL_JOB_STATES, MCP_JOB_POLL, SNAPSHOT_TIMEOUT_MS, PAGE_LIMIT, MAX_JOB_ITEMS, MIN_PAGES, maxPagesFor, JOB_ID_CHARSET, MAX_JOB_ID_LENGTH, UUID_RE2, LEAD_REASON_CHARS, SIGNAL_CHARS, MAX_SIGNALS, MAX_ALTERNATIVE_CONTACTS, RESULT_CHARS, LEAN_ROW_CHARS, COUNTRY_ALIASES, SUBNATIONAL_EXEMPTIONS, ALL_EXEMPTIONS, COUNTRY_LOCATION_VALUES, MAX_EXCLUDE_LEAD_IDS, MAX_LEAD_REFS, MAX_WAIT_SECONDS;
|
|
22782
22804
|
var init_mcp_job_helpers = __esm({
|
|
22783
22805
|
"../core/dist/composite/_mcp-job-helpers.js"() {
|
|
22784
22806
|
"use strict";
|
|
@@ -22842,6 +22864,7 @@ var init_mcp_job_helpers = __esm({
|
|
|
22842
22864
|
COUNTRY_LOCATION_VALUES = buildCountryLocationValues();
|
|
22843
22865
|
MAX_EXCLUDE_LEAD_IDS = 500;
|
|
22844
22866
|
MAX_LEAD_REFS = 500;
|
|
22867
|
+
MAX_WAIT_SECONDS = 45;
|
|
22845
22868
|
}
|
|
22846
22869
|
});
|
|
22847
22870
|
|
|
@@ -23031,13 +23054,14 @@ var init_find_new_leads = __esm({
|
|
|
23031
23054
|
},
|
|
23032
23055
|
wait_seconds: {
|
|
23033
23056
|
type: "number",
|
|
23034
|
-
description: "How long to poll before returning (default 45
|
|
23057
|
+
description: "How long to poll before returning (default and maximum 45 \u2014 the whole call is bounded by it, so a slow submit shortens the poll; 0 = submit + one snapshot). Free searches usually finish inside the window; qualified exploration can take minutes \u2014 the result then carries still_running:true and the job_id to check with leadbay_lead_job_status."
|
|
23035
23058
|
}
|
|
23036
23059
|
},
|
|
23037
23060
|
required: ["count", "request_id"],
|
|
23038
23061
|
additionalProperties: false
|
|
23039
23062
|
},
|
|
23040
23063
|
execute: async (client, params, ctx) => {
|
|
23064
|
+
const startedAt = Date.now();
|
|
23041
23065
|
params = coerceArrayParams(params, [
|
|
23042
23066
|
"contact_titles",
|
|
23043
23067
|
"channels",
|
|
@@ -23176,7 +23200,7 @@ var init_find_new_leads = __esm({
|
|
|
23176
23200
|
const mocked = mockedSubmitPreview(submit, "leadbay_find_new_leads", client.region);
|
|
23177
23201
|
if (mocked)
|
|
23178
23202
|
return mocked;
|
|
23179
|
-
const waitSeconds = clampWaitSeconds(params.wait_seconds, DEFAULT_WAIT_SECONDS);
|
|
23203
|
+
const waitSeconds = clampWaitSeconds(params.wait_seconds, DEFAULT_WAIT_SECONDS, startedAt);
|
|
23180
23204
|
const snapshot = await snapshotAfterSubmit(client, submit.job_id, waitSeconds, ctx, params.count);
|
|
23181
23205
|
const done = TERMINAL_JOB_STATES.has(snapshot.job.state);
|
|
23182
23206
|
const { leads, skipped } = splitItems(snapshot);
|
|
@@ -23221,7 +23245,7 @@ var init_find_new_leads = __esm({
|
|
|
23221
23245
|
// INCREMENTALLY instead of re-reading (and re-rendering) the
|
|
23222
23246
|
// rows already delivered in this response.
|
|
23223
23247
|
since: snapshot.next_since ?? null,
|
|
23224
|
-
suggested_wait_seconds: done ? 0 :
|
|
23248
|
+
suggested_wait_seconds: done ? 0 : MAX_WAIT_SECONDS
|
|
23225
23249
|
},
|
|
23226
23250
|
...sectorNote(attempt.rewritten),
|
|
23227
23251
|
...countryNote,
|
|
@@ -23346,7 +23370,7 @@ function identityAnswer(jobId, snapshot, items, offset) {
|
|
|
23346
23370
|
job_id: jobId,
|
|
23347
23371
|
compact: true,
|
|
23348
23372
|
offset: nextOffset,
|
|
23349
|
-
suggested_wait_seconds: done ? 0 :
|
|
23373
|
+
suggested_wait_seconds: done ? 0 : MAX_WAIT_SECONDS
|
|
23350
23374
|
}
|
|
23351
23375
|
};
|
|
23352
23376
|
}
|
|
@@ -23571,12 +23595,13 @@ var init_qualify_leads = __esm({
|
|
|
23571
23595
|
},
|
|
23572
23596
|
wait_seconds: {
|
|
23573
23597
|
type: "number",
|
|
23574
|
-
description: "How long to poll before returning (default 45
|
|
23598
|
+
description: "How long to poll before returning (default and maximum 45 \u2014 the whole call is bounded by it, so a slow submit shortens the poll; 0 = submit + one snapshot). Large or research-heavy batches can take minutes \u2014 the result then carries still_running:true and the job_id for leadbay_lead_job_status."
|
|
23575
23599
|
}
|
|
23576
23600
|
},
|
|
23577
23601
|
additionalProperties: false
|
|
23578
23602
|
},
|
|
23579
23603
|
execute: async (client, params, ctx) => {
|
|
23604
|
+
const startedAt = Date.now();
|
|
23580
23605
|
params = coerceArrayParams(params, [
|
|
23581
23606
|
"lead_refs",
|
|
23582
23607
|
"contact_titles",
|
|
@@ -23636,7 +23661,7 @@ var init_qualify_leads = __esm({
|
|
|
23636
23661
|
const mocked = mockedSubmitPreview(submit, "leadbay_qualify_leads", client.region);
|
|
23637
23662
|
if (mocked)
|
|
23638
23663
|
return mocked;
|
|
23639
|
-
const waitSeconds = clampWaitSeconds(params.wait_seconds, DEFAULT_WAIT_SECONDS2);
|
|
23664
|
+
const waitSeconds = clampWaitSeconds(params.wait_seconds, DEFAULT_WAIT_SECONDS2, startedAt);
|
|
23640
23665
|
const snapshot = await snapshotAfterSubmit(client, submit.job_id, waitSeconds, ctx, submit.items_requested);
|
|
23641
23666
|
const done = TERMINAL_JOB_STATES.has(snapshot.job.state);
|
|
23642
23667
|
const indexed = submit.duplicate ?? false ? remapInputIndexes(snapshot.items, params.lead_refs) : { items: snapshot.items, remapped: true };
|
|
@@ -23713,7 +23738,7 @@ var init_qualify_leads = __esm({
|
|
|
23713
23738
|
// INCREMENTALLY instead of re-reading (and re-rendering) the
|
|
23714
23739
|
// rows already delivered in this response.
|
|
23715
23740
|
since: snapshot.next_since ?? null,
|
|
23716
|
-
suggested_wait_seconds: done ? 0 :
|
|
23741
|
+
suggested_wait_seconds: done ? 0 : MAX_WAIT_SECONDS
|
|
23717
23742
|
},
|
|
23718
23743
|
region: client.region
|
|
23719
23744
|
};
|
|
@@ -23757,7 +23782,7 @@ var init_lead_job_status = __esm({
|
|
|
23757
23782
|
},
|
|
23758
23783
|
wait_seconds: {
|
|
23759
23784
|
type: "number",
|
|
23760
|
-
description: "0 (default) = instant snapshot. >0 = keep polling up to this many seconds until the job is terminal \u2014 use
|
|
23785
|
+
description: "0 (default) = instant snapshot. >0 = keep polling up to this many seconds (maximum 45) until the job is terminal \u2014 use 45 when the user asked to wait for results."
|
|
23761
23786
|
},
|
|
23762
23787
|
compact: {
|
|
23763
23788
|
type: "boolean",
|
|
@@ -23809,7 +23834,7 @@ var init_lead_job_status = __esm({
|
|
|
23809
23834
|
// Same incremental handoff as the submit tools — following
|
|
23810
23835
|
// next_poll without the cursor re-reads the rows just returned.
|
|
23811
23836
|
since: snapshot.next_since ?? null,
|
|
23812
|
-
suggested_wait_seconds: done ? 0 :
|
|
23837
|
+
suggested_wait_seconds: done ? 0 : MAX_WAIT_SECONDS
|
|
23813
23838
|
},
|
|
23814
23839
|
region: client.region
|
|
23815
23840
|
};
|
|
@@ -27229,7 +27254,7 @@ searched. Render the delivery table and judge fit honestly: are these the
|
|
|
27229
27254
|
kind of companies I asked for?
|
|
27230
27255
|
|
|
27231
27256
|
- **\`still_running: true\`** \u2192 the job is ALIVE. Do not judge the seed and do
|
|
27232
|
-
not relaunch \u2014 poll \`leadbay_lead_job_status\` (\`wait_seconds:
|
|
27257
|
+
not relaunch \u2014 poll \`leadbay_lead_job_status\` (\`wait_seconds: 45\`) until
|
|
27233
27258
|
it goes terminal, reporting progress. Relaunching now burns an active-job
|
|
27234
27259
|
slot and rate-limit budget on a search that may be about to deliver.
|
|
27235
27260
|
- **On-profile** (terminal) \u2192 offer Phase 4.
|
|
@@ -27287,7 +27312,7 @@ When I want qualification evidence and/or reachable contacts:
|
|
|
27287
27312
|
\`exploration_cap\` can examine more than that, so send the most recent 500
|
|
27288
27313
|
rejects rather than an over-long list the tool refuses outright.
|
|
27289
27314
|
3. While the job runs, poll with \`leadbay_lead_job_status\`
|
|
27290
|
-
(\`wait_seconds:
|
|
27315
|
+
(\`wait_seconds: 45\`); report progress, not silence.
|
|
27291
27316
|
|
|
27292
27317
|
# PHASE 5 \u2014 DELIVER
|
|
27293
27318
|
|
|
@@ -32137,7 +32162,7 @@ var OAUTH_BASE_URLS = {
|
|
|
32137
32162
|
fr: "https://staging.api.leadbay.app"
|
|
32138
32163
|
}
|
|
32139
32164
|
};
|
|
32140
|
-
var VERSION = "0.39.
|
|
32165
|
+
var VERSION = "0.39.8";
|
|
32141
32166
|
var HELP = `
|
|
32142
32167
|
leadbay-mcp ${VERSION} \u2014 Leadbay Model Context Protocol server
|
|
32143
32168
|
|
package/dist/http-server.js
CHANGED
|
@@ -1400,7 +1400,7 @@ searched. Render the delivery table and judge fit honestly: are these the
|
|
|
1400
1400
|
kind of companies I asked for?
|
|
1401
1401
|
|
|
1402
1402
|
- **\`still_running: true\`** \u2192 the job is ALIVE. Do not judge the seed and do
|
|
1403
|
-
not relaunch \u2014 poll \`leadbay_lead_job_status\` (\`wait_seconds:
|
|
1403
|
+
not relaunch \u2014 poll \`leadbay_lead_job_status\` (\`wait_seconds: 45\`) until
|
|
1404
1404
|
it goes terminal, reporting progress. Relaunching now burns an active-job
|
|
1405
1405
|
slot and rate-limit budget on a search that may be about to deliver.
|
|
1406
1406
|
- **On-profile** (terminal) \u2192 offer Phase 4.
|
|
@@ -1458,7 +1458,7 @@ When I want qualification evidence and/or reachable contacts:
|
|
|
1458
1458
|
\`exploration_cap\` can examine more than that, so send the most recent 500
|
|
1459
1459
|
rejects rather than an over-long list the tool refuses outright.
|
|
1460
1460
|
3. While the job runs, poll with \`leadbay_lead_job_status\`
|
|
1461
|
-
(\`wait_seconds:
|
|
1461
|
+
(\`wait_seconds: 45\`); report progress, not silence.
|
|
1462
1462
|
|
|
1463
1463
|
# PHASE 5 \u2014 DELIVER
|
|
1464
1464
|
|
|
@@ -5946,7 +5946,7 @@ Pick the 2-3 options that match what actually happened \u2014 never all seven:
|
|
|
5946
5946
|
|
|
5947
5947
|
| Observation | Suggest | Calls |
|
|
5948
5948
|
|---|---|---|
|
|
5949
|
-
| Job still running (\`still_running: true\`) | "Check on it in ~1 min" | leadbay_lead_job_status(job_id, wait_seconds:
|
|
5949
|
+
| Job still running (\`still_running: true\`) | "Check on it in ~1 min" | leadbay_lead_job_status(job_id, wait_seconds: 45) |
|
|
5950
5950
|
| Free run delivered on-profile leads | "Qualify these N against your criteria (uses quota \u2014 \`dry_run\` first)" | leadbay_qualify_leads(prior_deliveries: {job_id}) |
|
|
5951
5951
|
| Delivered leads look right | "Draft outreach for the top ones" | leadbay_prepare_outreach |
|
|
5952
5952
|
| Delivered 0 or off-profile | "Reshape the example and retry" (name the fix from funnel + scope_notes) | leadbay_find_new_leads (NEW request_id) |
|
|
@@ -6942,7 +6942,7 @@ Trigger phrases: "is the lead search done", "any results yet on that job", "chec
|
|
|
6942
6942
|
|
|
6943
6943
|
Do NOT use for: "is the enrichment done" \u2192 \`leadbay_bulk_enrich_status\`; "is my import done" \u2192 \`leadbay_import_status\`; "is the top-N qualification done" \u2192 \`leadbay_qualify_status\`.
|
|
6944
6944
|
|
|
6945
|
-
Prefer when: a find_new_leads / qualify_leads result carried next_poll \u2014 pass its job_id; use wait_seconds
|
|
6945
|
+
Prefer when: a find_new_leads / qualify_leads result carried next_poll \u2014 pass its job_id; use wait_seconds 45 when the user asked to wait for results.
|
|
6946
6946
|
|
|
6947
6947
|
Examples that SHOULD invoke this tool:
|
|
6948
6948
|
- "Any leads yet from that search you started?"
|
|
@@ -6969,7 +6969,7 @@ usage counters (never render them), and the \`explain\` block
|
|
|
6969
6969
|
(basis, seed strategy, scope notes). Items are immutable once emitted \u2014
|
|
6970
6970
|
polling never re-reads live data, so numbers only ever grow.
|
|
6971
6971
|
|
|
6972
|
-
\`wait_seconds: 0\` (default) answers instantly; set
|
|
6972
|
+
\`wait_seconds: 0\` (default) answers instantly; set 45 (the maximum) to block-wait for
|
|
6973
6973
|
completion when the user asked for results "in this reply". \`since\` (from a
|
|
6974
6974
|
prior poll's \`next_since\`) pages only the new items. Jobs terminalize
|
|
6975
6975
|
server-side: past the 30-min wall clock a job reads \`completed_partial\`
|
|
@@ -7151,7 +7151,7 @@ is a status tool, keep it terse:
|
|
|
7151
7151
|
|
|
7152
7152
|
| Observation | Suggest | Calls |
|
|
7153
7153
|
|---|---|---|
|
|
7154
|
-
| Still running | "Keep waiting (~1 min) or leave it \u2014 results are kept 30 days" | leadbay_lead_job_status(job_id, wait_seconds:
|
|
7154
|
+
| Still running | "Keep waiting (~1 min) or leave it \u2014 results are kept 30 days" | leadbay_lead_job_status(job_id, wait_seconds: 45) |
|
|
7155
7155
|
| Terminal (completed / partial / failed) | Render the delivery per the RENDERING block, then offer the matching find_new_leads / qualify_leads NEXT STEPS | \u2014 |
|
|
7156
7156
|
| \`expired\` (past the 30-day window) | "Re-read the delivered leads from your delivery ledger" \u2014 there is nothing left to render: the job terminalized and its items are no longer listed, so do NOT present an empty delivery as a result | leadbay_qualify_leads(prior_deliveries: {job_id}) |
|
|
7157
7157
|
`;
|
|
@@ -8491,7 +8491,7 @@ Pick the 2-3 options that match what actually happened:
|
|
|
8491
8491
|
|
|
8492
8492
|
| Observation | Suggest | Calls |
|
|
8493
8493
|
|---|---|---|
|
|
8494
|
-
| Job still running | "Check on it in ~1 min" | leadbay_lead_job_status(job_id, wait_seconds:
|
|
8494
|
+
| Job still running | "Check on it in ~1 min" | leadbay_lead_job_status(job_id, wait_seconds: 45) |
|
|
8495
8495
|
| Fit leads with contacts delivered | "Draft outreach for the qualified ones" | leadbay_prepare_outreach |
|
|
8496
8496
|
| Items skipped \`not_in_universe\` | "Import those companies first, then re-qualify" | leadbay_import_leads \u2192 leadbay_qualify_leads |
|
|
8497
8497
|
| Items skipped \`low_confidence_identity\` | "Pick the right match" (show \`resolution.alternatives\`) | leadbay_qualify_leads with the chosen lead_id |
|
|
@@ -10744,7 +10744,7 @@ Pick the 2-3 options that match what actually happened \u2014 never all seven:
|
|
|
10744
10744
|
|
|
10745
10745
|
| Observation | Suggest | Calls |
|
|
10746
10746
|
|---|---|---|
|
|
10747
|
-
| Job still running (\`still_running: true\`) | "Check on it in ~1 min" | leadbay_lead_job_status(job_id, wait_seconds:
|
|
10747
|
+
| Job still running (\`still_running: true\`) | "Check on it in ~1 min" | leadbay_lead_job_status(job_id, wait_seconds: 45) |
|
|
10748
10748
|
| Free run delivered on-profile leads | "Qualify these N against your criteria (uses quota \u2014 \`dry_run\` first)" | leadbay_qualify_leads(prior_deliveries: {job_id}) |
|
|
10749
10749
|
| Delivered leads look right | "Draft outreach for the top ones" | leadbay_prepare_outreach |
|
|
10750
10750
|
| Delivered 0 or off-profile | "Reshape the example and retry" (name the fix from funnel + scope_notes) | leadbay_find_new_leads (NEW request_id) |
|
|
@@ -23742,6 +23742,21 @@ function mergeFilter(current, toAddSectors, toExcludeSectors, sizes, toAddLocati
|
|
|
23742
23742
|
const items = current?.lens_filter?.items ?? [];
|
|
23743
23743
|
const item = items[0] ?? { criteria: [] };
|
|
23744
23744
|
const criteria = item.criteria ? [...item.criteria] : [];
|
|
23745
|
+
const dropIds = (type, fromExcluded, ids) => {
|
|
23746
|
+
if (ids.length === 0)
|
|
23747
|
+
return;
|
|
23748
|
+
const idx = criteria.findIndex((c) => c.type === type && !!c.is_excluded === fromExcluded);
|
|
23749
|
+
if (idx < 0)
|
|
23750
|
+
return;
|
|
23751
|
+
const cur = criteria[idx];
|
|
23752
|
+
const key = type === "sector_ids" ? "sectors" : "locations";
|
|
23753
|
+
const held = cur[key] ?? [];
|
|
23754
|
+
const kept = held.filter((s) => !ids.includes(s));
|
|
23755
|
+
if (kept.length === 0)
|
|
23756
|
+
criteria.splice(idx, 1);
|
|
23757
|
+
else
|
|
23758
|
+
criteria[idx] = { ...cur, [key]: kept };
|
|
23759
|
+
};
|
|
23745
23760
|
if (toAddSectors.length > 0) {
|
|
23746
23761
|
const idx = criteria.findIndex((c) => c.type === "sector_ids" && !c.is_excluded);
|
|
23747
23762
|
if (idx >= 0) {
|
|
@@ -23756,6 +23771,7 @@ function mergeFilter(current, toAddSectors, toExcludeSectors, sizes, toAddLocati
|
|
|
23756
23771
|
});
|
|
23757
23772
|
}
|
|
23758
23773
|
}
|
|
23774
|
+
dropIds("sector_ids", true, toAddSectors);
|
|
23759
23775
|
if (toExcludeSectors.length > 0) {
|
|
23760
23776
|
const idx = criteria.findIndex((c) => c.type === "sector_ids" && c.is_excluded);
|
|
23761
23777
|
if (idx >= 0) {
|
|
@@ -23770,6 +23786,7 @@ function mergeFilter(current, toAddSectors, toExcludeSectors, sizes, toAddLocati
|
|
|
23770
23786
|
});
|
|
23771
23787
|
}
|
|
23772
23788
|
}
|
|
23789
|
+
dropIds("sector_ids", false, toExcludeSectors);
|
|
23773
23790
|
if (toAddLocations.length > 0) {
|
|
23774
23791
|
const idx = criteria.findIndex((c) => c.type === "location_ids" && !c.is_excluded);
|
|
23775
23792
|
if (idx >= 0) {
|
|
@@ -23784,6 +23801,7 @@ function mergeFilter(current, toAddSectors, toExcludeSectors, sizes, toAddLocati
|
|
|
23784
23801
|
});
|
|
23785
23802
|
}
|
|
23786
23803
|
}
|
|
23804
|
+
dropIds("location_ids", true, toAddLocations);
|
|
23787
23805
|
if (toExcludeLocations.length > 0) {
|
|
23788
23806
|
const idx = criteria.findIndex((c) => c.type === "location_ids" && c.is_excluded);
|
|
23789
23807
|
if (idx >= 0) {
|
|
@@ -23798,6 +23816,7 @@ function mergeFilter(current, toAddSectors, toExcludeSectors, sizes, toAddLocati
|
|
|
23798
23816
|
});
|
|
23799
23817
|
}
|
|
23800
23818
|
}
|
|
23819
|
+
dropIds("location_ids", false, toExcludeLocations);
|
|
23801
23820
|
if (sizes && sizes.length > 0) {
|
|
23802
23821
|
const normalizedSizes = sizes.map((s) => ({
|
|
23803
23822
|
min: s.min ?? 0,
|
|
@@ -24993,10 +25012,14 @@ function normalizeSearchFilters(filters) {
|
|
|
24993
25012
|
}
|
|
24994
25013
|
return out;
|
|
24995
25014
|
}
|
|
24996
|
-
|
|
24997
|
-
|
|
24998
|
-
|
|
24999
|
-
|
|
25015
|
+
var MAX_WAIT_SECONDS = 45;
|
|
25016
|
+
function clampWaitSeconds(requested, fallback, startedAtMs) {
|
|
25017
|
+
const asked = requested == null || Number.isNaN(requested) ? fallback : requested;
|
|
25018
|
+
const bounded = Math.min(Math.max(asked, 0), MAX_WAIT_SECONDS);
|
|
25019
|
+
if (startedAtMs == null)
|
|
25020
|
+
return bounded;
|
|
25021
|
+
const spentSeconds = (Date.now() - startedAtMs) / 1e3;
|
|
25022
|
+
return Math.max(0, Math.min(bounded, MAX_WAIT_SECONDS - spentSeconds));
|
|
25000
25023
|
}
|
|
25001
25024
|
|
|
25002
25025
|
// ../core/dist/composite/find-new-leads.js
|
|
@@ -25177,13 +25200,14 @@ var findNewLeads = {
|
|
|
25177
25200
|
},
|
|
25178
25201
|
wait_seconds: {
|
|
25179
25202
|
type: "number",
|
|
25180
|
-
description: "How long to poll before returning (default 45
|
|
25203
|
+
description: "How long to poll before returning (default and maximum 45 \u2014 the whole call is bounded by it, so a slow submit shortens the poll; 0 = submit + one snapshot). Free searches usually finish inside the window; qualified exploration can take minutes \u2014 the result then carries still_running:true and the job_id to check with leadbay_lead_job_status."
|
|
25181
25204
|
}
|
|
25182
25205
|
},
|
|
25183
25206
|
required: ["count", "request_id"],
|
|
25184
25207
|
additionalProperties: false
|
|
25185
25208
|
},
|
|
25186
25209
|
execute: async (client, params, ctx) => {
|
|
25210
|
+
const startedAt = Date.now();
|
|
25187
25211
|
params = coerceArrayParams(params, [
|
|
25188
25212
|
"contact_titles",
|
|
25189
25213
|
"channels",
|
|
@@ -25322,7 +25346,7 @@ var findNewLeads = {
|
|
|
25322
25346
|
const mocked = mockedSubmitPreview(submit, "leadbay_find_new_leads", client.region);
|
|
25323
25347
|
if (mocked)
|
|
25324
25348
|
return mocked;
|
|
25325
|
-
const waitSeconds = clampWaitSeconds(params.wait_seconds, DEFAULT_WAIT_SECONDS);
|
|
25349
|
+
const waitSeconds = clampWaitSeconds(params.wait_seconds, DEFAULT_WAIT_SECONDS, startedAt);
|
|
25326
25350
|
const snapshot = await snapshotAfterSubmit(client, submit.job_id, waitSeconds, ctx, params.count);
|
|
25327
25351
|
const done = TERMINAL_JOB_STATES.has(snapshot.job.state);
|
|
25328
25352
|
const { leads, skipped } = splitItems(snapshot);
|
|
@@ -25367,7 +25391,7 @@ var findNewLeads = {
|
|
|
25367
25391
|
// INCREMENTALLY instead of re-reading (and re-rendering) the
|
|
25368
25392
|
// rows already delivered in this response.
|
|
25369
25393
|
since: snapshot.next_since ?? null,
|
|
25370
|
-
suggested_wait_seconds: done ? 0 :
|
|
25394
|
+
suggested_wait_seconds: done ? 0 : MAX_WAIT_SECONDS
|
|
25371
25395
|
},
|
|
25372
25396
|
...sectorNote(attempt.rewritten),
|
|
25373
25397
|
...countryNote,
|
|
@@ -25501,7 +25525,7 @@ function identityAnswer(jobId, snapshot, items, offset) {
|
|
|
25501
25525
|
job_id: jobId,
|
|
25502
25526
|
compact: true,
|
|
25503
25527
|
offset: nextOffset,
|
|
25504
|
-
suggested_wait_seconds: done ? 0 :
|
|
25528
|
+
suggested_wait_seconds: done ? 0 : MAX_WAIT_SECONDS
|
|
25505
25529
|
}
|
|
25506
25530
|
};
|
|
25507
25531
|
}
|
|
@@ -25699,12 +25723,13 @@ var qualifyLeads = {
|
|
|
25699
25723
|
},
|
|
25700
25724
|
wait_seconds: {
|
|
25701
25725
|
type: "number",
|
|
25702
|
-
description: "How long to poll before returning (default 45
|
|
25726
|
+
description: "How long to poll before returning (default and maximum 45 \u2014 the whole call is bounded by it, so a slow submit shortens the poll; 0 = submit + one snapshot). Large or research-heavy batches can take minutes \u2014 the result then carries still_running:true and the job_id for leadbay_lead_job_status."
|
|
25703
25727
|
}
|
|
25704
25728
|
},
|
|
25705
25729
|
additionalProperties: false
|
|
25706
25730
|
},
|
|
25707
25731
|
execute: async (client, params, ctx) => {
|
|
25732
|
+
const startedAt = Date.now();
|
|
25708
25733
|
params = coerceArrayParams(params, [
|
|
25709
25734
|
"lead_refs",
|
|
25710
25735
|
"contact_titles",
|
|
@@ -25764,7 +25789,7 @@ var qualifyLeads = {
|
|
|
25764
25789
|
const mocked = mockedSubmitPreview(submit, "leadbay_qualify_leads", client.region);
|
|
25765
25790
|
if (mocked)
|
|
25766
25791
|
return mocked;
|
|
25767
|
-
const waitSeconds = clampWaitSeconds(params.wait_seconds, DEFAULT_WAIT_SECONDS2);
|
|
25792
|
+
const waitSeconds = clampWaitSeconds(params.wait_seconds, DEFAULT_WAIT_SECONDS2, startedAt);
|
|
25768
25793
|
const snapshot = await snapshotAfterSubmit(client, submit.job_id, waitSeconds, ctx, submit.items_requested);
|
|
25769
25794
|
const done = TERMINAL_JOB_STATES.has(snapshot.job.state);
|
|
25770
25795
|
const indexed = submit.duplicate ?? false ? remapInputIndexes(snapshot.items, params.lead_refs) : { items: snapshot.items, remapped: true };
|
|
@@ -25841,7 +25866,7 @@ var qualifyLeads = {
|
|
|
25841
25866
|
// INCREMENTALLY instead of re-reading (and re-rendering) the
|
|
25842
25867
|
// rows already delivered in this response.
|
|
25843
25868
|
since: snapshot.next_since ?? null,
|
|
25844
|
-
suggested_wait_seconds: done ? 0 :
|
|
25869
|
+
suggested_wait_seconds: done ? 0 : MAX_WAIT_SECONDS
|
|
25845
25870
|
},
|
|
25846
25871
|
region: client.region
|
|
25847
25872
|
};
|
|
@@ -25876,7 +25901,7 @@ var leadJobStatus = {
|
|
|
25876
25901
|
},
|
|
25877
25902
|
wait_seconds: {
|
|
25878
25903
|
type: "number",
|
|
25879
|
-
description: "0 (default) = instant snapshot. >0 = keep polling up to this many seconds until the job is terminal \u2014 use
|
|
25904
|
+
description: "0 (default) = instant snapshot. >0 = keep polling up to this many seconds (maximum 45) until the job is terminal \u2014 use 45 when the user asked to wait for results."
|
|
25880
25905
|
},
|
|
25881
25906
|
compact: {
|
|
25882
25907
|
type: "boolean",
|
|
@@ -25928,7 +25953,7 @@ var leadJobStatus = {
|
|
|
25928
25953
|
// Same incremental handoff as the submit tools — following
|
|
25929
25954
|
// next_poll without the cursor re-reads the rows just returned.
|
|
25930
25955
|
since: snapshot.next_since ?? null,
|
|
25931
|
-
suggested_wait_seconds: done ? 0 :
|
|
25956
|
+
suggested_wait_seconds: done ? 0 : MAX_WAIT_SECONDS
|
|
25932
25957
|
},
|
|
25933
25958
|
region: client.region
|
|
25934
25959
|
};
|
|
@@ -29410,7 +29435,7 @@ function parseWriteEnv(env = process.env) {
|
|
|
29410
29435
|
}
|
|
29411
29436
|
|
|
29412
29437
|
// src/http-server.ts
|
|
29413
|
-
var VERSION = true ? "0.39.
|
|
29438
|
+
var VERSION = true ? "0.39.8" : "0.0.0-dev";
|
|
29414
29439
|
var PORT = Number(process.env.PORT ?? 8080);
|
|
29415
29440
|
var HOST = process.env.HOST ?? "0.0.0.0";
|
|
29416
29441
|
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.39.
|
|
3
|
+
"version": "0.39.8",
|
|
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",
|