@sellable/mcp 0.1.224 → 0.1.225
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.js
CHANGED
|
@@ -59,6 +59,10 @@ export const MAX_SIGNAL_DISCOVERY_POSTS = 10;
|
|
|
59
59
|
// capacity math instead of raw visible engagement counts.
|
|
60
60
|
const signalDiscoveryReactionFetchLimit = 1000;
|
|
61
61
|
const signalDiscoveryCommentFetchLimit = 1000;
|
|
62
|
+
const prospeoCompanySearchTokenRefs = new Map();
|
|
63
|
+
let prospeoCompanySearchTokenRefCounter = 0;
|
|
64
|
+
const PROSPEO_COMPANY_SEARCH_TOKEN_REF_PREFIX = "mcp-prospeo-company-search-token:";
|
|
65
|
+
const MAX_PROSPEO_COMPANY_SEARCH_TOKEN_REFS = 200;
|
|
62
66
|
const prospeoFilterValueSchema = {
|
|
63
67
|
type: "object",
|
|
64
68
|
description: "Include/exclude list filter (values must match Prospeo enums)",
|
|
@@ -477,12 +481,14 @@ search_prospeo_companies({
|
|
|
477
481
|
}
|
|
478
482
|
})
|
|
479
483
|
Do not invent company_oids. When seedCompanies or seedDomains are present, omit company_oids and let the backend resolve real Prospeo company IDs.
|
|
484
|
+
After account approval, copy the companySearchToken exactly into confirm_prospeo_company_accounts; package-backed MCP may return a short mcp-prospeo-company-search-token:* reference to avoid long-token copy errors.
|
|
480
485
|
For accounts in the news: { "company_news": { "categories": ["Funding & Investment"], "timeframe_days": 90 } }.
|
|
481
486
|
For awards: { "company_awards": { "include": ["G2"], "match_mode": "CONTAINS" } }.
|
|
482
487
|
For website traffic: { "company_website_traffic": { "min_monthly_visits": 50000 } }.
|
|
483
488
|
For products/services, integrations, key customers, and Google discovery use "company_products_services", "company_integrations", "company_key_customers", and "company_google_discovery".
|
|
484
489
|
For headcount by location: { "company_headcount_by_location": { "entries": [{ "country": "United States", "city": "Austin", "min_headcount": 10 }] } }.
|
|
485
490
|
For structured ICP: { "company_icp": { "titles_include": ["Head of RevOps"], "company_sizes": ["midmarket"], "departments": { "include": ["Sales"], "match_mode": "ANY" }, "geographic_scope": "multi_country", "geographic_markets": ["United States", "Canada"] }, "company_headcount_range": ["101-200", "201-500", "501-1000"] }. Pair company_icp.company_sizes with company_headcount_range or rely on MCP normalization that derives the range; inspect the account sample for size drift. Product is not a company_icp.departments value; use titles_include for product roles. geographic_scope only accepts single_country or multi_country.
|
|
491
|
+
Use company_key_customers as a standalone first-pass account filter. Do not combine company_key_customers with company_website_search, company_icp, company_keywords, or broad AI Attributes in the first call. Do not use AI, API, GTM, or SaaS as company keyword terms; use confirmed attributes or spell out artificial intelligence, application programming interface, go to market, and software as a service. Do not send company_keywords.exclude without an include keyword. For post-confirm people search, prefer person_job_title.boolean_search for long role synonym lists.
|
|
486
492
|
company_intent is unsupported; support-channel AI Attribute guesses like phone/email/chat/ticket/social are not exposed. public API rows do not include lookalike tier/score/reason.`;
|
|
487
493
|
function loadSignalDiscoveryConfig() {
|
|
488
494
|
if (!existsSync(signalProviderConfigPath)) {
|
|
@@ -2310,14 +2316,15 @@ export async function searchProspeoCompanies(input) {
|
|
|
2310
2316
|
campaignOfferId: input?.campaignOfferId,
|
|
2311
2317
|
});
|
|
2312
2318
|
const api = getApi();
|
|
2319
|
+
const safeInput = normalizeProspeoCompanySearchInputForMcp(input);
|
|
2313
2320
|
const response = await api.post("/api/v3/prospeo/company-search", removeUndefinedValues({
|
|
2314
|
-
campaignOfferId:
|
|
2315
|
-
seedCompanies:
|
|
2316
|
-
seedDomains:
|
|
2317
|
-
filters:
|
|
2318
|
-
limit:
|
|
2319
|
-
page:
|
|
2320
|
-
sort:
|
|
2321
|
+
campaignOfferId: safeInput?.campaignOfferId,
|
|
2322
|
+
seedCompanies: safeInput?.seedCompanies,
|
|
2323
|
+
seedDomains: safeInput?.seedDomains,
|
|
2324
|
+
filters: safeInput?.filters ?? {},
|
|
2325
|
+
limit: safeInput?.limit,
|
|
2326
|
+
page: safeInput?.page,
|
|
2327
|
+
sort: safeInput?.sort,
|
|
2321
2328
|
}));
|
|
2322
2329
|
return compactProspeoCompanySearchResponse(response);
|
|
2323
2330
|
}
|
|
@@ -2346,7 +2353,7 @@ export async function confirmProspeoCompanyAccounts(input) {
|
|
|
2346
2353
|
const api = getApi();
|
|
2347
2354
|
const response = await api.post("/api/v3/prospeo/company-search/confirm-domain-filter", removeUndefinedValues({
|
|
2348
2355
|
campaignOfferId: input?.campaignOfferId,
|
|
2349
|
-
companySearchToken: input.companySearchToken,
|
|
2356
|
+
companySearchToken: resolveProspeoCompanySearchTokenRef(input.companySearchToken),
|
|
2350
2357
|
selectedCompanyIds: input.selectedCompanyIds,
|
|
2351
2358
|
name: input.name,
|
|
2352
2359
|
}));
|
|
@@ -2355,6 +2362,49 @@ export async function confirmProspeoCompanyAccounts(input) {
|
|
|
2355
2362
|
function removeUndefinedValues(input) {
|
|
2356
2363
|
return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined));
|
|
2357
2364
|
}
|
|
2365
|
+
function normalizeProspeoCompanySearchInputForMcp(input) {
|
|
2366
|
+
const filters = input?.filters && typeof input.filters === "object"
|
|
2367
|
+
? clonePlainObject(input.filters)
|
|
2368
|
+
: {};
|
|
2369
|
+
const hasSeeds = (input.seedCompanies?.length ?? 0) > 0 ||
|
|
2370
|
+
(input.seedDomains?.length ?? 0) > 0;
|
|
2371
|
+
if (hasSeeds) {
|
|
2372
|
+
const lookalike = filters.company_lookalike;
|
|
2373
|
+
if (lookalike &&
|
|
2374
|
+
typeof lookalike === "object" &&
|
|
2375
|
+
!Array.isArray(lookalike) &&
|
|
2376
|
+
"company_oids" in lookalike) {
|
|
2377
|
+
delete lookalike.company_oids;
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
return {
|
|
2381
|
+
...input,
|
|
2382
|
+
filters,
|
|
2383
|
+
};
|
|
2384
|
+
}
|
|
2385
|
+
function clonePlainObject(input) {
|
|
2386
|
+
return JSON.parse(JSON.stringify(input));
|
|
2387
|
+
}
|
|
2388
|
+
function storeProspeoCompanySearchTokenRef(token) {
|
|
2389
|
+
if (typeof token !== "string" || token.length === 0) {
|
|
2390
|
+
return token;
|
|
2391
|
+
}
|
|
2392
|
+
if (token.startsWith(PROSPEO_COMPANY_SEARCH_TOKEN_REF_PREFIX)) {
|
|
2393
|
+
return token;
|
|
2394
|
+
}
|
|
2395
|
+
const ref = `${PROSPEO_COMPANY_SEARCH_TOKEN_REF_PREFIX}${Date.now().toString(36)}-${(++prospeoCompanySearchTokenRefCounter).toString(36)}`;
|
|
2396
|
+
prospeoCompanySearchTokenRefs.set(ref, token);
|
|
2397
|
+
while (prospeoCompanySearchTokenRefs.size > MAX_PROSPEO_COMPANY_SEARCH_TOKEN_REFS) {
|
|
2398
|
+
const oldest = prospeoCompanySearchTokenRefs.keys().next().value;
|
|
2399
|
+
if (!oldest)
|
|
2400
|
+
break;
|
|
2401
|
+
prospeoCompanySearchTokenRefs.delete(oldest);
|
|
2402
|
+
}
|
|
2403
|
+
return ref;
|
|
2404
|
+
}
|
|
2405
|
+
function resolveProspeoCompanySearchTokenRef(token) {
|
|
2406
|
+
return prospeoCompanySearchTokenRefs.get(token) ?? token;
|
|
2407
|
+
}
|
|
2358
2408
|
function compactProspeoCompanySearchResponse(response) {
|
|
2359
2409
|
if (!response || typeof response !== "object") {
|
|
2360
2410
|
return response;
|
|
@@ -2384,7 +2434,7 @@ function compactProspeoCompanySearchResponse(response) {
|
|
|
2384
2434
|
},
|
|
2385
2435
|
requestedFilters: response.normalizedFilters ?? response.filters ?? {},
|
|
2386
2436
|
warnings: Array.isArray(response.warnings) ? response.warnings : [],
|
|
2387
|
-
companySearchToken: response.companySearchToken
|
|
2437
|
+
companySearchToken: storeProspeoCompanySearchTokenRef(response.companySearchToken),
|
|
2388
2438
|
nextStep: "Review accounts, then call confirm_prospeo_company_accounts with companySearchToken and selectedCompanyIds. These accounts are not people leads yet.",
|
|
2389
2439
|
};
|
|
2390
2440
|
}
|
package/package.json
CHANGED
|
@@ -214,9 +214,12 @@ before person search, use the Prospeo account approval flow:
|
|
|
214
214
|
First return an account sample and ask the user to approve the account set.
|
|
215
215
|
Only call `confirm_prospeo_company_accounts` with the `companySearchToken`
|
|
216
216
|
returned by `search_prospeo_companies` and selected Prospeo company IDs; never
|
|
217
|
-
reconstruct raw account rows or domains manually
|
|
218
|
-
|
|
219
|
-
|
|
217
|
+
reconstruct raw account rows or domains manually; always copy the `companySearchToken` exactly.
|
|
218
|
+
Package-backed MCP may return a short `mcp-prospeo-company-search-token:*`
|
|
219
|
+
reference to avoid long-token copy errors. Account rows are not people leads
|
|
220
|
+
yet. The confirmation creates the `domainFilterId` that constrains the follow-on
|
|
221
|
+
`search_prospeo` people search.
|
|
222
|
+
Always copy the `companySearchToken` exactly.
|
|
220
223
|
|
|
221
224
|
Prospeo company/account search is useful when the source plan depends on
|
|
222
225
|
website traffic (`company_website_traffic`), confirmed AI Attributes including
|
|
@@ -238,6 +241,22 @@ company_icp.departments value; use `titles_include` for product roles.
|
|
|
238
241
|
`uses_ai` when that is the actual signal. Do not use `company_intent`, and do
|
|
239
242
|
not invent unsupported support-channel filters or AI Attribute guesses like
|
|
240
243
|
phone/email/chat/ticket/social.
|
|
244
|
+
Use `company_key_customers` as a standalone first-pass account filter; in short,
|
|
245
|
+
run company_key_customers as a standalone first-pass. Do not combine
|
|
246
|
+
`company_key_customers` with `company_website_search`, `company_icp`,
|
|
247
|
+
`company_keywords`, or broad AI Attributes in the first call. Do not use `AI`,
|
|
248
|
+
`API`, `GTM`, or `SaaS` as company keyword terms; use confirmed attributes or
|
|
249
|
+
spell out artificial intelligence, application programming interface, go to
|
|
250
|
+
market, and software as a service. Do not send `company_keywords.exclude`
|
|
251
|
+
without an include keyword, and do not duplicate `company_industry` when
|
|
252
|
+
`company_icp.industries` already carries the industry. For post-confirm people
|
|
253
|
+
search, prefer `person_job_title.boolean_search` for long role synonym lists
|
|
254
|
+
instead of many `person_job_title.include` values plus broad department/seniority
|
|
255
|
+
filters.
|
|
256
|
+
Do not use `AI`, `API`, `GTM`, or `SaaS` as company keyword terms.
|
|
257
|
+
Do not combine `company_key_customers` with ICP, website-search, keyword,
|
|
258
|
+
attribute, industry, or headcount filters until the standalone pass proves
|
|
259
|
+
useful.
|
|
241
260
|
|
|
242
261
|
After scouting, ask for a second approval on Start Import. For
|
|
243
262
|
LinkedIn engagement (`signal-discovery` internally), name how many
|
|
@@ -414,15 +414,33 @@ Use first for broad persona expansion, ABM/domain targeting, hiring-led targetin
|
|
|
414
414
|
- `company_keywords.include/exclude` values must be at least 3 characters; use
|
|
415
415
|
`artificial intelligence` instead of `AI`, or use confirmed attributes such
|
|
416
416
|
as `uses_ai` when that is the actual signal.
|
|
417
|
+
- Use `company_key_customers` as a standalone first-pass account filter; in
|
|
418
|
+
short, run company_key_customers as a standalone first-pass. Do not
|
|
419
|
+
combine `company_key_customers` with `company_website_search`, `company_icp`,
|
|
420
|
+
`company_keywords`, or broad AI Attributes in the first call.
|
|
421
|
+
- Do not combine `company_key_customers` with ICP, website-search, keyword,
|
|
422
|
+
attribute, industry, or headcount filters until the standalone pass proves
|
|
423
|
+
useful.
|
|
424
|
+
- Do not use `AI`, `API`, `GTM`, or `SaaS` as company keyword terms; use
|
|
425
|
+
confirmed attributes or spell out artificial intelligence, application
|
|
426
|
+
programming interface, go to market, and software as a service.
|
|
427
|
+
- Do not send `company_keywords.exclude` unless at least one include keyword is
|
|
428
|
+
present. Do not duplicate `company_industry` when `company_icp.industries`
|
|
429
|
+
already carries the industry.
|
|
417
430
|
- Do not use `company_intent`. Do not invent unsupported support-channel filters
|
|
418
431
|
or AI Attribute guesses like phone/email/chat/ticket/social.
|
|
419
432
|
- Company/account search returns an account sample only; account rows are not people leads yet. Ask the user to approve the account sample.
|
|
420
433
|
- After approval, call `confirm_prospeo_company_accounts` with the
|
|
421
434
|
`companySearchToken` and selected Prospeo company IDs from
|
|
422
435
|
`search_prospeo_companies`; do not reconstruct account rows or domains
|
|
423
|
-
manually.
|
|
436
|
+
manually. Always copy the `companySearchToken` exactly; package-backed MCP may
|
|
437
|
+
return a short `mcp-prospeo-company-search-token:*` reference to avoid
|
|
438
|
+
long-token copy errors.
|
|
424
439
|
- Use the returned `domainFilterId` in the follow-on `search_prospeo` people
|
|
425
440
|
search.
|
|
441
|
+
- For post-confirm people search, prefer `person_job_title.boolean_search` for
|
|
442
|
+
long role synonym lists instead of many `person_job_title.include` values plus
|
|
443
|
+
broad department/seniority filters.
|
|
426
444
|
- Prospeo is the terminal fallback for this chain. If projected fit is still
|
|
427
445
|
below the 10% planning floor after reasonable Prospeo refinement, stop and ask
|
|
428
446
|
for a tighter ICP/source direction instead of inventing another provider.
|
|
@@ -80,7 +80,7 @@ confirm_prospeo_company_accounts({
|
|
|
80
80
|
})
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
Use the returned `domainFilterId` in `search_prospeo` with people filters. Do not reconstruct raw account rows or domains manually as Prospeo-sourced provenance.
|
|
83
|
+
Use the returned `domainFilterId` in `search_prospeo` with people filters. Do not reconstruct raw account rows or domains manually as Prospeo-sourced provenance. Always copy the `companySearchToken` exactly from `search_prospeo_companies`; package-backed MCP may return a short `mcp-prospeo-company-search-token:*` reference so the model does not have to copy a long signed backend token.
|
|
84
84
|
|
|
85
85
|
When `seedDomains` or `seedCompanies` are present, omit `company_oids`; the MCP
|
|
86
86
|
backend resolves real Prospeo company IDs. Do not invent company_oids such as
|
|
@@ -100,6 +100,14 @@ company ICP departments include Consumers, Customer Success, Data, Design,
|
|
|
100
100
|
Engineering, Finance, HR, IT, Legal, Marketing, Operations, Procurement, SMB
|
|
101
101
|
Owners, Sales, and Security.
|
|
102
102
|
|
|
103
|
+
Avoidable-400 guardrails:
|
|
104
|
+
|
|
105
|
+
- Use `company_key_customers` as a standalone first-pass account filter; in short, run company_key_customers as a standalone first-pass. Do not combine `company_key_customers` with `company_website_search`, `company_icp`, `company_keywords`, or broad AI Attributes in the first call; inspect the account sample, then refine if needed.
|
|
106
|
+
- Do not use `AI`, `API`, `GTM`, or `SaaS` as company keyword terms. Use confirmed attributes such as `uses_ai` / `has_api`, or spell out `artificial intelligence`, `application programming interface`, `go to market`, and `software as a service`.
|
|
107
|
+
- Do not send `company_keywords.exclude` unless at least one include keyword is present.
|
|
108
|
+
- Do not duplicate `company_industry` when `company_icp.industries` already carries the industry.
|
|
109
|
+
- For post-confirm people search, prefer `person_job_title.boolean_search` for long role synonym lists instead of many `person_job_title.include` values plus broad department/seniority filters.
|
|
110
|
+
|
|
103
111
|
Unsupported/caveats:
|
|
104
112
|
|
|
105
113
|
- `company_intent` is unsupported by Prospeo public API.
|
|
@@ -234,10 +242,13 @@ Preference rules:
|
|
|
234
242
|
- If user input starts as company names, resolve names to domains first, then use `save_domain_filters`.
|
|
235
243
|
- Prefer comprehensive `person_job_title.include` lists (synonyms + role variants) for role precision.
|
|
236
244
|
- Use `person_department + person_seniority` as supporting constraints when title variance is expected.
|
|
245
|
+
- For long title synonym lists, prefer `person_job_title.boolean_search` with explicit OR terms instead of oversized include arrays.
|
|
237
246
|
- In security, AppSec, SOC, RevOps, Demand Gen, and similar function-specific lanes, do not rely on bare `Head` / `Director` / `VP` widening by itself. Pair seniority with explicit function keywords in `person_job_title` and verify the sample for off-function titles like `Head of Social Media`.
|
|
238
247
|
- Prefer `company_headcount_range` for most sizing; use `company_headcount_custom` for precise numeric bounds.
|
|
239
248
|
- Prefer `company_industry` before `company_keywords`; use keywords for refinement, not first-pass targeting.
|
|
240
249
|
- `company_keywords.include/exclude` values must be at least 3 characters; use `artificial intelligence` instead of `AI`, or use confirmed attributes such as `uses_ai` when that is the real signal.
|
|
250
|
+
- Do not use `AI`, `API`, `GTM`, or `SaaS` as company keyword terms; use longer phrases such as `artificial intelligence`, `application programming interface`, `go to market`, or `software as a service`.
|
|
251
|
+
- Run `company_key_customers` as a standalone first-pass filter. Do not combine `company_key_customers` with ICP, website-search, keyword, attribute, industry, or headcount filters until the standalone pass proves useful.
|
|
241
252
|
|
|
242
253
|
### Person Filters
|
|
243
254
|
|