Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@

> **New to Leadbay?** [Create your account here](https://wow.leadbay.ai/?register=true) before installing the plugin.

## How Leadbay thinks (mental model for your agent)

- **Inbox, not a database.** Each day your user logs back in, a fresh batch of leads is delivered. Batch size is paced by how many leads the user has actually acted on recently — some workflows produce a big stream of smaller prospects, others a narrow stream of bigger ones. Pulling more won't produce more; acting on leads does.
- **Two scoring layers.** Every lead ships with a basic `score` (firmographic — already decent, usually correlates with AI). Roughly the top 10 of each batch are also AI-qualified (targeted web research + qualification questions → `ai_agent_lead_score`). Leads below the top 10 aren't worse — the system is saving resources. The agent can request deeper qualification (`leadbay_bulk_qualify_leads`) or contact enrichment (`leadbay_enrich_titles`) on any lead that looks worth it.
- **Daily rhythm.** The agent works best as a daily check-in: pull fresh leads, skim the auto-qualified top, deepen 1-3 promising ones, propose outreach, then log what actually got sent via `leadbay_report_outreach`. If your host supports scheduling, set up a daily run.

## Install

```bash
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/composite/account-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ export const accountStatus: Tool<Record<string, never>> = {
description:
"Show the user's account state — admin rights, language, last-active lens, current quota usage across " +
"daily/weekly/monthly windows for llm_completion / ai_rescore / web_fetch resources, and whether the org's " +
"intelligence is mid-regeneration. " +
"intelligence is mid-regeneration. Quota windows also hint at the user's consumption pace: heavy recent " +
"activity (ai_rescore / web_fetch near their window limits) is a signal that Leadbay will deliver a larger " +
"fresh batch next time the user logs back in, since batch size is paced by real consumption. " +
"When to use: at the start of a session to know what the agent can/can't do, or after a 429 to explain to " +
"the user which resource window was exhausted and when it resets. " +
"When NOT to use: as a pre-flight gate before bulk ops — operations themselves return 429; this tool is " +
Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/composite/bulk-qualify-leads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ export const bulkQualifyLeads: Tool<BulkQualifyLeadsParams> = {
"until the answers are populated or a budget is exhausted. Already-qualified leads (those with a non-null " +
"ai_agent_lead_score) are silently no-ops on the backend, so this composite paginates past them to find " +
"fresh candidates. On 429 mid-fanout, stops launching but keeps polling already-launched leads. " +
"When to use: when the user wants more qualified leads than what's currently shown. " +
"Context: Leadbay auto-qualifies roughly the top 10 of each daily batch. Leads below the top ~10 are NOT " +
"worse — the system is saving resources. This tool is how the agent spends more resources to go deeper on " +
"promising-looking leads the user hasn't had time to surface yet. " +
"When to use: when the user wants more qualified leads than what's currently shown, or when a lead looks " +
"promising in leadbay_pull_leads but has an empty qualification_summary. " +
"When NOT to use: to qualify a single specific lead — that's leadbay_qualify_lead (granular, advanced).",
inputSchema: {
type: "object",
Expand Down
9 changes: 6 additions & 3 deletions packages/core/src/composite/enrich-titles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,17 @@ const DEFAULT_CANDIDATE_COUNT = 25;
export const enrichTitles: Tool<EnrichTitlesParams> = {
name: "leadbay_enrich_titles",
description:
"Order contact enrichments by job title across many leads. Two modes: " +
"Order contact enrichments by job title across many leads. Contacts are NOT returned by default with a lead " +
"(Leadbay keeps enrichment out-of-band to control cost); the agent requests them on demand via this tool when " +
"it's ready to actually reach out. Two modes: " +
"(A) NO titles param — returns the available titles + Leadbay's title_suggestions + auto_included_titles " +
"+ a count of enrichable contacts, so the agent can ask the user which titles to enrich. " +
"(B) titles given — calls preview, then launches if there's anything enrichable. " +
"On 429 returns {status:'quota_exceeded'} cleanly. Selection lifecycle is wrapped in a try/finally so the " +
"user's selection is left clean even on error. " +
"When to use: as the agent's go-to enrichment entry point. " +
"When NOT to use: to enrich a single contact — that's leadbay_enrich_contacts (granular).",
"When to use: as the agent's go-to enrichment entry point, immediately before proposing outreach. " +
"When NOT to use: to enrich a single contact — that's leadbay_enrich_contacts (granular). " +
"Speculatively, before the user has committed to outreaching — enrichment spends credits.",
inputSchema: {
type: "object",
properties: {
Expand Down
10 changes: 8 additions & 2 deletions packages/core/src/composite/pull-leads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,16 @@ function summarise(responses: AiAgentResponse[]): QualificationSummary {
export const pullLeads: Tool<PullLeadsParams> = {
name: "leadbay_pull_leads",
description:
"Pull up new leads from the user's last-active lens (the canonical 'show me prospects to work on' tool). " +
"Pull up new leads from the user's last-active lens — the canonical 'show me today's prospects' tool. " +
"Leadbay works like an inbox: each time the user logs back in, a fresh batch is delivered, paced by how " +
"many leads they've actually acted on recently. Pulling more won't produce more; user outreach/skips/saves does. " +
"Each returned lead carries a one-line qualification_summary built from leadbay_ai_agent_responses, plus " +
"the rich tags / scores / recommended_contact_title / engagement counters / in-flight flags from the lead summary. " +
"When to use: as the agent's default opening move when the user wants to see leads. " +
"Roughly the top 10 of the batch come pre-qualified (populated qualification_summary + ai_agent_lead_score); " +
"leads below the top ~10 carry only the basic firmographic `score` — not worse, just resource-saved by the system. " +
"Call leadbay_bulk_qualify_leads to deepen any of them on demand. " +
"When to use: as the agent's default opening move when the user wants to see leads, or as a daily check-in " +
"for what's new today. " +
"When NOT to use: when the user has named a specific lens — pass lensId to override the auto-resolution. " +
"Replaces the older leadbay_find_prospects (which is removed in v0.2.0).",
inputSchema: {
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/composite/research-lead.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ export const researchLead: Tool<ResearchLeadParams> = {
"qualification answers (the agent's knowledge-base food), the structured web-research signals (with hot " +
"flags + sources), the enriched contacts, and the recent notes/epilogue/prospecting activity in one call. " +
"Order is deliberate: qualification first, then signals, then firmographics, then contacts, then engagement. " +
"Scoring has two layers: the basic `score` (firmographic, always present, already decent) and the AI " +
"qualification layer (`ai_agent_lead_score` + per-question answers + web_fetch signals). The AI layer is " +
"pre-populated for roughly the top 10 of each daily batch, and on-demand (via leadbay_bulk_qualify_leads) " +
"for anything below that. Combine both layers when judging a lead. " +
"When to use: when picking up a single lead from leadbay_pull_leads to decide whether to act on it. " +
"When NOT to use: across many leads at once — that's leadbay_pull_leads' job. " +
"(This composite supersedes the lower-level leadbay_get_lead_profile in agent flow; the granular tool stays " +
Expand Down
8 changes: 8 additions & 0 deletions packages/leadclaw/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog — @leadbay/leadclaw

## 0.2.2 — 2026-04-21

Docs-only release. Shares the `@leadbay/mcp@0.2.2` mental-model copy updates since both packages consume the same `@leadbay/core` tool descriptions.

- Plugin manifest description rewritten from "Leadbay lead discovery, qualification, and contact enrichment for AI agents" to a framing that names the inbox model, the two scoring layers, and on-demand deepening.
- Composite tool descriptions (`pull_leads`, `research_lead`, `bulk_qualify_leads`, `enrich_titles`, `account_status`) now teach the agent that Leadbay delivers a fresh batch per user login, paced by recent consumption; that roughly the top 10 are pre-AI-qualified while the rest are resource-saved (not worse); and that contacts are enriched on demand when the agent is ready to reach out.
- No new tools, no schema changes. Version kept in sync with `@leadbay/mcp@0.2.2`.

## 0.2.1 — 2026-04-21

No functional changes. First release shipping with signed sigstore provenance (repo is now public, which unblocks npm's provenance gate). Version kept in sync with `@leadbay/mcp@0.2.1`.
Expand Down
4 changes: 2 additions & 2 deletions packages/leadclaw/openclaw.plugin.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"id": "leadclaw",
"name": "LeadClaw",
"description": "Leadbay lead discovery, qualification, and contact enrichment for AI agents",
"version": "0.2.1",
"description": "Leadbay for AI agents: a daily sales-lead inbox with firmographic + AI qualification layers, plus on-demand deeper qualification and contact enrichment. Each login delivers a fresh batch of leads paced by the user's recent consumption; the agent skims, deepens promising ones, and proposes outreach.",
"version": "0.2.2",
"contracts": {
"tools": [
"leadbay_login",
Expand Down
2 changes: 1 addition & 1 deletion packages/leadclaw/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@leadbay/leadclaw",
"version": "0.2.1",
"version": "0.2.2",
"description": "OpenClaw plugin for Leadbay — AI lead discovery, qualification, and enrichment",
"type": "module",
"main": "dist/index.js",
Expand Down
9 changes: 9 additions & 0 deletions packages/mcp/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog — @leadbay/mcp

## 0.2.2 — 2026-04-21

Docs-only release. Teach the agent the Leadbay mental model — agents were calling `pull_leads` like a generic query rather than as a daily inbox, missing that leads past the top ~10 still exist and can be deepened on demand.

- `SERVER_INSTRUCTIONS` gains three new paragraphs: "How Leadbay works" (inbox + consumption-based pacing), "Two scoring layers" (basic `score` vs AI-qualified top ~10 with `ai_agent_lead_score`), and "Suggested rhythm" (daily check-in + host-agnostic scheduling hint).
- `leadbay_pull_leads`, `leadbay_research_lead`, `leadbay_bulk_qualify_leads`, `leadbay_enrich_titles`, and `leadbay_account_status` descriptions updated to reinforce the same model so the agent sees it at both top level and per-tool.
- New non-regression test asserts the inbox/pace/scoring/daily language stays in `SERVER_INSTRUCTIONS`.
- No schema changes, no tool-shape changes — wire-compatible with 0.2.1.

## 0.2.1 — 2026-04-21

Docs-only release.
Expand Down
2 changes: 1 addition & 1 deletion packages/mcp/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@leadbay/mcp",
"version": "0.2.1",
"version": "0.2.2",
"description": "Model Context Protocol (MCP) server for Leadbay — AI lead discovery, qualification, and enrichment for Claude Desktop, Cursor, and Claude Code.",
"type": "module",
"bin": {
Expand Down
20 changes: 17 additions & 3 deletions packages/mcp/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,32 @@ import {
type ToolLogger,
} from "@leadbay/core";

// Lead-paragraph mandate is the first thing the agent reads — followed by
// three sentences, then per-tool descriptions carry the runbook detail.
// Verification mandate stays first (test asserts report_outreach appears in
// the first 200 chars). Then the mental-model paragraphs teach the agent
// what Leadbay is (an inbox, not a DB), the two scoring layers, the
// start-here sequence, and a recommended daily rhythm.
export const SERVER_INSTRUCTIONS =
"After every email, call, message, or meeting with a lead's contact, you MUST call leadbay_report_outreach " +
"with verification={source, ref} (gmail_message_id from the Gmail send, calendar_event_id from a booking, " +
"or user_confirmed='<the user's literal confirmation>'). Skipping or fabricating verification poisons the " +
"human team's pipeline.\n\n" +
"How Leadbay works (mental model): Leadbay is a sales inbox, not a queryable database. Each day the user " +
"logs back in, a fresh batch of leads is delivered. Batch size is paced by how many leads the user has " +
"actually acted on recently — some workflows produce a big stream of smaller prospects, others a narrow " +
"stream of bigger ones. Pulling more won't produce more; the user acting on leads (outreach, skips, saves) does.\n\n" +
"Two scoring layers: every lead has a basic `score` (firmographic — already decent, usually correlates " +
"with AI). Roughly the top 10 of each batch are also AI-qualified (targeted web research + qualification " +
"questions → `ai_agent_lead_score`, surfaced as `qualification_summary` on leadbay_pull_leads). Leads past " +
"the top ~10 are not worse — the system is saving resources. Call leadbay_bulk_qualify_leads for deeper " +
"qualification or leadbay_enrich_titles for contacts on any lead that looks worth it.\n\n" +
"Start with leadbay_account_status to see the user's state, then leadbay_pull_leads to surface fresh leads. " +
"Use leadbay_research_lead to dig into one lead deeply (qualification answers, signals, contacts). " +
"When the user wants more leads, narrower audience, refined criteria, or contact enrichment, use the matching " +
"composite tool (bulk_qualify_leads / adjust_audience / refine_prompt / enrich_titles) — they hide lens " +
"permissions, region routing, polling, and selection state from you.";
"permissions, region routing, polling, and selection state from you.\n\n" +
"Suggested rhythm: a healthy agent pattern is a daily check-in — pull fresh leads, skim the auto-qualified " +
"top, deepen 1-3 promising ones, propose outreach to the user, then leadbay_report_outreach on what actually " +
"got sent. If your host supports scheduling, offer to set up a daily run.";

interface BuildServerOptions {
includeAdvanced?: boolean;
Expand Down
16 changes: 16 additions & 0 deletions packages/mcp/test/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,22 @@ describe("server.instructions — LLM guidance string", () => {
expect(SERVER_INSTRUCTIONS).toMatch(/leadbay_research_lead/);
expect(SERVER_INSTRUCTIONS).toMatch(/leadbay_account_status/);
});

it("server instructions teach the inbox + pace + scoring mental model", async () => {
const { SERVER_INSTRUCTIONS } = await import("../src/server.js");
// Inbox framing — the daily-cadence anchor.
expect(SERVER_INSTRUCTIONS).toMatch(/inbox/i);
// Consumption-based pacing.
expect(SERVER_INSTRUCTIONS).toMatch(/paced|pace/i);
// Two scoring layers with concrete field names.
expect(SERVER_INSTRUCTIONS).toMatch(/two scoring layers/i);
expect(SERVER_INSTRUCTIONS).toMatch(/ai_agent_lead_score/);
// Daily-rhythm recommendation.
expect(SERVER_INSTRUCTIONS).toMatch(/daily|each day/i);
// Points to the on-demand deepening tools.
expect(SERVER_INSTRUCTIONS).toMatch(/leadbay_bulk_qualify_leads/);
expect(SERVER_INSTRUCTIONS).toMatch(/leadbay_enrich_titles/);
});
});

describe("resolveClientFromEnv — region auto-probe", () => {
Expand Down
Loading