@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.
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +58 -17
- package/dist/registry.js.map +1 -1
- package/dist/tools/search-companies.d.ts +2 -1
- package/dist/tools/search-companies.d.ts.map +1 -1
- package/dist/tools/search-companies.js +10 -3
- package/dist/tools/search-companies.js.map +1 -1
- package/dist/utils/compact-companies.d.ts +21 -0
- package/dist/utils/compact-companies.d.ts.map +1 -0
- package/dist/utils/compact-companies.js +198 -0
- package/dist/utils/compact-companies.js.map +1 -0
- package/package.json +1 -1
- package/src/registry.ts +58 -17
- package/src/tools/search-companies.ts +10 -3
- package/src/utils/compact-companies.ts +208 -0
- package/tests/live/companyenrich-keyword-tag-probe.ts +14 -3
- package/tests/registry-polling.test.ts +9 -3
- package/tests/registry-search-companies.test.ts +32 -5
- package/tests/registry.test.ts +33 -11
- package/tests/tools/search-companies.test.ts +78 -2
- package/tests/utils/compact-companies.test.ts +146 -0
package/dist/registry.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
-
//
|
|
248
|
-
//
|
|
249
|
-
//
|
|
250
|
-
//
|
|
251
|
-
//
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
//
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
|
|
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
|
-
//
|
|
1079
|
-
//
|
|
1080
|
-
|
|
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;
|