@sellable/mcp 0.1.55 → 0.1.57
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/dist/tools/leads.d.ts +56 -0
- package/dist/tools/leads.js +66 -2
- package/package.json +1 -1
- package/skills/create-campaign-v2/SKILL.md +20 -9
- package/skills/create-campaign-v2/references/watch-link-handoff.md +1 -1
- package/skills/find-leads/SKILL.md +11 -0
- package/skills/providers/sales-nav.md +23 -0
package/dist/tools/leads.d.ts
CHANGED
|
@@ -314,6 +314,7 @@ export declare const leadToolDefinitions: ({
|
|
|
314
314
|
filters: {
|
|
315
315
|
type: string;
|
|
316
316
|
description: string;
|
|
317
|
+
items?: undefined;
|
|
317
318
|
properties?: undefined;
|
|
318
319
|
};
|
|
319
320
|
companyFilters: {
|
|
@@ -473,6 +474,59 @@ export declare const leadToolDefinitions: ({
|
|
|
473
474
|
filters: {
|
|
474
475
|
type: string;
|
|
475
476
|
description: string;
|
|
477
|
+
items: {
|
|
478
|
+
type: string;
|
|
479
|
+
properties: {
|
|
480
|
+
type: {
|
|
481
|
+
type: string;
|
|
482
|
+
};
|
|
483
|
+
values: {
|
|
484
|
+
type: string;
|
|
485
|
+
items: {
|
|
486
|
+
type: string;
|
|
487
|
+
properties: {
|
|
488
|
+
id: {
|
|
489
|
+
type: string;
|
|
490
|
+
};
|
|
491
|
+
text: {
|
|
492
|
+
type: string;
|
|
493
|
+
};
|
|
494
|
+
selectionType: {
|
|
495
|
+
type: string;
|
|
496
|
+
enum: string[];
|
|
497
|
+
};
|
|
498
|
+
parent: {
|
|
499
|
+
type: string;
|
|
500
|
+
properties: {
|
|
501
|
+
id: {
|
|
502
|
+
type: string;
|
|
503
|
+
};
|
|
504
|
+
};
|
|
505
|
+
};
|
|
506
|
+
};
|
|
507
|
+
required: string[];
|
|
508
|
+
};
|
|
509
|
+
};
|
|
510
|
+
rangeValue: {
|
|
511
|
+
type: string;
|
|
512
|
+
properties: {
|
|
513
|
+
min: {
|
|
514
|
+
type: string;
|
|
515
|
+
};
|
|
516
|
+
max: {
|
|
517
|
+
type: string;
|
|
518
|
+
};
|
|
519
|
+
};
|
|
520
|
+
required: string[];
|
|
521
|
+
};
|
|
522
|
+
selectedSubFilter: {
|
|
523
|
+
anyOf: {
|
|
524
|
+
type: string;
|
|
525
|
+
}[];
|
|
526
|
+
};
|
|
527
|
+
};
|
|
528
|
+
required: string[];
|
|
529
|
+
};
|
|
476
530
|
properties?: undefined;
|
|
477
531
|
};
|
|
478
532
|
keywords: {
|
|
@@ -1347,6 +1401,7 @@ export declare const leadToolDefinitions: ({
|
|
|
1347
1401
|
};
|
|
1348
1402
|
};
|
|
1349
1403
|
};
|
|
1404
|
+
items?: undefined;
|
|
1350
1405
|
};
|
|
1351
1406
|
page: {
|
|
1352
1407
|
type: string;
|
|
@@ -2015,6 +2070,7 @@ export declare const leadToolDefinitions: ({
|
|
|
2015
2070
|
})[];
|
|
2016
2071
|
export declare function searchApollo(input: ApolloSearchInput): Promise<unknown>;
|
|
2017
2072
|
export declare function searchSalesNav(input: SalesNavSearchInput): Promise<unknown>;
|
|
2073
|
+
export declare function normalizeSalesNavSearchInput(input: SalesNavSearchInput): SalesNavSearchInput;
|
|
2018
2074
|
export declare function searchProspeo(input: ProspeoSearchInput): Promise<any>;
|
|
2019
2075
|
export declare function loadCsvDomains(input: LoadCsvDomainsInput): Promise<{
|
|
2020
2076
|
ok: boolean;
|
package/dist/tools/leads.js
CHANGED
|
@@ -32,6 +32,46 @@ const prospeoFilterValueSchema = {
|
|
|
32
32
|
exclude: { type: "array", items: { type: "string" } },
|
|
33
33
|
},
|
|
34
34
|
};
|
|
35
|
+
const salesNavFilterValueSchema = {
|
|
36
|
+
type: "object",
|
|
37
|
+
properties: {
|
|
38
|
+
id: { type: "string" },
|
|
39
|
+
text: { type: "string" },
|
|
40
|
+
selectionType: {
|
|
41
|
+
type: "string",
|
|
42
|
+
enum: ["INCLUDED", "EXCLUDED"],
|
|
43
|
+
},
|
|
44
|
+
parent: {
|
|
45
|
+
type: "object",
|
|
46
|
+
properties: {
|
|
47
|
+
id: { type: "string" },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
required: ["id", "text", "selectionType"],
|
|
52
|
+
};
|
|
53
|
+
const salesNavFilterSchema = {
|
|
54
|
+
type: "object",
|
|
55
|
+
properties: {
|
|
56
|
+
type: { type: "string" },
|
|
57
|
+
values: {
|
|
58
|
+
type: "array",
|
|
59
|
+
items: salesNavFilterValueSchema,
|
|
60
|
+
},
|
|
61
|
+
rangeValue: {
|
|
62
|
+
type: "object",
|
|
63
|
+
properties: {
|
|
64
|
+
min: { type: "number" },
|
|
65
|
+
max: { type: "number" },
|
|
66
|
+
},
|
|
67
|
+
required: ["min", "max"],
|
|
68
|
+
},
|
|
69
|
+
selectedSubFilter: {
|
|
70
|
+
anyOf: [{ type: "string" }, { type: "number" }],
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
required: ["type"],
|
|
74
|
+
};
|
|
35
75
|
const prospeoRangeFilterSchema = {
|
|
36
76
|
type: "object",
|
|
37
77
|
description: "Numeric range filter (min/max)",
|
|
@@ -540,7 +580,8 @@ export const leadToolDefinitions = [
|
|
|
540
580
|
properties: {
|
|
541
581
|
filters: {
|
|
542
582
|
type: "array",
|
|
543
|
-
description: "Sales Nav filter array",
|
|
583
|
+
description: "Sales Nav filter array. Pass filter objects, not JSON strings.",
|
|
584
|
+
items: salesNavFilterSchema,
|
|
544
585
|
},
|
|
545
586
|
keywords: {
|
|
546
587
|
type: "string",
|
|
@@ -1312,7 +1353,30 @@ export async function searchSalesNav(input) {
|
|
|
1312
1353
|
campaignOfferId: input?.campaignOfferId,
|
|
1313
1354
|
});
|
|
1314
1355
|
const api = getApi();
|
|
1315
|
-
return api.post(`/api/v3/sales-nav/search`, input);
|
|
1356
|
+
return api.post(`/api/v3/sales-nav/search`, normalizeSalesNavSearchInput(input));
|
|
1357
|
+
}
|
|
1358
|
+
export function normalizeSalesNavSearchInput(input) {
|
|
1359
|
+
return {
|
|
1360
|
+
...input,
|
|
1361
|
+
filters: normalizeSalesNavFilters(input.filters),
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1364
|
+
function normalizeSalesNavFilters(filters) {
|
|
1365
|
+
return filters.map((filter, index) => {
|
|
1366
|
+
if (typeof filter !== "string") {
|
|
1367
|
+
return filter;
|
|
1368
|
+
}
|
|
1369
|
+
try {
|
|
1370
|
+
const parsed = JSON.parse(filter);
|
|
1371
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1372
|
+
throw new Error("not an object");
|
|
1373
|
+
}
|
|
1374
|
+
return parsed;
|
|
1375
|
+
}
|
|
1376
|
+
catch {
|
|
1377
|
+
throw new Error(`Invalid Sales Nav filter at index ${index}. Pass filter objects, not JSON strings.`);
|
|
1378
|
+
}
|
|
1379
|
+
});
|
|
1316
1380
|
}
|
|
1317
1381
|
export async function searchProspeo(input) {
|
|
1318
1382
|
if (input?.campaignOfferId) {
|
package/package.json
CHANGED
|
@@ -423,7 +423,7 @@ should test for this campaign. Those can run in parallel and usually take
|
|
|
423
423
|
batching is available, batch independent tool reads/lookups only. If real
|
|
424
424
|
parallel execution is not available or not allowed, run the same DAG
|
|
425
425
|
sequentially and use honest copy: `I’ll tighten the filter first, then draft
|
|
426
|
-
|
|
426
|
+
the message from the same sample.` Never say `kicking off two workstreams`,
|
|
427
427
|
`in parallel`, or `background` unless parallel branches were actually
|
|
428
428
|
launched.
|
|
429
429
|
- Never run a downstream stage until the active `flow.v2.json` step's
|
|
@@ -574,14 +574,25 @@ Required behavior:
|
|
|
574
574
|
- Branch C: Prospeo / domains only when the campaign has a domain/account
|
|
575
575
|
path or the user supplied domains. Estimate email/contact scale and call out
|
|
576
576
|
weaker LinkedIn activity.
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
577
|
+
If the host cannot run these branches in parallel, run them sequentially and
|
|
578
|
+
do not claim they ran in parallel. If only one source angle is credible, say
|
|
579
|
+
that and run the best primary source plus one cheap fallback/quality check
|
|
580
|
+
when available. For Signals-first campaigns, search multiple Signals keyword
|
|
581
|
+
lanes and fetch top-post engagers in parallel when tool batching allows it.
|
|
582
|
+
For Sales Nav-first or Prospeo/account-first campaigns, run multiple preview
|
|
583
|
+
lanes for that provider in parallel and use Signals only as a warmth/quality
|
|
584
|
+
check when relevant
|
|
585
|
+
- after every Sales Nav preview, validate that filters actually applied before
|
|
586
|
+
using the lane in `lead-review.md`: `searchUrl` should include filters, the
|
|
587
|
+
first page should match the intended roles/companies, and the result count
|
|
588
|
+
should be plausible. If Sales Nav returns a giant unfiltered pool, drops the
|
|
589
|
+
filters, or errors after one clean retry, call it out as a provider/tool
|
|
590
|
+
issue and do not recommend Sales Nav.
|
|
591
|
+
- when the brief or user names target roles, Sales Nav must preserve those role
|
|
592
|
+
names through `CURRENT_TITLE` lookups. Do not use broad seniority as a
|
|
593
|
+
substitute for role intent. VP Sales, VP Revenue, CRO, Head of Growth, and
|
|
594
|
+
similar targets should stay visible in the filter recipe and the source
|
|
595
|
+
explanation.
|
|
585
596
|
- for Signals-first campaigns, raw post search volume is only inventory, not
|
|
586
597
|
lead volume. `492 post results` means matching LinkedIn posts found across
|
|
587
598
|
keyword lanes; it does not mean 492 prospects. The source decision must name
|
|
@@ -40,7 +40,7 @@ must contain:
|
|
|
40
40
|
- a plain confirmation that the campaign was created and the approved brief was
|
|
41
41
|
saved
|
|
42
42
|
- a one-line orientation sentence: `Open this to watch lead sourcing, rubric
|
|
43
|
-
|
|
43
|
+
scoring, and messaging populate live.`
|
|
44
44
|
- the `watchUrl` captured from the `create_campaign` response
|
|
45
45
|
|
|
46
46
|
Example skeleton:
|
|
@@ -275,6 +275,10 @@ Use first when LinkedIn activity plus tighter role / company filters matter.
|
|
|
275
275
|
|
|
276
276
|
- Always call `lookup_sales_nav_filter` before dynamic filters.
|
|
277
277
|
- Start with a broad-but-reasonable baseline: company size + core roles + core industries.
|
|
278
|
+
- If the campaign gives specific target role names, preserve them with
|
|
279
|
+
`CURRENT_TITLE` lookups. Seniority filters are supporting constraints, not a
|
|
280
|
+
substitute for target roles. Example: VP Sales / VP Revenue / Head of Growth
|
|
281
|
+
should use title filters, not only `SENIORITY_LEVEL: Vice President`.
|
|
278
282
|
- For reply-likelihood-first outbound, Sales Nav is the second choice after Signals: use it when the TAM is not active enough on LinkedIn, when Signals cannot sustain enough good fits, or when the targeting thesis depends on tighter role/company control than Signals can provide.
|
|
279
283
|
- For InMail or LinkedIn-send motions, establish the baseline TAM first, then test a `POSTED_ON_LINKEDIN` slice when the pool can still sustain a campaign.
|
|
280
284
|
- Treat recent posters as a preferred first-send slice, not just a nice-to-have proxy. When the recently-posted slice still yields enough projected good fits, prefer it because reply / acceptance performance is usually materially better than the cold full-TAM pool.
|
|
@@ -293,6 +297,13 @@ Use first when LinkedIn activity plus tighter role / company filters matter.
|
|
|
293
297
|
- When you fall back from the recently-posted slice, say clearly that the posted filter was tested, explain why it was dropped, and keep the best non-posted lane as the source of truth.
|
|
294
298
|
- Use `RECENTLY_CHANGED_JOBS` when job-change activity is part of the targeting thesis.
|
|
295
299
|
- If quality is poor, tighten the lane with role, industry, seniority, geography, or activity filters.
|
|
300
|
+
- After each Sales Nav preview, sanity-check the result before using it in the
|
|
301
|
+
decision: the returned `searchUrl` should include filters, the first page
|
|
302
|
+
should visibly match the intended roles and companies, and the count should
|
|
303
|
+
be plausible. If filters did not apply, the search errors, or the total looks
|
|
304
|
+
obviously unfiltered, retry once with clean filter objects. If it still fails,
|
|
305
|
+
mark Sales Nav as a provider/tool issue and do not include it as a winning
|
|
306
|
+
source.
|
|
296
307
|
- If quality is good but scale is too small, widen the lane by adding/removing roles, expanding industries, widening headcount bands, or relaxing activity constraints.
|
|
297
308
|
- Use as many smart refinement steps as needed within the remaining probe budget instead of returning the first under-scaled recipe.
|
|
298
309
|
- If the lane is meant to support scalable outbound, do not stop at a merely borderline workable result when obvious expansion steps remain.
|
|
@@ -50,6 +50,13 @@ Example search name: "Active CXOs at 201-500 companies"
|
|
|
50
50
|
2. LOOKUP DYNAMIC FILTERS - Get IDs for geography, industry, titles, etc.
|
|
51
51
|
Before using ANY dynamic filter, you MUST call lookup_sales_nav_filter.
|
|
52
52
|
|
|
53
|
+
When the campaign names target roles, use CURRENT_TITLE lookups for those
|
|
54
|
+
role names. Seniority alone is not enough. For example, "VP Sales / VP
|
|
55
|
+
Revenue / Head of Growth" should become title filters plus seniority, not
|
|
56
|
+
only `SENIORITY_LEVEL: Vice President`, because generic VP seniority pulls
|
|
57
|
+
business development, partnerships, marketing, operations, and other noisy
|
|
58
|
+
roles.
|
|
59
|
+
|
|
53
60
|
Dynamic filters (REQUIRE lookup):
|
|
54
61
|
|
|
55
62
|
- REGION: "United States", "California", "London"
|
|
@@ -114,6 +121,7 @@ NEVER do these:
|
|
|
114
121
|
|
|
115
122
|
<filter_format>
|
|
116
123
|
ALL filter values MUST include id, text, and selectionType.
|
|
124
|
+
Pass `filters` as an array of objects. Do not stringify filters as JSON.
|
|
117
125
|
|
|
118
126
|
CORRECT:
|
|
119
127
|
|
|
@@ -145,6 +153,21 @@ WRONG:
|
|
|
145
153
|
|
|
146
154
|
</filter_format>
|
|
147
155
|
|
|
156
|
+
<result_validation>
|
|
157
|
+
After every `search_sales_nav` call, sanity-check the returned result before
|
|
158
|
+
using it in a source decision:
|
|
159
|
+
|
|
160
|
+
- `searchUrl` should include a `filters:List(...)` query, not only `keywords`.
|
|
161
|
+
- The sample rows should visibly match the intended role/company lane.
|
|
162
|
+
- The total result count should be plausible for the filters used.
|
|
163
|
+
|
|
164
|
+
If filters did not apply, the search errors, or the result count is obviously
|
|
165
|
+
unfiltered, do not present Sales Nav as a valid lane. Retry once with clean
|
|
166
|
+
filter objects if the issue was formatting. If it still fails, say plainly:
|
|
167
|
+
"Sales Nav did not apply the filters cleanly, so I’m not using it for this
|
|
168
|
+
decision." Then continue with Signals or Prospeo if they have usable evidence.
|
|
169
|
+
</result_validation>
|
|
170
|
+
|
|
148
171
|
<static_filters>
|
|
149
172
|
These filters need NO lookup - use these IDs directly:
|
|
150
173
|
|