Skip to content

Commit b4feaf5

Browse files
milstanclaude
andauthored
fix(mcp,core): #3504 silent npx exit, dead URL hints, misleading score contracts (#15)
Three failure modes a user hits before they get any work done, fixed in one pass. 1. #3504 — `npx -y @leadbay/mcp` silently exits 0 on Node 25. The `isEntrypoint` check compared `process.argv[1]` (the npx shim symlink) to `import.meta.url` (the resolved `dist/bin.js`); they never match, so `main()` never runs and the MCP host sees a dead connection with no diagnostic. Both sides now go through `realpathSync` so the symlink collapses. Reproduce-then-fix verified with a new `test/smoke/npx-entrypoint.test.ts` regression guard. 2. Dead `app.leadbay.ai` URLs in error strings and READMEs replaced with the real recovery commands (`leadbay-mcp install`, `leadbay-mcp login`). Hints now include `--region <us|fr>` because the CLI refuses without it (anti- cross-region credential-leak guard) — earlier draft would have dead-ended the user a second time. 3. `avg_score_0_to_10` on `pull_leads` / `bulk_qualify_leads` was lying: per- question AI agent scores are discrete boosts (-10/0/10/20), not 0-10 averages. Renamed to `avg_qualification_boost` with JSDoc on the underlying `AiAgentResponse.score` interface explaining the real contract. Documented `score` (ICP similarity) vs `ai_agent_lead_score` (deep-AI qualification boost) — they're combined and normalized server-side to 0-100. Live smoke harness extended with composite-level checks + optional thinking- model judge that writes a redacted report to `.context/`. Token-first via `LEADBAY_TEST_TOKEN`; macOS Keychain fallback (refuses plaintext password env var, on purpose). Versions: core 0.2.0→0.2.1 (private), leadclaw + mcp 0.2.1→0.2.2 (openclaw plugin manifest synced). Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent f04033f commit b4feaf5

16 files changed

Lines changed: 736 additions & 75 deletions

‎packages/core/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@leadbay/core",
3-
"version": "0.2.0",
3+
"version": "0.2.1",
44
"private": true,
55
"description": "Leadbay shared core: HTTP client and protocol-agnostic tool definitions. Consumed by @leadbay/leadclaw and @leadbay/mcp.",
66
"type": "module",

‎packages/core/src/client.ts‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ export class LeadbayClient {
352352
throw this.makeError(
353353
"NOT_AUTHENTICATED",
354354
"Not logged in to Leadbay",
355-
"Set LEADBAY_TOKEN env var (obtain token at https://app.leadbay.ai/settings/api-tokens), or use the OpenClaw leadbay_login tool",
355+
"Set LEADBAY_TOKEN in your MCP client config, or run: npx -y @leadbay/mcp install --email <you> --region <us|fr>",
356356
path
357357
);
358358
}
@@ -403,7 +403,7 @@ export class LeadbayClient {
403403
throw this.makeError(
404404
"NOT_AUTHENTICATED",
405405
"Not logged in to Leadbay",
406-
"Set LEADBAY_TOKEN env var (obtain token at https://app.leadbay.ai/settings/api-tokens), or use the OpenClaw leadbay_login tool",
406+
"Set LEADBAY_TOKEN in your MCP client config, or run: npx -y @leadbay/mcp install --email <you> --region <us|fr>",
407407
path
408408
);
409409
}
@@ -487,7 +487,7 @@ export class LeadbayClient {
487487
return this.makeError(
488488
"AUTH_EXPIRED",
489489
"Authentication token expired or invalid",
490-
"Your LEADBAY_TOKEN is no longer valid. Regenerate at https://app.leadbay.ai/settings/api-tokens and restart.",
490+
"Your LEADBAY_TOKEN is no longer valid. Regenerate it: npx -y @leadbay/mcp login --email <you> --region <us|fr>, then restart your MCP client.",
491491
endpoint
492492
);
493493
}
@@ -519,14 +519,14 @@ export class LeadbayClient {
519519
return this.makeError(
520520
"BILLING_SUSPENDED",
521521
"Account billing is suspended",
522-
"Your Leadbay account billing is suspended. Update at https://app.leadbay.ai",
522+
"Your Leadbay account billing is suspended. Contact Leadbay support.",
523523
endpoint
524524
);
525525
}
526526
return this.makeError(
527527
"FORBIDDEN",
528528
"Insufficient permissions",
529-
"Your token does not have access to this resource. Check account permissions at https://app.leadbay.ai",
529+
"Your token does not have access to this resource. Contact Leadbay support to verify account permissions.",
530530
endpoint
531531
);
532532
}

‎packages/core/src/composite/bulk-qualify-leads.ts‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ interface QualResult {
2626
qualification_summary: {
2727
answered: number;
2828
total: number;
29-
avg_score_0_to_10: number | null;
29+
/**
30+
* Average of per-question AI agent boost scores (each -10/0/10/20).
31+
* NOT a 0-10 average. Negative = net negative signal across questions.
32+
*/
33+
avg_qualification_boost: number | null;
3034
} | null;
3135
signals_count: number | null;
3236
}
@@ -220,7 +224,7 @@ export const bulkQualifyLeads: Tool<BulkQualifyLeadsParams> = {
220224
? {
221225
answered: responses.filter((r) => r.score != null).length,
222226
total: responses.length,
223-
avg_score_0_to_10: avg,
227+
avg_qualification_boost: avg,
224228
}
225229
: null,
226230
signals_count: lastWf?.content

‎packages/core/src/composite/pull-leads.ts‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ interface PullLeadsParams {
1717
interface QualificationSummary {
1818
answered: number;
1919
total: number;
20-
avg_score_0_to_10: number | null;
20+
/**
21+
* Average of per-question AI agent boost scores (each -10/0/10/20).
22+
* NOT a 0-10 average. Negative = net negative signal across questions.
23+
*/
24+
avg_qualification_boost: number | null;
2125
best_response_excerpt: string | null;
2226
}
2327

@@ -44,7 +48,7 @@ function summarise(responses: AiAgentResponse[]): QualificationSummary {
4448
excerpt = excerpt.slice(0, 197) + "...";
4549
}
4650

47-
return { answered, total, avg_score_0_to_10: avg, best_response_excerpt: excerpt };
51+
return { answered, total, avg_qualification_boost: avg, best_response_excerpt: excerpt };
4852
}
4953

5054
export const pullLeads: Tool<PullLeadsParams> = {
@@ -114,7 +118,7 @@ export const pullLeads: Tool<PullLeadsParams> = {
114118
summary: {
115119
answered: 0,
116120
total: 0,
117-
avg_score_0_to_10: null,
121+
avg_qualification_boost: null,
118122
best_response_excerpt: null,
119123
},
120124
};

‎packages/core/src/tools/enrich-contacts.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export const enrichContacts: Tool<EnrichContactsParams> = {
6060
throw client.makeError(
6161
"QUOTA_EXCEEDED",
6262
"No enrichment credits remaining",
63-
"Purchase more credits at app.leadbay.ai"
63+
"Contact Leadbay support to extend your credit quota"
6464
);
6565
}
6666
} catch (e: any) {

‎packages/core/src/tools/get-taste-profile.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export const getTasteProfile: Tool<Record<string, never>> = {
4141
})),
4242
...(isEmpty
4343
? {
44-
hint: "No taste profile configured yet. Set it up at app.leadbay.ai for better lead matching.",
44+
hint: "No taste profile configured yet. Use leadbay_refine_prompt or contact Leadbay support to set one up for better lead matching.",
4545
}
4646
: {}),
4747
};

‎packages/core/src/types.ts‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,17 @@ export interface SocialPresence {
8585
export interface LeadPayload {
8686
id: string;
8787
name: string;
88+
/**
89+
* Similarity score: how closely this lead matches the user's ideal-customer
90+
* profile (the active lens). Combined with `ai_agent_lead_score` and
91+
* normalized server-side to a 0-100 scale before display.
92+
*/
8893
score: number | null;
94+
/**
95+
* Deep AI qualification adjustment, computed after running web searches and
96+
* lead intelligence lookups. Acts as a boost/penalty on top of `score`;
97+
* the two are combined and normalized server-side to a 0-100 scale.
98+
*/
8999
ai_agent_lead_score: number | null;
90100
location: LocationPayload | null;
91101
description: string | null;
@@ -133,11 +143,16 @@ export interface WishlistResponse {
133143
}
134144

135145
// AI-rescore answers — the highest-signal payload Leadbay produces per lead.
136-
// Score is 0-10 PER QUESTION (different from the 0-100 lead-level scores).
146+
// Per-question qualification boost from the AI agent. Discrete values:
147+
// -10 (negative signal), 0 (neutral / no signal), 10 (positive signal),
148+
// 20 (strong positive signal). These boosts are summed and combined with
149+
// the lead's similarity `score`, then normalized server-side to the 0-100
150+
// lead-level scale before display. NOT a 0-10 scale despite legacy naming.
137151
export interface AiAgentResponse {
138152
question: string;
139153
question_created_at: string;
140154
lead_id: string;
155+
/** Discrete boost: -10, 0, 10, or 20. See interface comment above. */
141156
score: number | null;
142157
response: string | null;
143158
computed_at: string | null;

‎packages/leadclaw/CHANGELOG.md‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
## 0.2.2 — 2026-04-21
44

5-
Docs-only release. Shares the `@leadbay/mcp@0.2.2` mental-model copy updates since both packages consume the same `@leadbay/core` tool descriptions.
5+
Bug fix + contract correction + mental-model docs release. Picks up `@leadbay/core@0.2.1` underneath.
66

7+
- Renamed misleading `avg_score_0_to_10` field on the `pull_leads` / `bulk_qualify_leads` qualification summaries to `avg_qualification_boost`. Per-question AI agent scores are discrete boosts (-10/0/10/20), not a 0-10 average — interface JSDoc now reflects the real contract.
8+
- Replaced stale `app.leadbay.ai` URLs in client-side error strings with runnable recovery commands. Recovery hints now include `--region <us|fr>` because the CLI refuses without it (anti-cross-region credential-leak guard).
9+
- README: stale `app.leadbay.ai` references swept.
710
- 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.
811
- 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.
9-
- No new tools, no schema changes. Version kept in sync with `@leadbay/mcp@0.2.2`.
12+
- Version kept in sync with `@leadbay/mcp@0.2.2`.
1013

1114
## 0.2.1 — 2026-04-21
1215

‎packages/leadclaw/README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ The canonical tool list + schemas live in [`openclaw.plugin.json`](./openclaw.pl
7878
| Problem | Cause | Fix |
7979
|---------|-------|-----|
8080
| Plugin loads but agent sees no Leadbay tools | `token` missing and no login step taken | Have the agent call `leadbay_login`, or pre-set `token` in the plugin config |
81-
| `Authentication token expired or invalid` | Token revoked or wrong region | Mint a new token at [app.leadbay.ai](https://app.leadbay.ai); verify `region` matches your account |
82-
| `No enrichment credits remaining` | Out of quota | Buy credits at [app.leadbay.ai](https://app.leadbay.ai) |
81+
| `Authentication token expired or invalid` | Token revoked or wrong region | Have the agent call `leadbay_login` to mint a fresh token; verify `region` matches your account |
82+
| `No enrichment credits remaining` | Out of quota | Contact Leadbay support to extend quota |
8383
| Agent keeps picking granular tools over composites | `exposeGranular: true` set | Flip to `false`; the composites are usually what you want |
8484
| Write tool "not found" | `exposeWrite: false` (default) | Set `exposeWrite: true` after explicitly opting in |
8585

‎packages/mcp/CHANGELOG.md‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22

33
## 0.2.2 — 2026-04-21
44

5-
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.
5+
Bug fix + contract correction + mental-model docs release.
66

7+
- **Fix [product#3504](https://github.com/leadbay/product/issues/3504)**: `npx -y @leadbay/mcp` no longer exits silently on Node 25. The `isEntrypoint` check now resolves both sides through `realpathSync`, so the npx shim symlink path matches the real `dist/bin.js`. Previously `main()` never ran under npx and the MCP host saw a dead connection with no diagnostic.
8+
- Replaced stale `app.leadbay.ai` URLs in error strings (NOT_AUTHENTICATED, AUTH_INVALID, BILLING_SUSPENDED, PERMISSION_DENIED) and CLI help text with runnable commands (`leadbay-mcp install`, `leadbay-mcp login`) or "contact support". Recovery hints include `--region <us|fr>` because the CLI refuses without it (anti-cross-region credential-leak guard).
9+
- Renamed misleading `avg_score_0_to_10` field on `pull_leads` / `bulk_qualify_leads` qualification summaries to `avg_qualification_boost`. Per-question AI agent scores are discrete boosts (-10/0/10/20), not 0-10 averages — interface JSDoc now reflects the real contract.
710
- `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).
811
- `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.
9-
- New non-regression test asserts the inbox/pace/scoring/daily language stays in `SERVER_INSTRUCTIONS`.
10-
- No schema changes, no tool-shape changes — wire-compatible with 0.2.1.
12+
- New regression test `test/smoke/npx-entrypoint.test.ts` guards the symlink invocation path. New non-regression test asserts the inbox/pace/scoring/daily language stays in `SERVER_INSTRUCTIONS`.
13+
- Live smoke harness extended with composite-level checks + (optional) thinking-model judge that writes a redacted report to `.context/`.
1114

1215
## 0.2.1 — 2026-04-21
1316

0 commit comments

Comments
 (0)