@coldiq/mcp 0.3.8 → 0.3.10

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.
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,2EAA2E;IAC3E,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAA;IACpC;;;;OAIG;IACH,cAAc,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,CAAA;IACtD,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAA;IACjB,qCAAqC;IACrC,UAAU,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAA;IACtC,kDAAkD;IAClD,SAAS,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAA;CACzC;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,KAAK,GAAG,MAAM,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK;QAAE,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAA;IACvG,SAAS,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAA;IACrC,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAA;IAC1D;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAA;CACxE;AAED,MAAM,MAAM,UAAU,GAClB,kBAAkB,GAClB,aAAa,GACb,YAAY,GACZ,cAAc,GACd,YAAY,GACZ,gBAAgB,GAChB,eAAe,GACf,YAAY,GACZ,aAAa,GACb,YAAY,GACZ,eAAe,GACf,mBAAmB,GACnB,kBAAkB,GAClB,eAAe,GACf,YAAY,GACZ,cAAc,GACd,oBAAoB,CAAA;AA4ExB,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAIlG;AA0BD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOrD;AAgCD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAclE;AAKD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAAG,MAAM,EAAE,CAGpF;AA+pGD;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,EAAE,CAE1E;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,UAAU,EACtB,UAAU,EAAE,MAAM,GACjB,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,GAAG,SAAS,CAI3D;AAID;;;GAGG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,UAAU,GAAG,aAAa,EAAE,CASpE;AAID;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,OAAO,GAAG,aAAa,EAAE,CAkB5E"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,2EAA2E;IAC3E,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAA;IACpC;;;;OAIG;IACH,cAAc,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,CAAA;IACtD,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAA;IACjB,qCAAqC;IACrC,UAAU,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAA;IACtC,kDAAkD;IAClD,SAAS,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAA;CACzC;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,KAAK,GAAG,MAAM,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK;QAAE,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAA;IACvG,SAAS,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAA;IACrC,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAA;IAC1D;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAA;CACxE;AAED,MAAM,MAAM,UAAU,GAClB,kBAAkB,GAClB,aAAa,GACb,YAAY,GACZ,cAAc,GACd,YAAY,GACZ,gBAAgB,GAChB,eAAe,GACf,YAAY,GACZ,aAAa,GACb,YAAY,GACZ,eAAe,GACf,mBAAmB,GACnB,kBAAkB,GAClB,eAAe,GACf,YAAY,GACZ,cAAc,GACd,oBAAoB,CAAA;AA4ExB,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAIlG;AA0BD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOrD;AAgCD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAclE;AAKD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAAG,MAAM,EAAE,CAGpF;AAwsGD;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,EAAE,CAE1E;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,UAAU,EACtB,UAAU,EAAE,MAAM,GACjB,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,GAAG,SAAS,CAI3D;AAID;;;GAGG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,UAAU,GAAG,aAAa,EAAE,CASpE;AAID;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,OAAO,GAAG,aAAa,EAAE,CAkB5E"}
package/dist/registry.js CHANGED
@@ -237,6 +237,36 @@ function mapEmployeesToLimaDataHeadcount(min, max) {
237
237
  const hi = max ?? Number.MAX_SAFE_INTEGER;
238
238
  return buckets.filter((b) => b.max >= lo && b.min <= hi).map((b) => b.code);
239
239
  }
240
+ // CompanyEnrich's `category` filter is a controlled vocabulary matched against a
241
+ // company's clean `categories` classification (NOT its free-text keyword tags).
242
+ // These are the only accepted values — verified empirically against the live count
243
+ // API, where any value outside this set returns an empty result set (see
244
+ // mcp/tests/live/companyenrich-keyword-tag-probe.ts).
245
+ const COMPANYENRICH_CATEGORIES = new Set([
246
+ 'b2b', 'b2c', 'b2g', 'saas', 'service-provider', 'media', 'e-commerce', 'mobile',
247
+ ]);
248
+ // Split discovery terms into (a) controlled categories routed to the precise
249
+ // `category` filter and (b) free-text themes routed to the forgiving `keywords`
250
+ // tag filter. ANDing categories means "is classified b2b AND is classified saas"
251
+ // — what "B2B SaaS" actually means. ANDing the noisy keyword *text* tags instead
252
+ // would concentrate agencies whose copy merely name-drops both terms, while
253
+ // free-text themes like "fintech" are rejected outright by the category filter.
254
+ function splitCompanyEnrichTerms(terms) {
255
+ const categories = [];
256
+ const keywords = [];
257
+ for (const raw of terms) {
258
+ const slug = raw.trim().toLowerCase().replace(/\s+/g, '-');
259
+ const canonical = slug === 'ecommerce' ? 'e-commerce' : slug;
260
+ if (COMPANYENRICH_CATEGORIES.has(canonical)) {
261
+ if (!categories.includes(canonical))
262
+ categories.push(canonical);
263
+ }
264
+ else {
265
+ keywords.push(raw);
266
+ }
267
+ }
268
+ return { categories, keywords };
269
+ }
240
270
  const searchCompaniesProviders = [
241
271
  {
242
272
  id: 'companyenrich',
@@ -244,22 +274,25 @@ const searchCompaniesProviders = [
244
274
  method: 'POST',
245
275
  priority: 1,
246
276
  mapParams: (input) => {
247
- // Route thematic free-text through CompanyEnrich's `keywords` tag filter, NOT
248
- // `query`. `query` is a full-text match on company NAME + domain, so a theme
249
- // like "SaaS" surfaces companies merely named "SAAS Partners" / "AVi SaaS"
250
- // instead of actual SaaS businesses. The `keywords` tag filter matches what a
251
- // company DOES (its activity tags), which is what our unified `keywords` param
252
- // means ("topics, business models, themes"). `keywordsOperator: 'Or'` keeps
253
- // multi-term searches broad (saas OR b2b) rather than ANDing them to near-zero.
254
- // Industries are folded into the same tag list because CompanyEnrich's
255
- // `industries` field requires exact taxonomy matches and silently ignores
256
- // free-text like "SaaS" (returning the unfiltered global set).
257
- // Empirically validated against the live count/search API see
258
- // mcp/tests/live/companyenrich-keyword-tag-probe.ts.
259
- const keywordTags = [
277
+ // Hybrid routing for free-text discovery terms. CompanyEnrich exposes two
278
+ // distinct matchers and the difference is decisive:
279
+ // - `category`: a controlled vocabulary (b2b/b2c/b2g/saas/...) matched on a
280
+ // company's clean classification. ANDing these gives a precise "is B2B AND
281
+ // is SaaS" filter (10/10 real SaaS companies in live tests).
282
+ // - `keywords`: a forgiving tag filter over description-derived text tags.
283
+ // Right for open themes (fintech, cybersecurity) that have no category.
284
+ // We must NOT route everything to `keywords`: ANDing text tags concentrates
285
+ // agencies that merely name-drop "saas"/"b2b" in their copy, and `query`
286
+ // (the original mapping) matched company NAME + domain, surfacing firms merely
287
+ // *named* "SAAS Partners". So we split: vocabulary terms -> `category` (And),
288
+ // everything else -> `keywords` (Or). Industries are folded in because
289
+ // CompanyEnrich's `industries` field needs exact taxonomy matches and silently
290
+ // ignores free-text like "SaaS". Empirically validated against the live
291
+ // count/search API — see mcp/tests/live/companyenrich-keyword-tag-probe.ts.
292
+ const { categories: categoryTerms, keywords: keywordTags } = splitCompanyEnrichTerms([
260
293
  ...(input.keywords ?? []),
261
294
  ...(input.industries ?? []),
262
- ];
295
+ ]);
263
296
  const excludeObj = {};
264
297
  if (isNonEmptyArray(input.exclude_domains))
265
298
  excludeObj.domains = input.exclude_domains;
@@ -270,6 +303,8 @@ const searchCompaniesProviders = [
270
303
  return {
271
304
  body: {
272
305
  countries: input.countries,
306
+ category: categoryTerms.length > 0 ? categoryTerms : undefined,
307
+ categoryOperator: categoryTerms.length > 0 ? 'And' : undefined,
273
308
  keywords: keywordTags.length > 0 ? keywordTags : undefined,
274
309
  keywordsOperator: keywordTags.length > 0 ? 'Or' : undefined,
275
310
  technologies: isNonEmptyArray(input.technologies) ? input.technologies : undefined,
@@ -1075,9 +1110,15 @@ const findPeopleProviders = [
1075
1110
  },
1076
1111
  async: {
1077
1112
  pollEndpoint: (id) => `/leadsfactory/contact-finder/searches/${id}`,
1078
- // Growing backoff: fast early probes, then steady growth bounded by timeoutMs.
1079
- // Schedule: 2s, 5s, 12s, 20s, 28s, 36s, +8s per attempt thereafter.
1080
- pollIntervalMs: (attempt) => attempt === 1 ? 2000 : attempt === 2 ? 5000 : 12000 + (attempt - 3) * 8000,
1113
+ // Fast early probes, then a flat 12s ceiling. LeadsFactory streams results
1114
+ // for minutes; the flat ceiling bounds completion-detection lag to ≤12s
1115
+ // (the old unbounded +8s/attempt growth reached 44–72s gaps, so a search
1116
+ // that finished mid-interval sat unnoticed for up to ~a minute). Poll
1117
+ // responses aren't billed and 12s is gentler than the 2s/5s front probes,
1118
+ // so this only removes idle waiting — the returned data is unchanged (we
1119
+ // still only return once status flips to SUCCESSFUL/FAILED).
1120
+ // Schedule: 2s, 5s, then 12s flat.
1121
+ pollIntervalMs: (attempt) => attempt === 1 ? 2000 : attempt === 2 ? 5000 : 12000,
1081
1122
  timeoutMs: 300_000, // 5 minutes
1082
1123
  isComplete: (data) => {
1083
1124
  const d = data;