@sellable/mcp 0.1.226 → 0.1.227

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.
@@ -2404,11 +2404,12 @@ function removeUndefinedValues(input) {
2404
2404
  return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined));
2405
2405
  }
2406
2406
  function normalizeProspeoCompanySearchInputForMcp(input) {
2407
+ const seedCompanies = normalizeMcpSeeds(input.seedCompanies, "company");
2408
+ const seedDomains = normalizeMcpSeeds(input.seedDomains, "domain");
2407
2409
  const filters = input?.filters && typeof input.filters === "object"
2408
2410
  ? clonePlainObject(input.filters)
2409
2411
  : {};
2410
- const hasSeeds = (input.seedCompanies?.length ?? 0) > 0 ||
2411
- (input.seedDomains?.length ?? 0) > 0;
2412
+ const hasSeeds = seedCompanies.length > 0 || seedDomains.length > 0;
2412
2413
  if (hasSeeds) {
2413
2414
  const lookalike = filters.company_lookalike;
2414
2415
  if (lookalike &&
@@ -2420,11 +2421,15 @@ function normalizeProspeoCompanySearchInputForMcp(input) {
2420
2421
  }
2421
2422
  }
2422
2423
  normalizeMcpCompanyKeywords(filters);
2424
+ normalizeMcpCompanyWebsiteSearch(filters);
2423
2425
  normalizeMcpCompanyIcp(filters);
2424
2426
  stripMcpDuplicateCompanyIndustry(filters);
2427
+ stripMcpSeededLookalikeRiskyRefinements(filters, hasSeeds);
2425
2428
  stripMcpKeyCustomerCompanionFilters(filters);
2426
2429
  return {
2427
2430
  ...input,
2431
+ seedCompanies: seedCompanies.length > 0 ? seedCompanies : undefined,
2432
+ seedDomains: seedDomains.length > 0 ? seedDomains : undefined,
2428
2433
  filters,
2429
2434
  };
2430
2435
  }
@@ -2460,6 +2465,21 @@ function normalizeMcpCompanyKeywords(filters) {
2460
2465
  }
2461
2466
  removeEmptyObjectFilter(filters, "company_keywords");
2462
2467
  }
2468
+ function normalizeMcpCompanyWebsiteSearch(filters) {
2469
+ const websiteSearch = filters.company_website_search;
2470
+ if (!isPlainObject(websiteSearch)) {
2471
+ return;
2472
+ }
2473
+ const includeKeywords = normalizeStringArray(websiteSearch.include_keywords);
2474
+ const hasPositiveWebsiteSignal = includeKeywords.length > 0 ||
2475
+ normalizeStringArray(websiteSearch.url_contains).length > 0 ||
2476
+ normalizeStringArray(websiteSearch.urls).length > 0 ||
2477
+ Object.entries(websiteSearch).some(([key, value]) => key.startsWith("has_") && typeof value === "boolean" && value);
2478
+ if (!hasPositiveWebsiteSignal) {
2479
+ delete websiteSearch.exclude_keywords;
2480
+ }
2481
+ removeEmptyObjectFilter(filters, "company_website_search");
2482
+ }
2463
2483
  function normalizeMcpCompanyIcp(filters) {
2464
2484
  const icp = filters.company_icp;
2465
2485
  if (!isPlainObject(icp)) {
@@ -2545,6 +2565,21 @@ function stripMcpDuplicateCompanyIndustry(filters) {
2545
2565
  delete filters.company_industry;
2546
2566
  }
2547
2567
  }
2568
+ function stripMcpSeededLookalikeRiskyRefinements(filters, hasSeeds) {
2569
+ if (!hasSeeds || !isPlainObject(filters.company_lookalike)) {
2570
+ return;
2571
+ }
2572
+ const icp = filters.company_icp;
2573
+ if (isPlainObject(icp) &&
2574
+ Array.isArray(icp.industries) &&
2575
+ icp.industries.length > 0 &&
2576
+ !filters.company_industry) {
2577
+ filters.company_industry = { include: icp.industries };
2578
+ }
2579
+ delete filters.company_icp;
2580
+ delete filters.company_keywords;
2581
+ delete filters.company_website_search;
2582
+ }
2548
2583
  function stripMcpKeyCustomerCompanionFilters(filters) {
2549
2584
  const keyCustomers = filters.company_key_customers;
2550
2585
  if (!isPlainObject(keyCustomers)) {
@@ -2586,6 +2621,20 @@ function normalizeStringArray(input) {
2586
2621
  .map((value) => value.trim())
2587
2622
  .filter((value) => value.length > 0);
2588
2623
  }
2624
+ function normalizeMcpSeeds(input, kind) {
2625
+ return uniqueStrings(normalizeStringArray(input).filter((seed) => kind === "domain" ? isLikelyConcreteDomain(seed) : isLikelyConcreteSeedCompany(seed)));
2626
+ }
2627
+ function isLikelyConcreteDomain(input) {
2628
+ return /^[a-z0-9.-]+\.[a-z]{2,}$/i.test(input);
2629
+ }
2630
+ function isLikelyConcreteSeedCompany(input) {
2631
+ const normalized = input.toLowerCase();
2632
+ return !(normalized.includes("placeholder") ||
2633
+ normalized.includes("another approved") ||
2634
+ normalized.includes("approved seed") ||
2635
+ normalized.includes("best-customer seed") ||
2636
+ normalized.includes("example seed"));
2637
+ }
2589
2638
  function uniqueStrings(input) {
2590
2639
  return Array.from(new Set(input));
2591
2640
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.226",
3
+ "version": "0.1.227",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -252,6 +252,12 @@ without an include keyword, and do not duplicate `company_industry` when
252
252
  search, prefer `person_job_title.boolean_search` for long role synonym lists
253
253
  instead of many `person_job_title.include` values plus broad department/seniority
254
254
  filters.
255
+ For seeded company lookalikes, keep the first call simple: resolved seed
256
+ company/domain plus `company_lookalike.minimum_tier` and simple confirmed
257
+ attributes, headcount, or industry. Do not add `company_website_search`,
258
+ `company_keywords`, or `company_icp` until the account sample proves the seed
259
+ works. Do not send placeholder seed names like `another approved best-customer seed`,
260
+ and only use concrete companies or domains you actually resolved. Do not send `company_website_search.exclude_keywords` without a positive website include signal.
255
261
  Do not use `AI`, `API`, `GTM`, or `SaaS` as company keyword terms.
256
262
  Do not combine `company_key_customers` with ICP, website-search, keyword,
257
263
  attribute, industry, or headcount filters until the standalone pass proves
@@ -427,6 +427,12 @@ Use first for broad persona expansion, ABM/domain targeting, hiring-led targetin
427
427
  - Do not send `company_keywords.exclude` unless at least one include keyword is
428
428
  present. Do not duplicate `company_industry` when `company_icp.industries`
429
429
  already carries the industry.
430
+ - For seeded company lookalikes, keep the first call simple: resolved seed
431
+ company/domain plus `company_lookalike.minimum_tier` and simple confirmed
432
+ attributes, headcount, or industry. Do not add `company_website_search`,
433
+ `company_keywords`, or `company_icp` until the account sample proves the seed
434
+ works. Do not send placeholder seed names like `another approved best-customer seed`,
435
+ and only use concrete companies or domains you actually resolved. Do not send `company_website_search.exclude_keywords` without a positive website include signal.
430
436
  - Do not use `company_intent`. Do not invent unsupported support-channel filters
431
437
  or AI Attribute guesses like phone/email/chat/ticket/social.
432
438
  - Company/account search returns an account sample only; account rows are not people leads yet. Ask the user to approve the account sample.
@@ -106,6 +106,9 @@ Avoidable-400 guardrails:
106
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
107
  - Do not send `company_keywords.exclude` unless at least one include keyword is present.
108
108
  - Do not duplicate `company_industry` when `company_icp.industries` already carries the industry.
109
+ - For seeded company lookalikes, keep the first call simple: seed company/domain + `company_lookalike.minimum_tier` + confirmed attributes, headcount, or industry. Do not add `company_website_search`, `company_keywords`, or `company_icp` until the account sample proves the seed works.
110
+ - Do not send placeholder seed names such as `another approved best-customer seed`; use only concrete company names or domains you have actually resolved.
111
+ - Do not send `company_website_search.exclude_keywords` without a positive website include signal.
109
112
  - 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
113
 
111
114
  Unsupported/caveats:
@@ -249,6 +252,7 @@ Preference rules:
249
252
  - `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
253
  - 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
254
  - 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.
255
+ - For seeded lookalikes, avoid first-call `company_website_search`, `company_keywords`, and `company_icp`; use resolved seeds plus lookalike tier and simple attributes/headcount/industry first.
252
256
 
253
257
  ### Person Filters
254
258