@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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog — @leadbay/mcp
|
|
2
2
|
|
|
3
|
+
## 0.7.0 — UNRELEASED
|
|
4
|
+
|
|
5
|
+
**Compile pipeline for prompts and tool descriptions**: every MCP prompt body and every Tool description is now authored as a `.md.tmpl` source file in the new `@leadbay/promptforge` workspace package and compiled into `prompts.generated.ts` / `tool-descriptions.generated.ts` at build time. Authors edit prose in one place; the generated TS modules are the bundle's source of inlined strings. No wire-format change: every consumer (Claude Desktop, Cursor, Claude Code, OpenClaw) sees the same MCP protocol shape — `tools/list`, `prompts/list`, `prompts/get` are unchanged. Backwards-compat: every tool name, every `inputSchema`, every annotation set, every `outputSchema` is preserved byte-for-byte. The descriptions themselves are rewritten (see below) — that is the visible change.
|
|
6
|
+
|
|
7
|
+
**PHASES + IRON LAWS + GATES in every prompt**: all 6 prompts (`leadbay_daily_check_in`, `leadbay_research_a_domain`, `leadbay_import_file`, `leadbay_refine_audience`, `leadbay_log_outreach`, `leadbay_qualify_top_n`) restructured into explicit phases, with byproduct gates the agent must emit before progressing (`COLUMN PRESERVATION PLAN`, `DECISION LOG`, `FINAL REPORT` for file import; `STOP — awaiting user decision` for daily check-in). IRON LAW lines codify non-negotiables (no fabrication of leadIds; verification source required before `report_outreach`; top-N triage with motivational framing for `daily_check_in`).
|
|
8
|
+
|
|
9
|
+
**All 60 tool descriptions rewritten through shared snippets**: 21 composite + 37 granular + 2 file-import companion tools migrated. Each description uses shared snippet partials (`{{include:headers/tool-when-to-use}}`, `{{include:headers/tool-when-not-to-use}}`, `{{include:headers/verification-required-if-write}}` for write tools). 58 of 60 fit under 1500 chars; the two longest (`leadbay_import_leads`, `leadbay_list_mappable_fields`) retain their load-bearing detail at ~3100 chars. Audit test enforces a 3500-char per-tool budget.
|
|
10
|
+
|
|
11
|
+
**Shared snippet library**: 11 partials live in `packages/promptforge/snippets/` (iron-laws, gates, heuristics, headers). Most reusable today: `heuristics/crm-record-link.md` (extends the previous HubSpot guidance to Salesforce, Pipedrive, Close, Attio with per-CRM `url_template` examples for EXTERNAL_ID custom fields) and `heuristics/consumer-email-domains.md` with the explicit "domain extraction is a key factor of match success" framing.
|
|
12
|
+
|
|
13
|
+
**Daily check-in prompt overhaul** (per user direction): triage scope widened from top-3 to **top-10**, preferring leads with a fresh `ai_agent_lead_score`; each summary now framed motivationally ("why prospecting this lead today might be a good idea"); auto-top-up via `leadbay_bulk_qualify_leads` when the batch is short; PHASE 4 now researches **every** promising lead and surfaces the recommended contacts; the agent ASKS the user before consuming contact-enrichment quota.
|
|
14
|
+
|
|
15
|
+
**Import-file prompt overhaul** (per user direction): explicit GOAL section at the top — the job is to maximize how many rows the Leadbay system can ingest and match. Two deliverables named: max-coverage column mapping + the user's original file augmented with a `LEADBAY_ID` column populated for confidently disambiguated rows. WHAT GOOD LOOKS LIKE section teaches the disambiguation bar positively; the "never pick from score alone..." guardrail remains as a hard rule.
|
|
16
|
+
|
|
17
|
+
**Test framework spine** (`packages/mcp/test/eval/`): full eval harness landed. Drives scripted Claude sessions against the in-process MCP Server, captures L1+L2+L3 Evidence (tool calls, transcript, invariants, judge scores), runs a mission-match judge that reads its rubric from the prompt's frontmatter, enforces an Evidence-pyramid completeness rule, supports atomic partial saves to `.context/evals/<run-id>.json`, computes budget-regression flags, ships a worktree-based drift detector for main-vs-branch comparison, and includes a tool-routing classifier eval with 70+ (intent → expected_tool) fixtures. Gated by `EVAL=1`; default `pnpm test` ignores it. Replay-by-default for backend HTTP (`EVAL_RECORD=1` opt-in) keeps the user's Leadbay quota safe by construction.
|
|
18
|
+
|
|
19
|
+
**Audit tests** (`packages/mcp/test/audit/`): five new T0 meta-properties enforced on every commit — every prompt has eval coverage, tool descriptions imported (never inline in `server.ts`), per-tool char budget, `leadbay_<verb>_<noun>` naming convention, snippet orphans + dead `{{include:...}}` refs. Snapshot regression test catches accidental prose loss in migrated prompts.
|
|
20
|
+
|
|
21
|
+
**Agentic file import prep** (carryover entry — work landed in the merged 0.6.4 PR but never got a published changelog entry; rolled forward here): `leadbay_resolve_import_rows` — a read-only resolver that calls the backend `/leads/resolve` endpoint for messy CSV-shaped rows, returns matched/ambiguous/unresolved candidates, can hydrate ambiguous candidates with active-lens profile facts, and emits `records_for_import` plus safe identity-only `mappings_for_import` for `leadbay_import_leads` / `leadbay_import_and_qualify`. Import mappings accept `LEADBAY_ID`, `CRM_ID`, and `SIREN` as resolver fields.
|
|
22
|
+
|
|
3
23
|
## 0.6.3 — 2026-05-12
|
|
4
24
|
|
|
5
25
|
**Async import schema fix**: `leadbay_import_leads` now declares both its legacy blocking result shape and its async kickoff shape (`{status: "running", handle_id, importIds, progress}`) in `outputSchema`, so Claude Desktop and other MCP SDK clients accept the fast handle response instead of rejecting `structuredContent`.
|
package/dist/bin.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
granularReadTools,
|
|
9
9
|
granularWriteTools,
|
|
10
10
|
resolveRegion
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-3WNCQ7MP.js";
|
|
12
12
|
|
|
13
13
|
// src/bin.ts
|
|
14
14
|
import { realpathSync } from "fs";
|
|
@@ -31,24 +31,306 @@ import {
|
|
|
31
31
|
CompleteRequestSchema
|
|
32
32
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
33
33
|
|
|
34
|
+
// src/prompts.generated.ts
|
|
35
|
+
var leadbay_daily_check_in = `
|
|
36
|
+
Run the Leadbay daily check-in for me. Treat this prompt the same way for any equivalent ask: "get me leadbay leads", "best leads to prospect today", "what should I work on", "show me my batch".
|
|
37
|
+
|
|
38
|
+
# PHASE 1 \u2014 STATE
|
|
39
|
+
Call \`leadbay_account_status\` to see what quota I have left and which lens is active. Note the remaining \`ai_rescore_remaining\` and \`web_fetch_remaining\` budgets \u2014 Phase 4 enrichment depends on them.
|
|
40
|
+
|
|
41
|
+
# PHASE 2 \u2014 FRESH BATCH
|
|
42
|
+
Call \`leadbay_pull_leads\` to get today's fresh batch.
|
|
43
|
+
|
|
44
|
+
# PHASE 3 \u2014 TRIAGE (top 10, motivational framing)
|
|
45
|
+
|
|
46
|
+
Pick the top **10** leads \u2014 prefer leads with a fresh \`ai_agent_lead_score\` (those have been newly AI-qualified); fall back to \`score\` only when \`ai_agent_lead_score\` is absent. For each, write ONE motivational sentence \u2014 framed as *why prospecting this lead today might be a good idea right now* (almost a coach's nudge, not a flat description). Lean on \`qualification_summary\` for the substance, but reframe \u2014 don't paste it verbatim.
|
|
47
|
+
|
|
48
|
+
If the batch returns fewer than 10 qualified leads, top it up by calling \`leadbay_bulk_qualify_leads\` with \`count\` set a bit above what you still need (some leads may get disqualified during qualification \u2014 request 1.5x the deficit, capped at 25). Then re-pull the batch and continue. (The \`leadbay_qualify_top_n\` slash-prompt wraps this same tool with a friendlier surface for users; agents should call the underlying tool directly here.)
|
|
49
|
+
|
|
50
|
+
# PHASE 4 \u2014 DEEP DIVE (every promising lead)
|
|
51
|
+
|
|
52
|
+
Call \`leadbay_research_lead\` on **every** lead from your top 10 that the user might realistically prospect today (filter out clearly weak fits if any). Don't pick just one. For each researched lead surface:
|
|
53
|
+
- what makes it promising (1\u20132 sentences citing signals from the research)
|
|
54
|
+
- the **recommended contacts** the research returns \u2014 name, title, why they're the right starting point
|
|
55
|
+
|
|
56
|
+
Then ASK the user (don't auto-run): "Want me to enrich the contacts on these leads to acquire their emails / phone numbers?" If the user says yes, call \`leadbay_enrich_contacts\` for the relevant lead IDs (this consumes enrichment quota \u2014 that's why we ask first).
|
|
57
|
+
|
|
58
|
+
# GATE \u2014 STOP
|
|
59
|
+
|
|
60
|
+
IRON LAW \u2014 DO NOT TAKE OUTBOUND ACTION. Do not call \`leadbay_report_outreach\`. Do not draft an outreach message into a tool argument. Outreach is the user's call after they've reviewed your research.
|
|
61
|
+
|
|
62
|
+
Render this acknowledgment VERBATIM as the last line of your message:
|
|
63
|
+
|
|
64
|
+
\`\`\`
|
|
65
|
+
STOP \u2014 awaiting user decision. I will not take any further action until you tell me what to do next.
|
|
66
|
+
\`\`\`
|
|
67
|
+
|
|
68
|
+
Do not propose a next action. Do not call any more tools. Hand control back to the user.
|
|
69
|
+
`;
|
|
70
|
+
var leadbay_import_file = `
|
|
71
|
+
Import the user's Leadbay file{{arg:file_paren}} and satisfy this instruction: {{arg:instruction_or_default}}.
|
|
72
|
+
|
|
73
|
+
# GOAL \u2014 what we're actually trying to do
|
|
74
|
+
|
|
75
|
+
The job is to **prepare the file so the Leadbay system ingests and matches as many rows as possible**. Every choice you make in the phases below \u2014 column mapping, domain derivation, identity resolution, custom-field creation \u2014 exists to maximize that match rate. The user shouldn't have to lose data because a header was unusual, a website was missing, or a CRM column had no obvious counterpart in Leadbay.
|
|
76
|
+
|
|
77
|
+
A job well done has TWO deliverables:
|
|
78
|
+
1. **Maximum-coverage column mapping**: every meaningful source column is mapped \u2014 to a standard Leadbay field where one exists, to a CONTACT_* field for person columns, to a custom field where no standard fits (including the CRM record link as a special EXTERNAL_ID custom field that's later clickable inside Leadbay), or to a Leadbay note for free-text context. Dropping data is the failure case; finding it a home is the success case.
|
|
79
|
+
2. **An augmented file**: the user's original file enriched with a new \`LEADBAY_ID\` column populated wherever you confidently disambiguated a match. The user keeps this as their audit trail of what got ingested and what didn't.
|
|
80
|
+
|
|
81
|
+
IRON LAW \u2014 NO FABRICATION. Every lead id, contact email, custom field id, mapping decision, and tool argument must trace to a value you read from the file the user attached or to an output from a leadbay_* tool call in this session. Do not invent values. Do not "fill in" a missing leadId with a name match. Do not synthesize a CRM id from a guess. If a value is missing, leave the field blank and say so.
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# PHASE 1 \u2014 SCAN
|
|
85
|
+
|
|
86
|
+
Read the file yourself. Inspect every header, sample values from multiple rows, row count, duplicate/blank columns, and obvious dirty data. Build a column preservation plan before importing: for each meaningful column decide standard field, CONTACT_* field, Leadbay note, custom field, derived helper, or skip with a reason. Default to preserving client-provided business data; skip only blank placeholders, duplicate plumbing, raw unparsed blobs after extracting their useful values, or values that would actively harm data quality.
|
|
87
|
+
|
|
88
|
+
## GATE 1 \u2014 COLUMN PRESERVATION PLAN
|
|
89
|
+
|
|
90
|
+
Before calling any leadbay_* tool, render the COLUMN PRESERVATION PLAN byproduct in your response. Do NOT proceed to PHASE 2 until this table is in your output.
|
|
91
|
+
|
|
92
|
+
Render this block VERBATIM as your byproduct:
|
|
93
|
+
|
|
94
|
+
\`\`\`
|
|
95
|
+
COLUMN PRESERVATION PLAN
|
|
96
|
+
========================
|
|
97
|
+
| Source column | Disposition | Reason |
|
|
98
|
+
|--------------------|-----------------------------------|-----------------------------------|
|
|
99
|
+
| <header from file> | standard:LEAD_NAME | cleaned company name |
|
|
100
|
+
| <header> | standard:LEAD_WEBSITE | domain agrees with brand |
|
|
101
|
+
| <header> | contact:CONTACT_EMAIL | per-person mailbox |
|
|
102
|
+
| <header> | custom:HubSpot record (EXTERNAL_ID)| preserve link via url_template |
|
|
103
|
+
| <header> | note | meaningful per-lead context |
|
|
104
|
+
| <header> | derived:company_domain | extracted from biz email |
|
|
105
|
+
| <header> | skip | blank placeholder / dup plumbing |
|
|
106
|
+
========================
|
|
107
|
+
\`\`\`
|
|
108
|
+
|
|
109
|
+
One row per meaningful source column. If you have 30+ columns, group blank/duplicate-plumbing columns under a single "skip" row with the count.
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
# PHASE 2 \u2014 DERIVE (especially: company domain)
|
|
113
|
+
|
|
114
|
+
**Domain extraction is the single biggest lever for match success.** A row with a company website resolves cleanly; a row without one drops to name-based fuzzy matching, which is unreliable. Whenever a row has no \`website\`/\`domain\` column but DOES have a contact email at a real business domain, derive a \`company_domain\` (and treat it as \`LEAD_WEBSITE\` for resolution) \u2014 that one move can turn a 60% match rate into 90%.
|
|
115
|
+
|
|
116
|
+
**Domain extraction is a key factor of match success.** Build semantic helper columns BEFORE resolving identities. The Leadbay matcher leans heavily on \`LEAD_WEBSITE\`/\`company_domain\`; rows that arrive without one drop to fuzzy name matching, which is unreliable. Whenever a row lacks a website/domain column but contains a contact email at a real business domain, derive a \`company_domain\` from the email and treat it as \`LEAD_WEBSITE\` for resolution \u2014 but ONLY when that domain agrees with the company/deal/brand context (do not blindly use whatever's after the @).
|
|
117
|
+
|
|
118
|
+
Ignore consumer mailbox domains such as gmail.com, hotmail.com, outlook.com, yahoo.com, icloud.com, proton.me/protonmail.com, aol.com, live.com, msn.com, me.com, gmx.*, and similar personal email providers \u2014 these are NOT company domains. Also ignore POS/vendor/group domains that conflict with the company (e.g. a \`square.com\` email on a coffee-shop row is the POS provider, not the shop). Keep the original email for CONTACT_EMAIL.
|
|
119
|
+
|
|
120
|
+
When in doubt, sample several rows that share a candidate derived domain and confirm the company names cluster sensibly under it before committing.
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# PHASE 3 \u2014 RESOLVE IDENTITIES
|
|
124
|
+
|
|
125
|
+
Decide resolver \`identity_mappings\` from the actual file semantics. Prefer: website/domain/url or **vetted derived business email domain** -> website; cleaned company/account/restaurant/establishment name -> name; CRM/system id -> crm_id; registry/SIREN/SIRET/company number -> registry_number; full address/city/postcode/country/phone/email/socials when present. For HubSpot/deal exports, clean campaign suffixes like BYOC, BYOC only, DD, Uber, trailing separators, and duplicate pipeline labels before using the value as LEAD_NAME. If a column is ambiguous, inspect row values before mapping it. Do not rely on fixed header names.
|
|
126
|
+
|
|
127
|
+
Call \`leadbay_resolve_import_rows\` with representative or all rows and your explicit \`identity_mappings\`. For large files, batch rows so responses stay readable. Use \`include_candidate_profiles=true\` for small batches or rerun it on ambiguous rows only. If a row is ambiguous and candidate profiles are missing or truncated, rerun just those rows with \`include_candidate_profiles=true\` and a larger \`candidate_profile_limit\` before deciding.
|
|
128
|
+
|
|
129
|
+
Disambiguate relentlessly. Use matched \`lead_id\` values directly. For ambiguous candidates, first make sure you have enough evidence: rerun the ambiguous rows with \`include_candidate_profiles=true\` and a larger \`candidate_profile_limit\` if profiles are truncated, and include every trustworthy source signal available (website, full address, postcode, city, phone, registry/CRM id, source URL path, neighborhood/location words).
|
|
130
|
+
|
|
131
|
+
Compare addresses intelligently as a human would: recognize ordinary formatting, abbreviation, spelling, punctuation, casing, direction, ordinal, and suite/unit differences without reducing the decision to rigid rules.
|
|
132
|
+
|
|
133
|
+
Write LEADBAY_ID when candidate facts uniquely agree with strong source evidence: exact registry/CRM id, exact phone, exact canonical website/domain with only one candidate, or name plus clear same-place address match with postcode/city and no conflict. If several candidates share the same website/domain, treat it as a chain/multi-location problem and use street address, postcode, city/neighborhood, phone, source URL path/location slug, and location words in the source name to pick the specific place when exactly one candidate matches.
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# WHAT GOOD LOOKS LIKE \u2014 the disambiguation bar
|
|
137
|
+
|
|
138
|
+
A "good" run produces an **augmented file** (input file + a new LEADBAY_ID column) where every row that COULD be confidently matched IS matched, and every row that couldn't is honestly left blank with the reason. Concretely:
|
|
139
|
+
|
|
140
|
+
- Write LEADBAY_ID when candidate facts uniquely agree with strong source evidence: exact registry/CRM id, exact phone, exact canonical website/domain with only one candidate, or name plus clear same-place address match with postcode/city and no conflict.
|
|
141
|
+
- For chain/multi-location problems (several candidates share the same website/domain), use street address, postcode, city/neighborhood, phone, source URL path/location slug, and location words in the source name to pick the specific place when exactly one candidate matches.
|
|
142
|
+
- **Never** pick LEADBAY_ID from score alone, name-only, fuzzy-name-only, generic directory websites, root-domain-only, brand-only, postcode-only, or city-only evidence.
|
|
143
|
+
- Leave LEADBAY_ID blank only after those checks still leave real ambiguity, and record why in the DECISION LOG.
|
|
144
|
+
|
|
145
|
+
A row left blank with a clear reason is a SUCCESS, not a failure \u2014 it gives the user an honest audit trail. Fabricating an ID is a critical failure.
|
|
146
|
+
|
|
147
|
+
## GATE 3 \u2014 DECISION LOG
|
|
148
|
+
|
|
149
|
+
Before writing LEADBAY_ID for any ambiguous row, render the DECISION LOG byproduct in your response. One line per row that was not a deterministic match.
|
|
150
|
+
|
|
151
|
+
Append one line per ambiguous-or-resolved row to the DECISION LOG block:
|
|
152
|
+
|
|
153
|
+
\`\`\`
|
|
154
|
+
DECISION LOG
|
|
155
|
+
============
|
|
156
|
+
row <N>: LEADBAY_ID=<id|blank> evidence=<which signals agreed> rejected=<why other candidates were not chosen>
|
|
157
|
+
row <N>: LEADBAY_ID=<id|blank> evidence=<...> rejected=<...>
|
|
158
|
+
============
|
|
159
|
+
\`\`\`
|
|
160
|
+
|
|
161
|
+
For rows where no resolution was possible, write \`LEADBAY_ID=blank evidence=insufficient\` and explain in \`rejected=\` why the available signals were not enough.
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# PHASE 4 \u2014 PRESERVE & COMMIT
|
|
165
|
+
|
|
166
|
+
Build a clean records array for import from the preservation plan. Preserve user-requested and semantically meaningful business fields, add LEADBAY_ID where resolved, normalize obvious scalar fields, and split JSON/list blobs into useful scalar columns when they contain real business data. For meaningful columns with no standard Leadbay field, call \`leadbay_list_mappable_fields\` and create/reuse custom fields rather than dropping the data. Drop blank-header columns and placeholder values like \`couldn't find\`, \`yes\`, empty arrays, and raw JSON after useful values have been extracted. Do not preserve scraper plumbing, duplicate blank columns, or long reasoning text, but do preserve meaningful client notes, data-quality warnings that affect outreach, source record links, and evidence URLs when they help the user's workflow.
|
|
167
|
+
|
|
168
|
+
Treat contact exports and embedded owner/contact data as lead+contact imports. Map the parent company identity columns (LEADBAY_ID/LEAD_WEBSITE/LEAD_NAME/CRM_ID/SIREN) and also map person columns to CONTACT_FIRST_NAME, CONTACT_LAST_NAME, CONTACT_EMAIL, CONTACT_PHONE_NUMBER, CONTACT_TITLE, CONTACT_LINKEDIN. If a restaurant/company 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 share the same LEADBAY_ID/company; import each row as a contact for that lead.
|
|
169
|
+
|
|
170
|
+
**Preserve the source CRM record as a clickable link.** Source CRM URLs/ids \u2014 HubSpot, Salesforce, Pipedrive, Close, Attio, or anything similar \u2014 are high-value: they let the user click straight from a Leadbay lead back to the original record in their CRM. Don't drop them.
|
|
171
|
+
|
|
172
|
+
Workflow:
|
|
173
|
+
1. Call \`leadbay_list_mappable_fields\` first; if a suitable EXTERNAL_ID-style field already exists for the source CRM, reuse it.
|
|
174
|
+
2. If no suitable field exists, call \`leadbay_create_custom_field\` with \`kind=EXTERNAL_ID\` and a \`config.url_template\` for the specific CRM. Pass the stable object id (not the URL) as the value.
|
|
175
|
+
|
|
176
|
+
Per-CRM templates \u2014 pass the CRM's stable object id as \`{value}\`:
|
|
177
|
+
- **HubSpot**: \`https://app.hubspot.com/contacts/<portal-id>/record/0-1/{value}\` (companies) or \`.../record/0-2/{value}\` (contacts) or \`.../record/0-3/{value}\` (deals)
|
|
178
|
+
- **Salesforce**: \`https://<your-instance>.lightning.force.com/lightning/r/Account/{value}/view\` (Accounts) or \`.../Lead/{value}/view\`, \`.../Contact/{value}/view\`, \`.../Opportunity/{value}/view\`
|
|
179
|
+
- **Pipedrive**: \`https://<your-domain>.pipedrive.com/organization/{value}\` or \`.../person/{value}\` or \`.../deal/{value}\`
|
|
180
|
+
- **Close**: \`https://app.close.com/lead/{value}/\`
|
|
181
|
+
- **Attio**: \`https://app.attio.com/<workspace-slug>/company/{value}\`
|
|
182
|
+
- **Other CRMs**: ask the user for the URL template; if they don't know, fall back to a TEXT custom field for the full URL.
|
|
183
|
+
|
|
184
|
+
Preserve raw source identifiers (e.g. \`hubspot_id\`, \`salesforce_account_id\`, \`associated_deal\`, \`pipedrive_org_id\`) in custom fields when they aren't already represented by a better standard/custom field. If only a full URL exists and no stable id/template can be recovered, create/use a TEXT custom field for the URL.
|
|
185
|
+
|
|
186
|
+
Leadbay has CONTACT_PHONE_NUMBER but no standard LEAD_PHONE in this tool surface; preserve establishment/company phone only via an intentional custom field.
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
Preserve notes intentionally. If the file contains meaningful per-lead notes/context that should live as Leadbay notes, keep them aside during import and, after the import returns lead IDs, call \`leadbay_add_note\` for the relevant imported/resolved leads when that tool is available. For dry runs, report which notes would be written. If lead notes are not available and the user asked to preserve the text, create/reuse an import-notes custom field instead of dropping it.
|
|
190
|
+
|
|
191
|
+
Build the final mappings yourself. Start from \`leadbay_resolve_import_rows.mappings_for_import\`, then map semantically: LEADBAY_ID, LEAD_WEBSITE, LEAD_NAME, CRM_ID, SIREN, LEAD_LOCATION*, LEAD_SECTOR, LEAD_SIZE, contact fields, and useful \`CUSTOM.<id>\` fields. Call \`leadbay_list_mappable_fields\` before using custom fields.
|
|
192
|
+
|
|
193
|
+
# PHASE 5 \u2014 QUALIFY (optional) + REPORT
|
|
194
|
+
|
|
195
|
+
Prefer \`leadbay_import_and_qualify\` when the user asks to qualify/research after import; otherwise use \`leadbay_import_leads\`. For large files or short client timeouts, pass \`wait_for_completion=false\` and poll \`leadbay_import_status\`. After import, qualify only lead IDs returned by the import; late website matches may appear later via \`import_status\`.
|
|
196
|
+
|
|
197
|
+
**Deliver the augmented file back to the user**: the original file plus a new \`LEADBAY_ID\` column populated from the resolution step. This is the second deliverable of a job well done.
|
|
198
|
+
|
|
199
|
+
## GATE 5 \u2014 FINAL REPORT
|
|
200
|
+
|
|
201
|
+
Before ending the session, render the FINAL REPORT byproduct in your response.
|
|
202
|
+
|
|
203
|
+
Render the FINAL REPORT block VERBATIM as your byproduct:
|
|
204
|
+
|
|
205
|
+
\`\`\`
|
|
206
|
+
FINAL REPORT
|
|
207
|
+
============
|
|
208
|
+
rows read: <n>
|
|
209
|
+
rows skipped (blank/dup): <n>
|
|
210
|
+
deterministic matches: <n>
|
|
211
|
+
ambiguous left unresolved: <n>
|
|
212
|
+
contacts imported: <n>
|
|
213
|
+
notes written or staged: <n>
|
|
214
|
+
custom fields created: <n>
|
|
215
|
+
custom fields reused: <n>
|
|
216
|
+
import IDs / handle IDs: <list>
|
|
217
|
+
leads imported now: <list-or-count>
|
|
218
|
+
needs later polling: <yes/no, via leadbay_import_status>
|
|
219
|
+
============
|
|
220
|
+
\`\`\`
|
|
221
|
+
|
|
222
|
+
If any field is N/A for this run, render the row with \`n/a\` instead of dropping it.
|
|
223
|
+
`;
|
|
224
|
+
var leadbay_log_outreach = `
|
|
225
|
+
IRON LAW \u2014 VERIFICATION REQUIRED. Before calling leadbay_report_outreach, you MUST collect one of: a gmail message id (verification.source = 'gmail_message_id'), a calendar event id (verification.source = 'calendar_event_id'), or a literal one-sentence user confirmation (verification.source = 'user_confirmed', verification.ref = the user's exact words). Skipping or fabricating verification poisons the human team's pipeline.
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
Log this outreach on Leadbay lead {{arg:lead_id}}:
|
|
229
|
+
Summary: {{arg:summary}}
|
|
230
|
+
|
|
231
|
+
# PHASE 1 \u2014 COLLECT VERIFICATION (ask the user EXACTLY once)
|
|
232
|
+
|
|
233
|
+
Before calling \`leadbay_report_outreach\`, ask me ONCE which of these applies:
|
|
234
|
+
|
|
235
|
+
- I sent an **email** \u2192 ask for the Gmail message id (\`verification.source = 'gmail_message_id'\`, \`verification.ref = <the id>\`).
|
|
236
|
+
- I booked a **meeting** \u2192 ask for the calendar event id (\`verification.source = 'calendar_event_id'\`, \`verification.ref = <the id>\`).
|
|
237
|
+
- **Other** \u2192 ask me for a literal one-sentence confirmation that the outreach happened (\`verification.source = 'user_confirmed'\`, \`verification.ref = my exact words\`).
|
|
238
|
+
|
|
239
|
+
# PHASE 2 \u2014 RECORD
|
|
240
|
+
|
|
241
|
+
After I answer, call \`leadbay_report_outreach({lead_id: '{{arg:lead_id}}', note: <summary>, verification: {source, ref}})\`. Optionally pass \`dry_run:true\` first to confirm exactly what would be sent \u2014 recommended if I described the outreach but you're not 100% sure how to phrase the note.
|
|
242
|
+
|
|
243
|
+
# PHASE 3 \u2014 CONFIRM
|
|
244
|
+
Tell me the outreach was logged, name the verification.source used, and surface the response's \`outreach_id\` if present so I can refer back to it.
|
|
245
|
+
`;
|
|
246
|
+
var leadbay_qualify_top_n = `
|
|
247
|
+
Qualify the top {{arg:count_or_default}} un-qualified leads in the active Leadbay lens.
|
|
248
|
+
|
|
249
|
+
# PHASE 1 \u2014 LAUNCH
|
|
250
|
+
Call \`leadbay_bulk_qualify_leads\` with \`count={{arg:count_or_default}}\`.
|
|
251
|
+
|
|
252
|
+
# PHASE 2 \u2014 POLL
|
|
253
|
+
While it polls, expect notifications / progress events showing per-lead transitions. Surface meaningful ones (e.g. "lead X just finished") to me as they arrive.
|
|
254
|
+
|
|
255
|
+
# PHASE 3 \u2014 SUMMARIZE
|
|
256
|
+
|
|
257
|
+
When \`bulk_qualify_leads\` returns, summarize:
|
|
258
|
+
- How many qualified (name the count)
|
|
259
|
+
- How many are still running (name them, by lead_id + lead name if available, so I can poll again later)
|
|
260
|
+
- The 3 highest-\`ai_agent_lead_score\` leads from THIS batch, with their \`qualification_summary\` (one sentence each)
|
|
261
|
+
|
|
262
|
+
# PHASE 4 \u2014 RECOMMEND
|
|
263
|
+
Recommend the single most-promising lead from this batch and offer to research it deeply with \`leadbay_research_lead\`. Do not actually call \`research_lead\` yet \u2014 wait for my go.
|
|
264
|
+
`;
|
|
265
|
+
var leadbay_refine_audience = `
|
|
266
|
+
Refine the Leadbay audience prompt to: {{arg:instruction}}
|
|
267
|
+
|
|
268
|
+
# PHASE 1 \u2014 REFINE
|
|
269
|
+
Call \`leadbay_refine_prompt\` with \`prompt=<the instruction above>\`.
|
|
270
|
+
|
|
271
|
+
# PHASE 2 \u2014 CLARIFICATION ROUND-TRIP (if needed)
|
|
272
|
+
|
|
273
|
+
IRON LAW \u2014 DO NOT ANSWER CLARIFICATIONS ON THE USER'S BEHALF. If the response includes a \`clarification\` block, surface the question and options to me VERBATIM and wait. Do NOT call \`leadbay_answer_clarification\`. I want to choose.
|
|
274
|
+
|
|
275
|
+
# PHASE 3 \u2014 APPLIED OR NOT
|
|
276
|
+
If the response status is \`applied\`, tell me Leadbay is regenerating intelligence and recommend I check back in a few minutes via \`leadbay_account_status\` (\`computing_intelligence\` flips to false when ready). If the status is anything else, name it explicitly.
|
|
277
|
+
`;
|
|
278
|
+
var leadbay_research_a_domain = `
|
|
279
|
+
IRON LAW \u2014 NO FABRICATION. Every lead id, contact email, custom field id, mapping decision, and tool argument must trace to a value you read from the file the user attached or to an output from a leadbay_* tool call in this session. Do not invent values. Do not "fill in" a missing leadId with a name match. Do not synthesize a CRM id from a guess. If a value is missing, leave the field blank and say so.
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
Research the company with domain '{{arg:domain}}' for me using Leadbay.
|
|
283
|
+
|
|
284
|
+
# PHASE 1 \u2014 IMPORT + QUALIFY
|
|
285
|
+
Call \`leadbay_import_and_qualify\` with \`domains=[{domain:'{{arg:domain}}'}]\`. This imports the lead AND runs AI qualification in one call. If the response indicates \`quota_blocked\` or \`still_running\`, say so explicitly.
|
|
286
|
+
|
|
287
|
+
# PHASE 2 \u2014 DEEP DIVE
|
|
288
|
+
When the import resolves, call \`leadbay_research_lead\` on the new leadId.
|
|
289
|
+
|
|
290
|
+
# PHASE 3 \u2014 SUMMARY
|
|
291
|
+
Summarize:
|
|
292
|
+
- Who is this company (1 sentence)
|
|
293
|
+
- Their fit (cite specific \`qualification_answers\` from the qualification response)
|
|
294
|
+
- What signals stand out (cite specific research findings)
|
|
295
|
+
- Which contact would I email first (name, role, source)
|
|
296
|
+
|
|
297
|
+
Be honest about uncertainty: if any field above is missing from tool responses, say "not surfaced by qualification" rather than guessing.
|
|
298
|
+
`;
|
|
299
|
+
var PROMPT_META = {
|
|
300
|
+
leadbay_daily_check_in: { "name": "leadbay_daily_check_in", "short_description": `Run the canonical daily check-in: see account state, pull a fresh batch,
|
|
301
|
+
triage the top 10, deep-dive on every promising one, and offer contact
|
|
302
|
+
enrichment. The user's typical morning workflow. Trigger when the user
|
|
303
|
+
asks for "leadbay leads", "best leads to prospect today", "what should
|
|
304
|
+
I work on", or anything resembling "show me the day's batch".
|
|
305
|
+
`, "arguments": [], "expected_calls": ["leadbay_account_status", "leadbay_pull_leads", "leadbay_research_lead", "leadbay_bulk_qualify_leads", "leadbay_enrich_contacts"], "failure_modes": ["Calls leadbay_report_outreach without explicit user authorization", "Surfaces fewer than 10 leads when more are available, or fails to top up via leadbay_qualify_top_n when the batch is short", 'Writes flat one-line summaries instead of motivational "why prospect this today" framing', "Skips deep research on promising leads (Phase 4) \u2014 the agent must call leadbay_research_lead on each, not just one", "Triggers contact enrichment without asking the user first (it consumes quota)", "Skips the STOP byproduct and proposes next actions on its own"] },
|
|
306
|
+
leadbay_import_file: { "name": "leadbay_import_file", "short_description": "Import a user-supplied CSV/file into Leadbay through five phases with\nevidence gates \u2014 scan, derive, resolve identities, preserve & commit,\nthen optionally qualify and report. The job is to maximize how many\nrows the Leadbay system actually ingests and matches.\n", "arguments": [{ "name": "file", "description": "Path or user-visible name of the CSV/file to import. If omitted, use the file the user attached or referenced.", "required": false }, { "name": "instruction", "description": 'Additional user goal, e.g. "then qualify the leads", "preserve owner phone as a custom field", or "only import restaurants in Manhattan".', "required": false }], "expected_calls": ["leadbay_resolve_import_rows", "leadbay_list_mappable_fields", "leadbay_create_custom_field", "leadbay_import_leads", "leadbay_import_and_qualify", "leadbay_add_note", "leadbay_import_status"], "failure_modes": ["Picks LEADBAY_ID from score alone, name-only, fuzzy-name-only, root-domain-only, brand-only, postcode-only, or city-only evidence", "Drops meaningful business notes or CRM record links instead of preserving them as custom fields or lead notes", "Treats a consumer mailbox domain (gmail.com, hotmail.com, ...) as the company domain", "Skips deriving company_domain from a business email when no website column exists (this kills match rate)", "Skips the COLUMN PRESERVATION PLAN byproduct before importing", "Skips the DECISION LOG byproduct before writing LEADBAY_ID", "Returns the imported records WITHOUT writing LEADBAY_ID values back into the user's file (leaves the user no audit trail of what matched)", "Fabricates leadIds, contact emails, or mapping IDs not present in the file or a tool response"] },
|
|
307
|
+
leadbay_log_outreach: { "name": "leadbay_log_outreach", "short_description": "Log outreach (an email I sent, a call I made, a meeting I had) on a\nspecific lead. Captures verification so the SDR pipeline trusts the entry.\n", "arguments": [{ "name": "lead_id", "description": "The lead UUID. Get it from leadbay_pull_leads or leadbay_research_lead.", "required": true }, { "name": "summary", "description": "1-2 sentences describing what I did (e.g. 'Sent intro email to CTO citing recent Hornsea contract').", "required": true }], "expected_calls": ["leadbay_report_outreach"], "failure_modes": ["Calls leadbay_report_outreach without first collecting a verification source", "Fabricates a gmail_message_id or calendar_event_id (the human team treats verification as canonical)", "Records outreach to a different lead_id than the one the user supplied", "Skips the dry_run step when the user is unsure what would be sent"] },
|
|
308
|
+
leadbay_qualify_top_n: { "name": "leadbay_qualify_top_n", "short_description": "Bulk-qualify the top N un-qualified leads in the active lens. Uses\nleadbay_bulk_qualify_leads with a sensible default budget.\n", "arguments": [{ "name": "count", "description": "How many leads to qualify (default 10, max 25). Higher counts may take 5+ minutes.", "required": false }], "expected_calls": ["leadbay_bulk_qualify_leads", "leadbay_research_lead"], "failure_modes": ["Picks a count larger than the user asked for (or larger than the max 25)", "Glosses over still-running leads in the summary instead of naming them", "Recommends a lead from the existing qualified pool instead of one from this batch's actual results"] },
|
|
309
|
+
leadbay_refine_audience: { "name": "leadbay_refine_audience", "short_description": "Refine the kind of leads Leadbay surfaces beyond firmographics, with a\nfree-text instruction. Handles the clarification round-trip if the new\nprompt is ambiguous.\n", "arguments": [{ "name": "instruction", "description": "The refinement (e.g. 'focus on hospitals running their own IT'). Set to plain English.", "required": true }], "expected_calls": ["leadbay_refine_prompt", "leadbay_account_status"], "failure_modes": ["Calls leadbay_answer_clarification on the user's behalf instead of surfacing the clarification verbatim", "Glosses over the clarification options instead of presenting them as offered", "Promises immediate effect when status='applied' actually triggers an async intelligence recompute"] },
|
|
310
|
+
leadbay_research_a_domain: { "name": "leadbay_research_a_domain", "short_description": "Import a company by domain and run deep qualification + research in one\npass. Use when a colleague mentions a name and you want everything Leadbay\nknows about it.\n", "arguments": [{ "name": "domain", "description": "The company's primary domain (e.g. 'acme.com'). Protocol/path are stripped.", "required": true }], "expected_calls": ["leadbay_import_and_qualify", "leadbay_research_lead"], "failure_modes": ["Fabricates qualification answers not present in any tool response", "Reports certainty about fit when qualification didn't actually run (e.g. quota_blocked)", "Skips the research step after import completes"] }
|
|
311
|
+
};
|
|
312
|
+
|
|
34
313
|
// src/prompts.ts
|
|
35
314
|
function userMessage(text) {
|
|
36
315
|
return { role: "user", content: { type: "text", text } };
|
|
37
316
|
}
|
|
317
|
+
function substitutePlaceholders(body, substitutions) {
|
|
318
|
+
let out = body;
|
|
319
|
+
for (const [placeholder, value] of Object.entries(substitutions)) {
|
|
320
|
+
out = out.split(`{{arg:${placeholder}}}`).join(value);
|
|
321
|
+
}
|
|
322
|
+
return out;
|
|
323
|
+
}
|
|
38
324
|
var CATALOG = [
|
|
39
325
|
{
|
|
40
326
|
name: "leadbay_daily_check_in",
|
|
41
|
-
description:
|
|
327
|
+
description: PROMPT_META.leadbay_daily_check_in.short_description,
|
|
42
328
|
arguments: [],
|
|
43
|
-
render: () => [
|
|
44
|
-
userMessage(
|
|
45
|
-
"Run the Leadbay daily check-in for me:\n1. Call leadbay_account_status to see what quota I have left and which lens is active.\n2. Call leadbay_pull_leads to get today's fresh batch.\n3. Show me the top 3 \u2014 by ai_agent_lead_score when present, otherwise by score. For each, summarize qualification_summary in one sentence.\n4. Recommend ONE lead to research deeply, and call leadbay_research_lead on it. Tell me what makes it promising, what signals stand out, and what would be the right outreach move.\n5. Stop. Wait for me to decide what to do next. Do not call leadbay_report_outreach unless I explicitly say so."
|
|
46
|
-
)
|
|
47
|
-
]
|
|
329
|
+
render: () => [userMessage(leadbay_daily_check_in)]
|
|
48
330
|
},
|
|
49
331
|
{
|
|
50
332
|
name: "leadbay_research_a_domain",
|
|
51
|
-
description:
|
|
333
|
+
description: PROMPT_META.leadbay_research_a_domain.short_description,
|
|
52
334
|
arguments: [
|
|
53
335
|
{
|
|
54
336
|
name: "domain",
|
|
@@ -58,16 +340,39 @@ var CATALOG = [
|
|
|
58
340
|
],
|
|
59
341
|
render: (args) => [
|
|
60
342
|
userMessage(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
343
|
+
substitutePlaceholders(leadbay_research_a_domain, {
|
|
344
|
+
domain: args.domain ?? "<missing>"
|
|
345
|
+
})
|
|
346
|
+
)
|
|
347
|
+
]
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
name: "leadbay_import_file",
|
|
351
|
+
description: PROMPT_META.leadbay_import_file.short_description,
|
|
352
|
+
arguments: [
|
|
353
|
+
{
|
|
354
|
+
name: "file",
|
|
355
|
+
description: "Path or user-visible name of the CSV/file to import. If omitted, use the file the user attached or referenced.",
|
|
356
|
+
required: false
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
name: "instruction",
|
|
360
|
+
description: "Additional user goal, e.g. 'then qualify the leads', 'preserve owner phone as a custom field', or 'only import restaurants in Manhattan'.",
|
|
361
|
+
required: false
|
|
362
|
+
}
|
|
363
|
+
],
|
|
364
|
+
render: (args) => [
|
|
365
|
+
userMessage(
|
|
366
|
+
substitutePlaceholders(leadbay_import_file, {
|
|
367
|
+
file_paren: args.file ? ` (${args.file})` : "",
|
|
368
|
+
instruction_or_default: args.instruction ?? "import the rows, resolve identities, and qualify leads if the user asked for qualification"
|
|
369
|
+
})
|
|
65
370
|
)
|
|
66
371
|
]
|
|
67
372
|
},
|
|
68
373
|
{
|
|
69
374
|
name: "leadbay_refine_audience",
|
|
70
|
-
description:
|
|
375
|
+
description: PROMPT_META.leadbay_refine_audience.short_description,
|
|
71
376
|
arguments: [
|
|
72
377
|
{
|
|
73
378
|
name: "instruction",
|
|
@@ -77,17 +382,15 @@ var CATALOG = [
|
|
|
77
382
|
],
|
|
78
383
|
render: (args) => [
|
|
79
384
|
userMessage(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
2. If the response includes a 'clarification' block, surface the question + options to me VERBATIM and wait. Do NOT call leadbay_answer_clarification on my behalf \u2014 I want to choose.
|
|
84
|
-
3. If the response status is 'applied', tell me Leadbay is regenerating intelligence and recommend I check back in a few minutes via leadbay_account_status (computing_intelligence flips to false when ready).`
|
|
385
|
+
substitutePlaceholders(leadbay_refine_audience, {
|
|
386
|
+
instruction: args.instruction ?? "<missing>"
|
|
387
|
+
})
|
|
85
388
|
)
|
|
86
389
|
]
|
|
87
390
|
},
|
|
88
391
|
{
|
|
89
392
|
name: "leadbay_log_outreach",
|
|
90
|
-
description:
|
|
393
|
+
description: PROMPT_META.leadbay_log_outreach.short_description,
|
|
91
394
|
arguments: [
|
|
92
395
|
{
|
|
93
396
|
name: "lead_id",
|
|
@@ -102,21 +405,16 @@ var CATALOG = [
|
|
|
102
405
|
],
|
|
103
406
|
render: (args) => [
|
|
104
407
|
userMessage(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
- If I sent an email: ask for the Gmail message id (verification.source = 'gmail_message_id').
|
|
110
|
-
- If I booked a meeting: ask for the calendar event id (verification.source = 'calendar_event_id').
|
|
111
|
-
- Otherwise: ask me for a literal one-sentence confirmation that the outreach happened (verification.source = 'user_confirmed', verification.ref = my exact words).
|
|
112
|
-
|
|
113
|
-
After I answer, call leadbay_report_outreach({lead_id, note: <summary>, verification: {source, ref}}). Optionally pass dry_run:true first to confirm what would be sent.`
|
|
408
|
+
substitutePlaceholders(leadbay_log_outreach, {
|
|
409
|
+
lead_id: args.lead_id ?? "<missing>",
|
|
410
|
+
summary: args.summary ?? "<missing>"
|
|
411
|
+
})
|
|
114
412
|
)
|
|
115
413
|
]
|
|
116
414
|
},
|
|
117
415
|
{
|
|
118
416
|
name: "leadbay_qualify_top_n",
|
|
119
|
-
description:
|
|
417
|
+
description: PROMPT_META.leadbay_qualify_top_n.short_description,
|
|
120
418
|
arguments: [
|
|
121
419
|
{
|
|
122
420
|
name: "count",
|
|
@@ -128,11 +426,9 @@ After I answer, call leadbay_report_outreach({lead_id, note: <summary>, verifica
|
|
|
128
426
|
const n = args.count ?? "10";
|
|
129
427
|
return [
|
|
130
428
|
userMessage(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
3. When it returns, summarize: how many qualified, how many still running, and the 3 highest-boost-score leads with their qualification_summary.
|
|
135
|
-
4. Recommend the single most promising lead and offer to research it deeply with leadbay_research_lead.`
|
|
429
|
+
substitutePlaceholders(leadbay_qualify_top_n, {
|
|
430
|
+
count_or_default: n
|
|
431
|
+
})
|
|
136
432
|
)
|
|
137
433
|
];
|
|
138
434
|
}
|
|
@@ -264,6 +560,7 @@ function buildSlashCommandsParagraph(has) {
|
|
|
264
560
|
commands.push("`/leadbay daily-check-in` (chains account_status \u2192 pull_leads \u2192 research_lead on the top hit)");
|
|
265
561
|
if (has("leadbay_import_and_qualify")) {
|
|
266
562
|
commands.push("`/leadbay research-a-domain {domain}` (import_and_qualify \u2192 research_lead)");
|
|
563
|
+
commands.push("`/leadbay import-file {file}` (resolve_import_rows \u2192 import_leads/import_and_qualify \u2192 status)");
|
|
267
564
|
}
|
|
268
565
|
if (has("leadbay_refine_prompt")) {
|
|
269
566
|
commands.push("`/leadbay refine-audience {instruction}` (refine_prompt with clarification handling)");
|
|
@@ -587,7 +884,7 @@ function buildServer(client, opts = {}) {
|
|
|
587
884
|
|
|
588
885
|
// src/bin.ts
|
|
589
886
|
import { createRequire } from "module";
|
|
590
|
-
var VERSION = "0.
|
|
887
|
+
var VERSION = "0.7.0";
|
|
591
888
|
var HELP = `
|
|
592
889
|
leadbay-mcp ${VERSION} \u2014 Leadbay Model Context Protocol server
|
|
593
890
|
|
|
@@ -876,7 +1173,7 @@ async function runLogin(args) {
|
|
|
876
1173
|
let result;
|
|
877
1174
|
try {
|
|
878
1175
|
if (pinnedRegion && !allowFallback) {
|
|
879
|
-
const { REGIONS } = await import("./dist-
|
|
1176
|
+
const { REGIONS } = await import("./dist-2RTYPHB3.js");
|
|
880
1177
|
const baseUrl = REGIONS[pinnedRegion];
|
|
881
1178
|
const c = createClient({ region: pinnedRegion });
|
|
882
1179
|
const token = await loginAt(baseUrl, email, password);
|
|
@@ -1368,7 +1665,7 @@ leadbay-mcp install \u2014 detected MCP clients on this machine:
|
|
|
1368
1665
|
let region;
|
|
1369
1666
|
try {
|
|
1370
1667
|
if (pinnedRegion && !allowFallback) {
|
|
1371
|
-
const { REGIONS } = await import("./dist-
|
|
1668
|
+
const { REGIONS } = await import("./dist-2RTYPHB3.js");
|
|
1372
1669
|
const baseUrl = REGIONS[pinnedRegion];
|
|
1373
1670
|
token = await loginAt(baseUrl, email, password);
|
|
1374
1671
|
region = pinnedRegion;
|