@leadbay/mcp 0.11.0 → 0.12.1
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 +18 -0
- package/README.md +11 -11
- package/dist/bin.js +296 -13
- package/dist/{chunk-MZZMIZXA.js → chunk-J2Y4LCFM.js} +1095 -24
- package/dist/{dist-JZ2FLLN6.js → dist-YYVFSDMH.js} +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog — @leadbay/mcp
|
|
2
2
|
|
|
3
|
+
## 0.12.1 — 2026-05-21
|
|
4
|
+
|
|
5
|
+
MCPB hotfix for Claude Desktop.
|
|
6
|
+
|
|
7
|
+
- **Fix packaged-server startup**: the MCPB bundle now injects a Node `createRequire` shim before esbuild's ESM wrapper so CommonJS dependencies can still require Node built-ins such as `perf_hooks`. This fixes the Claude Desktop disconnect where the server exited during initialization.
|
|
8
|
+
- **Packaging guardrail**: `@leadbay/dxt build` now runs the staged `server/index.js --version` before zipping, and the smoke suite extracts the MCPB and completes a real MCP initialize/tools-list handshake.
|
|
9
|
+
- **Manifest refresh**: MCPB manifests now declare `manifest_version: "0.3"` and the smoke assertions match the current MCPB manifest spec.
|
|
10
|
+
|
|
11
|
+
## 0.12.0 — 2026-05-21
|
|
12
|
+
|
|
13
|
+
Campaign and field-sales workflow release.
|
|
14
|
+
|
|
15
|
+
- **Campaign workflows**: adds campaign creation/listing, add-leads, progression summaries, and a `leadbay_campaign_call_sheet` composite that returns phone-ready, LinkedIn-ready, and map-ready lead/contact payloads.
|
|
16
|
+
- **Agent routing + skills**: adds the `leadbay_work_campaign`, `leadbay_plan_tour_in_city`, and `leadbay_setup_team_prospecting` prompt/skill flows so agents start with readiness checks, route to the right workflow tool, and keep outreach reporting grounded in verified user action.
|
|
17
|
+
- **Progression accuracy**: contacted/already-contacted summaries now use outreach/prospecting signals instead of treating contact coverage as outreach completion.
|
|
18
|
+
- **Coverage**: adds workflow audits, prompt-eval coverage for `leadbay_work_campaign`, live campaign smoke coverage, and focused unit tests for the new campaign progression/call-sheet composites.
|
|
19
|
+
- **Pin bumps**: every `@leadbay/mcp@0.11` install/runtime reference in docs, generated client config, DXT, and Claude plugin metadata is now `@0.12`.
|
|
20
|
+
|
|
3
21
|
## 0.11.0 — 2026-05-20
|
|
4
22
|
|
|
5
23
|
In-server auto-update flow: the MCP server now self-polls GitHub releases (24h throttle, ETag-aware, in-flight guarded) and surfaces an `update_available` block on `leadbay_account_status` when a newer version is published — both at boot AND on every tool call, so long-running Claude Desktop sessions still pick up new releases without restart.
|
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ A Model Context Protocol server that lets Claude Desktop, Cursor, Claude Code, a
|
|
|
24
24
|
## 1. Install (one command)
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
npx -y @leadbay/mcp@0.
|
|
27
|
+
npx -y @leadbay/mcp@0.12 install --email you@yourcompany.com --region us
|
|
28
28
|
# (you'll be prompted for your password — it's not echoed)
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -67,14 +67,14 @@ Claude Desktop 2026 ships the DXT (Desktop Extension) system — the legacy `cla
|
|
|
67
67
|
|
|
68
68
|
If you installed Node from the official [nodejs.org](https://nodejs.org) `.pkg`, `/usr/local/lib/node_modules` is root-owned. Any of these works:
|
|
69
69
|
|
|
70
|
-
- **Use `npx` (recommended, no global install):** all examples above use `npx -y @leadbay/mcp@0.
|
|
70
|
+
- **Use `npx` (recommended, no global install):** all examples above use `npx -y @leadbay/mcp@0.12 ...` — no global install needed.
|
|
71
71
|
- **`sudo npm install -g @leadbay/mcp`** (enter your macOS password).
|
|
72
72
|
- **Use a Node version manager** — [nvm](https://github.com/nvm-sh/nvm), [volta](https://volta.sh), [fnm](https://github.com/Schniz/fnm). They install Node under your home directory, so `npm install -g` works without sudo.
|
|
73
73
|
|
|
74
74
|
### If you'd rather mint a token without auto-install
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
|
-
npx -y @leadbay/mcp@0.
|
|
77
|
+
npx -y @leadbay/mcp@0.12 login \
|
|
78
78
|
--email you@yourcompany.com \
|
|
79
79
|
--region us
|
|
80
80
|
```
|
|
@@ -92,7 +92,7 @@ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) o
|
|
|
92
92
|
"mcpServers": {
|
|
93
93
|
"leadbay": {
|
|
94
94
|
"command": "npx",
|
|
95
|
-
"args": ["-y", "@leadbay/mcp@0.
|
|
95
|
+
"args": ["-y", "@leadbay/mcp@0.12"],
|
|
96
96
|
"env": {
|
|
97
97
|
"LEADBAY_TOKEN": "<paste-token-from-step-1>",
|
|
98
98
|
"LEADBAY_REGION": "us"
|
|
@@ -113,7 +113,7 @@ In Cursor settings, add the MCP server:
|
|
|
113
113
|
"mcp.servers": {
|
|
114
114
|
"leadbay": {
|
|
115
115
|
"command": "npx",
|
|
116
|
-
"args": ["-y", "@leadbay/mcp@0.
|
|
116
|
+
"args": ["-y", "@leadbay/mcp@0.12"],
|
|
117
117
|
"env": { "LEADBAY_TOKEN": "<paste-token>", "LEADBAY_REGION": "us" }
|
|
118
118
|
}
|
|
119
119
|
}
|
|
@@ -126,7 +126,7 @@ In Cursor settings, add the MCP server:
|
|
|
126
126
|
claude mcp add leadbay --scope user \
|
|
127
127
|
--env LEADBAY_TOKEN=<paste-token> \
|
|
128
128
|
--env LEADBAY_REGION=us \
|
|
129
|
-
-- npx -y @leadbay/mcp@0.
|
|
129
|
+
-- npx -y @leadbay/mcp@0.12
|
|
130
130
|
```
|
|
131
131
|
|
|
132
132
|
> **`--scope user`** registers Leadbay globally for your account (visible from any project). Without it, `claude mcp add` defaults to project-local scope and the server only appears in conversations opened from the directory where you ran the command.
|
|
@@ -138,7 +138,7 @@ claude mcp add leadbay --scope user \
|
|
|
138
138
|
Before starting Claude, run:
|
|
139
139
|
|
|
140
140
|
```bash
|
|
141
|
-
LEADBAY_TOKEN=<paste-token> npx -y @leadbay/mcp@0.
|
|
141
|
+
LEADBAY_TOKEN=<paste-token> npx -y @leadbay/mcp@0.12 doctor
|
|
142
142
|
```
|
|
143
143
|
|
|
144
144
|
Expected output:
|
|
@@ -366,14 +366,14 @@ The user's literal text replaces `verification.ref` in the outreach record, and
|
|
|
366
366
|
| `No enrichment credits remaining` | Out of quota | Contact Leadbay support to extend quota |
|
|
367
367
|
| Claude Desktop "loading forever" on first use | `npx` cold-start fetching the package | First run takes ~10s. Prefer `npm install -g @leadbay/mcp` for faster startup. |
|
|
368
368
|
| Claude Desktop doesn't show Leadbay tools | Server crashed at startup | Check `~/Library/Logs/Claude/mcp*.log` (macOS) or `%APPDATA%\Claude\logs\mcp*.log` (Windows). |
|
|
369
|
-
| Claude Code can't find Leadbay in a new conversation | MCP server installed at project scope (default before 0.3.0) | Re-run with `--scope user`: `claude mcp remove leadbay && claude mcp add leadbay --scope user --env LEADBAY_TOKEN=… --env LEADBAY_REGION=us -- npx -y @leadbay/mcp@0.
|
|
369
|
+
| Claude Code can't find Leadbay in a new conversation | MCP server installed at project scope (default before 0.3.0) | Re-run with `--scope user`: `claude mcp remove leadbay && claude mcp add leadbay --scope user --env LEADBAY_TOKEN=… --env LEADBAY_REGION=us -- npx -y @leadbay/mcp@0.12` |
|
|
370
370
|
| Agent reports "tool not found" for `refine_prompt` / `adjust_audience` etc. | Pre-0.3.0 install with `LEADBAY_MCP_WRITE` unset (writes were off) | Either re-run `npx @leadbay/mcp install` or remove `LEADBAY_MCP_WRITE=0` from your client config (writes are on by default in 0.3.0+) |
|
|
371
371
|
|
|
372
372
|
## 5. Upgrade & rotation
|
|
373
373
|
|
|
374
|
-
**Upgrade**: change the pinned minor in your config, e.g. `"@leadbay/mcp@0.2"` → `"@leadbay/mcp@0.
|
|
374
|
+
**Upgrade**: change the pinned minor in your config, e.g. `"@leadbay/mcp@0.2"` → `"@leadbay/mcp@0.12"`, then restart the client. **0.3.0 enables composite write tools by default** — see [MIGRATION.md](./MIGRATION.md). See also the [changelog](https://github.com/leadbay/leadclaw/releases).
|
|
375
375
|
|
|
376
|
-
**Rotate token**: re-run `npx -y @leadbay/mcp@0.
|
|
376
|
+
**Rotate token**: re-run `npx -y @leadbay/mcp@0.12 install --email you@yourcompany.com --region us` (or `login`) — the new session token replaces the old one in your MCP client config, and logging in again invalidates the prior session on most session backends.
|
|
377
377
|
|
|
378
378
|
## 6. Advanced
|
|
379
379
|
|
|
@@ -486,7 +486,7 @@ After your first authenticated call, your PostHog `distinctId` is set to your Le
|
|
|
486
486
|
"mcpServers": {
|
|
487
487
|
"leadbay": {
|
|
488
488
|
"command": "npx",
|
|
489
|
-
"args": ["-y", "@leadbay/mcp@0.
|
|
489
|
+
"args": ["-y", "@leadbay/mcp@0.12"],
|
|
490
490
|
"env": {
|
|
491
491
|
"LEADBAY_TOKEN": "u.…",
|
|
492
492
|
"LEADBAY_REGION": "us",
|
package/dist/bin.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
granularReadTools,
|
|
10
10
|
granularWriteTools,
|
|
11
11
|
resolveRegion
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-J2Y4LCFM.js";
|
|
13
13
|
|
|
14
14
|
// src/bin.ts
|
|
15
15
|
import { realpathSync } from "fs";
|
|
@@ -361,6 +361,55 @@ After I answer, call \`leadbay_report_outreach({lead_id: '{{arg:lead_id}}', note
|
|
|
361
361
|
# PHASE 3 \u2014 CONFIRM
|
|
362
362
|
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.
|
|
363
363
|
`;
|
|
364
|
+
var leadbay_plan_tour_in_city = `
|
|
365
|
+
Plan a field sales tour for me in **{{arg:city}}**{{arg:date_paren}}.
|
|
366
|
+
|
|
367
|
+
GATE \u2014 DEFER TO TOOL RENDERING. When you call a Leadbay composite that ships its own RENDERING block (every composite in 0.9.0+ does), render the response using that block's recipe verbatim \u2014 score bars, glyph palette, column order, hide-list, link priorities, all of it. Do NOT substitute prose, a numbered list, or a different column structure even when an orchestrating prompt's body suggests alternate framing. Prompt-specific commentary (motivational nudges, summaries, next-action recommendations) belongs ABOVE or BELOW the canonical table, never in place of it.
|
|
368
|
+
|
|
369
|
+
If the prompt's body and the tool's RENDERING appear to conflict, the tool's RENDERING wins for the structural layout; the prompt's voice wins for the commentary that surrounds it.
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
# PHASE 1 \u2014 BUILD THE ITINERARY
|
|
373
|
+
|
|
374
|
+
Call \`leadbay_tour_plan({city: "{{arg:city}}"})\` with the default counts (6 follow-ups + 6 discover). If the response is \`status: "ambiguous_locations"\`, surface the candidates and ask me to pick one, then re-call with \`city_id\`.
|
|
375
|
+
|
|
376
|
+
Split the returned \`monitor_leads\` into two buckets client-side using \`last_monitor_action\`:
|
|
377
|
+
|
|
378
|
+
- **Customers** \u2014 leads with any \`last_monitor_action\` history (CONTACTED, MEETING_BOOKED, etc.). Treat as known accounts with prior engagement.
|
|
379
|
+
- **Qualified prospects** \u2014 leads with high \`ai_agent_lead_score\` (or \`score\`) but no recent action.
|
|
380
|
+
|
|
381
|
+
\`discover_leads\` are the **New** bucket.
|
|
382
|
+
|
|
383
|
+
Aim for a 3+3+3 split if possible. If the customers bucket has fewer than 3, fill from qualified. If discover_filter_note indicates a low match ratio for the city, mention it: "Only N/30 fresh leads matched your city" \u2014 better honest than padded.
|
|
384
|
+
|
|
385
|
+
# PHASE 2 \u2014 RENDER THE MAP
|
|
386
|
+
|
|
387
|
+
Route the union of \`monitor_leads + discover_leads\` into \`places_map_display_v0\` (when the host exposes it). Per-lead \`notes\` string:
|
|
388
|
+
|
|
389
|
+
- \`\u2605 Customer \u2014 <one-sentence sector + why-now>. Reach <name>, <role>: <bare phone>, <bare email>.\`
|
|
390
|
+
- \`\u2605 Qualified \u2014 <one-sentence>. Reach <name>...\`
|
|
391
|
+
- \`\u2726 New \u2014 <one-sentence>. Reach <name>...\`
|
|
392
|
+
|
|
393
|
+
Skip leads with \`location.pos === null\` (no coordinates \u2192 no pin) \u2014 list them as "+ N leads without coordinates" below the widget.
|
|
394
|
+
|
|
395
|
+
Below the widget, emit a chat-prose summary grouped by mode (Customers / Qualified / New), with LinkedIn-linked contact name + bare phone/email pills per lead. Use the canonical \`linking/contact-linkedin\` rules.
|
|
396
|
+
|
|
397
|
+
# PHASE 3 \u2014 DRAFT IN-AREA OUTREACH (optional, ask first)
|
|
398
|
+
|
|
399
|
+
After the map, ask me ONCE: "Want me to draft 'I'll be in {{arg:city}}{{arg:date_paren}}' outreach for the top accounts?" If I say yes, for each of the top 3 leads (1 Customer / 1 Qualified / 1 New), call \`leadbay_prepare_outreach(leadId)\` and route the draft through \`message_compose_v1\` with a single variant labeled "In-area visit" \u2014 body opens with the visit context, references the AI-summary angle, ends with a clear ask (15-min coffee / on-site stopover).
|
|
400
|
+
|
|
401
|
+
Serialize the prepare_outreach calls (max 3 in parallel \u2014 see the long-running-tools rule).
|
|
402
|
+
|
|
403
|
+
# PHASE 4 \u2014 PERSIST AS A CAMPAIGN (optional, ask first)
|
|
404
|
+
|
|
405
|
+
After drafts, ask me ONCE: "Save these 9 accounts as a campaign called '**{{arg:city}} Tour{{arg:date_dash}}**'?" If I say yes, call \`leadbay_create_campaign({lead_ids: [...all_nine_lead_ids], name: "{{arg:city}} Tour{{arg:date_dash}}"})\`. Surface the returned \`id\` + \`name\` as a confirmation line, and offer the NEXT STEPS chip "View progression" (which routes to \`leadbay_campaign_progression\`).
|
|
406
|
+
|
|
407
|
+
If I declined the campaign step, end the turn \u2014 the map + drafts are enough for an ad-hoc trip.
|
|
408
|
+
|
|
409
|
+
# PHASE 5 \u2014 STOP
|
|
410
|
+
|
|
411
|
+
Done. The map is the surface; the drafts are the action; the campaign is the persistence layer for managerial follow-up after the trip.
|
|
412
|
+
`;
|
|
364
413
|
var leadbay_prospecting_overview = `
|
|
365
414
|
# Leadbay Prospecting \u2014 Orientation
|
|
366
415
|
|
|
@@ -736,6 +785,160 @@ Place a 2\u20133 sentence summary ABOVE the card with:
|
|
|
736
785
|
|
|
737
786
|
The card itself handles the signal callouts (\`\u{1F4C8} business signals\`, \`\u{1F4A1} prospecting clues\`). Do NOT re-narrate signals in prose above the card \u2014 that's what the card sections are for. Be honest about uncertainty: if any field is missing from tool responses, say "not surfaced by qualification" rather than guessing.
|
|
738
787
|
`;
|
|
788
|
+
var leadbay_setup_team_prospecting = `
|
|
789
|
+
Set up manager-led prospecting for me: turn the audience into a lens, validate candidates, then persist as named campaigns.
|
|
790
|
+
|
|
791
|
+
Audience: **{{arg:audience}}**
|
|
792
|
+
{{arg:rep_split_block}}
|
|
793
|
+
|
|
794
|
+
GATE \u2014 DEFER TO TOOL RENDERING. When you call a Leadbay composite that ships its own RENDERING block (every composite in 0.9.0+ does), render the response using that block's recipe verbatim \u2014 score bars, glyph palette, column order, hide-list, link priorities, all of it. Do NOT substitute prose, a numbered list, or a different column structure even when an orchestrating prompt's body suggests alternate framing. Prompt-specific commentary (motivational nudges, summaries, next-action recommendations) belongs ABOVE or BELOW the canonical table, never in place of it.
|
|
795
|
+
|
|
796
|
+
If the prompt's body and the tool's RENDERING appear to conflict, the tool's RENDERING wins for the structural layout; the prompt's voice wins for the commentary that surrounds it.
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
# PHASE 1 \u2014 INTERPRET INTENT INTO A LENS
|
|
800
|
+
|
|
801
|
+
Call \`leadbay_refine_prompt({user_prompt: "{{arg:audience}}"})\`. This handles the clarification protocol natively \u2014 if the system needs more info (e.g. industry disambiguation, geography precision), it returns \`status: "clarification_needed"\` with options. Surface those to me; on my answer, re-call \`leadbay_refine_prompt\` until the prompt converges.
|
|
802
|
+
|
|
803
|
+
When the prompt has converged, call \`leadbay_create_lens({user_prompt: <refined>, name: "<short descriptive name>"})\` to create a draft lens, then \`leadbay_promote_lens({lensId})\` to make it the active lens.
|
|
804
|
+
|
|
805
|
+
# PHASE 2 \u2014 PULL + VALIDATE CANDIDATES
|
|
806
|
+
|
|
807
|
+
Call \`leadbay_pull_leads({count: 20, lensId: <the new lens id>})\` to surface the top 20 candidates from the freshly-created lens. Render with the canonical \`pull_leads\` table layout.
|
|
808
|
+
|
|
809
|
+
Ask me ONCE: "Want me to deep-research the top N for validation?" If yes, call \`leadbay_research_lead_by_id\` serialized over the top 3-5 (one at a time, max 3 in parallel per the long-running-tools rule). Surface a research summary per lead.
|
|
810
|
+
|
|
811
|
+
Then ask me ONCE: "Which of these should we drop?" If I name leads to drop, exclude them from the working set. The remaining is the validated set.
|
|
812
|
+
|
|
813
|
+
# PHASE 3 \u2014 DECIDE THE CAMPAIGN SHAPE
|
|
814
|
+
|
|
815
|
+
If I provided a \`rep_split\` ("one campaign per rep: John gets Tulsa, Sarah gets OKC"), partition the validated leads accordingly. If I didn't, ask ONCE: "Create one campaign for the whole batch, or split per rep / region / sector?" \u2014 surface 2-4 options via \`ask_user_input_v0\` when available, else as a bulleted list.
|
|
816
|
+
|
|
817
|
+
For each campaign-shape decision, derive a name. Templates:
|
|
818
|
+
- Whole batch: \`"<lens-name> \u2013 <YYYY-MM-DD>"\`
|
|
819
|
+
- Per rep: \`"<lens-name> \u2013 <RepName>"\`
|
|
820
|
+
- Per region: \`"<lens-name> \u2013 <RegionName>"\`
|
|
821
|
+
|
|
822
|
+
# PHASE 4 \u2014 PERSIST
|
|
823
|
+
|
|
824
|
+
For each campaign-shape partition, call \`leadbay_create_campaign({lead_ids: [...partition], name: "<derived>"})\`. Surface the returned \`id\` + \`name\` per campaign as a confirmation line.
|
|
825
|
+
|
|
826
|
+
# PHASE 5 \u2014 BE HONEST ABOUT SCOPE
|
|
827
|
+
|
|
828
|
+
Once the campaigns are created, surface this caveat in plain prose:
|
|
829
|
+
|
|
830
|
+
> Campaign visibility is currently scoped to the user who CREATED the campaign \u2014 the reps won't see these in their own MCP \`leadbay_list_campaigns\` calls. They CAN see them in the web UI at app.leadbay.ai \u2192 Campaigns. Cross-user MCP visibility would need backend work; flag this as a #3630 US3 product gap if your reps work primarily through MCP.
|
|
831
|
+
|
|
832
|
+
End with a NEXT STEPS chip via \`ask_user_input_v0\`: "View progression on one of these now?" \u2192 routes to \`leadbay_campaign_progression\`.
|
|
833
|
+
|
|
834
|
+
# PHASE 6 \u2014 STOP
|
|
835
|
+
|
|
836
|
+
Done. The lens is live, the validated cohort is persisted as named campaigns, and the manager knows where the cross-user-visibility gap is.
|
|
837
|
+
`;
|
|
838
|
+
var leadbay_work_campaign = `
|
|
839
|
+
Work my **{{arg:campaign_or_default}}** campaign as an outreach session{{arg:mode_paren}}.
|
|
840
|
+
|
|
841
|
+
GATE \u2014 DEFER TO TOOL RENDERING. When you call a Leadbay composite that ships its own RENDERING block (every composite in 0.9.0+ does), render the response using that block's recipe verbatim \u2014 score bars, glyph palette, column order, hide-list, link priorities, all of it. Do NOT substitute prose, a numbered list, or a different column structure even when an orchestrating prompt's body suggests alternate framing. Prompt-specific commentary (motivational nudges, summaries, next-action recommendations) belongs ABOVE or BELOW the canonical table, never in place of it.
|
|
842
|
+
|
|
843
|
+
If the prompt's body and the tool's RENDERING appear to conflict, the tool's RENDERING wins for the structural layout; the prompt's voice wins for the commentary that surrounds it.
|
|
844
|
+
|
|
845
|
+
|
|
846
|
+
# PHASE 0 \u2014 PICK THE CAMPAIGN
|
|
847
|
+
|
|
848
|
+
If I gave you a name or id, resolve it. Otherwise call \`leadbay_list_campaigns()\` and surface the active campaigns as a \`single_select\` via \`ask_user_input_v0\` (cap at 4 \u2014 sort by \`updated_at\` desc, archived hidden):
|
|
849
|
+
|
|
850
|
+
> Which campaign do you want to work?
|
|
851
|
+
> - <Name 1> \xB7 <N leads> \xB7 last touched <date>
|
|
852
|
+
> - <Name 2> \xB7 <N leads> \xB7 last touched <date>
|
|
853
|
+
> - \u2026
|
|
854
|
+
|
|
855
|
+
When the user picks, capture the \`campaign_id\`. If \`{{arg:campaign}}\` is a name, fuzzy-match against \`campaigns[].campaign.name\`. On ambiguous matches, surface a \`single_select\` instead of guessing.
|
|
856
|
+
|
|
857
|
+
# PHASE 1 \u2014 FETCH + ASSESS READINESS (the load-bearing phase)
|
|
858
|
+
|
|
859
|
+
Call \`leadbay_campaign_call_sheet({campaign_id})\`. The response carries \`summary\` + \`readiness\` \u2014 use them to figure out what the user CAN actually do today, then PROPOSE the right session mode rather than auto-rendering.
|
|
860
|
+
|
|
861
|
+
**Read the summary numbers**:
|
|
862
|
+
- \`total_leads\`, \`total_contacts\`
|
|
863
|
+
- \`leads_with_phone\` \u2014 can call from this many leads
|
|
864
|
+
- \`leads_with_email\` \u2014 can email this many
|
|
865
|
+
- \`leads_with_coords\` \u2014 can map this many
|
|
866
|
+
- \`leads_without_contacts\` \u2014 these need enrichment before any outreach is possible
|
|
867
|
+
- \`leads_already_contacted\` \u2014 these have prior touches; the rep may want to skip them for cold work
|
|
868
|
+
|
|
869
|
+
**Read the \`readiness\` booleans** (pre-computed thresholds):
|
|
870
|
+
- \`ready_for_calling\` (phone coverage \u226560%) \u2014 call session viable
|
|
871
|
+
- \`ready_for_emailing\` (email coverage \u226560%) \u2014 email session viable
|
|
872
|
+
- \`needs_enrichment\` (\u226530% no-contacts OR both phone+email coverage <40%) \u2014 enrichment recommended first
|
|
873
|
+
- \`travel_friendly\` (\u22655 geocoded leads AND coord coverage \u226560%) \u2014 map mode worth proposing
|
|
874
|
+
|
|
875
|
+
**One-line situation report** (always emit BEFORE the proposal):
|
|
876
|
+
|
|
877
|
+
\`\`\`
|
|
878
|
+
\u{1F4CB} <total_leads> leads \xB7 \u{1F4DE} <leads_with_phone> with a phone \xB7 \u2709 <leads_with_email> with an email \xB7 \u{1F5FA} <leads_with_coords> with coords \xB7 \u{1F534} <leads_without_contacts> need enrichment \xB7 \u2705 <leads_already_contacted> already touched
|
|
879
|
+
\`\`\`
|
|
880
|
+
|
|
881
|
+
**Then PROPOSE the right modes via \`ask_user_input_v0\`** (2-4 options, sorted by what makes the most sense for THIS campaign's data):
|
|
882
|
+
|
|
883
|
+
- "\u{1F4DE} Start calling now" \u2014 IF \`ready_for_calling\`. Top option when phones are there.
|
|
884
|
+
- "\u2709 Email session instead" \u2014 IF \`ready_for_emailing\` AND \`email_ratio > phone_ratio\`. Don't surface this when calling is more obvious.
|
|
885
|
+
- "\u{1F527} Enrich titles first" \u2014 IF \`needs_enrichment\`. Top option when most leads have no contacts. Phrase as "<N> leads have no reachable contact yet \u2014 enrich titles before we start?" so the user understands the cost.
|
|
886
|
+
- "\u{1F5FA} View on a map" \u2014 IF \`travel_friendly\` **AND** the user hasn't previously signaled disinterest in maps (check your conversation memory; if you've seen the user dismiss map renders before in this session or saved a "no maps" preference, drop this option).
|
|
887
|
+
|
|
888
|
+
If the MCP prompt argument \`mode\` was actually supplied, skip the proposal and jump to the matching mode below. If \`mode\` was omitted, do not treat \`call_sheet\` as implicit user consent \u2014 propose first.
|
|
889
|
+
|
|
890
|
+
# PHASE 2A \u2014 CALL-SHEET MODE (default after "\u{1F4DE} Start calling now")
|
|
891
|
+
|
|
892
|
+
Render per the \`leadbay_campaign_call_sheet\` RENDERING block \u2014 one CARD per lead with the 4-col contact table (Contact / Phone / Role / Recent). The phone in column 2 MUST be \`[bare](tel:URL)\` (use \`contact.phone_tel_url\` verbatim \u2014 the composite has already canonicalized it). The contact name in column 1 MUST be \`[Name](linkedin_url)\`. Email stacks under the name when present (\`\u2709 [email](mailto_url)\`). Recent stacks \`\u{1F4DD} last note\` + \`\u{1F4DE} last_action_headline\`.
|
|
893
|
+
|
|
894
|
+
End the turn with the standby line:
|
|
895
|
+
|
|
896
|
+
> Ready to start calling. Tell me what happened after each call \u2014 I'll record the note + outcome.
|
|
897
|
+
|
|
898
|
+
# PHASE 2B \u2014 EMAIL-SHEET MODE (after "\u2709 Email session instead")
|
|
899
|
+
|
|
900
|
+
Same data, slightly different render emphasis: drop the Phone column, put \`\u2709 [email](mailto_url)\` as column 2. Below each lead's table, generate a SUGGESTED short email draft per the next-step \u2014 but DON'T send. Drafts are for the user to copy-paste / send themselves.
|
|
901
|
+
|
|
902
|
+
# PHASE 2C \u2014 ENRICH-FIRST MODE (after "\u{1F527} Enrich titles first")
|
|
903
|
+
|
|
904
|
+
Extract \`leadIds\` from \`sheet.leads[].lead_id\`, then call \`leadbay_enrich_titles({leadIds, \u2026})\` (consult its description for titles / email / phone selection; do not pass \`campaign_id\`, because that is not part of the tool schema). Surface progress to the user. When complete, automatically loop back to Phase 1 (re-fetch the call sheet, re-assess readiness, re-propose).
|
|
905
|
+
|
|
906
|
+
# PHASE 2D \u2014 MAP MODE (after "\u{1F5FA} View on a map")
|
|
907
|
+
|
|
908
|
+
Pass \`response.map_locations\` directly to \`places_map_display_v0\` \u2014 the composite has already built the per-pin notes string with the top contact's phone inline. After the widget, emit the standard 4-col card list anyway so the rich detail is still scannable.
|
|
909
|
+
|
|
910
|
+
# PHASE 3 \u2014 RECORD OUTCOMES, ONE AT A TIME (after the user starts dictating)
|
|
911
|
+
|
|
912
|
+
When the user says something like *"Called Bree, voicemail, trying again Tuesday"* or *"Talked to John, wants pricing sent next week"*, parse:
|
|
913
|
+
|
|
914
|
+
1. **Which lead** \u2014 by company name OR contact name (cross-reference with the cards you just rendered).
|
|
915
|
+
2. **The note** \u2014 the user's exact words about what happened (the SDR's voice \u2014 don't paraphrase).
|
|
916
|
+
3. **The outcome** \u2014 pick ONE of these four epilogue values based on what the user said:
|
|
917
|
+
- \`STILL_CHASING\` \u2014 pursuing, no decision yet ("trying again", "they'll get back to me")
|
|
918
|
+
- \`COULD_NOT_REACH_STILL_TRYING\` \u2014 voicemail, no answer, wrong number, gatekeeper blocked
|
|
919
|
+
- \`INTEREST_VALIDATED_OR_MEETING_PLANED\` \u2014 meeting booked, quote requested, "send me more info"
|
|
920
|
+
- \`NOT_INTERESTED_LOST\` \u2014 declined, "not now", "not a fit", "remove from list"
|
|
921
|
+
|
|
922
|
+
Call \`leadbay_report_outreach({lead_id, note: <user's words>, epilogue_status: <picked>, verification: {source: "user_confirmed", ref: <user's exact words verbatim>}})\`. Confirm in ONE line: *"\u2705 Logged: <Company> \u2192 <epilogue>. Next?"*
|
|
923
|
+
|
|
924
|
+
Then wait for the next dictation. Don't ask "anything else?" \u2014 just acknowledge and wait.
|
|
925
|
+
|
|
926
|
+
# PHASE 4 \u2014 STOP
|
|
927
|
+
|
|
928
|
+
When the user says "done" / "that's it" / "wrapping up" / similar, surface a session summary chip:
|
|
929
|
+
|
|
930
|
+
> Session complete \u2014 N calls logged: X meetings booked \xB7 Y still chasing \xB7 Z couldn't reach \xB7 W declined.
|
|
931
|
+
|
|
932
|
+
Optional: offer to review the \`leadbay_campaign_progression\` for the same campaign to see the updated counts.
|
|
933
|
+
|
|
934
|
+
# Iron laws
|
|
935
|
+
|
|
936
|
+
- The \`verification\` field on \`leadbay_report_outreach\` is REQUIRED. For calls (no message id), always use \`{source: "user_confirmed", ref: <user's verbatim words>}\`. Skipping it is forbidden; fabricating a gmail_message_id for a call is forbidden.
|
|
937
|
+
- ONE call \u2192 ONE \`leadbay_report_outreach\` invocation. Don't batch; each call has its own note + outcome.
|
|
938
|
+
- Map mode is OPT-IN, never automatic. The user invokes it via the proposal options or by passing \`mode=map\`.
|
|
939
|
+
- If you've seen the user dismiss / dislike map renders earlier in the session, don't propose map mode again.
|
|
940
|
+
- If the user dictates an outcome that doesn't cleanly map to one of the four epilogue values, ASK ONCE before guessing.
|
|
941
|
+
`;
|
|
739
942
|
var PROMPT_META = {
|
|
740
943
|
leadbay_daily_check_in: { "name": "leadbay_daily_check_in", "short_description": `Run the canonical daily check-in: account state, fresh batch, triage
|
|
741
944
|
top 10, deep-dive every promising one, offer contact enrichment. The
|
|
@@ -755,6 +958,7 @@ anything that implies pre-existing pipeline context.
|
|
|
755
958
|
`, "arguments": [], "expected_calls": ["leadbay_pull_followups", "leadbay_research_lead_by_id", "leadbay_prepare_outreach"], "failure_modes": ["Calls leadbay_pull_leads (the Discover entry point) instead of leadbay_pull_followups \u2014 these are different data sources; the Discover queue does NOT contain Monitor's known-but-cold pipeline", 'Iterates pages of leadbay_pull_leads filtering by engagement_count to "fake" a follow-up view (a real bug observed in 0.9.0 \u2014 the right move is to call pull_followups directly)', "Replaces the canonical pull_followups table layout with prose per row (the per-tool RENDERING block is the structural contract; commentary belongs above or below)", 'Skips the cross-mode pivot offer at the end ("Want to see NEW leads from your wishlist instead?" routes to leadbay_pull_leads)'] },
|
|
756
959
|
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"] },
|
|
757
960
|
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_by_id.", "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"] },
|
|
961
|
+
leadbay_plan_tour_in_city: { "name": "leadbay_plan_tour_in_city", "short_description": "Plan a field sales tour: in one flow, surface follow-ups + fresh\nDiscover leads in the target city via `leadbay_tour_plan`, render\nto a map, draft in-area outreach via `leadbay_prepare_outreach`,\nand optionally persist the selected accounts as a named campaign\nvia `leadbay_create_campaign`. Closes #3630 US1 end-to-end.\n", "arguments": [{ "name": "city", "description": "City or region the user is visiting (e.g. 'Limoges', 'Bay Area'). Used as the geo filter for both Monitor and Discover lookups.", "required": true }, { "name": "date", "description": "When the visit is (e.g. 'May 24', 'next Thursday'). Surfaced in the outreach drafts as 'I'll be in <city> on <date>'.", "required": false }], "expected_calls": ["leadbay_tour_plan", "leadbay_research_lead_by_id", "leadbay_prepare_outreach", "leadbay_create_campaign"], "failure_modes": ["Calls leadbay_followups_map (Monitor-only) instead of leadbay_tour_plan \u2014 loses the Discover (fresh-lead) half that the user explicitly asked for", "Calls leadbay_pull_leads then drops the geo filter \u2014 returns the lens-wide wishlist instead of city-relevant fresh leads", 'Skips the campaign-persist step ("would you like to save these as a tour?") \u2014 leaves the rep with a one-shot map but no follow-up artifact', "Creates a campaign WITHOUT asking the user first \u2014 the persist step is high-intent; offer it, don't assume", "Fabricates lead_ids when seeding the campaign instead of using the ids returned by tour_plan"] },
|
|
758
962
|
leadbay_prospecting_overview: { "name": "leadbay_prospecting_overview", "short_description": `Orientation for working with Leadbay from any host \u2014 discovery vs.
|
|
759
963
|
follow-up, the outreach loop, outcome recording, imports, pushback /
|
|
760
964
|
snooze, and the connected-outreach-tool registry. Trigger when the
|
|
@@ -764,7 +968,9 @@ should I follow up on" to "I'll send via lemlist".
|
|
|
764
968
|
`, "arguments": [], "expected_calls": ["leadbay_account_status", "leadbay_pull_leads", "leadbay_pull_followups", "leadbay_research_lead_by_id", "leadbay_research_lead_by_name_fuzzy", "leadbay_prepare_outreach", "leadbay_report_outreach", "leadbay_set_pushback", "leadbay_remove_pushback", "leadbay_bulk_qualify_leads", "leadbay_enrich_titles", "leadbay_import_leads", "leadbay_add_note", "leadbay_adjust_audience"], "failure_modes": ['Drives outreach without asking the user "how did it go?" afterwards \u2014 leaving prospecting_actions and epilogue_status stale', 'Says "epilogue" in user-facing dialogue instead of "outcome"', 'Says "Monitor" in user-facing dialogue instead of "follow-ups"', 'Treats a "not now / next quarter" reply as a note instead of routing through the pushback mechanism', "Drafts outreach in a generic format when the user has a connected sequencer (lemlist, Outreach.io, etc.) that has its own idiom", "Re-pulls leads without passing the captured lensId, allowing a backend lens shift to discard prior work", "Skips the STOP byproduct in any multi-step workflow it triggers", "Calls leadbay_pull_leads (Discover wishlist) for a follow-up query, or leadbay_pull_followups (Monitor view) for a discovery query \u2014 the two entry points read from different backend tables; the right orchestrators are leadbay_daily_check_in (discovery) and leadbay_followup_check_in (follow-up)"] },
|
|
765
969
|
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_qualify_status", "leadbay_pull_leads", "leadbay_research_lead_by_id"], "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", 'Replaces the canonical pull_leads table with prose when rendering the newly-qualified batch (the per-tool RENDERING block is the structural contract; "standouts" commentary sits above it)', "Expands the qualify-status sentence into a card or table instead of the one-line status-inline render"] },
|
|
766
970
|
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"] },
|
|
767
|
-
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_by_id"], "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", "Renders the research_lead_by_id result as a freeform narrative instead of the canonical research-company-card layout (the card with header score bar, pill row, signal sections, contacts table is the structural contract; commentary belongs ABOVE or BELOW it)", "Enumerates every imported lead in prose instead of the terse single-record summary from the import-result rendering snippet"] }
|
|
971
|
+
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_by_id"], "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", "Renders the research_lead_by_id result as a freeform narrative instead of the canonical research-company-card layout (the card with header score bar, pill row, signal sections, contacts table is the structural contract; commentary belongs ABOVE or BELOW it)", "Enumerates every imported lead in prose instead of the terse single-record summary from the import-result rendering snippet"] },
|
|
972
|
+
leadbay_setup_team_prospecting: { "name": "leadbay_setup_team_prospecting", "short_description": "Manager-led prospecting setup: conversationally turn a natural-language\naudience ask into a Leadbay lens, validate the candidate leads, and\npersist them as one or more named campaigns the rep(s) can work\nthrough. Closes #3630 US3 end-to-end (within the current\ncreator-scoped campaign visibility model).\n", "arguments": [{ "name": "audience", "description": "Natural-language audience description (e.g. 'plumbing companies with 10-50 employees in Seine-Maritime'). The lens-creation step (`leadbay_refine_prompt` \u2192 `leadbay_create_lens`) interprets it.", "required": true }, { "name": "rep_split", "description": "Optional: how to split the validated leads into per-rep campaigns. Free text \u2014 e.g. 'split by city' or 'one campaign per rep: John gets Tulsa, Sarah gets OKC'.", "required": false }], "expected_calls": ["leadbay_refine_prompt", "leadbay_create_lens", "leadbay_promote_lens", "leadbay_pull_leads", "leadbay_research_lead_by_id", "leadbay_create_campaign", "leadbay_add_leads_to_campaign"], "failure_modes": ["Skips the validation step \u2014 creates a campaign of unvetted leads from a freshly-created lens without giving the manager a chance to drop weak fits", "Creates ONE campaign for all reps without asking about the split \u2014 the user explicitly mentioned per-rep distribution and the prompt should honor it", "Pretends the backend supports cross-user assignment \u2014 campaigns are owned by the caller (creator-scoped). Surface this honestly instead of fabricating an assignment model", "Asks ALL clarifying questions inline before tool calls \u2014 instead, run the lens refinement loop with `leadbay_refine_prompt` which handles the clarification protocol natively"] },
|
|
973
|
+
leadbay_work_campaign: { "name": "leadbay_work_campaign", "short_description": "Work a campaign as a real outreach session: pick the campaign,\nassess what the user has (phones / emails / coords), then PROPOSE\nthe right session mode (call sheet, email sheet, enrich titles\nfirst, map). After they pick, render \u2014 and as they dictate\noutcomes per lead, record both note + epilogue via\n`leadbay_report_outreach` in one round trip.\n", "arguments": [{ "name": "campaign", "description": "Campaign name (fuzzy match against your own campaigns) or campaign UUID. Omit to list and pick interactively.", "required": false }, { "name": "mode", "description": "Optional: skip the readiness-assessment proposal and jump directly into 'call_sheet' / 'email_sheet' / 'map' / 'enrich_first'. Omit (recommended) and let the prompt propose based on the data.", "required": false }], "expected_calls": ["leadbay_list_campaigns", "leadbay_campaign_call_sheet", "leadbay_enrich_titles", "leadbay_report_outreach"], "failure_modes": ["Renders the call sheet immediately without proposing the right mode \u2014 if 60% of leads have no contacts, calling is futile; enrich first. Always assess `readiness` first.", "Auto-renders the map widget without asking \u2014 maps are intrusive when the user just wants to scroll a list. Map mode is a proposed option, not a default.", "Proposes map mode after the user has previously said they don't like maps \u2014 check conversation memory before adding 'View on a map' to the options list.", "Calls `leadbay_campaign_progression` instead of `leadbay_campaign_call_sheet` \u2014 progression has counts but no phones / LinkedIn / call-ready data; the user can't actually dial from progression rows.", "Renders contacts WITHOUT making the phone number a `[bare](tel:URL)` link \u2014 on mobile that breaks one-tap calling, which is the whole point of the cheat sheet.", "Records outreach WITHOUT epilogue_status \u2014 leaves the lead's pipeline state unchanged; the rep then sees the same lead surfaced again next session.", "Records outreach WITHOUT verification \u2014 verification.source/ref is REQUIRED. For calls, pass `{source: 'user_confirmed', ref: <user's exact words>}`.", "Loops through ALL leads in a 50-lead campaign before recording any outreach \u2014 the call-then-record loop must be per-lead, not batched."] }
|
|
768
974
|
};
|
|
769
975
|
var PROMPT_CATALOG_HEADER = `This server exposes the following workflow prompts via \`prompts/list\` and \`prompts/get\`. Some MCP clients render them as slash commands; if your client does not, you (the agent) should invoke them directly via \`prompts/get\` when the user's request matches one of the triggers described below.`;
|
|
770
976
|
var PROMPT_CATALOG_BULLETS = {
|
|
@@ -772,10 +978,13 @@ var PROMPT_CATALOG_BULLETS = {
|
|
|
772
978
|
leadbay_followup_check_in: `- \`leadbay_followup_check_in\`: Run the canonical follow-up check-in: surface KNOWN leads from the Monitor view that need re-engagement today, ranked by AI urgency, with the canonical pull_followups table layout. Trigger when the user asks "follow up", "already known leads", "leads I haven't contacted", "leads in [city]", "before my trip", "this week", "this month", "what's overdue", "who should I re-engage", or anything that implies pre-existing pipeline context.`,
|
|
773
979
|
leadbay_import_file: `- \`leadbay_import_file\` (optional args: file, instruction): Import a user-supplied CSV/file into Leadbay through five phases with evidence gates \u2014 scan, derive, resolve identities, preserve & commit, then optionally qualify and report. The job is to maximize how many rows the Leadbay system actually ingests and matches.`,
|
|
774
980
|
leadbay_log_outreach: `- \`leadbay_log_outreach\` (required args: lead_id, summary): Log outreach (an email I sent, a call I made, a meeting I had) on a specific lead. Captures verification so the SDR pipeline trusts the entry.`,
|
|
981
|
+
leadbay_plan_tour_in_city: `- \`leadbay_plan_tour_in_city\` (required args: city; optional args: date): Plan a field sales tour: in one flow, surface follow-ups + fresh Discover leads in the target city via \`leadbay_tour_plan\`, render to a map, draft in-area outreach via \`leadbay_prepare_outreach\`, and optionally persist the selected accounts as a named campaign via \`leadbay_create_campaign\`. Closes #3630 US1 end-to-end.`,
|
|
775
982
|
leadbay_prospecting_overview: `- \`leadbay_prospecting_overview\`: Orientation for working with Leadbay from any host \u2014 discovery vs. follow-up, the outreach loop, outcome recording, imports, pushback / snooze, and the connected-outreach-tool registry. Trigger when the conversation involves Leadbay leads, prospecting, pipeline, follow-up, outreach, or lens / ICP \u2014 anything from "show me my leads" to "what should I follow up on" to "I'll send via lemlist".`,
|
|
776
983
|
leadbay_qualify_top_n: `- \`leadbay_qualify_top_n\` (optional args: count): Bulk-qualify the top N un-qualified leads in the active lens. Uses leadbay_bulk_qualify_leads with a sensible default budget.`,
|
|
777
984
|
leadbay_refine_audience: `- \`leadbay_refine_audience\` (required args: instruction): Refine the kind of leads Leadbay surfaces beyond firmographics, with a free-text instruction. Handles the clarification round-trip if the new prompt is ambiguous.`,
|
|
778
|
-
leadbay_research_a_domain: `- \`leadbay_research_a_domain\` (required args: domain): Import a company by domain and run deep qualification + research in one pass. Use when a colleague mentions a name and you want everything Leadbay knows about it
|
|
985
|
+
leadbay_research_a_domain: `- \`leadbay_research_a_domain\` (required args: domain): Import a company by domain and run deep qualification + research in one pass. Use when a colleague mentions a name and you want everything Leadbay knows about it.`,
|
|
986
|
+
leadbay_setup_team_prospecting: `- \`leadbay_setup_team_prospecting\` (required args: audience; optional args: rep_split): Manager-led prospecting setup: conversationally turn a natural-language audience ask into a Leadbay lens, validate the candidate leads, and persist them as one or more named campaigns the rep(s) can work through. Closes #3630 US3 end-to-end (within the current creator-scoped campaign visibility model).`,
|
|
987
|
+
leadbay_work_campaign: `- \`leadbay_work_campaign\` (optional args: campaign, mode): Work a campaign as a real outreach session: pick the campaign, assess what the user has (phones / emails / coords), then PROPOSE the right session mode (call sheet, email sheet, enrich titles first, map). After they pick, render \u2014 and as they dictate outcomes per lead, record both note + epilogue via \`leadbay_report_outreach\` in one round trip.`
|
|
779
988
|
};
|
|
780
989
|
|
|
781
990
|
// src/prompts.ts
|
|
@@ -886,6 +1095,80 @@ var CATALOG = [
|
|
|
886
1095
|
)
|
|
887
1096
|
]
|
|
888
1097
|
},
|
|
1098
|
+
{
|
|
1099
|
+
name: "leadbay_plan_tour_in_city",
|
|
1100
|
+
description: PROMPT_META.leadbay_plan_tour_in_city.short_description,
|
|
1101
|
+
arguments: [
|
|
1102
|
+
{
|
|
1103
|
+
name: "city",
|
|
1104
|
+
description: "City or region the user is visiting (e.g. 'Limoges', 'Bay Area'). Used as the geo filter for both Monitor and Discover lookups.",
|
|
1105
|
+
required: true
|
|
1106
|
+
},
|
|
1107
|
+
{
|
|
1108
|
+
name: "date",
|
|
1109
|
+
description: "When the visit is (e.g. 'May 24', 'next Thursday'). Surfaced in the outreach drafts as 'I'll be in <city> on <date>'.",
|
|
1110
|
+
required: false
|
|
1111
|
+
}
|
|
1112
|
+
],
|
|
1113
|
+
render: (args) => [
|
|
1114
|
+
userMessage(
|
|
1115
|
+
substitutePlaceholders(leadbay_plan_tour_in_city, {
|
|
1116
|
+
city: args.city ?? "<missing>",
|
|
1117
|
+
date_paren: args.date ? ` on ${args.date}` : "",
|
|
1118
|
+
date_dash: args.date ? ` \u2013 ${args.date}` : ""
|
|
1119
|
+
})
|
|
1120
|
+
)
|
|
1121
|
+
]
|
|
1122
|
+
},
|
|
1123
|
+
{
|
|
1124
|
+
name: "leadbay_setup_team_prospecting",
|
|
1125
|
+
description: PROMPT_META.leadbay_setup_team_prospecting.short_description,
|
|
1126
|
+
arguments: [
|
|
1127
|
+
{
|
|
1128
|
+
name: "audience",
|
|
1129
|
+
description: "Natural-language audience description (e.g. 'plumbing companies with 10-50 employees in Seine-Maritime').",
|
|
1130
|
+
required: true
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
name: "rep_split",
|
|
1134
|
+
description: "Optional: how to split validated leads into per-rep campaigns. Free text (e.g. 'split by city', 'one campaign per rep').",
|
|
1135
|
+
required: false
|
|
1136
|
+
}
|
|
1137
|
+
],
|
|
1138
|
+
render: (args) => [
|
|
1139
|
+
userMessage(
|
|
1140
|
+
substitutePlaceholders(leadbay_setup_team_prospecting, {
|
|
1141
|
+
audience: args.audience ?? "<missing>",
|
|
1142
|
+
rep_split_block: args.rep_split ? `Rep split preference: **${args.rep_split}**
|
|
1143
|
+
` : ""
|
|
1144
|
+
})
|
|
1145
|
+
)
|
|
1146
|
+
]
|
|
1147
|
+
},
|
|
1148
|
+
{
|
|
1149
|
+
name: "leadbay_work_campaign",
|
|
1150
|
+
description: PROMPT_META.leadbay_work_campaign.short_description,
|
|
1151
|
+
arguments: [
|
|
1152
|
+
{
|
|
1153
|
+
name: "campaign",
|
|
1154
|
+
description: "Campaign name (fuzzy match) or campaign UUID. Omit to list and pick interactively.",
|
|
1155
|
+
required: false
|
|
1156
|
+
},
|
|
1157
|
+
{
|
|
1158
|
+
name: "mode",
|
|
1159
|
+
description: "Optional: skip readiness proposal and jump to 'call_sheet', 'email_sheet', 'map', or 'enrich_first'. Omit to let the prompt propose based on campaign data.",
|
|
1160
|
+
required: false
|
|
1161
|
+
}
|
|
1162
|
+
],
|
|
1163
|
+
render: (args) => [
|
|
1164
|
+
userMessage(
|
|
1165
|
+
substitutePlaceholders(leadbay_work_campaign, {
|
|
1166
|
+
campaign_or_default: args.campaign ?? "<pick from the list>",
|
|
1167
|
+
mode_paren: args.mode ? ` (mode: ${args.mode})` : ""
|
|
1168
|
+
})
|
|
1169
|
+
)
|
|
1170
|
+
]
|
|
1171
|
+
},
|
|
889
1172
|
{
|
|
890
1173
|
name: "leadbay_qualify_top_n",
|
|
891
1174
|
description: PROMPT_META.leadbay_qualify_top_n.short_description,
|
|
@@ -2259,7 +2542,7 @@ async function createDefaultUpdateStateStore(opts = {}) {
|
|
|
2259
2542
|
|
|
2260
2543
|
// src/bin.ts
|
|
2261
2544
|
import { createRequire } from "module";
|
|
2262
|
-
var VERSION = "0.
|
|
2545
|
+
var VERSION = "0.12.1";
|
|
2263
2546
|
var HELP = `
|
|
2264
2547
|
leadbay-mcp ${VERSION} \u2014 Leadbay Model Context Protocol server
|
|
2265
2548
|
|
|
@@ -2308,7 +2591,7 @@ EXAMPLE Claude Desktop config (~/Library/Application Support/Claude/claude_deskt
|
|
|
2308
2591
|
"mcpServers": {
|
|
2309
2592
|
"leadbay": {
|
|
2310
2593
|
"command": "npx",
|
|
2311
|
-
"args": ["-y", "@leadbay/mcp@0.
|
|
2594
|
+
"args": ["-y", "@leadbay/mcp@0.12"],
|
|
2312
2595
|
"env": {
|
|
2313
2596
|
"LEADBAY_TOKEN": "lb_...",
|
|
2314
2597
|
"LEADBAY_REGION": "us",
|
|
@@ -2609,7 +2892,7 @@ async function runLogin(args) {
|
|
|
2609
2892
|
let result;
|
|
2610
2893
|
try {
|
|
2611
2894
|
if (pinnedRegion && !allowFallback) {
|
|
2612
|
-
const { REGIONS } = await import("./dist-
|
|
2895
|
+
const { REGIONS } = await import("./dist-YYVFSDMH.js");
|
|
2613
2896
|
const baseUrl = REGIONS[pinnedRegion];
|
|
2614
2897
|
const c = createClient({ region: pinnedRegion });
|
|
2615
2898
|
const token = await loginAt(baseUrl, email, password);
|
|
@@ -2628,7 +2911,7 @@ async function runLogin(args) {
|
|
|
2628
2911
|
mcpServers: {
|
|
2629
2912
|
leadbay: {
|
|
2630
2913
|
command: "npx",
|
|
2631
|
-
args: ["-y", "@leadbay/mcp@0.
|
|
2914
|
+
args: ["-y", "@leadbay/mcp@0.12"],
|
|
2632
2915
|
env: {
|
|
2633
2916
|
LEADBAY_TOKEN: result.token,
|
|
2634
2917
|
LEADBAY_REGION: result.region
|
|
@@ -2668,7 +2951,7 @@ Or for Claude Code (token included \u2014 same warning applies):
|
|
|
2668
2951
|
claude mcp add leadbay --scope user \\
|
|
2669
2952
|
--env LEADBAY_TOKEN=${result.token} \\
|
|
2670
2953
|
--env LEADBAY_REGION=${result.region} \\
|
|
2671
|
-
-- npx -y @leadbay/mcp@0.
|
|
2954
|
+
-- npx -y @leadbay/mcp@0.12
|
|
2672
2955
|
|
|
2673
2956
|
Restart your MCP client to pick up the new server.
|
|
2674
2957
|
`
|
|
@@ -2774,7 +3057,7 @@ For Claude Code, run:
|
|
|
2774
3057
|
claude mcp add leadbay --scope user \\
|
|
2775
3058
|
--env LEADBAY_TOKEN=$(jq -r .mcpServers.leadbay.env.LEADBAY_TOKEN ${quotedPath}) \\
|
|
2776
3059
|
--env LEADBAY_REGION=${result.region} \\
|
|
2777
|
-
-- npx -y @leadbay/mcp@0.
|
|
3060
|
+
-- npx -y @leadbay/mcp@0.12
|
|
2778
3061
|
`
|
|
2779
3062
|
);
|
|
2780
3063
|
}
|
|
@@ -2954,7 +3237,7 @@ function buildClaudeCodeAddArgs(token, region, includeWrite, telemetryEnabled) {
|
|
|
2954
3237
|
`LEADBAY_TELEMETRY_ENABLED=${telemetryEnabled ? "true" : "false"}`
|
|
2955
3238
|
];
|
|
2956
3239
|
if (!includeWrite) args.push("--env", `LEADBAY_MCP_WRITE=0`);
|
|
2957
|
-
args.push("--", "npx", "-y", "@leadbay/mcp@0.
|
|
3240
|
+
args.push("--", "npx", "-y", "@leadbay/mcp@0.12");
|
|
2958
3241
|
return args;
|
|
2959
3242
|
}
|
|
2960
3243
|
async function installInClaudeCode(token, region, includeWrite, telemetryEnabled) {
|
|
@@ -3004,7 +3287,7 @@ async function installInJsonConfig(configPath, token, region, includeWrite, tele
|
|
|
3004
3287
|
if (!includeWrite) env.LEADBAY_MCP_WRITE = "0";
|
|
3005
3288
|
parsed.mcpServers.leadbay = {
|
|
3006
3289
|
command: "npx",
|
|
3007
|
-
args: ["-y", "@leadbay/mcp@0.
|
|
3290
|
+
args: ["-y", "@leadbay/mcp@0.12"],
|
|
3008
3291
|
env
|
|
3009
3292
|
};
|
|
3010
3293
|
const tmp = configPath + ".tmp";
|
|
@@ -3108,7 +3391,7 @@ leadbay-mcp install \u2014 detected MCP clients on this machine:
|
|
|
3108
3391
|
let region;
|
|
3109
3392
|
try {
|
|
3110
3393
|
if (pinnedRegion && !allowFallback) {
|
|
3111
|
-
const { REGIONS } = await import("./dist-
|
|
3394
|
+
const { REGIONS } = await import("./dist-YYVFSDMH.js");
|
|
3112
3395
|
const baseUrl = REGIONS[pinnedRegion];
|
|
3113
3396
|
token = await loginAt(baseUrl, email, password);
|
|
3114
3397
|
region = pinnedRegion;
|
|
@@ -3181,7 +3464,7 @@ leadbay-mcp install \u2014 detected MCP clients on this machine:
|
|
|
3181
3464
|
process.stderr.write(
|
|
3182
3465
|
`
|
|
3183
3466
|
The token was written into client config files but never printed to your terminal.
|
|
3184
|
-
Verify with: LEADBAY_TOKEN=$(...) npx -y @leadbay/mcp@0.
|
|
3467
|
+
Verify with: LEADBAY_TOKEN=$(...) npx -y @leadbay/mcp@0.12 doctor
|
|
3185
3468
|
Restart your MCP client(s) to pick up the new server.
|
|
3186
3469
|
If you ever leak the token, run \`leadbay-mcp login --email <you> --region <us|fr>\` to mint a fresh one (which invalidates the prior session).
|
|
3187
3470
|
`
|