@leadbay/mcp 0.6.3 → 0.7.0
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 +20 -0
- package/dist/bin.js +334 -37
- package/dist/{chunk-QAOJARMK.js → chunk-3WNCQ7MP.js} +935 -88
- package/dist/{dist-KHXRELXF.js → dist-2RTYPHB3.js} +5 -1
- package/package.json +2 -1
|
@@ -532,6 +532,444 @@ function parseRetryAfter(value) {
|
|
|
532
532
|
return null;
|
|
533
533
|
}
|
|
534
534
|
|
|
535
|
+
// ../core/dist/tool-descriptions.generated.js
|
|
536
|
+
var leadbay_account_status = `Show the user's account state \u2014 admin rights, language, last-active lens, current quota usage across daily/weekly/monthly windows for llm_completion / ai_rescore / web_fetch resources, and whether the org's intelligence is mid-regeneration. Quota windows also hint at the user's consumption pace: heavy recent activity (ai_rescore / web_fetch near their window limits) is a signal that Leadbay will deliver a larger fresh batch next time the user logs back in, since batch size is paced by real consumption.
|
|
537
|
+
|
|
538
|
+
WHEN TO USE: at the start of a session to know what the agent can/can't do, or after a 429 to explain to the user which resource window was exhausted and when it resets.
|
|
539
|
+
|
|
540
|
+
WHEN NOT TO USE: as a pre-flight gate before bulk ops \u2014 operations themselves return 429; this tool is for context, not gating.
|
|
541
|
+
`;
|
|
542
|
+
var leadbay_add_note = `Add a note to a lead. Notes are visible to the whole organization in Leadbay.
|
|
543
|
+
|
|
544
|
+
WHEN TO USE: low-level \u2014 for free-form notes not tied to outreach actions, including meaningful per-lead notes/context preserved from an imported file after the import returns lead IDs.
|
|
545
|
+
|
|
546
|
+
WHEN NOT TO USE: to log an outreach action \u2014 use leadbay_report_outreach, which requires verification (gmail/calendar/user_confirmed) to prevent hallucinated outreach poisoning the SDR pipeline.
|
|
547
|
+
|
|
548
|
+
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\`.
|
|
549
|
+
`;
|
|
550
|
+
var leadbay_adjust_audience = `Restrict (or expand) the lens audience by sector / size. Free-text sectors are auto-resolved against the sector taxonomy; ambiguous matches are surfaced to the agent rather than guessed silently. Permission routing is hidden: the default lens auto-clones to a new user lens; an org-level lens defaults to a per-user draft (admins can override with \`save_for_org:true\`). Filter MERGES with existing criteria (unrelated criteria are not dropped).
|
|
551
|
+
|
|
552
|
+
WHEN TO USE: when the user wants to see different kinds of leads (sector / size / etc.).
|
|
553
|
+
|
|
554
|
+
WHEN NOT TO USE: to refine BEYOND firmographics \u2014 that's leadbay_refine_prompt.
|
|
555
|
+
|
|
556
|
+
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\`.
|
|
557
|
+
`;
|
|
558
|
+
var leadbay_answer_clarification = `Answer the pending clarification question Leadbay raised after a refine_prompt. The answer is stored as the new \`user_prompt\` and triggers regeneration. Pass \`option_id\` (preferred \u2014 pick from the offered options) or \`text_answer\` (free-text). Admin-only.
|
|
559
|
+
|
|
560
|
+
WHEN TO USE: after leadbay_refine_prompt returns \`status='clarification_pending'\`.
|
|
561
|
+
|
|
562
|
+
WHEN NOT TO USE: to set a brand-new prompt \u2014 use leadbay_refine_prompt.
|
|
563
|
+
|
|
564
|
+
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\`.
|
|
565
|
+
`;
|
|
566
|
+
var leadbay_bulk_enrich_status = `Check status + per-lead contacts for a bulk enrichment you previously launched via leadbay_enrich_titles. Returns the \`bulk_id\`, progress per lead (done/total enrichable contacts), and overall progress. When \`include_contacts=true\` (opt-in), includes each contact's email/phone/job_title/enrichment.done.
|
|
567
|
+
|
|
568
|
+
WHEN TO USE: poll this after leadbay_enrich_titles returns a \`bulk_id\`. Default \`include_contacts=false\` for cheap status polls; set \`include_contacts=true\` once \`all_done\` flips for the final read.
|
|
569
|
+
|
|
570
|
+
WHEN NOT TO USE: as a substitute for leadbay_research_lead \u2014 that already includes enriched contacts for a single lead.
|
|
571
|
+
`;
|
|
572
|
+
var leadbay_bulk_qualify_leads = `Pick the next N unqualified leads in the active lens and qualify them (run AI rescore + web fetch). Pass \`wait_for_completion:false\` to return quickly with \`{status:'running', qualify_id}\`; poll leadbay_qualify_status with that id. With \`wait_for_completion\` omitted/true, the legacy behavior polls until the answers are populated or a budget is exhausted. Already-qualified leads (those with a non-null \`ai_agent_lead_score\`) are silently no-ops on the backend, so this composite paginates past them to find fresh candidates. On 429 mid-fanout, stops launching but keeps polling already-launched leads.
|
|
573
|
+
|
|
574
|
+
Context: Leadbay auto-qualifies roughly the top 10 of each daily batch. Leads below the top ~10 are NOT worse \u2014 the system is saving resources. This tool is how the agent spends more resources to go deeper on promising-looking leads the user hasn't had time to surface yet.
|
|
575
|
+
|
|
576
|
+
WHEN TO USE: when the user wants more qualified leads than what's currently shown, or when a lead looks promising in leadbay_pull_leads but has an empty \`qualification_summary\`.
|
|
577
|
+
|
|
578
|
+
WHEN NOT TO USE: to qualify a single specific lead \u2014 that's leadbay_qualify_lead (granular, advanced).
|
|
579
|
+
|
|
580
|
+
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\`.
|
|
581
|
+
`;
|
|
582
|
+
var leadbay_clear_selection = `Clear the user's transient selection.
|
|
583
|
+
|
|
584
|
+
WHEN TO USE: cleanup after manual selection work, or recovery from a stuck composite.
|
|
585
|
+
|
|
586
|
+
WHEN NOT TO USE: in normal flow \u2014 composites clear in their own finally blocks.
|
|
587
|
+
|
|
588
|
+
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\`.
|
|
589
|
+
`;
|
|
590
|
+
var leadbay_clear_user_prompt = `Remove the org's intelligence-refinement prompt (revert to AI-only generation). Admin-only. Triggers full intelligence regeneration.
|
|
591
|
+
|
|
592
|
+
WHEN TO USE: when a refinement turned out to be the wrong direction.
|
|
593
|
+
|
|
594
|
+
WHEN NOT TO USE: to replace with a different prompt \u2014 just call leadbay_refine_prompt; that overwrites.
|
|
595
|
+
|
|
596
|
+
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\`.
|
|
597
|
+
`;
|
|
598
|
+
var leadbay_create_custom_field = `Create an org-level CRM custom field for imports, then use the returned \`mapping_value\` in leadbay_import_leads / leadbay_import_and_qualify mappings. Use when the user's file contains valuable columns that do not fit Leadbay's standard fields, such as source-system deep links, source record IDs, campaign provenance, or user-requested enrichment attributes.
|
|
599
|
+
|
|
600
|
+
For HubSpot record links, prefer \`type:'EXTERNAL_ID'\` with \`config.url_template\` and import only the stable HubSpot id as the CSV value. Example: create field name 'HubSpot Contact', type 'EXTERNAL_ID', config \`{url_template:'https://app.hubspot.com/contacts/<portal-id>/record/0-1/{value}'}\`; then map \`hubspot_id\` to the returned \`mapping_value\`. If only a full URL column exists and the id cannot be safely extracted, use a TEXT field instead.
|
|
601
|
+
|
|
602
|
+
WHEN TO USE: after leadbay_list_mappable_fields shows no suitable existing custom field and preserving the column matters to the user's goal.
|
|
603
|
+
|
|
604
|
+
WHEN NOT TO USE: for standard company/contact data that maps to LEAD_WEBSITE, LEAD_NAME, CONTACT_EMAIL, etc.; do not create custom fields for noisy scraper notes unless the user explicitly asks to preserve them.
|
|
605
|
+
`;
|
|
606
|
+
var leadbay_create_lens = `Create a new user-level lens by cloning an existing lens's filter/scoring as the starting point.
|
|
607
|
+
|
|
608
|
+
WHEN TO USE: when adjust_audience determined the current lens cannot be edited (e.g. it's the org default).
|
|
609
|
+
|
|
610
|
+
WHEN NOT TO USE: to update an existing lens \u2014 use leadbay_update_lens or leadbay_update_lens_filter.
|
|
611
|
+
|
|
612
|
+
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\`.
|
|
613
|
+
`;
|
|
614
|
+
var leadbay_create_lens_draft = `Create (or fetch existing) draft of an org-level lens. Idempotent \u2014 same user calling twice returns the same draft. The returned lens has \`draft_of\` set to the original lens id.
|
|
615
|
+
|
|
616
|
+
WHEN TO USE: when a non-admin needs to modify an org-level lens \u2014 make a draft, edit the draft.
|
|
617
|
+
|
|
618
|
+
WHEN NOT TO USE: from agent flow \u2014 leadbay_adjust_audience handles the draft-routing transparently.
|
|
619
|
+
|
|
620
|
+
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\`.
|
|
621
|
+
`;
|
|
622
|
+
var leadbay_deselect_leads = `Remove leads from the user's transient selection.
|
|
623
|
+
|
|
624
|
+
WHEN TO USE: when narrowing a previously-built selection without clearing it entirely.
|
|
625
|
+
|
|
626
|
+
WHEN NOT TO USE: in normal flow \u2014 leadbay_enrich_titles handles selection lifecycle.
|
|
627
|
+
|
|
628
|
+
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\`.
|
|
629
|
+
`;
|
|
630
|
+
var leadbay_discover_leads = `Get AI-recommended leads from Leadbay. Returns paginated lead summaries with scores, AI summaries, tags, and recommended contacts. Auto-resolves to the active lens if \`lensId\` is omitted; \`count\` is capped at 50 per page.
|
|
631
|
+
|
|
632
|
+
WHEN TO USE: low-level \u2014 when you need raw paginated wishlist access without the qualification_summary attached by leadbay_pull_leads.
|
|
633
|
+
|
|
634
|
+
WHEN NOT TO USE: as the agent's default lead-discovery entry point \u2014 use leadbay_pull_leads, which adds a one-line qualification summary per lead.
|
|
635
|
+
`;
|
|
636
|
+
var leadbay_dismiss_clarification = `Dismiss the pending clarification without answering. Leadbay proceeds with its best guess. Admin-only.
|
|
637
|
+
|
|
638
|
+
WHEN TO USE: when the user explicitly doesn't want to answer the disambiguation.
|
|
639
|
+
|
|
640
|
+
WHEN NOT TO USE: as a default \u2014 answering with even a free-text reason gives Leadbay better signal.
|
|
641
|
+
|
|
642
|
+
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\`.
|
|
643
|
+
`;
|
|
644
|
+
var leadbay_enrich_contacts = `Order email and/or phone enrichment for a specific contact. Performs an advisory credit check, then tries the paid-contact path and falls back to the org-contact path on NOT_FOUND. Consumes enrichment credits.
|
|
645
|
+
|
|
646
|
+
WHEN TO USE: when you have a specific \`contact_id\` (from leadbay_get_contacts) and want to enrich just that one.
|
|
647
|
+
|
|
648
|
+
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.
|
|
649
|
+
|
|
650
|
+
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\`.
|
|
651
|
+
`;
|
|
652
|
+
var leadbay_enrich_titles = `Order contact enrichments by job title across many leads. Contacts are NOT returned by default with a lead (Leadbay keeps enrichment out-of-band to control cost); the agent requests them on demand via this tool when it's ready to actually reach out. Two modes: (A) NO \`titles\` param \u2014 returns the available titles + Leadbay's \`title_suggestions\` + \`auto_included_titles\` + a count of enrichable contacts, so the agent can ask the user which titles to enrich. (B) \`titles\` given \u2014 calls preview, then launches if there's anything enrichable. On 429 returns \`{status:'quota_exceeded'}\` cleanly. Selection lifecycle is wrapped in a try/finally so the user's selection is left clean even on error.
|
|
653
|
+
|
|
654
|
+
WHEN TO USE: as the agent's go-to enrichment entry point, immediately before proposing outreach.
|
|
655
|
+
|
|
656
|
+
WHEN NOT TO USE: to enrich a single contact \u2014 that's leadbay_enrich_contacts (granular). Speculatively, before the user has committed to outreaching \u2014 enrichment spends credits.
|
|
657
|
+
|
|
658
|
+
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\`.
|
|
659
|
+
`;
|
|
660
|
+
var leadbay_get_clarification = `Check whether Leadbay has a pending clarification question \u2014 a question raised when refining the intelligence prompt produced contradictory or ambiguous criteria. Returns \`{pending: false, clarification: null}\` when nothing is pending (the backend returns 204).
|
|
661
|
+
|
|
662
|
+
WHEN TO USE: after leadbay_refine_prompt, to see if Leadbay needs the user to disambiguate.
|
|
663
|
+
|
|
664
|
+
WHEN NOT TO USE: to answer the question \u2014 use leadbay_answer_clarification.
|
|
665
|
+
`;
|
|
666
|
+
var leadbay_get_contacts = `Get contacts for a lead, including enriched email and phone data. Returns both organization contacts and enrichable contacts with IDs, tagged with \`source:'org'|'paid'\`.
|
|
667
|
+
|
|
668
|
+
WHEN TO USE: to check enrichment status (\`contact.enrichment.done\`) on individual leads after a bulk enrichment was launched, or to find the \`contact_id\` needed by leadbay_enrich_contacts.
|
|
669
|
+
|
|
670
|
+
WHEN NOT TO USE: as a substitute for leadbay_research_lead, which already includes enriched contacts in its return.
|
|
671
|
+
`;
|
|
672
|
+
var leadbay_get_enrichment_job_titles = `List the actual job titles present across the leads currently in the user's selection \u2014 the candidate set the user can ask to enrich.
|
|
673
|
+
|
|
674
|
+
WHEN TO USE: after leadbay_select_leads, to know which titles are even available before launching a bulk enrichment.
|
|
675
|
+
|
|
676
|
+
WHEN NOT TO USE: standalone \u2014 the selection must already be populated, otherwise the result is an empty array. leadbay_enrich_titles wraps this whole flow when you don't need to inspect the title list manually.
|
|
677
|
+
`;
|
|
678
|
+
var leadbay_get_epilogue_responses = `Read the lead's epilogue history \u2014 what status (still chasing, meeting booked, etc.) was set when, and by whom. Paginated (\`count\` 1-200 default 20, \`page\` 0-indexed).
|
|
679
|
+
|
|
680
|
+
WHEN TO USE: to see the lead's outreach progression before deciding the next step.
|
|
681
|
+
|
|
682
|
+
WHEN NOT TO USE: when the lead summary's \`epilogue_actions_count\` is 0.
|
|
683
|
+
`;
|
|
684
|
+
var leadbay_get_lead_activities = `Get prospecting activity history for a lead (emails sent, calls made, status changes, notes). Each entry is \`{type, date}\`; older activities are trimmed by \`count\` (max 100, default 50).
|
|
685
|
+
|
|
686
|
+
WHEN TO USE: to avoid redundant outreach and understand where this lead is in the sales process.
|
|
687
|
+
|
|
688
|
+
WHEN NOT TO USE: when leadbay_research_lead has already been called \u2014 it includes recent prospecting actions in its engagement block.
|
|
689
|
+
`;
|
|
690
|
+
var leadbay_get_lead_notes = `Read existing notes on a lead \u2014 context the human team or prior agent runs have already captured.
|
|
691
|
+
|
|
692
|
+
WHEN TO USE: before adding a note via leadbay_report_outreach, to avoid duplicating or overwriting context the SDR already wrote.
|
|
693
|
+
|
|
694
|
+
WHEN NOT TO USE: when the lead summary's \`notes_count\` is 0 \u2014 there's nothing to fetch.
|
|
695
|
+
`;
|
|
696
|
+
var leadbay_get_lead_profile = `Get a full lead profile including company details, AI qualification scores, web insights, and contacts. Also marks the lead as SEEN+CLICKED in the user's lens so it ages out of the "new" Discover view (fire-and-forget; profile fetch never blocks on it).
|
|
697
|
+
|
|
698
|
+
WHEN TO USE: low-level \u2014 for fine-grained access to the raw shape of the lead profile.
|
|
699
|
+
|
|
700
|
+
WHEN NOT TO USE: as the agent's default lead-detail tool \u2014 use leadbay_research_lead, which structures the data top-down (qualification first, then signals, then firmographics, then contacts, then engagement) and reshapes web_fetch.content into a stable array form.
|
|
701
|
+
`;
|
|
702
|
+
var leadbay_get_lens_filter = `Read the firmographic filter (sectors, sizes, locations) currently applied to a lens.
|
|
703
|
+
|
|
704
|
+
WHEN TO USE: before adjusting an audience \u2014 see what's already restricted so changes are diffs, not full replacements.
|
|
705
|
+
|
|
706
|
+
WHEN NOT TO USE: to actually apply changes \u2014 use the leadbay_adjust_audience composite, which handles permissions transparently.
|
|
707
|
+
`;
|
|
708
|
+
var leadbay_get_lens_scoring = `Read the AI-scoring criteria configured on a lens (what makes a lead score 100 vs 30).
|
|
709
|
+
|
|
710
|
+
WHEN TO USE: when explaining why a lead got the score it did.
|
|
711
|
+
|
|
712
|
+
WHEN NOT TO USE: to mutate scoring \u2014 that's an admin/setup operation, not part of the agent loop.
|
|
713
|
+
`;
|
|
714
|
+
var leadbay_get_prospecting_actions = `Read the CRM-style activity log for a lead (calls, emails, meetings \u2014 actions performed by humans or prior agent runs). Paginated (\`count\` 1-200 default 20, \`page\` 0-indexed).
|
|
715
|
+
|
|
716
|
+
WHEN TO USE: before contacting the lead, to avoid duplicating outreach the team already did.
|
|
717
|
+
|
|
718
|
+
WHEN NOT TO USE: when the lead summary's \`prospecting_actions_count\` is 0.
|
|
719
|
+
`;
|
|
720
|
+
var leadbay_get_quota = `Read remaining quota / spend across daily, weekly, and monthly windows for the org's resources (\`llm_completion\`, \`ai_rescore\`, \`web_fetch\`). Each entry shows \`current_units\` vs \`max_units\` and \`resets_at\`.
|
|
721
|
+
|
|
722
|
+
WHEN TO USE: after a 429 error, to explain to the user which window was hit and when it resets.
|
|
723
|
+
|
|
724
|
+
WHEN NOT TO USE: as a pre-flight gate before bulk operations \u2014 operations themselves return 429 with hints; this tool is for diagnostics, not gating.
|
|
725
|
+
`;
|
|
726
|
+
var leadbay_get_selection_ids = `List the lead ids currently in the user's selection (the transient set that bulk operations like enrichment act on).
|
|
727
|
+
|
|
728
|
+
WHEN TO USE: to verify the selection state before/after bulk ops if a composite call has misbehaved.
|
|
729
|
+
|
|
730
|
+
WHEN NOT TO USE: in the normal flow \u2014 leadbay_enrich_titles manages selection lifecycle automatically (select \u2192 action \u2192 clear).
|
|
731
|
+
`;
|
|
732
|
+
var leadbay_get_taste_profile = `Get the user's Ideal Buyer Profile, purchase-intent tags, and qualification questions. The result is cached on the client. Returns an operator \`hint\` when no profile is configured yet.
|
|
733
|
+
|
|
734
|
+
WHEN TO USE: at the very start of a session to understand what kind of leads the user is looking for.
|
|
735
|
+
|
|
736
|
+
WHEN NOT TO USE: per-lead \u2014 leadbay_research_lead already includes the per-lead qualification answers (which are scored against these org-level questions).
|
|
737
|
+
`;
|
|
738
|
+
var leadbay_get_user_prompt = `Read the org's intelligence-refinement prompt (free-text instruction that steers lead recommendations beyond firmographics). Returns \`{prompt: null, set: false}\` when none is configured (the backend returns 204 in that case).
|
|
739
|
+
|
|
740
|
+
WHEN TO USE: to know what's currently steering the agent's recommendations before suggesting a refine.
|
|
741
|
+
|
|
742
|
+
WHEN NOT TO USE: to set/change the prompt \u2014 use leadbay_refine_prompt.
|
|
743
|
+
`;
|
|
744
|
+
var leadbay_get_web_fetch = `Read the AI-generated web-research summary for a lead \u2014 company profile, business signals, prospecting clues, each with sources and "hot" flags marking high-signal recent items. The content is dictioned by emoji-prefixed section labels in the raw API.
|
|
745
|
+
|
|
746
|
+
WHEN TO USE: when the agent already qualified this lead and wants the underlying research to reason from.
|
|
747
|
+
|
|
748
|
+
WHEN NOT TO USE: as the first read on a lead \u2014 the leadbay_research_lead composite bundles this with qualification answers and reshapes the dict into a stable array form.
|
|
749
|
+
`;
|
|
750
|
+
var leadbay_import_and_qualify = `Import + qualify leads in one call. Pass either \`domains: [{domain, name?}]\` (Mode A) OR \`records[]\` with \`mappings\` (Mode B). At least one mapped field must be LEADBAY_ID, CRM_ID, SIREN, LEAD_NAME, or LEAD_WEBSITE. Discover the org's mappable surface via \`leadbay_list_mappable_fields\`. For messy files, prefer the \`leadbay_import_file\` prompt which walks an agent through scan \u2192 resolve \u2192 preserve \u2192 commit phases.
|
|
751
|
+
|
|
752
|
+
WHEN TO USE: agent has a list of companies (domains, or CSV-shaped rows from the user's CRM) and wants the full AI qualification \u2014 qualification answers, web-research signals \u2014 without orchestrating import + bulk_qualify_leads + lead_profile chains by hand.
|
|
753
|
+
|
|
754
|
+
WHEN NOT TO USE: discovery (use leadbay_pull_leads); single-lead deep dive (use leadbay_research_lead); high-cadence or untrusted automation \u2014 this mutates user state and consumes ai_rescore + web_fetch quota.
|
|
755
|
+
|
|
756
|
+
Budgets: \`total_budget_ms\` caps wall-clock; \`per_lead_budget_ms\` caps each lead's poll. For short transport timeouts, pass \`wait_for_completion:false\` and poll \`leadbay_import_status\`. Outputs \`qualified[]\`, \`still_running[]\`, \`not_imported[]\`, \`qualify_id\` (resumable handle). Idempotent within a 5-min window. \`dry_run:'preview'\` returns mapping hints + custom-field candidates without importing.
|
|
757
|
+
|
|
758
|
+
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\`.
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
Requires: LEADBAY_MCP_WRITE=1 (MCP) or exposeWrite=true (OpenClaw); admin role; active billing. Imported leads are NOT auto-promoted to the Monitor view; lens-scoring threshold decides.
|
|
762
|
+
`;
|
|
763
|
+
var leadbay_import_leads = `Import leads into Leadbay's CRM via the file-import wizard. Returns stable Leadbay leadIds for downstream chaining into leadbay_bulk_qualify_leads / leadbay_research_lead. For MCP clients with short transport timeouts, pass \`wait_for_completion:false\` to return quickly with \`{status:'running', handle_id}\`; poll leadbay_import_status with that handle. For end-to-end import+qualify in one call, prefer leadbay_import_and_qualify. For messy files, prefer the \`leadbay_import_file\` prompt which walks an agent through scan \u2192 resolve \u2192 preserve \u2192 commit phases.
|
|
764
|
+
|
|
765
|
+
TWO MODES: (A) Domain-list shortcut \u2014 pass \`domains: [{domain, name?}]\`. The tool builds a 2-column CSV (LEAD_NAME, LEAD_WEBSITE) and imports with the default mapping. (B) Custom records + mapping \u2014 pass \`records: [{Col1, Col2, ...}]\` plus \`mappings.fields: {Col1: 'LEAD_NAME', ...}\`. \`mappings.fields\` must include LEADBAY_ID, CRM_ID, SIREN, LEAD_NAME, or LEAD_WEBSITE (resolver needs at least one identity key). Pass exactly one of \`domains\` / \`records\`. Reserved column \`MCP_ROW_ID\` cannot appear in records/mappings \u2014 the tool injects it for stable reconciliation.
|
|
766
|
+
|
|
767
|
+
MUTATES USER STATE: each call creates a row in the user's CRM-imports list (visible in the web UI) and touches onboarding state. Suitable for occasional automation, NOT for high-cadence (>5 calls/day). Imported leads are NOT auto-promoted to the user's Monitor view; lens-scoring threshold decides. For messy files call leadbay_resolve_import_rows first, then pass \`records_for_import\`/\`mappings_for_import\` here. Agents should inspect every column, build a preservation plan, and pass an explicit final mapping. For each meaningful column decide standard field, CONTACT_* field, Leadbay note, custom field, derived helper, or skip with a reason. For contact-only exports, derive a company-domain column from CONTACT_EMAIL only when it's a real business domain. Multiple rows can share the same LEADBAY_ID and import as separate contacts on that lead. Custom fields use \`CUSTOM.<id>\` in \`mappings.fields\` or the \`mappings.custom_fields\` shorthand. For source-system deep links create a custom field via leadbay_create_custom_field first (prefer EXTERNAL_ID + url_template). Preserve meaningful per-lead notes by calling leadbay_add_note after import returns lead IDs.
|
|
768
|
+
|
|
769
|
+
WHEN TO USE: you have a list of company domains from another system (CRM, analytics, email correspondents) and need stable Leadbay leadIds; or CRM-shaped rows with custom columns and want to drive the wizard with explicit field mappings.
|
|
770
|
+
|
|
771
|
+
WHEN NOT TO USE: for prospect discovery (use leadbay_pull_leads); for one specific company's profile (use leadbay_research_company); when you can't tolerate the side effects above; when you also want qualification in the same call (use leadbay_import_and_qualify).
|
|
772
|
+
|
|
773
|
+
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\`.
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
Requires: LEADBAY_MCP_WRITE=1 (MCP) or exposeWrite=true (OpenClaw); admin role on the Leadbay account; active billing.
|
|
777
|
+
`;
|
|
778
|
+
var leadbay_import_status = `Retrieve the current state of an async lead import. Pass \`handle_id\` returned by \`leadbay_import_leads({wait_for_completion:false})\`, or pass legacy \`importIds[]\` to inspect backend wizard rows. This status call performs a single refresh pass and never polls in a loop.
|
|
779
|
+
|
|
780
|
+
WHEN TO USE: after leadbay_import_leads or leadbay_import_and_qualify returns \`{status:'running', handle_id}\` for the import phase, call this tool later to retrieve progress or the final import result without re-running the import.
|
|
781
|
+
|
|
782
|
+
WHEN NOT TO USE: for qualification handles returned as \`qualify_id\` \u2014 use leadbay_qualify_status for those; or when you still want the legacy blocking behavior from leadbay_import_leads with \`wait_for_completion=true\`.
|
|
783
|
+
`;
|
|
784
|
+
var leadbay_launch_bulk_enrichment = `Launch a bulk-enrichment job against the current selection. The backend requires \`email=true\` OR \`phone=true\` (both can be true). Returns 204 with no body \u2014 there is no bulk_id and no per-job status endpoint. Track results by polling individual leads via leadbay_get_contacts after ~60s; \`contact.enrichment.done\` flips to true. \`dry_run:true\` returns the call shape without contacting the backend.
|
|
785
|
+
|
|
786
|
+
WHEN TO USE: low-level.
|
|
787
|
+
|
|
788
|
+
WHEN NOT TO USE: from agent flow \u2014 leadbay_enrich_titles handles selection lifecycle, preview, launch, and cleanup.
|
|
789
|
+
|
|
790
|
+
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\`.
|
|
791
|
+
`;
|
|
792
|
+
var leadbay_list_lenses = `List all available Leadbay lenses (saved lead-search configurations). Each lens defines a different target market or buyer segment. The lens with \`is_last_active=true\` is used by default for lead discovery.
|
|
793
|
+
|
|
794
|
+
WHEN TO USE: when the user wants to switch lens or asks "what lenses do I have".
|
|
795
|
+
|
|
796
|
+
WHEN NOT TO USE: in normal flow \u2014 composites auto-resolve the active lens via \`/me.last_requested_lens\`.
|
|
797
|
+
`;
|
|
798
|
+
var leadbay_list_mappable_fields = `List every CRM field the agent can target when calling leadbay_import_leads or leadbay_import_and_qualify. Returns two arrays: \`standard_fields\` (Leadbay's built-in StandardCrmFieldType enum \u2014 LEAD_NAME, LEAD_WEBSITE, LEAD_STATUS, contact + location + sector fields) and \`custom_fields\` (this org's user-defined fields \u2014 id, name, type, and the literal \`mapping_value\` you pass in \`mappings.fields\`). For custom fields, \`mapping_value\` is the wire-format string \`CUSTOM.<id>\` \u2014 pass it verbatim.
|
|
799
|
+
|
|
800
|
+
For contact exports, map person data to CONTACT_* fields and still provide parent-company identity via LEADBAY_ID/LEAD_WEBSITE/LEAD_NAME/CRM_ID/SIREN. When contact emails contain business domains, agents may derive a clean company-domain column for LEAD_WEBSITE only when the domain agrees with the row's company/deal/brand context, while preserving the original email as CONTACT_EMAIL. For import files, audit every meaningful source column. If no standard/contact field fits, preserve the data by creating or reusing a custom field unless the column is blank, duplicate plumbing, raw unparsed noise after useful extraction, or harmful to data quality. For HubSpot or other source-system deep links, create or reuse an EXTERNAL_ID/TEXT custom field with leadbay_create_custom_field, then map the source id/link to the returned \`mapping_value\`. Backend mapping_hints are advisory only; for contact files, do not accept hints such as first_name -> LEAD_NAME when the column is clearly a person field.
|
|
801
|
+
|
|
802
|
+
Optional \`for_records\` param: pass a sample of CSV-shaped rows and the tool also runs the wizard's preprocess on them, attaching \`mapping_hints\` (per-column AI-confidence suggestions) and \`custom_field_candidates\` (custom fields that match unmapped columns by exact / case-insensitive / fuzzy name). Saves a separate preview round-trip.
|
|
803
|
+
|
|
804
|
+
WHEN TO USE: before authoring an import mapping, especially when the CSV has columns that aren't obvious matches for standard fields.
|
|
805
|
+
|
|
806
|
+
WHEN NOT TO USE: when you already know the mapping \u2014 this call is cheap (~50ms without for_records, ~5\u201310s with) but unnecessary if the agent has already cached the catalog within the same conversation.
|
|
807
|
+
`;
|
|
808
|
+
var leadbay_list_sectors = `List the sector taxonomy (id + display name in the requested language). Default: \`lang\` follows the caller's language; \`includeInvisible=false\` returns ~1,091 visible sectors.
|
|
809
|
+
|
|
810
|
+
WHEN TO USE: to resolve a free-text sector name (e.g. "Healthcare") into the sector ids that leadbay_adjust_audience needs.
|
|
811
|
+
|
|
812
|
+
WHEN NOT TO USE: when you already have sector ids \u2014 pass them directly.
|
|
813
|
+
`;
|
|
814
|
+
var leadbay_login = `Log in to Leadbay with email and password. Auto-detects region (us|fr) \u2014 the user does not need to know which backend their account lives on. On success, sets the client's bearer token and switches the active region if needed.
|
|
815
|
+
|
|
816
|
+
WHEN TO USE: at the start of a session if no token is preconfigured (cfg.token / LEADBAY_TOKEN).
|
|
817
|
+
|
|
818
|
+
WHEN NOT TO USE: if a token is already preconfigured \u2014 you'll just overwrite it. The user needs a Leadbay account first; they can register at https://wow.leadbay.ai/?register=true.
|
|
819
|
+
|
|
820
|
+
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\`.
|
|
821
|
+
`;
|
|
822
|
+
var leadbay_pick_clarification = `Answer the pending clarification question \u2014 either by picking one of the offered options (\`option_id\`) or by typing a free-text answer. The answer is stored as the new user_prompt and triggers regeneration. Admin-only.
|
|
823
|
+
|
|
824
|
+
WHEN TO USE: low-level.
|
|
825
|
+
|
|
826
|
+
WHEN NOT TO USE: from agent flow \u2014 use leadbay_answer_clarification.
|
|
827
|
+
|
|
828
|
+
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\`.
|
|
829
|
+
`;
|
|
830
|
+
var leadbay_prepare_outreach = `Prepare an outreach package for a single lead: recommended contact + enriched contact details + AI summary. Optionally trigger contact enrichment in-flight (\`enrich:true\`); enrichment is async, so poll leadbay_get_contacts after ~60s if you need the result inline.
|
|
831
|
+
|
|
832
|
+
WHEN TO USE: when the agent is about to draft outreach for ONE specific lead and needs the contact's email/phone.
|
|
833
|
+
|
|
834
|
+
WHEN NOT TO USE: across many leads \u2014 use leadbay_enrich_titles for bulk; for general lead detail use leadbay_research_lead (richer signals); to actually log the outreach action use leadbay_report_outreach (requires verification).
|
|
835
|
+
`;
|
|
836
|
+
var leadbay_preview_bulk_enrichment = `Preview a bulk-enrichment cost given a set of job titles applied to the current selection. Returns \`{selected_leads, enriched_contacts, enrichable_contacts, title_suggestions, auto_included_titles, previously_enriched_titles}\`. \`previously_enriched_titles\` is a newer field (in prod soon) \u2014 when present, the agent can recommend repeating those titles for new leads.
|
|
837
|
+
|
|
838
|
+
WHEN TO USE: between selecting leads and launching, to know what the enrichment will cost.
|
|
839
|
+
|
|
840
|
+
WHEN NOT TO USE: from agent flow \u2014 leadbay_enrich_titles wraps preview + launch with the right safety checks.
|
|
841
|
+
`;
|
|
842
|
+
var leadbay_promote_lens = `Promote a user-level lens (or draft) to org-level so all teammates see it. Admin-only.
|
|
843
|
+
|
|
844
|
+
WHEN TO USE: rare \u2014 when an admin user has built a lens (or refined a draft) and wants to share it org-wide.
|
|
845
|
+
|
|
846
|
+
WHEN NOT TO USE: as a non-admin (will fail with 403); for personal lens changes (those stay user-scoped).
|
|
847
|
+
|
|
848
|
+
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\`.
|
|
849
|
+
`;
|
|
850
|
+
var leadbay_pull_leads = `Pull up new leads from the user's last-active lens \u2014 the canonical "show me today's prospects" tool. Leadbay works like an inbox: each time the user logs back in, a fresh batch is delivered, paced by how many leads they've actually acted on recently. Pulling more won't produce more; user outreach/skips/saves does. Each returned lead carries a one-line \`qualification_summary\` built from leadbay_ai_agent_responses, plus the rich tags / scores / recommended_contact_title / engagement counters / in-flight flags from the lead summary.
|
|
851
|
+
|
|
852
|
+
Roughly the top 10 of the batch come pre-qualified (populated qualification_summary + ai_agent_lead_score); leads below the top ~10 carry only the basic firmographic \`score\` \u2014 not worse, just resource-saved by the system. Call leadbay_bulk_qualify_leads to deepen any of them on demand.
|
|
853
|
+
|
|
854
|
+
WHEN TO USE: as the agent's default opening move when the user wants to see leads, or as a daily check-in for what's new today.
|
|
855
|
+
|
|
856
|
+
WHEN NOT TO USE: when the user has named a specific lens \u2014 pass \`lensId\` to override the auto-resolution. Replaces the older leadbay_find_prospects (removed in v0.2.0).
|
|
857
|
+
`;
|
|
858
|
+
var leadbay_qualify_lead = `Trigger AI qualification for a single lead (web fetch + AI rescore). The operation is asynchronous \u2014 results take ~60s. \`forceFetch:true\` re-runs even if recent data exists.
|
|
859
|
+
|
|
860
|
+
WHEN TO USE: low-level \u2014 when you need to kick qualification on exactly one lead without composite orchestration.
|
|
861
|
+
|
|
862
|
+
WHEN NOT TO USE: as the agent's bulk-qualify path \u2014 use leadbay_bulk_qualify_leads, which paginates past already-qualified leads, fans out, polls, and bails out cleanly on 429.
|
|
863
|
+
|
|
864
|
+
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\`.
|
|
865
|
+
`;
|
|
866
|
+
var leadbay_qualify_status = `Retrieve the current state of an import_and_qualify (or bulk_qualify_leads) launch by \`qualify_id\`. Returns the same \`qualified[]\` / \`still_running[]\` shape as the original composite, refreshed against the backend at call time. The handle is persisted to \`~/.leadbay/bulks.json\` with a 30-day TTL and survives MCP restart.
|
|
867
|
+
|
|
868
|
+
WHEN TO USE: after leadbay_import_and_qualify or leadbay_bulk_qualify_leads returned a \`qualify_id\` with non-empty \`still_running[]\`, call this tool a few minutes later (or hours) to retrieve the now-completed qualifications without re-running the import or re-spending qualify quota.
|
|
869
|
+
|
|
870
|
+
WHEN NOT TO USE: as a substitute for leadbay_research_lead \u2014 that's a deeper per-lead profile and includes contacts. This tool is purely the qualification answers + signals_count.
|
|
871
|
+
`;
|
|
872
|
+
var leadbay_recall_ordered_titles = `Show job titles the org has previously enriched, so the agent can repeat the same titles for new leads (or skip already-saturated ones). Two implementation paths: (1) PREFERRED \u2014 a selection-scoped preview call that reads \`previously_enriched_titles\` from the backend (newer prod field). (2) FALLBACK \u2014 live aggregation across each lead's enriched contacts. The composite picks transparently.
|
|
873
|
+
|
|
874
|
+
WHEN TO USE: before leadbay_enrich_titles, to plan which titles to order.
|
|
875
|
+
|
|
876
|
+
WHEN NOT TO USE: when you already know the exact titles you want to enrich.
|
|
877
|
+
`;
|
|
878
|
+
var leadbay_refine_prompt = `Refine the kind of leads Leadbay surfaces, beyond firmographics. Free-text instruction (e.g. "focus on hospitals running their own IT"). Sets the org's \`user_prompt\`; if the new prompt produces ambiguous criteria, Leadbay raises a clarification question, which this composite polls for and surfaces. Admin-only on the backend (will return 403 for non-admins).
|
|
879
|
+
|
|
880
|
+
WHEN TO USE: when audience filters (leadbay_adjust_audience) aren't enough.
|
|
881
|
+
|
|
882
|
+
WHEN NOT TO USE: to answer a pending clarification \u2014 that's leadbay_answer_clarification.
|
|
883
|
+
|
|
884
|
+
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\`.
|
|
885
|
+
`;
|
|
886
|
+
var leadbay_remove_epilogue = `Bulk-clear the epilogue status from a set of leads.
|
|
887
|
+
|
|
888
|
+
WHEN TO USE: when an outreach action was logged in error and needs to be undone.
|
|
889
|
+
|
|
890
|
+
WHEN NOT TO USE: to change status \u2014 call leadbay_set_epilogue_status with the new status (it overwrites).
|
|
891
|
+
|
|
892
|
+
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\`.
|
|
893
|
+
`;
|
|
894
|
+
var leadbay_report_outreach = `Log an outreach action (email, call, message, meeting) on a lead so the human team using Leadbay sees the progress in their UI. Writes a NOTE on the lead and (optionally) sets an EPILOGUE status (still chasing, meeting booked, etc.). Bulk variant: pass \`lead_ids=[uuid,...]\` instead of \`lead_id\` (epilogue is bulk-native; notes fan out per-lead).
|
|
895
|
+
|
|
896
|
+
VERIFICATION REQUIRED: every call must include \`verification={source: 'gmail_message_id'|'calendar_event_id'|'user_confirmed', ref: '<id-or-confirmation>'}\` to prevent hallucinated outreach poisoning the pipeline. The verification is appended to the note body. Skipping or fabricating verification poisons the human team's pipeline.
|
|
897
|
+
|
|
898
|
+
WHEN TO USE: AFTER actually emailing/calling/meeting/messaging a contact, OR after a substantive decision the user wants logged (skip, save, hand off).
|
|
899
|
+
|
|
900
|
+
WHEN NOT TO USE: BEFORE doing the outreach (use \`dry_run:true\` to validate args first); without verification (call will be rejected); from a flow where the user did not consent to having actions logged automatically.
|
|
901
|
+
|
|
902
|
+
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\`.
|
|
903
|
+
`;
|
|
904
|
+
var leadbay_research_company = `Deep-dive research on a specific company by NAME (fuzzy match against the active lens's wishlist). Pass \`companyName\` (matches the top-scoring lead with that name) or \`leadId\` (takes precedence when both supplied).
|
|
905
|
+
|
|
906
|
+
WHEN TO USE: when the user references a company by name and you don't yet have its \`lead_id\`.
|
|
907
|
+
|
|
908
|
+
WHEN NOT TO USE: when you already have the lead_id \u2014 use leadbay_research_lead directly (it bundles richer signals + better top-down ordering for the agent).
|
|
909
|
+
`;
|
|
910
|
+
var leadbay_research_lead = `Tell me everything decision-relevant about a single lead. Bundles the lens-scoped lead profile, the AI qualification answers (the agent's knowledge-base food), the structured web-research signals (with hot flags + sources), the enriched contacts, and the recent notes/epilogue/prospecting activity in one call. Order is deliberate: qualification first, then signals, then firmographics, then contacts, then engagement.
|
|
911
|
+
|
|
912
|
+
Scoring has two layers: the basic \`score\` (firmographic, always present, already decent) and the AI qualification layer (\`ai_agent_lead_score\` + per-question answers + web_fetch signals). The AI layer is pre-populated for roughly the top 10 of each daily batch, and on-demand (via leadbay_bulk_qualify_leads) for anything below that. Combine both layers when judging a lead.
|
|
913
|
+
|
|
914
|
+
WHEN TO USE: when picking up a single lead from leadbay_pull_leads to decide whether to act on it.
|
|
915
|
+
|
|
916
|
+
WHEN NOT TO USE: across many leads at once \u2014 that's leadbay_pull_leads' job. (This composite supersedes the lower-level leadbay_get_lead_profile in agent flow; the granular tool stays available for fine-grained access.)
|
|
917
|
+
`;
|
|
918
|
+
var leadbay_resolve_import_rows = `Resolve messy CSV-shaped lead rows against Leadbay before file import. The tool sends each row's available identity signals to \`POST /leads/resolve\`, returns matched lead IDs or ambiguous candidate IDs, and produces \`records_for_import\` plus a SAFE identity-only \`mappings_for_import\` starting point for leadbay_import_leads / leadbay_import_and_qualify. This tool deliberately does not try to understand every CSV dialect; the agent should inspect the file, derive clean helper columns when useful, pass explicit \`identity_mappings\`, and build the final CRM mapping from \`mapping_guidance\`.
|
|
919
|
+
|
|
920
|
+
WHEN TO USE: before importing user-supplied files when domains, names, CRM IDs, registry numbers, or Leadbay IDs may be inconsistently formatted; when the agent needs to pre-resolve messy rows, inspect ambiguous candidates, or prepare LEADBAY_ID values for the import composites. For contact-only files, first derive company website/domain from business contact emails where possible, while ignoring consumer mailbox domains. Deterministic matches get a LEADBAY_ID column inserted so the standard import commits immediately. Ambiguous rows are deliberately left without LEADBAY_ID; inspect candidates and choose one only when the evidence is good. Rows with websites but no match can still be imported; Leadbay may crawl and match them later, and leadbay_import_status can surface late matches.
|
|
921
|
+
|
|
922
|
+
WHEN NOT TO USE: for prospect discovery from scratch (use leadbay_pull_leads); for one known company profile (use leadbay_research_company / leadbay_research_lead); or when the file already has clean, final LEADBAY_ID/CRM_ID/SIREN mappings and no row-level identity disambiguation is needed.
|
|
923
|
+
`;
|
|
924
|
+
var leadbay_select_leads = `Add leads to the user's transient selection (used by selection-scoped bulk operations). Accepts 1-1000 \`leadIds\` per call.
|
|
925
|
+
|
|
926
|
+
WHEN TO USE: low-level. The user's selection is a per-token global state \u2014 be careful when invoking directly.
|
|
927
|
+
|
|
928
|
+
WHEN NOT TO USE: in normal flow \u2014 leadbay_enrich_titles wraps select \u2192 action \u2192 clear in one call with proper Mutex protection. Calling this directly without acquiring the selection lock can clobber concurrent composite calls.
|
|
929
|
+
|
|
930
|
+
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\`.
|
|
931
|
+
`;
|
|
932
|
+
var leadbay_set_active_lens = `Mark a lens as last-used. Subsequent \`/me\` reads return it as \`last_requested_lens\`, so all composite tools default to it.
|
|
933
|
+
|
|
934
|
+
WHEN TO USE: after the user explicitly switched contexts (e.g. created a new lens via leadbay_create_lens).
|
|
935
|
+
|
|
936
|
+
WHEN NOT TO USE: in normal flow \u2014 leadbay_pull_leads and leadbay_adjust_audience auto-set the right lens.
|
|
937
|
+
|
|
938
|
+
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\`.
|
|
939
|
+
`;
|
|
940
|
+
var leadbay_set_epilogue_status = `Bulk-set the outreach progress (epilogue) status across a set of leads. Status values: \`STILL_CHASING\`, \`COULD_NOT_REACH_STILL_TRYING\`, \`INTEREST_VALIDATED_OR_MEETING_PLANED\` ("meeting booked"), \`NOT_INTERESTED_LOST\` (short labels accepted; mapped to the \`EPILOGUE_*\` enum). Up to 1000 leads per call.
|
|
941
|
+
|
|
942
|
+
WHEN TO USE: low-level.
|
|
943
|
+
|
|
944
|
+
WHEN NOT TO USE: from agent flow \u2014 leadbay_report_outreach pairs this with a note + verification, which is what humans actually need to see in Leadbay.
|
|
945
|
+
|
|
946
|
+
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\`.
|
|
947
|
+
`;
|
|
948
|
+
var leadbay_set_user_prompt = `Set the org's intelligence-refinement prompt \u2014 free-text instruction that steers Leadbay's lead recommendations beyond firmographics. Admin-only. Setting this clears any pending clarification and triggers a full intelligence regeneration (web search + high-reasoning). \`dry_run:true\` returns the call shape without contacting the backend.
|
|
949
|
+
|
|
950
|
+
WHEN TO USE: low-level.
|
|
951
|
+
|
|
952
|
+
WHEN NOT TO USE: from agent flow \u2014 use leadbay_refine_prompt, which polls for follow-up clarifications.
|
|
953
|
+
|
|
954
|
+
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\`.
|
|
955
|
+
`;
|
|
956
|
+
var leadbay_update_lens = `Update lens metadata (name, description, mode flags). Does NOT change the audience filter \u2014 use leadbay_update_lens_filter for that.
|
|
957
|
+
|
|
958
|
+
WHEN TO USE: rename a lens or toggle \`multi_product_mode\` / \`use_hq_only\`.
|
|
959
|
+
|
|
960
|
+
WHEN NOT TO USE: to change which leads the lens shows \u2014 that's a filter operation.
|
|
961
|
+
|
|
962
|
+
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\`.
|
|
963
|
+
`;
|
|
964
|
+
var leadbay_update_lens_filter = `Replace the audience filter (sectors, sizes, locations) on a lens. Body is the full \`Filter\` object \u2014 this is a REPLACE, not a merge. Returns 400 \`default_lens\` if applied to the org default lens (clone it first). \`dry_run:true\` returns the call shape without contacting the backend.
|
|
965
|
+
|
|
966
|
+
WHEN TO USE: low-level mutation when you've already prepared the merged filter.
|
|
967
|
+
|
|
968
|
+
WHEN NOT TO USE: from agent flow \u2014 use leadbay_adjust_audience, which handles draft-vs-direct routing, permission fallback, and the merge logic so unrelated criteria aren't dropped.
|
|
969
|
+
|
|
970
|
+
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\`.
|
|
971
|
+
`;
|
|
972
|
+
|
|
535
973
|
// ../core/dist/tools/login.js
|
|
536
974
|
var login = {
|
|
537
975
|
name: "leadbay_login",
|
|
@@ -542,7 +980,7 @@ var login = {
|
|
|
542
980
|
idempotentHint: false,
|
|
543
981
|
openWorldHint: true
|
|
544
982
|
},
|
|
545
|
-
description:
|
|
983
|
+
description: leadbay_login,
|
|
546
984
|
inputSchema: {
|
|
547
985
|
type: "object",
|
|
548
986
|
properties: {
|
|
@@ -593,7 +1031,7 @@ var listLenses = {
|
|
|
593
1031
|
idempotentHint: true,
|
|
594
1032
|
openWorldHint: true
|
|
595
1033
|
},
|
|
596
|
-
description:
|
|
1034
|
+
description: leadbay_list_lenses,
|
|
597
1035
|
inputSchema: {
|
|
598
1036
|
type: "object",
|
|
599
1037
|
properties: {},
|
|
@@ -641,7 +1079,7 @@ var discoverLeads = {
|
|
|
641
1079
|
idempotentHint: true,
|
|
642
1080
|
openWorldHint: true
|
|
643
1081
|
},
|
|
644
|
-
description:
|
|
1082
|
+
description: leadbay_discover_leads,
|
|
645
1083
|
inputSchema: {
|
|
646
1084
|
type: "object",
|
|
647
1085
|
properties: {
|
|
@@ -705,7 +1143,7 @@ var getLeadProfile = {
|
|
|
705
1143
|
idempotentHint: true,
|
|
706
1144
|
openWorldHint: true
|
|
707
1145
|
},
|
|
708
|
-
description:
|
|
1146
|
+
description: leadbay_get_lead_profile,
|
|
709
1147
|
inputSchema: {
|
|
710
1148
|
type: "object",
|
|
711
1149
|
properties: {
|
|
@@ -837,7 +1275,7 @@ var getContacts = {
|
|
|
837
1275
|
idempotentHint: true,
|
|
838
1276
|
openWorldHint: true
|
|
839
1277
|
},
|
|
840
|
-
description:
|
|
1278
|
+
description: leadbay_get_contacts,
|
|
841
1279
|
inputSchema: {
|
|
842
1280
|
type: "object",
|
|
843
1281
|
properties: {
|
|
@@ -897,7 +1335,7 @@ var getQuota = {
|
|
|
897
1335
|
idempotentHint: true,
|
|
898
1336
|
openWorldHint: true
|
|
899
1337
|
},
|
|
900
|
-
description:
|
|
1338
|
+
description: leadbay_get_quota,
|
|
901
1339
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
902
1340
|
outputSchema: {
|
|
903
1341
|
type: "object",
|
|
@@ -926,7 +1364,7 @@ var getTasteProfile = {
|
|
|
926
1364
|
idempotentHint: true,
|
|
927
1365
|
openWorldHint: true
|
|
928
1366
|
},
|
|
929
|
-
description:
|
|
1367
|
+
description: leadbay_get_taste_profile,
|
|
930
1368
|
inputSchema: {
|
|
931
1369
|
type: "object",
|
|
932
1370
|
properties: {},
|
|
@@ -990,7 +1428,7 @@ var qualifyLead = {
|
|
|
990
1428
|
idempotentHint: true,
|
|
991
1429
|
openWorldHint: true
|
|
992
1430
|
},
|
|
993
|
-
description:
|
|
1431
|
+
description: leadbay_qualify_lead,
|
|
994
1432
|
optional: true,
|
|
995
1433
|
inputSchema: {
|
|
996
1434
|
type: "object",
|
|
@@ -1027,7 +1465,7 @@ var enrichContacts = {
|
|
|
1027
1465
|
idempotentHint: true,
|
|
1028
1466
|
openWorldHint: true
|
|
1029
1467
|
},
|
|
1030
|
-
description:
|
|
1468
|
+
description: leadbay_enrich_contacts,
|
|
1031
1469
|
optional: true,
|
|
1032
1470
|
inputSchema: {
|
|
1033
1471
|
type: "object",
|
|
@@ -1101,7 +1539,7 @@ var addNote = {
|
|
|
1101
1539
|
idempotentHint: false,
|
|
1102
1540
|
openWorldHint: true
|
|
1103
1541
|
},
|
|
1104
|
-
description:
|
|
1542
|
+
description: leadbay_add_note,
|
|
1105
1543
|
optional: true,
|
|
1106
1544
|
inputSchema: {
|
|
1107
1545
|
type: "object",
|
|
@@ -1151,7 +1589,7 @@ var getLeadActivities = {
|
|
|
1151
1589
|
idempotentHint: true,
|
|
1152
1590
|
openWorldHint: true
|
|
1153
1591
|
},
|
|
1154
|
-
description:
|
|
1592
|
+
description: leadbay_get_lead_activities,
|
|
1155
1593
|
inputSchema: {
|
|
1156
1594
|
type: "object",
|
|
1157
1595
|
properties: {
|
|
@@ -1211,7 +1649,7 @@ var getLensFilter = {
|
|
|
1211
1649
|
idempotentHint: true,
|
|
1212
1650
|
openWorldHint: true
|
|
1213
1651
|
},
|
|
1214
|
-
description:
|
|
1652
|
+
description: leadbay_get_lens_filter,
|
|
1215
1653
|
inputSchema: {
|
|
1216
1654
|
type: "object",
|
|
1217
1655
|
properties: {
|
|
@@ -1235,7 +1673,7 @@ var getLensScoring = {
|
|
|
1235
1673
|
idempotentHint: true,
|
|
1236
1674
|
openWorldHint: true
|
|
1237
1675
|
},
|
|
1238
|
-
description:
|
|
1676
|
+
description: leadbay_get_lens_scoring,
|
|
1239
1677
|
inputSchema: {
|
|
1240
1678
|
type: "object",
|
|
1241
1679
|
properties: { lensId: { type: "number", description: "Lens id (required)" } },
|
|
@@ -1257,7 +1695,7 @@ var listSectors = {
|
|
|
1257
1695
|
idempotentHint: true,
|
|
1258
1696
|
openWorldHint: true
|
|
1259
1697
|
},
|
|
1260
|
-
description:
|
|
1698
|
+
description: leadbay_list_sectors,
|
|
1261
1699
|
inputSchema: {
|
|
1262
1700
|
type: "object",
|
|
1263
1701
|
properties: {
|
|
@@ -1295,7 +1733,7 @@ var getUserPrompt = {
|
|
|
1295
1733
|
idempotentHint: true,
|
|
1296
1734
|
openWorldHint: true
|
|
1297
1735
|
},
|
|
1298
|
-
description:
|
|
1736
|
+
description: leadbay_get_user_prompt,
|
|
1299
1737
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
1300
1738
|
outputSchema: {
|
|
1301
1739
|
type: "object",
|
|
@@ -1335,7 +1773,7 @@ var getClarification = {
|
|
|
1335
1773
|
idempotentHint: true,
|
|
1336
1774
|
openWorldHint: true
|
|
1337
1775
|
},
|
|
1338
|
-
description:
|
|
1776
|
+
description: leadbay_get_clarification,
|
|
1339
1777
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
1340
1778
|
outputSchema: {
|
|
1341
1779
|
type: "object",
|
|
@@ -1377,7 +1815,7 @@ var getLeadNotes = {
|
|
|
1377
1815
|
idempotentHint: true,
|
|
1378
1816
|
openWorldHint: true
|
|
1379
1817
|
},
|
|
1380
|
-
description:
|
|
1818
|
+
description: leadbay_get_lead_notes,
|
|
1381
1819
|
inputSchema: {
|
|
1382
1820
|
type: "object",
|
|
1383
1821
|
properties: { leadId: { type: "string", description: "Lead UUID (required)" } },
|
|
@@ -1399,7 +1837,7 @@ var getEpilogueResponses = {
|
|
|
1399
1837
|
idempotentHint: true,
|
|
1400
1838
|
openWorldHint: true
|
|
1401
1839
|
},
|
|
1402
|
-
description:
|
|
1840
|
+
description: leadbay_get_epilogue_responses,
|
|
1403
1841
|
inputSchema: {
|
|
1404
1842
|
type: "object",
|
|
1405
1843
|
properties: {
|
|
@@ -1427,7 +1865,7 @@ var getProspectingActions = {
|
|
|
1427
1865
|
idempotentHint: true,
|
|
1428
1866
|
openWorldHint: true
|
|
1429
1867
|
},
|
|
1430
|
-
description:
|
|
1868
|
+
description: leadbay_get_prospecting_actions,
|
|
1431
1869
|
inputSchema: {
|
|
1432
1870
|
type: "object",
|
|
1433
1871
|
properties: {
|
|
@@ -1455,7 +1893,7 @@ var getWebFetch = {
|
|
|
1455
1893
|
idempotentHint: true,
|
|
1456
1894
|
openWorldHint: true
|
|
1457
1895
|
},
|
|
1458
|
-
description:
|
|
1896
|
+
description: leadbay_get_web_fetch,
|
|
1459
1897
|
inputSchema: {
|
|
1460
1898
|
type: "object",
|
|
1461
1899
|
properties: { leadId: { type: "string", description: "Lead UUID (required)" } },
|
|
@@ -1498,7 +1936,7 @@ var getSelectionIds = {
|
|
|
1498
1936
|
idempotentHint: true,
|
|
1499
1937
|
openWorldHint: true
|
|
1500
1938
|
},
|
|
1501
|
-
description:
|
|
1939
|
+
description: leadbay_get_selection_ids,
|
|
1502
1940
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
1503
1941
|
execute: async (client) => {
|
|
1504
1942
|
return await client.request("GET", "/leads/selection/ids");
|
|
@@ -1515,7 +1953,7 @@ var getEnrichmentJobTitles = {
|
|
|
1515
1953
|
idempotentHint: true,
|
|
1516
1954
|
openWorldHint: true
|
|
1517
1955
|
},
|
|
1518
|
-
description:
|
|
1956
|
+
description: leadbay_get_enrichment_job_titles,
|
|
1519
1957
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
1520
1958
|
execute: async (client) => {
|
|
1521
1959
|
return await client.request("GET", "/leads/selection/enrichment/job_titles");
|
|
@@ -1883,6 +2321,13 @@ var STABILIZATION_POLLS = 2;
|
|
|
1883
2321
|
var MAX_COLUMN_NAME_LEN = 128;
|
|
1884
2322
|
var RESERVED_COLUMN_RE = /^mcp_row_id$/i;
|
|
1885
2323
|
var CUSTOM_FIELD_RE = /^CUSTOM\.(\d+)$/;
|
|
2324
|
+
var IMPORT_RESOLVER_FIELDS = /* @__PURE__ */ new Set([
|
|
2325
|
+
"LEADBAY_ID",
|
|
2326
|
+
"CRM_ID",
|
|
2327
|
+
"LEAD_NAME",
|
|
2328
|
+
"LEAD_WEBSITE",
|
|
2329
|
+
"SIREN"
|
|
2330
|
+
]);
|
|
1886
2331
|
function isCustomFieldMappingValue(v) {
|
|
1887
2332
|
return CUSTOM_FIELD_RE.test(v);
|
|
1888
2333
|
}
|
|
@@ -2125,8 +2570,8 @@ function prepareRecordsMode(client, records, mappings, customFieldCatalog) {
|
|
|
2125
2570
|
throw client.makeError("IMPORT_MAPPING_REQUIRED", "mappings.fields must contain at least one column \u2192 CRM field entry", "Map at least one CSV column to LEAD_NAME or LEAD_WEBSITE.", "POST /imports");
|
|
2126
2571
|
}
|
|
2127
2572
|
const targets = new Set(fieldEntries.map(([, v]) => v));
|
|
2128
|
-
if (!targets.
|
|
2129
|
-
throw client.makeError("IMPORT_MAPPING_NO_RESOLVER", "mappings.fields must include LEAD_NAME or LEAD_WEBSITE", "The wizard needs at least one
|
|
2573
|
+
if (![...targets].some((t) => IMPORT_RESOLVER_FIELDS.has(t))) {
|
|
2574
|
+
throw client.makeError("IMPORT_MAPPING_NO_RESOLVER", "mappings.fields must include LEADBAY_ID, CRM_ID, SIREN, LEAD_NAME, or LEAD_WEBSITE", "The wizard needs at least one identity field to match a lead. Use leadbay_resolve_import_rows to prepare LEADBAY_ID values when the input file is messy.", "POST /imports");
|
|
2130
2575
|
}
|
|
2131
2576
|
const targetCounts = /* @__PURE__ */ new Map();
|
|
2132
2577
|
for (const [col, target] of fieldEntries) {
|
|
@@ -2538,7 +2983,7 @@ var importLeads = {
|
|
|
2538
2983
|
idempotentHint: true,
|
|
2539
2984
|
openWorldHint: true
|
|
2540
2985
|
},
|
|
2541
|
-
description:
|
|
2986
|
+
description: leadbay_import_leads,
|
|
2542
2987
|
write: true,
|
|
2543
2988
|
version: "0.3.0",
|
|
2544
2989
|
inputSchema: {
|
|
@@ -2576,7 +3021,7 @@ var importLeads = {
|
|
|
2576
3021
|
properties: {
|
|
2577
3022
|
fields: {
|
|
2578
3023
|
type: "object",
|
|
2579
|
-
description: "Object whose keys are CSV column names (matching keys in `records`) and whose values are either Leadbay's StandardCrmFieldType (LEAD_NAME, LEAD_WEBSITE, LEAD_STATUS, LEAD_LOCATION, LEAD_LOCATION_*, LEAD_SECTOR, LEAD_SIZE, CRM_ID, LEADBAY_ID, EMAIL, DEAL_CRM_ID, CONTACT_FIRST_NAME, CONTACT_LAST_NAME, CONTACT_EMAIL, CONTACT_PHONE_NUMBER, CONTACT_TITLE, CONTACT_LINKEDIN, LEAD_STATUS_DATE, OWNER, SCORE, SIREN) or the wire-format string 'CUSTOM.<id>' for org-defined custom fields. At least one entry must target LEAD_NAME or LEAD_WEBSITE \u2014 the wizard needs
|
|
3024
|
+
description: "Object whose keys are CSV column names (matching keys in `records`) and whose values are either Leadbay's StandardCrmFieldType (LEAD_NAME, LEAD_WEBSITE, LEAD_STATUS, LEAD_LOCATION, LEAD_LOCATION_*, LEAD_SECTOR, LEAD_SIZE, CRM_ID, LEADBAY_ID, EMAIL, DEAL_CRM_ID, CONTACT_FIRST_NAME, CONTACT_LAST_NAME, CONTACT_EMAIL, CONTACT_PHONE_NUMBER, CONTACT_TITLE, CONTACT_LINKEDIN, LEAD_STATUS_DATE, OWNER, SCORE, SIREN) or the wire-format string 'CUSTOM.<id>' for org-defined custom fields. At least one entry must target LEADBAY_ID, CRM_ID, SIREN, LEAD_NAME, or LEAD_WEBSITE \u2014 the wizard needs an identity field to find leads. Use leadbay_resolve_import_rows to prepare LEADBAY_ID values, and leadbay_list_mappable_fields to discover the org's custom fields. Contact rows should include both parent lead identity fields and CONTACT_* fields; repeated LEADBAY_ID/company values create multiple contacts on the same lead. Preserve HubSpot/source links by mapping them to a CUSTOM.<id> field returned by leadbay_create_custom_field when no suitable custom field already exists."
|
|
2580
3025
|
},
|
|
2581
3026
|
custom_fields: {
|
|
2582
3027
|
type: "object",
|
|
@@ -2851,7 +3296,7 @@ async function runImportInBackground(client, prep, uploadedChunks, opts, ctx, ha
|
|
|
2851
3296
|
var STANDARD_FIELDS = [
|
|
2852
3297
|
{ name: "LEAD_NAME", description: "Company name. Required for fuzzy match." },
|
|
2853
3298
|
{ name: "LEAD_WEBSITE", description: "Company domain (preferred matcher; protocol/path auto-stripped)." },
|
|
2854
|
-
{ name: "EMAIL", description: "
|
|
3299
|
+
{ name: "EMAIL", description: "Lead/company email \u2014 domain part may be used as a website-fallback matcher. For a person's email, use CONTACT_EMAIL and optionally derive a separate business-domain column for LEAD_WEBSITE." },
|
|
2855
3300
|
{ name: "CRM_ID", description: "Your CRM's stable lead identifier (round-trips back as crm_id on the lead)." },
|
|
2856
3301
|
{ name: "LEADBAY_ID", description: "Leadbay UUID, if you already have one (matches by id, no fuzzy needed)." },
|
|
2857
3302
|
{ name: "DEAL_CRM_ID", description: "Your CRM's deal id (one deal per row; combined with LEAD_STATUS forms a sales record)." },
|
|
@@ -2869,7 +3314,7 @@ var STANDARD_FIELDS = [
|
|
|
2869
3314
|
{ name: "SIREN", description: "French SIREN registry number (9 digits) \u2014 auto-matches against the FR registry." },
|
|
2870
3315
|
{ name: "CONTACT_FIRST_NAME", description: "Contact first name (creates an org contact)." },
|
|
2871
3316
|
{ name: "CONTACT_LAST_NAME", description: "Contact last name." },
|
|
2872
|
-
{ name: "CONTACT_EMAIL", description: "Contact email." },
|
|
3317
|
+
{ name: "CONTACT_EMAIL", description: "Contact email. Does not replace the parent company's LEAD_WEBSITE; derive a company domain from this only when it is a business domain, not a personal mailbox provider." },
|
|
2873
3318
|
{ name: "CONTACT_PHONE_NUMBER", description: "Contact phone (free-form)." },
|
|
2874
3319
|
{ name: "CONTACT_TITLE", description: "Contact job title." },
|
|
2875
3320
|
{ name: "CONTACT_LINKEDIN", description: "Contact LinkedIn URL." }
|
|
@@ -2887,7 +3332,7 @@ function describeCustomField(f) {
|
|
|
2887
3332
|
case "DATETIME":
|
|
2888
3333
|
return `Custom DATETIME field${f.config?.format ? ` (format: ${f.config.format})` : " (ISO datetime)"}.`;
|
|
2889
3334
|
case "EXTERNAL_ID":
|
|
2890
|
-
return `Custom EXTERNAL_ID field \u2014 opaque id${f.config?.urlTemplate ? ` (deep-link template configured)` : ""}.`;
|
|
3335
|
+
return `Custom EXTERNAL_ID field \u2014 opaque id${f.config?.url_template || f.config?.urlTemplate ? ` (deep-link template configured)` : ""}.`;
|
|
2891
3336
|
default:
|
|
2892
3337
|
return `Custom field of unrecognized type "${f.type}" \u2014 pass values as strings.`;
|
|
2893
3338
|
}
|
|
@@ -2903,7 +3348,7 @@ var listMappableFields = {
|
|
|
2903
3348
|
idempotentHint: true,
|
|
2904
3349
|
openWorldHint: true
|
|
2905
3350
|
},
|
|
2906
|
-
description:
|
|
3351
|
+
description: leadbay_list_mappable_fields,
|
|
2907
3352
|
inputSchema: {
|
|
2908
3353
|
type: "object",
|
|
2909
3354
|
properties: {
|
|
@@ -2981,7 +3426,10 @@ var listMappableFields = {
|
|
|
2981
3426
|
}
|
|
2982
3427
|
};
|
|
2983
3428
|
if (Array.isArray(params.for_records) && params.for_records.length > 0) {
|
|
2984
|
-
const notes = [
|
|
3429
|
+
const notes = [
|
|
3430
|
+
"mapping_hints are backend suggestions, not final truth. Inspect values semantically before importing; person columns like first_name/last_name should map to CONTACT_* fields, not LEAD_NAME.",
|
|
3431
|
+
"If mapping_hints disagree with the user's file semantics, ignore the hint. Use leadbay_resolve_import_rows with explicit identity_mappings for identity matching, then author final mappings yourself."
|
|
3432
|
+
];
|
|
2985
3433
|
try {
|
|
2986
3434
|
const sample = params.for_records.slice(0, PREVIEW_SAMPLE_CAP);
|
|
2987
3435
|
const headerSet = /* @__PURE__ */ new Set();
|
|
@@ -3065,7 +3513,7 @@ var selectLeads = {
|
|
|
3065
3513
|
idempotentHint: true,
|
|
3066
3514
|
openWorldHint: true
|
|
3067
3515
|
},
|
|
3068
|
-
description:
|
|
3516
|
+
description: leadbay_select_leads,
|
|
3069
3517
|
optional: true,
|
|
3070
3518
|
write: true,
|
|
3071
3519
|
inputSchema: {
|
|
@@ -3109,7 +3557,7 @@ var deselectLeads = {
|
|
|
3109
3557
|
idempotentHint: true,
|
|
3110
3558
|
openWorldHint: true
|
|
3111
3559
|
},
|
|
3112
|
-
description:
|
|
3560
|
+
description: leadbay_deselect_leads,
|
|
3113
3561
|
optional: true,
|
|
3114
3562
|
write: true,
|
|
3115
3563
|
inputSchema: {
|
|
@@ -3142,7 +3590,7 @@ var clearSelection = {
|
|
|
3142
3590
|
idempotentHint: true,
|
|
3143
3591
|
openWorldHint: true
|
|
3144
3592
|
},
|
|
3145
|
-
description:
|
|
3593
|
+
description: leadbay_clear_selection,
|
|
3146
3594
|
optional: true,
|
|
3147
3595
|
write: true,
|
|
3148
3596
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
@@ -3162,7 +3610,7 @@ var setActiveLens = {
|
|
|
3162
3610
|
idempotentHint: true,
|
|
3163
3611
|
openWorldHint: true
|
|
3164
3612
|
},
|
|
3165
|
-
description:
|
|
3613
|
+
description: leadbay_set_active_lens,
|
|
3166
3614
|
optional: true,
|
|
3167
3615
|
write: true,
|
|
3168
3616
|
inputSchema: {
|
|
@@ -3189,7 +3637,7 @@ var createLens = {
|
|
|
3189
3637
|
idempotentHint: false,
|
|
3190
3638
|
openWorldHint: true
|
|
3191
3639
|
},
|
|
3192
|
-
description:
|
|
3640
|
+
description: leadbay_create_lens,
|
|
3193
3641
|
optional: true,
|
|
3194
3642
|
write: true,
|
|
3195
3643
|
inputSchema: {
|
|
@@ -3236,7 +3684,7 @@ var updateLens = {
|
|
|
3236
3684
|
idempotentHint: true,
|
|
3237
3685
|
openWorldHint: true
|
|
3238
3686
|
},
|
|
3239
|
-
description:
|
|
3687
|
+
description: leadbay_update_lens,
|
|
3240
3688
|
optional: true,
|
|
3241
3689
|
write: true,
|
|
3242
3690
|
inputSchema: {
|
|
@@ -3269,7 +3717,7 @@ var updateLensFilter = {
|
|
|
3269
3717
|
idempotentHint: true,
|
|
3270
3718
|
openWorldHint: true
|
|
3271
3719
|
},
|
|
3272
|
-
description:
|
|
3720
|
+
description: leadbay_update_lens_filter,
|
|
3273
3721
|
optional: true,
|
|
3274
3722
|
write: true,
|
|
3275
3723
|
inputSchema: {
|
|
@@ -3315,7 +3763,7 @@ var createLensDraft = {
|
|
|
3315
3763
|
idempotentHint: false,
|
|
3316
3764
|
openWorldHint: true
|
|
3317
3765
|
},
|
|
3318
|
-
description:
|
|
3766
|
+
description: leadbay_create_lens_draft,
|
|
3319
3767
|
optional: true,
|
|
3320
3768
|
write: true,
|
|
3321
3769
|
inputSchema: {
|
|
@@ -3339,7 +3787,7 @@ var promoteLens = {
|
|
|
3339
3787
|
idempotentHint: false,
|
|
3340
3788
|
openWorldHint: true
|
|
3341
3789
|
},
|
|
3342
|
-
description:
|
|
3790
|
+
description: leadbay_promote_lens,
|
|
3343
3791
|
optional: true,
|
|
3344
3792
|
write: true,
|
|
3345
3793
|
inputSchema: {
|
|
@@ -3365,7 +3813,7 @@ var setUserPrompt = {
|
|
|
3365
3813
|
idempotentHint: true,
|
|
3366
3814
|
openWorldHint: true
|
|
3367
3815
|
},
|
|
3368
|
-
description:
|
|
3816
|
+
description: leadbay_set_user_prompt,
|
|
3369
3817
|
optional: true,
|
|
3370
3818
|
write: true,
|
|
3371
3819
|
inputSchema: {
|
|
@@ -3410,7 +3858,7 @@ var clearUserPrompt = {
|
|
|
3410
3858
|
idempotentHint: true,
|
|
3411
3859
|
openWorldHint: true
|
|
3412
3860
|
},
|
|
3413
|
-
description:
|
|
3861
|
+
description: leadbay_clear_user_prompt,
|
|
3414
3862
|
optional: true,
|
|
3415
3863
|
write: true,
|
|
3416
3864
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
@@ -3432,7 +3880,7 @@ var pickClarification = {
|
|
|
3432
3880
|
idempotentHint: false,
|
|
3433
3881
|
openWorldHint: true
|
|
3434
3882
|
},
|
|
3435
|
-
description:
|
|
3883
|
+
description: leadbay_pick_clarification,
|
|
3436
3884
|
optional: true,
|
|
3437
3885
|
write: true,
|
|
3438
3886
|
inputSchema: {
|
|
@@ -3479,7 +3927,7 @@ var dismissClarification = {
|
|
|
3479
3927
|
idempotentHint: false,
|
|
3480
3928
|
openWorldHint: true
|
|
3481
3929
|
},
|
|
3482
|
-
description:
|
|
3930
|
+
description: leadbay_dismiss_clarification,
|
|
3483
3931
|
optional: true,
|
|
3484
3932
|
write: true,
|
|
3485
3933
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
@@ -3512,7 +3960,7 @@ var setEpilogueStatus = {
|
|
|
3512
3960
|
idempotentHint: true,
|
|
3513
3961
|
openWorldHint: true
|
|
3514
3962
|
},
|
|
3515
|
-
description:
|
|
3963
|
+
description: leadbay_set_epilogue_status,
|
|
3516
3964
|
optional: true,
|
|
3517
3965
|
write: true,
|
|
3518
3966
|
inputSchema: {
|
|
@@ -3559,7 +4007,7 @@ var removeEpilogue = {
|
|
|
3559
4007
|
idempotentHint: true,
|
|
3560
4008
|
openWorldHint: true
|
|
3561
4009
|
},
|
|
3562
|
-
description:
|
|
4010
|
+
description: leadbay_remove_epilogue,
|
|
3563
4011
|
optional: true,
|
|
3564
4012
|
write: true,
|
|
3565
4013
|
inputSchema: {
|
|
@@ -3592,7 +4040,7 @@ var previewBulkEnrichment = {
|
|
|
3592
4040
|
idempotentHint: true,
|
|
3593
4041
|
openWorldHint: true
|
|
3594
4042
|
},
|
|
3595
|
-
description:
|
|
4043
|
+
description: leadbay_preview_bulk_enrichment,
|
|
3596
4044
|
optional: true,
|
|
3597
4045
|
write: true,
|
|
3598
4046
|
inputSchema: {
|
|
@@ -3622,7 +4070,7 @@ var launchBulkEnrichment = {
|
|
|
3622
4070
|
idempotentHint: true,
|
|
3623
4071
|
openWorldHint: true
|
|
3624
4072
|
},
|
|
3625
|
-
description:
|
|
4073
|
+
description: leadbay_launch_bulk_enrichment,
|
|
3626
4074
|
optional: true,
|
|
3627
4075
|
write: true,
|
|
3628
4076
|
inputSchema: {
|
|
@@ -3675,6 +4123,98 @@ var launchBulkEnrichment = {
|
|
|
3675
4123
|
}
|
|
3676
4124
|
};
|
|
3677
4125
|
|
|
4126
|
+
// ../core/dist/tools/create-custom-field.js
|
|
4127
|
+
var createCustomField = {
|
|
4128
|
+
name: "leadbay_create_custom_field",
|
|
4129
|
+
annotations: {
|
|
4130
|
+
title: "Create CRM custom field",
|
|
4131
|
+
readOnlyHint: false,
|
|
4132
|
+
destructiveHint: false,
|
|
4133
|
+
idempotentHint: true,
|
|
4134
|
+
openWorldHint: true
|
|
4135
|
+
},
|
|
4136
|
+
description: leadbay_create_custom_field,
|
|
4137
|
+
write: true,
|
|
4138
|
+
version: "0.6.4",
|
|
4139
|
+
inputSchema: {
|
|
4140
|
+
type: "object",
|
|
4141
|
+
properties: {
|
|
4142
|
+
name: {
|
|
4143
|
+
type: "string",
|
|
4144
|
+
description: "User-visible custom field name, e.g. 'HubSpot Contact'."
|
|
4145
|
+
},
|
|
4146
|
+
type: {
|
|
4147
|
+
type: "string",
|
|
4148
|
+
description: "Custom field type: TEXT, NUMBER, PRICE, DATE, DATETIME, or EXTERNAL_ID. Defaults to TEXT."
|
|
4149
|
+
},
|
|
4150
|
+
config: {
|
|
4151
|
+
type: ["object", "null"],
|
|
4152
|
+
description: "Type-specific config. EXTERNAL_ID requires {url_template:'https://.../{value}'}; PRICE requires {currency:'USD'}; DATE/DATETIME may set {format}."
|
|
4153
|
+
},
|
|
4154
|
+
if_not_exists: {
|
|
4155
|
+
type: "boolean",
|
|
4156
|
+
description: "Default true. If a custom field with the same name already exists, return it instead of creating a duplicate."
|
|
4157
|
+
}
|
|
4158
|
+
},
|
|
4159
|
+
required: ["name"],
|
|
4160
|
+
additionalProperties: false
|
|
4161
|
+
},
|
|
4162
|
+
outputSchema: {
|
|
4163
|
+
type: "object",
|
|
4164
|
+
properties: {
|
|
4165
|
+
id: { type: "string" },
|
|
4166
|
+
name: { type: "string" },
|
|
4167
|
+
type: { type: "string" },
|
|
4168
|
+
config: { type: ["object", "null"] },
|
|
4169
|
+
mapping_value: {
|
|
4170
|
+
type: "string",
|
|
4171
|
+
description: "Wire mapping value to use in import mappings, e.g. CUSTOM.123."
|
|
4172
|
+
},
|
|
4173
|
+
existed: {
|
|
4174
|
+
type: "boolean",
|
|
4175
|
+
description: "True when if_not_exists reused an existing custom field."
|
|
4176
|
+
}
|
|
4177
|
+
},
|
|
4178
|
+
required: ["id", "name", "type", "mapping_value", "existed"]
|
|
4179
|
+
},
|
|
4180
|
+
execute: async (client, params) => {
|
|
4181
|
+
const name = params.name?.trim();
|
|
4182
|
+
if (!name) {
|
|
4183
|
+
throw client.makeError("CUSTOM_FIELD_NAME_REQUIRED", "name must be a non-empty string", "Pass a user-visible custom field name, e.g. 'HubSpot Contact'.", "POST /crm/custom_fields");
|
|
4184
|
+
}
|
|
4185
|
+
const type = params.type ?? "TEXT";
|
|
4186
|
+
const config = params.config ?? null;
|
|
4187
|
+
if (type === "EXTERNAL_ID") {
|
|
4188
|
+
const urlTemplate = config?.url_template ?? config?.urlTemplate;
|
|
4189
|
+
if (!urlTemplate || !urlTemplate.includes("{value}")) {
|
|
4190
|
+
throw client.makeError("CUSTOM_FIELD_EXTERNAL_ID_TEMPLATE_REQUIRED", "EXTERNAL_ID custom fields require config.url_template containing {value}", "Use a URL template like https://app.hubspot.com/contacts/<portal-id>/record/0-1/{value}.", "POST /crm/custom_fields");
|
|
4191
|
+
}
|
|
4192
|
+
}
|
|
4193
|
+
if (params.if_not_exists ?? true) {
|
|
4194
|
+
const existing = await client.request("GET", "/crm/custom_fields");
|
|
4195
|
+
const found = (existing ?? []).find((f) => f.name.toLowerCase() === name.toLowerCase());
|
|
4196
|
+
if (found) {
|
|
4197
|
+
return {
|
|
4198
|
+
...found,
|
|
4199
|
+
mapping_value: `CUSTOM.${found.id}`,
|
|
4200
|
+
existed: true
|
|
4201
|
+
};
|
|
4202
|
+
}
|
|
4203
|
+
}
|
|
4204
|
+
const body = {
|
|
4205
|
+
name,
|
|
4206
|
+
type,
|
|
4207
|
+
...config ? { config } : {}
|
|
4208
|
+
};
|
|
4209
|
+
const created = await client.request("POST", "/crm/custom_fields", body);
|
|
4210
|
+
return {
|
|
4211
|
+
...created,
|
|
4212
|
+
mapping_value: `CUSTOM.${created.id}`,
|
|
4213
|
+
existed: false
|
|
4214
|
+
};
|
|
4215
|
+
}
|
|
4216
|
+
};
|
|
4217
|
+
|
|
3678
4218
|
// ../core/dist/composite/research-company.js
|
|
3679
4219
|
var researchCompany = {
|
|
3680
4220
|
name: "leadbay_research_company",
|
|
@@ -3685,7 +4225,7 @@ var researchCompany = {
|
|
|
3685
4225
|
idempotentHint: true,
|
|
3686
4226
|
openWorldHint: true
|
|
3687
4227
|
},
|
|
3688
|
-
description:
|
|
4228
|
+
description: leadbay_research_company,
|
|
3689
4229
|
inputSchema: {
|
|
3690
4230
|
type: "object",
|
|
3691
4231
|
properties: {
|
|
@@ -3769,7 +4309,7 @@ var prepareOutreach = {
|
|
|
3769
4309
|
idempotentHint: true,
|
|
3770
4310
|
openWorldHint: true
|
|
3771
4311
|
},
|
|
3772
|
-
description:
|
|
4312
|
+
description: leadbay_prepare_outreach,
|
|
3773
4313
|
optional: true,
|
|
3774
4314
|
inputSchema: {
|
|
3775
4315
|
type: "object",
|
|
@@ -3884,7 +4424,7 @@ var pullLeads = {
|
|
|
3884
4424
|
idempotentHint: true,
|
|
3885
4425
|
openWorldHint: true
|
|
3886
4426
|
},
|
|
3887
|
-
description:
|
|
4427
|
+
description: leadbay_pull_leads,
|
|
3888
4428
|
inputSchema: {
|
|
3889
4429
|
type: "object",
|
|
3890
4430
|
properties: {
|
|
@@ -4142,7 +4682,7 @@ var researchLead = {
|
|
|
4142
4682
|
idempotentHint: true,
|
|
4143
4683
|
openWorldHint: true
|
|
4144
4684
|
},
|
|
4145
|
-
description:
|
|
4685
|
+
description: leadbay_research_lead,
|
|
4146
4686
|
inputSchema: {
|
|
4147
4687
|
type: "object",
|
|
4148
4688
|
properties: {
|
|
@@ -4427,7 +4967,7 @@ var recallOrderedTitles = {
|
|
|
4427
4967
|
idempotentHint: true,
|
|
4428
4968
|
openWorldHint: true
|
|
4429
4969
|
},
|
|
4430
|
-
description:
|
|
4970
|
+
description: leadbay_recall_ordered_titles,
|
|
4431
4971
|
inputSchema: {
|
|
4432
4972
|
type: "object",
|
|
4433
4973
|
properties: {
|
|
@@ -4548,7 +5088,7 @@ var accountStatus = {
|
|
|
4548
5088
|
idempotentHint: true,
|
|
4549
5089
|
openWorldHint: true
|
|
4550
5090
|
},
|
|
4551
|
-
description:
|
|
5091
|
+
description: leadbay_account_status,
|
|
4552
5092
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
4553
5093
|
outputSchema: {
|
|
4554
5094
|
type: "object",
|
|
@@ -4645,7 +5185,7 @@ var bulkQualifyLeads = {
|
|
|
4645
5185
|
idempotentHint: true,
|
|
4646
5186
|
openWorldHint: true
|
|
4647
5187
|
},
|
|
4648
|
-
description:
|
|
5188
|
+
description: leadbay_bulk_qualify_leads,
|
|
4649
5189
|
inputSchema: {
|
|
4650
5190
|
type: "object",
|
|
4651
5191
|
properties: {
|
|
@@ -4946,6 +5486,324 @@ var bulkQualifyLeads = {
|
|
|
4946
5486
|
}
|
|
4947
5487
|
};
|
|
4948
5488
|
|
|
5489
|
+
// ../core/dist/composite/resolve-import-rows.js
|
|
5490
|
+
var SOCIAL_FIELDS = /* @__PURE__ */ new Set(["linkedin", "facebook", "instagram", "twitter", "tiktok"]);
|
|
5491
|
+
var RESOLVER_TARGETS = /* @__PURE__ */ new Set(["LEADBAY_ID", "CRM_ID", "LEAD_NAME", "LEAD_WEBSITE", "SIREN"]);
|
|
5492
|
+
var DEFAULT_CANDIDATE_PROFILE_LIMIT = 5;
|
|
5493
|
+
function coerceCell2(v) {
|
|
5494
|
+
if (v == null)
|
|
5495
|
+
return "";
|
|
5496
|
+
if (typeof v === "string")
|
|
5497
|
+
return v;
|
|
5498
|
+
if (typeof v === "number" || typeof v === "boolean")
|
|
5499
|
+
return String(v);
|
|
5500
|
+
return JSON.stringify(v) ?? "";
|
|
5501
|
+
}
|
|
5502
|
+
function compactMappings(mappings) {
|
|
5503
|
+
const out = {};
|
|
5504
|
+
for (const [k, v] of Object.entries(mappings)) {
|
|
5505
|
+
if (v)
|
|
5506
|
+
out[k] = v;
|
|
5507
|
+
}
|
|
5508
|
+
return out;
|
|
5509
|
+
}
|
|
5510
|
+
function payloadForRecord(row, mappings) {
|
|
5511
|
+
const payload = {};
|
|
5512
|
+
const socials = {};
|
|
5513
|
+
for (const [field, column] of Object.entries(mappings)) {
|
|
5514
|
+
const value = (row[column] ?? "").trim();
|
|
5515
|
+
if (!value)
|
|
5516
|
+
continue;
|
|
5517
|
+
if (SOCIAL_FIELDS.has(field)) {
|
|
5518
|
+
socials[field] = value;
|
|
5519
|
+
} else {
|
|
5520
|
+
payload[field] = value;
|
|
5521
|
+
}
|
|
5522
|
+
}
|
|
5523
|
+
if (Object.keys(socials).length > 0)
|
|
5524
|
+
payload.socials = socials;
|
|
5525
|
+
return payload;
|
|
5526
|
+
}
|
|
5527
|
+
function identityMappingsForImport(records, identityMappings) {
|
|
5528
|
+
const fields = {};
|
|
5529
|
+
if (records.some((r) => (r.LEADBAY_ID ?? "").trim() !== "")) {
|
|
5530
|
+
fields.LEADBAY_ID = "LEADBAY_ID";
|
|
5531
|
+
}
|
|
5532
|
+
const add = (field, target) => {
|
|
5533
|
+
const column = identityMappings[field];
|
|
5534
|
+
if (!column || fields[column])
|
|
5535
|
+
return;
|
|
5536
|
+
if (!records.some((r) => (r[column] ?? "").trim() !== ""))
|
|
5537
|
+
return;
|
|
5538
|
+
fields[column] = target;
|
|
5539
|
+
};
|
|
5540
|
+
add("leadbay_id", "LEADBAY_ID");
|
|
5541
|
+
add("crm_id", "CRM_ID");
|
|
5542
|
+
add("registry_number", "SIREN");
|
|
5543
|
+
add("name", "LEAD_NAME");
|
|
5544
|
+
add("website", "LEAD_WEBSITE");
|
|
5545
|
+
return { fields, statuses: {}, default_status: null };
|
|
5546
|
+
}
|
|
5547
|
+
function disambiguationPolicy() {
|
|
5548
|
+
return [
|
|
5549
|
+
"Use `matched` lead_id values directly; the tool already writes those into LEADBAY_ID.",
|
|
5550
|
+
"For `ambiguous` rows, do not choose a candidate from score alone. Score is a tied evidence-band, not a confidence percentage.",
|
|
5551
|
+
"For every ambiguous row you resolve, keep a short decision note: selected candidate id, evidence used, conflicting evidence checked, or why LEADBAY_ID stayed blank. Report counts and examples to the user.",
|
|
5552
|
+
"Try to disambiguate relentlessly before giving up: rerun the row with include_candidate_profiles=true and a larger candidate_profile_limit if candidate facts are truncated, and include every trustworthy source signal available (website, full address, postcode, city, phone, registry/CRM id, source URL path, neighborhood/location words).",
|
|
5553
|
+
"Compare addresses intelligently as a human would. Recognize ordinary formatting, abbreviation, spelling, punctuation, casing, accent, direction, ordinal, and suite/unit differences without treating address comparison as a rigid rule checklist. A clear same-place street address match is strong evidence.",
|
|
5554
|
+
"Auto-select an ambiguous candidate when hydrated candidate facts uniquely agree with the source row on strong evidence: exact registry number, exact CRM ID, exact canonical website/domain with only one candidate, exact phone, or name plus clear same-place address match with postcode/city and no conflicting evidence.",
|
|
5555
|
+
"If several candidates share the same website/domain, do not fail fast. Treat it as a chain/multi-location problem: use source street address, postcode, city/neighborhood, phone, source URL path/location slug, and location words in the source name to pick the specific location when exactly one candidate matches.",
|
|
5556
|
+
"Postcode/city alone is not enough, and brand/root-domain alone is not enough for multi-location sources. If several candidates remain plausible after checking location/phone/path evidence, leave LEADBAY_ID blank.",
|
|
5557
|
+
"A domain derived from a contact email is useful only when it is a business domain (not gmail/hotmail/outlook/yahoo/icloud/proton/aol/etc.) and the company/contact context agrees with the candidate. If the domain looks like a POS/vendor/agency/group domain or conflicts with row notes, do not use it for LEADBAY_ID selection.",
|
|
5558
|
+
"If evidence is name-only, fuzzy-name-only, generic directory website, or multiple candidates remain plausible after exhausting location/phone/path evidence, leave LEADBAY_ID blank and import with website/name so Leadbay can crawl or late-match later.",
|
|
5559
|
+
"When the user asked for qualification after import, qualify only the lead IDs that the import returns. Late website matches may appear later via leadbay_import_status."
|
|
5560
|
+
];
|
|
5561
|
+
}
|
|
5562
|
+
function mappingGuidance() {
|
|
5563
|
+
return [
|
|
5564
|
+
"Treat mappings_for_import as a safe identity starting point, not a complete CRM mapping.",
|
|
5565
|
+
"Before importing, inspect every user column and sample values, then make a preservation plan: standard field, CONTACT_* field, Leadbay note, custom field, derived helper, or skip with a reason. The model, not this helper, should decide the complete mapping.",
|
|
5566
|
+
"Default to preserving client-provided business data. For meaningful columns with no standard Leadbay field, call leadbay_list_mappable_fields and create/reuse custom fields instead of silently dropping them. Skip only blank placeholders, duplicate plumbing, raw unparsed blobs after useful values are extracted, or values that would actively harm data quality.",
|
|
5567
|
+
"Always include LEADBAY_ID when records_for_import contains it; it makes deterministic matches import immediately.",
|
|
5568
|
+
"Also map the best available source identity columns: website/domain/url -> LEAD_WEBSITE, company/account/restaurant name -> LEAD_NAME, CRM/system id -> CRM_ID, registry/SIREN/SIRET/company number -> SIREN.",
|
|
5569
|
+
"For contact-only or HubSpot contact exports, derive a separate company_domain/company_website column from CONTACT_EMAIL only when the email domain is a real business domain and agrees with the company/deal/brand context. Do not use POS/vendor/group domains that conflict with the row, and do not derive company identity from private mailbox domains such as gmail.com, hotmail.com, outlook.com, yahoo.com, icloud.com, proton.me/protonmail.com, aol.com, or similar consumer email providers.",
|
|
5570
|
+
"Map contact-person columns when the file contains people: first_name -> CONTACT_FIRST_NAME, last_name -> CONTACT_LAST_NAME, job_title/title -> CONTACT_TITLE, contact email -> CONTACT_EMAIL, contact phone -> CONTACT_PHONE_NUMBER, contact LinkedIn -> CONTACT_LINKEDIN. If a company/restaurant row contains structured owners, decision makers, or contact lists, expand those people into additional import rows that repeat the parent lead identity and contain one CONTACT_* person per row. Multiple rows may point to the same LEADBAY_ID/company; import them as separate contacts on that lead.",
|
|
5571
|
+
"Preserve valuable source-system links. For HubSpot URLs, prefer extracting the stable object id into a clean column and mapping it to an existing or newly created EXTERNAL_ID custom field. Reuse an existing HubSpot linked-id field when present. Preserve raw source identifiers such as hubspot_id and associated_deal in custom fields when they are not already represented by a better standard/custom field. Use TEXT only when no stable id/template can be recovered.",
|
|
5572
|
+
"Clean source-system deal names before using them as LEAD_NAME: strip import campaign suffixes such as BYOC, BYOC only, DD, Uber, trailing separators, and duplicate pipeline labels, while preserving the original associated deal/source value in a custom field when it is meaningful to the user's workflow.",
|
|
5573
|
+
"Drop blank-header columns and placeholder values like `couldn't find`, `yes`, empty arrays, and raw JSON blobs unless you first extract meaningful scalar fields.",
|
|
5574
|
+
"Leadbay has CONTACT_PHONE_NUMBER but no standard LEAD_PHONE field in this surface. Preserve establishment/company phone only via an intentional custom field, not by pretending it is a contact phone.",
|
|
5575
|
+
"Preserve meaningful client notes, data-quality warnings that affect outreach, source record links, and owner/evidence URLs when they help the user's workflow. Do not map noisy scraper plumbing, duplicate blank columns, placeholder values, or long reasoning text.",
|
|
5576
|
+
"If the file contains meaningful per-lead notes/context, keep that text aside during import and add it to the imported/resolved leads with leadbay_add_note after import when that tool is available. For dry runs, report which notes would be written. If notes cannot be written and the user asked to preserve the text, create/reuse an import-notes custom field.",
|
|
5577
|
+
"For scraped owner/email JSON columns, extract the best scalar values into new clean columns before import; do not pass raw JSON blobs as core CRM fields.",
|
|
5578
|
+
"If no confident standard/custom mapping exists for a meaningful user column, create or reuse a custom field unless the column is blank/noisy/duplicate and record why it was skipped."
|
|
5579
|
+
];
|
|
5580
|
+
}
|
|
5581
|
+
async function hydrateCandidateProfiles(client, candidates, lensId, limit) {
|
|
5582
|
+
const selected = candidates.slice(0, Math.max(0, limit));
|
|
5583
|
+
const settled = await Promise.allSettled(selected.map((c) => client.request("GET", `/lenses/${lensId}/leads/${c.lead_id}`)));
|
|
5584
|
+
const out = [];
|
|
5585
|
+
settled.forEach((r, i) => {
|
|
5586
|
+
if (r.status !== "fulfilled")
|
|
5587
|
+
return;
|
|
5588
|
+
const lead = r.value;
|
|
5589
|
+
out.push({
|
|
5590
|
+
lead_id: selected[i].lead_id,
|
|
5591
|
+
name: lead.name ?? null,
|
|
5592
|
+
website: lead.website ?? null,
|
|
5593
|
+
location: lead.location ? {
|
|
5594
|
+
full: lead.location.full ?? null,
|
|
5595
|
+
city: lead.location.city ?? null,
|
|
5596
|
+
state: lead.location.state ?? null,
|
|
5597
|
+
country: lead.location.country ?? null
|
|
5598
|
+
} : null,
|
|
5599
|
+
phone_numbers: Array.isArray(lead.phone_numbers) ? lead.phone_numbers : [],
|
|
5600
|
+
description: typeof lead.description === "string" ? lead.description.slice(0, 400) : null
|
|
5601
|
+
});
|
|
5602
|
+
});
|
|
5603
|
+
return out;
|
|
5604
|
+
}
|
|
5605
|
+
function hasResolverTarget(mappings) {
|
|
5606
|
+
return Object.values(mappings.fields).some((v) => RESOLVER_TARGETS.has(v));
|
|
5607
|
+
}
|
|
5608
|
+
var resolveImportRows = {
|
|
5609
|
+
name: "leadbay_resolve_import_rows",
|
|
5610
|
+
annotations: {
|
|
5611
|
+
title: "Resolve import row identities",
|
|
5612
|
+
readOnlyHint: true,
|
|
5613
|
+
destructiveHint: false,
|
|
5614
|
+
idempotentHint: true,
|
|
5615
|
+
openWorldHint: true
|
|
5616
|
+
},
|
|
5617
|
+
description: leadbay_resolve_import_rows,
|
|
5618
|
+
write: false,
|
|
5619
|
+
version: "0.6.4",
|
|
5620
|
+
inputSchema: {
|
|
5621
|
+
type: "object",
|
|
5622
|
+
properties: {
|
|
5623
|
+
records: {
|
|
5624
|
+
type: "array",
|
|
5625
|
+
description: "CSV-shaped rows from the user file. Values may be strings, numbers, booleans, null, arrays, or objects; non-scalars are JSON-stringified for resolver/import preparation.",
|
|
5626
|
+
items: { type: "object" }
|
|
5627
|
+
},
|
|
5628
|
+
identity_mappings: {
|
|
5629
|
+
type: "object",
|
|
5630
|
+
description: "Resolver field -> source column map chosen by the agent after inspecting the file, e.g. {website:'company_domain', name:'Company', crm_id:'Salesforce ID', registry_number:'SIREN'}. May point to clean columns the agent derived before calling this tool. This tool does not infer mappings from header names.",
|
|
5631
|
+
properties: {
|
|
5632
|
+
leadbay_id: { type: "string" },
|
|
5633
|
+
crm_id: { type: "string" },
|
|
5634
|
+
name: { type: "string" },
|
|
5635
|
+
website: { type: "string" },
|
|
5636
|
+
phone: { type: "string" },
|
|
5637
|
+
email: { type: "string" },
|
|
5638
|
+
registry_number: { type: "string" },
|
|
5639
|
+
registry_type: { type: "string" },
|
|
5640
|
+
address: { type: "string" },
|
|
5641
|
+
city: { type: "string" },
|
|
5642
|
+
postcode: { type: "string" },
|
|
5643
|
+
country: { type: "string" },
|
|
5644
|
+
linkedin: { type: "string" },
|
|
5645
|
+
facebook: { type: "string" },
|
|
5646
|
+
instagram: { type: "string" },
|
|
5647
|
+
twitter: { type: "string" },
|
|
5648
|
+
tiktok: { type: "string" }
|
|
5649
|
+
},
|
|
5650
|
+
additionalProperties: false
|
|
5651
|
+
},
|
|
5652
|
+
include_candidate_profiles: {
|
|
5653
|
+
type: "boolean",
|
|
5654
|
+
description: "When true, hydrate ambiguous candidate IDs with lightweight lead facts from the active lens. Use on small batches or rerun on only ambiguous rows; large ambiguous files can return many candidates."
|
|
5655
|
+
},
|
|
5656
|
+
candidate_profile_limit: {
|
|
5657
|
+
type: "number",
|
|
5658
|
+
description: `Maximum candidates to hydrate per ambiguous row when include_candidate_profiles=true (default ${DEFAULT_CANDIDATE_PROFILE_LIMIT}).`
|
|
5659
|
+
},
|
|
5660
|
+
lensId: {
|
|
5661
|
+
type: "number",
|
|
5662
|
+
description: "Lens ID used for candidate profile hydration. Defaults to the user's active lens."
|
|
5663
|
+
}
|
|
5664
|
+
},
|
|
5665
|
+
required: ["records"],
|
|
5666
|
+
additionalProperties: false
|
|
5667
|
+
},
|
|
5668
|
+
outputSchema: {
|
|
5669
|
+
type: "object",
|
|
5670
|
+
properties: {
|
|
5671
|
+
rows: {
|
|
5672
|
+
type: "array",
|
|
5673
|
+
description: "Per-input resolution result. Matched rows include lead_id; ambiguous rows include candidates; none/unidentifiable rows explain what extra signal would help.",
|
|
5674
|
+
items: { type: "object" }
|
|
5675
|
+
},
|
|
5676
|
+
records_for_import: {
|
|
5677
|
+
type: "array",
|
|
5678
|
+
description: "Import-ready records. Matched rows include LEADBAY_ID. Ambiguous/unresolved rows preserve user data and rely on website/name/CRM fields for normal or late matching.",
|
|
5679
|
+
items: { type: "object" }
|
|
5680
|
+
},
|
|
5681
|
+
mappings_for_import: {
|
|
5682
|
+
type: "object",
|
|
5683
|
+
description: "Safe identity-only mapping starter. The agent should review/extend this using mapping_guidance and leadbay_list_mappable_fields before importing."
|
|
5684
|
+
},
|
|
5685
|
+
identity_mappings_used: { type: "object" },
|
|
5686
|
+
mapping_guidance: {
|
|
5687
|
+
type: "array",
|
|
5688
|
+
description: "Instructions for building the final import mappings from the source columns.",
|
|
5689
|
+
items: { type: "string" }
|
|
5690
|
+
},
|
|
5691
|
+
disambiguation_policy: {
|
|
5692
|
+
type: "array",
|
|
5693
|
+
description: "Rules the agent should follow before writing LEADBAY_ID onto ambiguous rows.",
|
|
5694
|
+
items: { type: "string" }
|
|
5695
|
+
},
|
|
5696
|
+
summary: { type: "object" },
|
|
5697
|
+
next_action: { type: "string" },
|
|
5698
|
+
region: { type: "string" },
|
|
5699
|
+
_meta: { type: "object" }
|
|
5700
|
+
},
|
|
5701
|
+
required: [
|
|
5702
|
+
"rows",
|
|
5703
|
+
"records_for_import",
|
|
5704
|
+
"mappings_for_import",
|
|
5705
|
+
"identity_mappings_used",
|
|
5706
|
+
"mapping_guidance",
|
|
5707
|
+
"disambiguation_policy",
|
|
5708
|
+
"summary",
|
|
5709
|
+
"next_action",
|
|
5710
|
+
"region",
|
|
5711
|
+
"_meta"
|
|
5712
|
+
]
|
|
5713
|
+
},
|
|
5714
|
+
execute: async (client, params) => {
|
|
5715
|
+
if (!Array.isArray(params.records) || params.records.length === 0) {
|
|
5716
|
+
throw client.makeError("RESOLVE_IMPORT_EMPTY_INPUT", "records[] must contain at least one row", "Pass the rows from the user file, then import records_for_import.", "POST /leads/resolve");
|
|
5717
|
+
}
|
|
5718
|
+
const rows = params.records.map((rec, i) => {
|
|
5719
|
+
if (rec == null || typeof rec !== "object" || Array.isArray(rec)) {
|
|
5720
|
+
throw client.makeError("RESOLVE_IMPORT_INVALID_ROW", `records[${i}] must be a plain object`, "Pass each input row as { ColumnName: value, ... }.", "POST /leads/resolve");
|
|
5721
|
+
}
|
|
5722
|
+
const out = {};
|
|
5723
|
+
for (const [k, v] of Object.entries(rec))
|
|
5724
|
+
out[k] = coerceCell2(v);
|
|
5725
|
+
return out;
|
|
5726
|
+
});
|
|
5727
|
+
const identityMappings = compactMappings(params.identity_mappings ?? {});
|
|
5728
|
+
const allColumns = new Set(rows.flatMap((r) => Object.keys(r)));
|
|
5729
|
+
for (const [field, column] of Object.entries(identityMappings)) {
|
|
5730
|
+
if (!allColumns.has(column)) {
|
|
5731
|
+
throw client.makeError("RESOLVE_IMPORT_MAPPING_KEY_UNKNOWN", `identity_mappings.${field} points to missing column ${JSON.stringify(column)}`, "Use a source column that exists in at least one input record.", "POST /leads/resolve");
|
|
5732
|
+
}
|
|
5733
|
+
}
|
|
5734
|
+
const outputs = [];
|
|
5735
|
+
const recordsForImport = [];
|
|
5736
|
+
const results = await Promise.all(rows.map(async (row) => {
|
|
5737
|
+
const payload = payloadForRecord(row, identityMappings);
|
|
5738
|
+
const result = await client.request("POST", "/leads/resolve", payload);
|
|
5739
|
+
return { payload, result };
|
|
5740
|
+
}));
|
|
5741
|
+
let matched = 0;
|
|
5742
|
+
let ambiguous = 0;
|
|
5743
|
+
let none = 0;
|
|
5744
|
+
let unidentifiable = 0;
|
|
5745
|
+
const hydrateProfiles = params.include_candidate_profiles === true;
|
|
5746
|
+
const candidateProfileLimit = params.candidate_profile_limit ?? DEFAULT_CANDIDATE_PROFILE_LIMIT;
|
|
5747
|
+
const hydrationLensId = hydrateProfiles ? params.lensId ?? await client.resolveDefaultLens() : null;
|
|
5748
|
+
for (let index = 0; index < results.length; index++) {
|
|
5749
|
+
const { payload, result } = results[index];
|
|
5750
|
+
const importRecord = { ...rows[index] };
|
|
5751
|
+
const rowOut = {
|
|
5752
|
+
index,
|
|
5753
|
+
type: result.type,
|
|
5754
|
+
resolver_payload: payload,
|
|
5755
|
+
import_record: importRecord
|
|
5756
|
+
};
|
|
5757
|
+
if (result.type === "matched") {
|
|
5758
|
+
matched++;
|
|
5759
|
+
importRecord.LEADBAY_ID = result.lead_id;
|
|
5760
|
+
rowOut.lead_id = result.lead_id;
|
|
5761
|
+
rowOut.matched_on = result.matched_on;
|
|
5762
|
+
} else if (result.type === "ambiguous") {
|
|
5763
|
+
ambiguous++;
|
|
5764
|
+
rowOut.candidates = result.candidates;
|
|
5765
|
+
if (hydrateProfiles && hydrationLensId !== null) {
|
|
5766
|
+
rowOut.candidate_profiles = await hydrateCandidateProfiles(client, result.candidates, hydrationLensId, candidateProfileLimit);
|
|
5767
|
+
}
|
|
5768
|
+
} else if (result.type === "none") {
|
|
5769
|
+
none++;
|
|
5770
|
+
rowOut.would_help = result.would_help;
|
|
5771
|
+
} else {
|
|
5772
|
+
unidentifiable++;
|
|
5773
|
+
rowOut.reason = result.reason;
|
|
5774
|
+
}
|
|
5775
|
+
recordsForImport.push(importRecord);
|
|
5776
|
+
outputs.push(rowOut);
|
|
5777
|
+
}
|
|
5778
|
+
const mappingsForImport = identityMappingsForImport(recordsForImport, identityMappings);
|
|
5779
|
+
const readyForImport = hasResolverTarget(mappingsForImport);
|
|
5780
|
+
return {
|
|
5781
|
+
rows: outputs,
|
|
5782
|
+
records_for_import: recordsForImport,
|
|
5783
|
+
mappings_for_import: mappingsForImport,
|
|
5784
|
+
identity_mappings_used: identityMappings,
|
|
5785
|
+
mapping_guidance: mappingGuidance(),
|
|
5786
|
+
disambiguation_policy: disambiguationPolicy(),
|
|
5787
|
+
summary: {
|
|
5788
|
+
total: rows.length,
|
|
5789
|
+
matched,
|
|
5790
|
+
ambiguous,
|
|
5791
|
+
none,
|
|
5792
|
+
unidentifiable,
|
|
5793
|
+
ready_for_import: readyForImport
|
|
5794
|
+
},
|
|
5795
|
+
next_action: readyForImport ? "Review ambiguous candidates using disambiguation_policy. Build the final mapping from mappings_for_import plus mapping_guidance, then call leadbay_import_leads or leadbay_import_and_qualify with records_for_import and the reviewed mapping." : "Add or map at least one import resolver column (LEADBAY_ID, CRM_ID, LEAD_NAME, LEAD_WEBSITE, or SIREN), then call leadbay_import_leads.",
|
|
5796
|
+
region: client.region,
|
|
5797
|
+
_meta: client.lastMeta ?? {
|
|
5798
|
+
region: client.region,
|
|
5799
|
+
endpoint: "POST /leads/resolve",
|
|
5800
|
+
latency_ms: null,
|
|
5801
|
+
retry_after: null
|
|
5802
|
+
}
|
|
5803
|
+
};
|
|
5804
|
+
}
|
|
5805
|
+
};
|
|
5806
|
+
|
|
4949
5807
|
// ../core/dist/composite/import-and-qualify.js
|
|
4950
5808
|
function escapeCsv(v) {
|
|
4951
5809
|
return escapeCsvCell(v);
|
|
@@ -5035,28 +5893,7 @@ var importAndQualify = {
|
|
|
5035
5893
|
idempotentHint: true,
|
|
5036
5894
|
openWorldHint: true
|
|
5037
5895
|
},
|
|
5038
|
-
description:
|
|
5039
|
-
|
|
5040
|
-
Inputs:
|
|
5041
|
-
- \`domains\`: list of \`{domain, name?}\` (Mode A) \u2014 mutually exclusive with \`records\`.
|
|
5042
|
-
- \`records\`: list of CSV-shaped objects (Mode B), accompanied by \`mappings\`. Use \`mappings.fields\` with StandardCrmFieldType names or 'CUSTOM.<id>' wire values; or \`mappings.custom_fields\` with field id or name shorthand. Discover the org's mappable surface via leadbay_list_mappable_fields.
|
|
5043
|
-
- Budgets: \`total_budget_ms\` (default ${DEFAULT_TOTAL_BUDGET_MS3 / 6e4} min) caps the entire wall-clock; \`per_lead_budget_ms\` (default ${DEFAULT_PER_LEAD_BUDGET_MS2 / 1e3}s) caps each lead's individual qualification poll.
|
|
5044
|
-
|
|
5045
|
-
Outputs include \`qualified[]\` (per-lead question answers), \`still_running[]\` (lead ids whose qualification exceeded the budget), \`not_imported[]\` (rows the wizard couldn't match), and \`qualify_id\` (the resumable handle when at least one lead is still running). Idempotent within a 5-min window: re-calling with the same records+mapping returns the same qualify_id (\`reused: true\`). The result has a \`kind\` discriminator (\`'result' | 'preview'\`); preview-mode (\`dry_run: 'preview'\`) returns mapping hints + custom-field candidates instead of importing. Pass \`dry_run: true\` for input-validation only (top-level \`dry_run: true\` appears in the result so the agent can distinguish from all-malformed input).
|
|
5046
|
-
|
|
5047
|
-
When to use: the agent has a list of companies (domains, or CSV-shaped rows from the user's CRM) and wants Leadbay's full AI qualification \u2014 qualification answers, web-research signals \u2014 without orchestrating import + bulk_qualify_leads + lead_profile chains by hand.
|
|
5048
|
-
When NOT to use: discovery (use leadbay_pull_leads); single-lead deep dive (use leadbay_research_lead); high-cadence or untrusted automation \u2014 this mutates user state by creating CRM-import rows and consumes ai_rescore + web_fetch quota.
|
|
5049
|
-
|
|
5050
|
-
\u26A0\uFE0F MUTATES USER STATE. Each call:
|
|
5051
|
-
- creates a CRM-imports row (visible in the web UI)
|
|
5052
|
-
- touches onboarding state
|
|
5053
|
-
- launches up to N\xD7ai_rescore + N\xD7web_fetch quota where N = imported lead count (unless \`skip_already_qualified: true\` (default) excludes already-scored leads)
|
|
5054
|
-
|
|
5055
|
-
\u2139\uFE0F Monitor-tab membership: imported leads are NOT auto-promoted to the user's Monitor view. The lens-scoring rule decides \u2014 only leads that score above the lens threshold get \`in_monitor: true\` server-side. Lower-scoring imports stay invisible to the Monitor tab. Tell the user this if they ask 'where did my import go?' \u2014 answer is the CRM-imports list, not Monitor.
|
|
5056
|
-
|
|
5057
|
-
\u2139\uFE0F In-lens-but-unscored leads: a lead may be admitted to the active lens (lens GET returns 200) but the lens-scoring job may not materialize for it (qualification never starts). The composite today surfaces hard-rejected leads (404 from lens GET) in \`not_in_lens[]\`, but in-lens-unscored leads currently sit in \`still_running[]\` \u2014 there's no per-lead 'scoring queued vs won't_compute' signal from the backend yet (tracked: leadbay/product#3571). If still_running stays non-empty for a lead more than 5 minutes after a successful import, suggest the user verify the lens covers that lead's profile (e.g., sector match) \u2014 qualify_status's poll won't ever produce answers.
|
|
5058
|
-
|
|
5059
|
-
Requires: LEADBAY_MCP_WRITE=1 (MCP) or exposeWrite=true (OpenClaw); admin role; active billing.`,
|
|
5896
|
+
description: leadbay_import_and_qualify,
|
|
5060
5897
|
write: true,
|
|
5061
5898
|
version: "0.2.0",
|
|
5062
5899
|
inputSchema: {
|
|
@@ -5097,7 +5934,7 @@ Requires: LEADBAY_MCP_WRITE=1 (MCP) or exposeWrite=true (OpenClaw); admin role;
|
|
|
5097
5934
|
properties: {
|
|
5098
5935
|
fields: {
|
|
5099
5936
|
type: "object",
|
|
5100
|
-
description: "Object whose keys are CSV column names and whose values are either StandardCrmFieldType (LEAD_NAME, LEAD_WEBSITE, ..., CONTACT_TITLE) or 'CUSTOM.<id>'. Discover via leadbay_list_mappable_fields. At least one entry must target LEAD_NAME or LEAD_WEBSITE."
|
|
5937
|
+
description: "Object whose keys are CSV column names and whose values are either StandardCrmFieldType (LEAD_NAME, LEAD_WEBSITE, ..., CONTACT_TITLE) or 'CUSTOM.<id>'. Discover via leadbay_list_mappable_fields. At least one entry must target LEADBAY_ID, CRM_ID, SIREN, LEAD_NAME, or LEAD_WEBSITE. Use leadbay_resolve_import_rows to prepare LEADBAY_ID values from messy user files. Contact exports and embedded owner/contact lists should map CONTACT_EMAIL/PHONE/TITLE/name fields while preserving parent lead identity; expand structured people into repeated parent rows. HubSpot/source links should map to CUSTOM.<id> fields created or discovered before import."
|
|
5101
5938
|
},
|
|
5102
5939
|
custom_fields: {
|
|
5103
5940
|
type: "object",
|
|
@@ -6285,7 +7122,7 @@ var importStatus = {
|
|
|
6285
7122
|
idempotentHint: true,
|
|
6286
7123
|
openWorldHint: true
|
|
6287
7124
|
},
|
|
6288
|
-
description:
|
|
7125
|
+
description: leadbay_import_status,
|
|
6289
7126
|
inputSchema: {
|
|
6290
7127
|
type: "object",
|
|
6291
7128
|
properties: {
|
|
@@ -6443,7 +7280,7 @@ var qualifyStatus = {
|
|
|
6443
7280
|
idempotentHint: true,
|
|
6444
7281
|
openWorldHint: true
|
|
6445
7282
|
},
|
|
6446
|
-
description:
|
|
7283
|
+
description: leadbay_qualify_status,
|
|
6447
7284
|
inputSchema: {
|
|
6448
7285
|
type: "object",
|
|
6449
7286
|
properties: {
|
|
@@ -6631,7 +7468,7 @@ var enrichTitles = {
|
|
|
6631
7468
|
idempotentHint: true,
|
|
6632
7469
|
openWorldHint: true
|
|
6633
7470
|
},
|
|
6634
|
-
description:
|
|
7471
|
+
description: leadbay_enrich_titles,
|
|
6635
7472
|
inputSchema: {
|
|
6636
7473
|
type: "object",
|
|
6637
7474
|
properties: {
|
|
@@ -6992,7 +7829,7 @@ var bulkEnrichStatus = {
|
|
|
6992
7829
|
idempotentHint: true,
|
|
6993
7830
|
openWorldHint: true
|
|
6994
7831
|
},
|
|
6995
|
-
description:
|
|
7832
|
+
description: leadbay_bulk_enrich_status,
|
|
6996
7833
|
inputSchema: {
|
|
6997
7834
|
type: "object",
|
|
6998
7835
|
properties: {
|
|
@@ -7312,7 +8149,7 @@ var adjustAudience = {
|
|
|
7312
8149
|
idempotentHint: true,
|
|
7313
8150
|
openWorldHint: true
|
|
7314
8151
|
},
|
|
7315
|
-
description:
|
|
8152
|
+
description: leadbay_adjust_audience,
|
|
7316
8153
|
inputSchema: {
|
|
7317
8154
|
type: "object",
|
|
7318
8155
|
properties: {
|
|
@@ -7497,7 +8334,7 @@ var refinePrompt = {
|
|
|
7497
8334
|
idempotentHint: false,
|
|
7498
8335
|
openWorldHint: true
|
|
7499
8336
|
},
|
|
7500
|
-
description:
|
|
8337
|
+
description: leadbay_refine_prompt,
|
|
7501
8338
|
inputSchema: {
|
|
7502
8339
|
type: "object",
|
|
7503
8340
|
properties: {
|
|
@@ -7685,7 +8522,7 @@ var answerClarification = {
|
|
|
7685
8522
|
idempotentHint: false,
|
|
7686
8523
|
openWorldHint: true
|
|
7687
8524
|
},
|
|
7688
|
-
description:
|
|
8525
|
+
description: leadbay_answer_clarification,
|
|
7689
8526
|
inputSchema: {
|
|
7690
8527
|
type: "object",
|
|
7691
8528
|
properties: {
|
|
@@ -7776,7 +8613,7 @@ var reportOutreach = {
|
|
|
7776
8613
|
idempotentHint: false,
|
|
7777
8614
|
openWorldHint: true
|
|
7778
8615
|
},
|
|
7779
|
-
description:
|
|
8616
|
+
description: leadbay_report_outreach,
|
|
7780
8617
|
optional: true,
|
|
7781
8618
|
write: true,
|
|
7782
8619
|
inputSchema: {
|
|
@@ -8085,7 +8922,8 @@ var granularWriteTools = [
|
|
|
8085
8922
|
setEpilogueStatus,
|
|
8086
8923
|
removeEpilogue,
|
|
8087
8924
|
previewBulkEnrichment,
|
|
8088
|
-
launchBulkEnrichment
|
|
8925
|
+
launchBulkEnrichment,
|
|
8926
|
+
createCustomField
|
|
8089
8927
|
];
|
|
8090
8928
|
var granularTools = [
|
|
8091
8929
|
login,
|
|
@@ -8103,6 +8941,7 @@ var compositeReadTools = [
|
|
|
8103
8941
|
bulkEnrichStatus,
|
|
8104
8942
|
qualifyStatus,
|
|
8105
8943
|
importStatus,
|
|
8944
|
+
resolveImportRows,
|
|
8106
8945
|
// listMappableFields is granular-shaped but the import composites depend on
|
|
8107
8946
|
// it for discoverability; expose it always-on so agents can find custom fields
|
|
8108
8947
|
// without needing LEADBAY_MCP_ADVANCED=1.
|
|
@@ -8119,7 +8958,13 @@ var compositeWriteTools = [
|
|
|
8119
8958
|
answerClarification,
|
|
8120
8959
|
reportOutreach,
|
|
8121
8960
|
importLeads,
|
|
8122
|
-
importAndQualify
|
|
8961
|
+
importAndQualify,
|
|
8962
|
+
// createCustomField is granular-shaped but file-import prompts depend on it
|
|
8963
|
+
// to preserve source-system links without requiring advanced-tool exposure.
|
|
8964
|
+
createCustomField,
|
|
8965
|
+
// addNote is granular-shaped but file-import prompts depend on it to preserve
|
|
8966
|
+
// meaningful source-file notes after imports return lead ids.
|
|
8967
|
+
addNote
|
|
8123
8968
|
];
|
|
8124
8969
|
var compositeTools = [
|
|
8125
8970
|
...compositeReadTools,
|
|
@@ -8176,6 +9021,7 @@ export {
|
|
|
8176
9021
|
removeEpilogue,
|
|
8177
9022
|
previewBulkEnrichment,
|
|
8178
9023
|
launchBulkEnrichment,
|
|
9024
|
+
createCustomField,
|
|
8179
9025
|
researchCompany,
|
|
8180
9026
|
prepareOutreach,
|
|
8181
9027
|
pullLeads,
|
|
@@ -8183,6 +9029,7 @@ export {
|
|
|
8183
9029
|
recallOrderedTitles,
|
|
8184
9030
|
accountStatus,
|
|
8185
9031
|
bulkQualifyLeads,
|
|
9032
|
+
resolveImportRows,
|
|
8186
9033
|
importAndQualify,
|
|
8187
9034
|
isValidBulkId,
|
|
8188
9035
|
LocalBulkStore,
|