@sellable/mcp 0.1.223 → 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.
@@ -1246,12 +1246,14 @@ export declare const leadToolDefinitions: ({
1246
1246
  type: string;
1247
1247
  items: {
1248
1248
  type: string;
1249
+ minLength: number;
1249
1250
  };
1250
1251
  };
1251
1252
  exclude: {
1252
1253
  type: string;
1253
1254
  items: {
1254
1255
  type: string;
1256
+ minLength: number;
1255
1257
  };
1256
1258
  };
1257
1259
  include_all: {
@@ -2386,12 +2388,14 @@ export declare const leadToolDefinitions: ({
2386
2388
  type: string;
2387
2389
  items: {
2388
2390
  type: string;
2391
+ minLength: number;
2389
2392
  };
2390
2393
  };
2391
2394
  exclude: {
2392
2395
  type: string;
2393
2396
  items: {
2394
2397
  type: string;
2398
+ minLength: number;
2395
2399
  };
2396
2400
  };
2397
2401
  include_all: {
@@ -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)",
@@ -177,10 +181,10 @@ const prospeoFundingFilterSchema = {
177
181
  };
178
182
  const prospeoCompanyKeywordsSchema = {
179
183
  type: "object",
180
- description: "Company keyword search",
184
+ description: 'Company keyword search. Prospeo requires keyword values to be at least 3 characters; use "artificial intelligence" instead of "AI".',
181
185
  properties: {
182
- include: { type: "array", items: { type: "string" } },
183
- exclude: { type: "array", items: { type: "string" } },
186
+ include: { type: "array", items: { type: "string", minLength: 3 } },
187
+ exclude: { type: "array", items: { type: "string", minLength: 3 } },
184
188
  include_all: { type: "boolean" },
185
189
  include_company_description: { type: "boolean" },
186
190
  include_company_description_seo: { type: "boolean" },
@@ -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: input?.campaignOfferId,
2315
- seedCompanies: input?.seedCompanies,
2316
- seedDomains: input?.seedDomains,
2317
- filters: input?.filters ?? {},
2318
- limit: input?.limit,
2319
- page: input?.page,
2320
- sort: input?.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 ?? null,
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
  }
@@ -2585,12 +2585,14 @@ export declare const allTools: ({
2585
2585
  type: string;
2586
2586
  items: {
2587
2587
  type: string;
2588
+ minLength: number;
2588
2589
  };
2589
2590
  };
2590
2591
  exclude: {
2591
2592
  type: string;
2592
2593
  items: {
2593
2594
  type: string;
2595
+ minLength: number;
2594
2596
  };
2595
2597
  };
2596
2598
  include_all: {
@@ -3725,12 +3727,14 @@ export declare const allTools: ({
3725
3727
  type: string;
3726
3728
  items: {
3727
3729
  type: string;
3730
+ minLength: number;
3728
3731
  };
3729
3732
  };
3730
3733
  exclude: {
3731
3734
  type: string;
3732
3735
  items: {
3733
3736
  type: string;
3737
+ minLength: number;
3734
3738
  };
3735
3739
  };
3736
3740
  include_all: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.223",
3
+ "version": "0.1.225",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -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. Account rows are not people
218
- leads yet. The confirmation creates the `domainFilterId` that constrains the
219
- follow-on `search_prospeo` people search.
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
@@ -232,9 +235,28 @@ the MCP backend resolves real Prospeo company IDs. Do not invent company_oids.
232
235
  For company ICP geography, `geographic_scope` only accepts `single_country` or
233
236
  `multi_country`; put North America style regions in `geographic_markets` as
234
237
  specific markets such as United States and Canada. Product is not a
235
- company_icp.departments value; use `titles_include` for product roles. Do not
236
- use `company_intent`, and do not invent unsupported support-channel filters or
237
- AI Attribute guesses like phone/email/chat/ticket/social.
238
+ company_icp.departments value; use `titles_include` for product roles.
239
+ `company_keywords.include/exclude` values must be at least 3 characters; use
240
+ `artificial intelligence` instead of `AI`, or use confirmed attributes such as
241
+ `uses_ai` when that is the actual signal. Do not use `company_intent`, and do
242
+ not invent unsupported support-channel filters or AI Attribute guesses like
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.
238
260
 
239
261
  After scouting, ask for a second approval on Start Import. For
240
262
  LinkedIn engagement (`signal-discovery` internally), name how many
@@ -411,15 +411,36 @@ Use first for broad persona expansion, ABM/domain targeting, hiring-led targetin
411
411
  product roles. Allowed company ICP departments include Consumers, Customer
412
412
  Success, Data, Design, Engineering, Finance, HR, IT, Legal, Marketing,
413
413
  Operations, Procurement, SMB Owners, Sales, and Security.
414
+ - `company_keywords.include/exclude` values must be at least 3 characters; use
415
+ `artificial intelligence` instead of `AI`, or use confirmed attributes such
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.
414
430
  - Do not use `company_intent`. Do not invent unsupported support-channel filters
415
431
  or AI Attribute guesses like phone/email/chat/ticket/social.
416
432
  - Company/account search returns an account sample only; account rows are not people leads yet. Ask the user to approve the account sample.
417
433
  - After approval, call `confirm_prospeo_company_accounts` with the
418
434
  `companySearchToken` and selected Prospeo company IDs from
419
435
  `search_prospeo_companies`; do not reconstruct account rows or domains
420
- 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.
421
439
  - Use the returned `domainFilterId` in the follow-on `search_prospeo` people
422
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.
423
444
  - Prospeo is the terminal fallback for this chain. If projected fit is still
424
445
  below the 10% planning floor after reasonable Prospeo refinement, stop and ask
425
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,9 +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.
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.
240
252
 
241
253
  ### Person Filters
242
254