@coldiq/mcp 0.4.0 → 0.4.2

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.
Files changed (56) hide show
  1. package/dist/index.js +0 -2
  2. package/dist/index.js.map +1 -1
  3. package/dist/provider-availability.d.ts +3 -0
  4. package/dist/provider-availability.d.ts.map +1 -0
  5. package/dist/provider-availability.js +25 -0
  6. package/dist/provider-availability.js.map +1 -0
  7. package/dist/registry.d.ts +7 -2
  8. package/dist/registry.d.ts.map +1 -1
  9. package/dist/registry.js +31 -20
  10. package/dist/registry.js.map +1 -1
  11. package/dist/tools/call-endpoint.js +1 -1
  12. package/dist/tools/call-endpoint.js.map +1 -1
  13. package/dist/tools/find-email.js +1 -1
  14. package/dist/tools/find-email.js.map +1 -1
  15. package/dist/tools/find-emails-bulk.js +1 -1
  16. package/dist/tools/find-emails-bulk.js.map +1 -1
  17. package/dist/tools/find-emails.js +1 -1
  18. package/dist/tools/find-emails.js.map +1 -1
  19. package/dist/tools/get-endpoint-details.js +2 -2
  20. package/dist/tools/get-endpoint-details.js.map +1 -1
  21. package/dist/tools/search-companies.d.ts.map +1 -1
  22. package/dist/tools/search-companies.js +5 -5
  23. package/dist/tools/search-companies.js.map +1 -1
  24. package/dist/utils/provider-resolver.d.ts +1 -0
  25. package/dist/utils/provider-resolver.d.ts.map +1 -1
  26. package/dist/utils/provider-resolver.js +3 -1
  27. package/dist/utils/provider-resolver.js.map +1 -1
  28. package/package.json +1 -1
  29. package/src/index.ts +0 -8
  30. package/src/provider-availability.ts +27 -0
  31. package/src/registry.ts +35 -19
  32. package/src/tools/call-endpoint.ts +1 -1
  33. package/src/tools/find-email.ts +1 -1
  34. package/src/tools/find-emails-bulk.ts +1 -1
  35. package/src/tools/find-emails.ts +1 -1
  36. package/src/tools/get-endpoint-details.ts +2 -2
  37. package/src/tools/search-companies.ts +5 -5
  38. package/src/utils/provider-resolver.ts +4 -1
  39. package/test-linkupapi-live.ts +1 -1
  40. package/test-phone-live.ts +1 -1
  41. package/test-search-live.ts +1 -1
  42. package/tests/live/companyenrich-hybrid-probe.ts +1 -1
  43. package/tests/provider-availability.test.ts +83 -0
  44. package/tests/registry-enrich-person.test.ts +7 -5
  45. package/tests/registry-find-people.test.ts +2 -2
  46. package/tests/registry.test.ts +32 -32
  47. package/tests/tools/call-endpoint.test.ts +4 -4
  48. package/tests/tools/get-endpoint-details.test.ts +4 -4
  49. package/tests/tools/search-endpoints.test.ts +2 -2
  50. package/tests/version.test.ts +2 -1
  51. package/dist/tools/find-docs.d.ts +0 -28
  52. package/dist/tools/find-docs.d.ts.map +0 -1
  53. package/dist/tools/find-docs.js +0 -25
  54. package/dist/tools/find-docs.js.map +0 -1
  55. package/src/tools/find-docs.ts +0 -25
  56. package/tests/tools/find-docs.test.ts +0 -28
@@ -121,7 +121,7 @@ async function run() {
121
121
  }
122
122
 
123
123
  // ─── 8. search_companies — is_hiring ──────────────────────────────────────
124
- // LimaData handles is_hiring at p9. Linkup-hiring at p16.
124
+ // Lima Data handles is_hiring at p9. Linkup-hiring at p16.
125
125
  {
126
126
  const r = await searchCompaniesHandler({
127
127
  is_hiring: true,
@@ -31,7 +31,7 @@ async function main() {
31
31
  linkedin_url: 'https://www.linkedin.com/in/michel-lieben',
32
32
  })
33
33
 
34
- // Scenario 2: Name + domain only — Findymail skipped, LimaData/AI Ark run
34
+ // Scenario 2: Name + domain only — Findymail skipped, Lima Data/AI Ark run
35
35
  await run('Name + domain only (no LinkedIn URL)', {
36
36
  first_name: 'Michel',
37
37
  last_name: 'Lieben',
@@ -68,7 +68,7 @@ async function main() {
68
68
  limit: 3,
69
69
  })
70
70
 
71
- // Test 5: is_hiring flag — LimaData picks this up
71
+ // Test 5: is_hiring flag — Lima Data picks this up
72
72
  await run('Hiring signal (is_hiring: true)', {
73
73
  keywords: ['fintech'],
74
74
  countries: ['US'],
@@ -35,7 +35,7 @@ async function main() {
35
35
 
36
36
  // Hypothesis D: maybe array of buckets like ["1001-5000"] or letter codes
37
37
  await count('D1: employees ["1001-5000"]', { employees: ['1001-5000'] })
38
- await count('D2: employees ["F"]', { employees: ['F'] }) // LimaData uses letter buckets, just in case
38
+ await count('D2: employees ["F"]', { employees: ['F'] }) // Lima Data uses letter buckets, just in case
39
39
 
40
40
  // Hypothesis E: snake_case field name
41
41
  await count('E: employees_from/employees_to', { employees_from: 1000, employees_to: 5000 })
@@ -0,0 +1,83 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { isProviderEnabled as isWorkerProviderEnabled } from '../../src/lib/provider-availability.js'
3
+ import { isProviderEnabled } from '../src/provider-availability.js'
4
+ import { getConfiguredProviders, getProviders, type Capability } from '../src/registry.js'
5
+ import { findEmailSchema } from '../src/tools/find-email.js'
6
+ import { findEmailsSchema } from '../src/tools/find-emails.js'
7
+ import { findEmailsBulkSchema } from '../src/tools/find-emails-bulk.js'
8
+ import { getEndpointDetailsSchema } from '../src/tools/get-endpoint-details.js'
9
+ import { listDataSourcesHandler } from '../src/tools/list-data-sources.js'
10
+ import {
11
+ CONFIGURED_FIND_EMAILS_PROVIDERS,
12
+ FIND_EMAILS_PROVIDERS,
13
+ getProvidersForCapability,
14
+ resolvePreferredProviders,
15
+ } from '../src/utils/provider-resolver.js'
16
+
17
+ const CAPABILITIES: Capability[] = [
18
+ 'search_companies',
19
+ 'find_people',
20
+ 'find_email',
21
+ 'verify_email',
22
+ 'find_phone',
23
+ 'enrich_company',
24
+ 'enrich_person',
25
+ 'search_web',
26
+ 'search_jobs',
27
+ 'search_ads',
28
+ 'search_places',
29
+ 'get_place_reviews',
30
+ 'find_influencers',
31
+ 'search_reddit',
32
+ 'search_seo',
33
+ 'find_signals',
34
+ 'fetch_page_content',
35
+ ]
36
+
37
+ describe('standalone MCP provider availability', () => {
38
+ it('mirrors every People Data Labs alias from the Worker policy', () => {
39
+ for (const alias of ['people-data-labs', 'People Data Labs', 'pdl', 'pdl-person-enrich', 'pdl-person-identify']) {
40
+ expect(isProviderEnabled(alias), alias).toBe(false)
41
+ expect(isProviderEnabled(alias), alias).toBe(isWorkerProviderEnabled(alias))
42
+ }
43
+ expect(isProviderEnabled('apollo')).toBe(true)
44
+ })
45
+
46
+ it('keeps dormant definitions testable but removes them from every active waterfall', () => {
47
+ expect(getConfiguredProviders('find_email').some((provider) => provider.id === 'pdl-person-enrich')).toBe(true)
48
+ expect(CONFIGURED_FIND_EMAILS_PROVIDERS).toContain('pdl-person-enrich')
49
+
50
+ for (const capability of CAPABILITIES) {
51
+ expect(getProviders(capability).every((provider) => isProviderEnabled(provider.id))).toBe(true)
52
+ }
53
+ expect(FIND_EMAILS_PROVIDERS.every(isProviderEnabled)).toBe(true)
54
+ expect(getProvidersForCapability('find_emails')).not.toContain('pdl-person-enrich')
55
+ })
56
+
57
+ it('rejects an explicit disabled-provider pin without advertising it as available', () => {
58
+ for (const capability of ['find_email', 'find_emails'] as const) {
59
+ const result = resolvePreferredProviders(capability, {}, ['pdl-person-enrich'])
60
+ expect(result.ok).toBe(false)
61
+ if (!result.ok) {
62
+ expect(result.error.available_providers.every(isProviderEnabled)).toBe(true)
63
+ expect(result.error.error).not.toContain('Available providers: pdl')
64
+ }
65
+ }
66
+ })
67
+
68
+ it('omits the disabled provider from data-source discovery and public schema descriptions', async () => {
69
+ const result = await listDataSourcesHandler({})
70
+ const publicMetadata = [
71
+ result.content[0].text,
72
+ findEmailSchema.use_providers.description,
73
+ findEmailSchema.max_credits.description,
74
+ findEmailsSchema.use_providers.description,
75
+ findEmailsSchema.max_credits.description,
76
+ findEmailsBulkSchema.max_credits.description,
77
+ getEndpointDetailsSchema.api.description,
78
+ ].join('\n').toLowerCase()
79
+
80
+ expect(publicMetadata).not.toContain('people data labs')
81
+ expect(publicMetadata).not.toMatch(/\bpdl\b/)
82
+ })
83
+ })
@@ -1,9 +1,10 @@
1
1
  import { describe, it, expect } from 'vitest'
2
- import { getProviders } from '../src/registry.js'
2
+ import { getConfiguredProviders, getProviders } from '../src/registry.js'
3
3
 
4
4
  const providers = () => getProviders('enrich_person')
5
+ const configuredProviders = () => getConfiguredProviders('enrich_person')
5
6
  const get = (id: string) => {
6
- const p = providers().find((p) => p.id === id)
7
+ const p = configuredProviders().find((p) => p.id === id)
7
8
  if (!p) throw new Error(`Provider '${id}' not found in enrich_person`)
8
9
  return p
9
10
  }
@@ -468,8 +469,9 @@ describe('enrich_person priority ordering', () => {
468
469
  expect(providers()[0].id).toBe('linkupapi-profile-enrich')
469
470
  })
470
471
 
471
- it('pdl-person-identify is last (priority 12)', () => {
472
- const ps = providers()
473
- expect(ps[ps.length - 1].id).toBe('pdl-person-identify')
472
+ it('keeps pdl-person-identify last in the dormant configuration but out of the active chain', () => {
473
+ const configured = configuredProviders()
474
+ expect(configured[configured.length - 1].id).toBe('pdl-person-identify')
475
+ expect(providers().some((provider) => provider.id.startsWith('pdl'))).toBe(false)
474
476
  })
475
477
  })
@@ -1,9 +1,9 @@
1
1
  import { describe, it, expect } from 'vitest'
2
- import { getProviders } from '../src/registry.js'
2
+ import { getConfiguredProviders, getProviders } from '../src/registry.js'
3
3
 
4
4
  const providers = () => getProviders('find_people')
5
5
  const get = (id: string) => {
6
- const p = providers().find((p) => p.id === id)
6
+ const p = getConfiguredProviders('find_people').find((p) => p.id === id)
7
7
  if (!p) throw new Error(`Provider '${id}' not found in find_people`)
8
8
  return p
9
9
  }
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest'
2
- import { getProviders, getSearchWebProviders, isoCountryToName, listCapabilityProviderIds } from '../src/registry.js'
2
+ import { getConfiguredProviders, getProviders, getSearchWebProviders, isoCountryToName, listCapabilityProviderIds } from '../src/registry.js'
3
3
  import type { Capability, ProviderEntry } from '../src/registry.js'
4
4
 
5
5
  const ALL_CAPABILITIES: Capability[] = [
@@ -291,7 +291,7 @@ describe('registry', () => {
291
291
  expect((result.body as { max_results: number }).max_results).toBe(50)
292
292
  })
293
293
 
294
- it('LimaData search folds keywords+industries into query and maps employees to A-H buckets', () => {
294
+ it('Lima Data search folds keywords+industries into query and maps employees to A-H buckets', () => {
295
295
  const providers = getProviders('search_companies')
296
296
  const ld = providers.find((p) => p.id === 'limadata')!
297
297
  const result = ld.mapParams({
@@ -307,7 +307,7 @@ describe('registry', () => {
307
307
  })
308
308
  })
309
309
 
310
- it('LimaData search uses fallback query when no keywords/industries provided', () => {
310
+ it('Lima Data search uses fallback query when no keywords/industries provided', () => {
311
311
  const providers = getProviders('search_companies')
312
312
  const ld = providers.find((p) => p.id === 'limadata')!
313
313
  const result = ld.mapParams({ min_employees: 51, max_employees: 200 })
@@ -427,7 +427,7 @@ describe('registry', () => {
427
427
  })
428
428
 
429
429
 
430
- it('LimaData prospect filter maps employees to company_headcount filter_type', () => {
430
+ it('Lima Data prospect filter maps employees to company_headcount filter_type', () => {
431
431
  const providers = getProviders('search_companies')
432
432
  const ld = providers.find((p) => p.id === 'limadata-prospect-filter')!
433
433
  const result = ld.mapParams({ min_employees: 51, max_employees: 500 })
@@ -436,7 +436,7 @@ describe('registry', () => {
436
436
  })
437
437
  })
438
438
 
439
- it('LimaData prospect filter isApplicable requires a non-trivial employee range', () => {
439
+ it('Lima Data prospect filter isApplicable requires a non-trivial employee range', () => {
440
440
  const providers = getProviders('search_companies')
441
441
  const ld = providers.find((p) => p.id === 'limadata-prospect-filter')!
442
442
  expect(ld.isApplicable!({})).toBe(false)
@@ -444,14 +444,14 @@ describe('registry', () => {
444
444
  expect(ld.isApplicable!({ min_employees: 51, max_employees: 500 })).toBe(true)
445
445
  })
446
446
 
447
- it('LimaData prospect URL passes search_url when provided', () => {
447
+ it('Lima Data prospect URL passes search_url when provided', () => {
448
448
  const providers = getProviders('search_companies')
449
449
  const ld = providers.find((p) => p.id === 'limadata-prospect-url')!
450
450
  const result = ld.mapParams({ linkedin_search_url: 'https://www.linkedin.com/sales/search/company?...' })
451
451
  expect(result.body).toEqual({ search_url: 'https://www.linkedin.com/sales/search/company?...' })
452
452
  })
453
453
 
454
- it('LimaData prospect URL isApplicable requires linkedin_search_url', () => {
454
+ it('Lima Data prospect URL isApplicable requires linkedin_search_url', () => {
455
455
  const providers = getProviders('search_companies')
456
456
  const ld = providers.find((p) => p.id === 'limadata-prospect-url')!
457
457
  expect(ld.isApplicable!({})).toBe(false)
@@ -459,12 +459,11 @@ describe('registry', () => {
459
459
  expect(ld.isApplicable!({ linkedin_search_url: 'https://...' })).toBe(true)
460
460
  })
461
461
 
462
- it('search_companies has 18 providers in priority order', () => {
462
+ it('search_companies omits disabled providers while preserving priority order', () => {
463
463
  const providers = getProviders('search_companies')
464
464
  expect(providers.map((p) => p.id)).toEqual([
465
465
  'companyenrich',
466
466
  'fullenrich',
467
- 'pdl',
468
467
  'theirstack',
469
468
  'signalbase',
470
469
  'blitzapi',
@@ -483,8 +482,8 @@ describe('registry', () => {
483
482
  ])
484
483
  })
485
484
 
486
- it('PDL uses POST to /pdl/company/search', () => {
487
- const pdl = getProviders('search_companies').find((p) => p.id === 'pdl')!
485
+ it('keeps the dormant PDL company-search definition testable', () => {
486
+ const pdl = getConfiguredProviders('search_companies').find((p) => p.id === 'pdl')!
488
487
  expect(pdl.endpoint).toBe('/pdl/company/search')
489
488
  expect(pdl.method).toBe('POST')
490
489
  })
@@ -598,8 +597,8 @@ describe('registry', () => {
598
597
  })
599
598
  })
600
599
 
601
- it('PDL uses POST to /pdl/person/search', () => {
602
- const pdl = getProviders('find_people').find((p) => p.id === 'pdl')!
600
+ it('keeps the dormant PDL people-search definition testable', () => {
601
+ const pdl = getConfiguredProviders('find_people').find((p) => p.id === 'pdl')!
603
602
  expect(pdl.endpoint).toBe('/pdl/person/search')
604
603
  expect(pdl.method).toBe('POST')
605
604
  })
@@ -707,7 +706,7 @@ describe('registry', () => {
707
706
  expect(ids[1]).toBe('leadmagic')
708
707
  })
709
708
 
710
- it('LimaData work-email maps full_name + company_domain', () => {
709
+ it('Lima Data work-email maps full_name + company_domain', () => {
711
710
  const providers = getProviders('find_email')
712
711
  const ld = providers.find((p) => p.id === 'limadata-work-email')!
713
712
  const result = ld.mapParams({
@@ -718,7 +717,7 @@ describe('registry', () => {
718
717
  expect(result.body).toEqual({ full_name: 'Michel Lieben', company_domain: 'coldiq.com' })
719
718
  })
720
719
 
721
- it('LimaData work-email hasResult accepts top-level email or nested data.email or emails array', () => {
720
+ it('Lima Data work-email hasResult accepts top-level email or nested data.email or emails array', () => {
722
721
  const providers = getProviders('find_email')
723
722
  const ld = providers.find((p) => p.id === 'limadata-work-email')!
724
723
  expect(ld.hasResult({ email: 'michel@coldiq.com' })).toBe(true)
@@ -752,7 +751,7 @@ describe('registry', () => {
752
751
  expect(blitz.hasResult({ email: null })).toBe(false)
753
752
  })
754
753
 
755
- it('LimaData work-email-linkedin maps linkedin_url passthrough', () => {
754
+ it('Lima Data work-email-linkedin maps linkedin_url passthrough', () => {
756
755
  const providers = getProviders('find_email')
757
756
  const ld = providers.find((p) => p.id === 'limadata-work-email-linkedin')!
758
757
  const result = ld.mapParams({
@@ -761,7 +760,7 @@ describe('registry', () => {
761
760
  expect(result.body).toEqual({ linkedin_url: 'https://linkedin.com/in/michel-lieben' })
762
761
  })
763
762
 
764
- it('LimaData work-email-linkedin isApplicable requires linkedin_url', () => {
763
+ it('Lima Data work-email-linkedin isApplicable requires linkedin_url', () => {
765
764
  const providers = getProviders('find_email')
766
765
  const ld = providers.find((p) => p.id === 'limadata-work-email-linkedin')!
767
766
  expect(ld.isApplicable!({})).toBe(false)
@@ -769,7 +768,7 @@ describe('registry', () => {
769
768
  expect(ld.isApplicable!({ linkedin_url: 'https://...' })).toBe(true)
770
769
  })
771
770
 
772
- it('LimaData work-email isApplicable requires first_name + last_name + domain', () => {
771
+ it('Lima Data work-email isApplicable requires first_name + last_name + domain', () => {
773
772
  const providers = getProviders('find_email')
774
773
  const ld = providers.find((p) => p.id === 'limadata-work-email')!
775
774
  expect(ld.isApplicable!({})).toBe(false)
@@ -840,10 +839,10 @@ describe('registry', () => {
840
839
  })
841
840
 
842
841
  describe('enrich_company', () => {
843
- it('providers are ordered companyenrich pdl apollo → limadata → prospeo → companyenrich_props → blitzapi → wiza → findymail → icypeas → builtwith → openmart → linkupapi-by-domain → linkupapi-by-url → discolike', () => {
842
+ it('providers preserve their order with disabled providers omitted', () => {
844
843
  const ids = getProviders('enrich_company').map((p) => p.id)
845
844
  expect(ids).toEqual([
846
- 'companyenrich', 'pdl', 'apollo', 'limadata', 'prospeo',
845
+ 'companyenrich', 'apollo', 'limadata', 'prospeo',
847
846
  'companyenrich_props', 'blitzapi', 'wiza', 'findymail', 'icypeas',
848
847
  'builtwith', 'openmart', 'linkupapi-by-domain', 'linkupapi-by-url', 'discolike',
849
848
  ])
@@ -866,8 +865,8 @@ describe('registry', () => {
866
865
  expect(apollo.isApplicable!({ name: 'Stripe' })).toBe(false)
867
866
  })
868
867
 
869
- it('PDL uses GET with correct path and maps all identifier types', () => {
870
- const pdl = getProviders('enrich_company').find((p) => p.id === 'pdl')!
868
+ it('keeps the dormant PDL company-enrichment definition testable', () => {
869
+ const pdl = getConfiguredProviders('enrich_company').find((p) => p.id === 'pdl')!
871
870
  expect(pdl.endpoint).toBe('/pdl/company/enrich')
872
871
  expect(pdl.method).toBe('GET')
873
872
  expect(pdl.mapParams({ domain: 'stripe.com' }).queryParams).toEqual({ website: 'stripe.com' })
@@ -899,7 +898,7 @@ describe('registry', () => {
899
898
  expect(wiza.mapParams({ name: 'Stripe' }).body).toEqual({ company_name: 'Stripe' })
900
899
  })
901
900
 
902
- it('LimaData is gated on domain or linkedin_url', () => {
901
+ it('Lima Data is gated on domain or linkedin_url', () => {
903
902
  const ld = getProviders('enrich_company').find((p) => p.id === 'limadata')!
904
903
  expect(ld.isApplicable!({ domain: 'stripe.com' })).toBe(true)
905
904
  expect(ld.isApplicable!({ linkedin_url: 'https://linkedin.com/company/stripe' })).toBe(true)
@@ -1028,7 +1027,7 @@ describe('registry', () => {
1028
1027
  expect(fm.hasResult({ phones: [] })).toBe(false)
1029
1028
  })
1030
1029
 
1031
- it('LimaData isApplicable accepts linkedin_url or name+company', () => {
1030
+ it('Lima Data isApplicable accepts linkedin_url or name+company', () => {
1032
1031
  const ld = getProviders('find_phone').find((p) => p.id === 'limadata')!
1033
1032
  expect(ld.isApplicable!({})).toBe(false)
1034
1033
  expect(ld.isApplicable!({ first_name: 'Michel' })).toBe(false)
@@ -1038,14 +1037,14 @@ describe('registry', () => {
1038
1037
  expect(ld.isApplicable!({ first_name: 'Michel', last_name: 'Lieben', company_domain: 'coldiq.com' })).toBe(true)
1039
1038
  })
1040
1039
 
1041
- it('LimaData mapParams forwards linkedin_url path', () => {
1040
+ it('Lima Data mapParams forwards linkedin_url path', () => {
1042
1041
  const ld = getProviders('find_phone').find((p) => p.id === 'limadata')!
1043
1042
  expect(ld.mapParams({ linkedin_url: 'https://linkedin.com/in/michel-lieben' }).body).toEqual({
1044
1043
  linkedin_url: 'https://linkedin.com/in/michel-lieben',
1045
1044
  })
1046
1045
  })
1047
1046
 
1048
- it('LimaData mapParams forwards name+company path', () => {
1047
+ it('Lima Data mapParams forwards name+company path', () => {
1049
1048
  const ld = getProviders('find_phone').find((p) => p.id === 'limadata')!
1050
1049
  expect(ld.mapParams({ first_name: 'Michel', last_name: 'Lieben', company_domain: 'coldiq.com' }).body).toEqual({
1051
1050
  name: 'Michel Lieben',
@@ -1053,7 +1052,7 @@ describe('registry', () => {
1053
1052
  })
1054
1053
  })
1055
1054
 
1056
- it('LimaData mapParams forwards company_name when no domain', () => {
1055
+ it('Lima Data mapParams forwards company_name when no domain', () => {
1057
1056
  const ld = getProviders('find_phone').find((p) => p.id === 'limadata')!
1058
1057
  expect(ld.mapParams({ first_name: 'Michel', last_name: 'Lieben', company_name: 'ColdIQ' }).body).toEqual({
1059
1058
  name: 'Michel Lieben',
@@ -1061,7 +1060,7 @@ describe('registry', () => {
1061
1060
  })
1062
1061
  })
1063
1062
 
1064
- it('LimaData hasResult checks multiple phone shapes including phone_numbers', () => {
1063
+ it('Lima Data hasResult checks multiple phone shapes including phone_numbers', () => {
1065
1064
  const ld = getProviders('find_phone').find((p) => p.id === 'limadata')!
1066
1065
  expect(ld.hasResult({ phone: '+33612345678' })).toBe(true)
1067
1066
  expect(ld.hasResult({ phones: ['+33612345678'] })).toBe(true)
@@ -1249,12 +1248,13 @@ describe('registry', () => {
1249
1248
  expect(apollo.hasResult(filtered)).toBe(false)
1250
1249
  })
1251
1250
 
1252
- it('Top-tier providers (companyenrich, fullenrich, pdl, signalbase) have no postFilter', () => {
1251
+ it('top-tier active providers have no postFilter', () => {
1253
1252
  const providers = getProviders('search_companies')
1254
- for (const id of ['companyenrich', 'fullenrich', 'pdl', 'signalbase']) {
1253
+ for (const id of ['companyenrich', 'fullenrich', 'signalbase']) {
1255
1254
  const p = providers.find((x) => x.id === id)!
1256
1255
  expect(p.postFilter, `${id} should not have postFilter`).toBeUndefined()
1257
1256
  }
1257
+ expect(getConfiguredProviders('search_companies').find((provider) => provider.id === 'pdl')?.postFilter).toBeUndefined()
1258
1258
  })
1259
1259
 
1260
1260
  it('Apollo isApplicable: rejects Phase B filters (technologies, funding, revenue, exclusions, workforce growth, is_hiring)', () => {
@@ -1347,13 +1347,13 @@ describe('registry', () => {
1347
1347
  })
1348
1348
 
1349
1349
 
1350
- it('LimaData passes has_jobs: true when is_hiring is true', () => {
1350
+ it('Lima Data passes has_jobs: true when is_hiring is true', () => {
1351
1351
  const ld = getProviders('search_companies').find((p) => p.id === 'limadata')!
1352
1352
  const result = ld.mapParams({ keywords: ['SaaS'], is_hiring: true })
1353
1353
  expect((result.body as Record<string, unknown>).has_jobs).toBe(true)
1354
1354
  })
1355
1355
 
1356
- it('LimaData omits has_jobs when is_hiring is false or unset', () => {
1356
+ it('Lima Data omits has_jobs when is_hiring is false or unset', () => {
1357
1357
  const ld = getProviders('search_companies').find((p) => p.id === 'limadata')!
1358
1358
  const falseResult = ld.mapParams({ keywords: ['SaaS'], is_hiring: false })
1359
1359
  const unsetResult = ld.mapParams({ keywords: ['SaaS'] })
@@ -20,19 +20,19 @@ describe('call_endpoint handler', () => {
20
20
  }) as typeof fetch
21
21
 
22
22
  const params = { filters: [{ filter_type: 'current_title', values: ['Head of Growth'] }] }
23
- const result = await callEndpointHandler({ api: 'coldiq', path: '/coldiq/prospect/people/filter', params })
23
+ const result = await callEndpointHandler({ api: 'limadata', path: '/limadata/prospect/people/filter', params })
24
24
 
25
25
  expect(capturedUrl).toBe('http://test-api.local/v1/endpoints/use')
26
26
  expect(capturedAuth).toBe('Bearer test-key')
27
- expect(capturedBody).toEqual({ api: 'coldiq', path: '/coldiq/prospect/people/filter', params })
27
+ expect(capturedBody).toEqual({ api: 'limadata', path: '/limadata/prospect/people/filter', params })
28
28
  expect(result.isError).toBeFalsy()
29
29
  const parsed = JSON.parse(result.content[0].text)
30
30
  expect(parsed.data.people).toHaveLength(1)
31
31
  })
32
32
 
33
33
  it('returns isError when the call fails (e.g. unknown endpoint 404)', async () => {
34
- globalThis.fetch = vi.fn(async () => new Response(JSON.stringify({ error: 'Endpoint not found for api=coldiq path=/nope', code: -32004 }), { status: 404 })) as typeof fetch
35
- const result = await callEndpointHandler({ api: 'coldiq', path: '/nope', params: {} })
34
+ globalThis.fetch = vi.fn(async () => new Response(JSON.stringify({ error: 'Endpoint not found for api=limadata path=/nope', code: -32004 }), { status: 404 })) as typeof fetch
35
+ const result = await callEndpointHandler({ api: 'limadata', path: '/nope', params: {} })
36
36
  expect(result.isError).toBe(true)
37
37
  const parsed = JSON.parse(result.content[0].text)
38
38
  expect(parsed.status).toBe(404)
@@ -14,13 +14,13 @@ describe('get_endpoint_details handler', () => {
14
14
  globalThis.fetch = vi.fn(async (url: string | URL | Request, opts?: RequestInit) => {
15
15
  capturedUrl = url.toString()
16
16
  capturedBody = opts?.body ? JSON.parse(opts.body as string) : undefined
17
- return new Response(JSON.stringify({ path: '/coldiq/prospect/people/filter', method: 'post' }), { status: 200 })
17
+ return new Response(JSON.stringify({ path: '/limadata/prospect/people/filter', method: 'post' }), { status: 200 })
18
18
  }) as typeof fetch
19
19
 
20
- const result = await getEndpointDetailsHandler({ api: 'coldiq', path: '/coldiq/prospect/people/filter' })
20
+ const result = await getEndpointDetailsHandler({ api: 'limadata', path: '/limadata/prospect/people/filter' })
21
21
 
22
22
  expect(capturedUrl).toBe('http://test-api.local/v1/endpoints/details')
23
- expect(capturedBody).toEqual({ api: 'coldiq', path: '/coldiq/prospect/people/filter' })
23
+ expect(capturedBody).toEqual({ api: 'limadata', path: '/limadata/prospect/people/filter' })
24
24
  expect(result.isError).toBeFalsy()
25
25
  const parsed = JSON.parse(result.content[0].text)
26
26
  expect(parsed.path).toContain('prospect/people/filter')
@@ -28,7 +28,7 @@ describe('get_endpoint_details handler', () => {
28
28
 
29
29
  it('returns isError when the endpoint is unknown (404)', async () => {
30
30
  globalThis.fetch = vi.fn(async () => new Response(JSON.stringify({ error: 'not found' }), { status: 404 })) as typeof fetch
31
- const result = await getEndpointDetailsHandler({ api: 'coldiq', path: '/nope' })
31
+ const result = await getEndpointDetailsHandler({ api: 'limadata', path: '/nope' })
32
32
  expect(result.isError).toBe(true)
33
33
  const parsed = JSON.parse(result.content[0].text)
34
34
  expect(parsed.status).toBe(404)
@@ -14,7 +14,7 @@ describe('search_endpoints handler', () => {
14
14
  globalThis.fetch = vi.fn(async (url: string | URL | Request, opts?: RequestInit) => {
15
15
  capturedUrl = url.toString()
16
16
  capturedBody = opts?.body ? JSON.parse(opts.body as string) : undefined
17
- return new Response(JSON.stringify({ results: [{ api: 'coldiq', provider: 'Lima Data', endpoints: [] }] }), { status: 200 })
17
+ return new Response(JSON.stringify({ results: [{ api: 'limadata', provider: 'Lima Data', endpoints: [] }] }), { status: 200 })
18
18
  }) as typeof fetch
19
19
 
20
20
  const result = await searchEndpointsHandler({ prompt: 'find people by title', limit: 5 })
@@ -23,7 +23,7 @@ describe('search_endpoints handler', () => {
23
23
  expect(capturedBody).toEqual({ prompt: 'find people by title', limit: 5 })
24
24
  expect(result.isError).toBeFalsy()
25
25
  const parsed = JSON.parse(result.content[0].text)
26
- expect(parsed.results[0].api).toBe('coldiq')
26
+ expect(parsed.results[0].api).toBe('limadata')
27
27
  })
28
28
 
29
29
  it('returns isError when the API responds non-2xx', async () => {
@@ -5,7 +5,8 @@ describe('readPackageVersion', () => {
5
5
  it('reports the real package.json version relative to the caller URL', () => {
6
6
  // This test file sits at mcp/tests/, so '../package.json' from here is mcp/package.json.
7
7
  const version = readPackageVersion(import.meta.url)
8
- expect(version).toBe('0.4.0')
8
+ expect(version).toMatch(/^\d+\.\d+\.\d+/)
9
+ expect(version).not.toBe('0.1.0') // the old hardcoded placeholder
9
10
  })
10
11
 
11
12
  it('falls back to 0.0.0 instead of throwing when package.json cannot be resolved', () => {
@@ -1,28 +0,0 @@
1
- import { z } from 'zod';
2
- export declare const findDocsName = "find_docs";
3
- export declare const findDocsDescription = "Find the relevant ColdIQ GTM knowledge docs for a task \u2014 plays with endpoint waterfalls, provider deep-docs, and strategy guides. Describe what you are trying to do; returns the most relevant docs. FREE.";
4
- export declare const findDocsSchema: {
5
- query: z.ZodString;
6
- types: z.ZodOptional<z.ZodArray<z.ZodEnum<["play", "provider", "strategy"]>, "many">>;
7
- limit: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
8
- include_bodies: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
9
- };
10
- export declare function findDocsHandler(input: {
11
- query: string;
12
- types?: string[];
13
- limit?: number;
14
- include_bodies?: boolean;
15
- }): Promise<{
16
- content: {
17
- type: "text";
18
- text: string;
19
- }[];
20
- isError: boolean;
21
- } | {
22
- content: {
23
- type: "text";
24
- text: string;
25
- }[];
26
- isError?: undefined;
27
- }>;
28
- //# sourceMappingURL=find-docs.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"find-docs.d.ts","sourceRoot":"","sources":["../../src/tools/find-docs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,YAAY,cAAc,CAAA;AACvC,eAAO,MAAM,mBAAmB,qNAAgN,CAAA;AAChP,eAAO,MAAM,cAAc;;;;;CAK1B,CAAA;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE;;;;;;;;;;;;GAYzH"}
@@ -1,25 +0,0 @@
1
- import { z } from 'zod';
2
- import { callApi } from '../client.js';
3
- export const findDocsName = 'find_docs';
4
- export const findDocsDescription = 'Find the relevant ColdIQ GTM knowledge docs for a task — plays with endpoint waterfalls, provider deep-docs, and strategy guides. Describe what you are trying to do; returns the most relevant docs. FREE.';
5
- export const findDocsSchema = {
6
- query: z.string().trim().min(1).max(2_000).describe('Natural-language GTM task or question.'),
7
- types: z.array(z.enum(['play', 'provider', 'strategy'])).max(3).optional(),
8
- limit: z.number().int().min(1).max(15).default(5).optional(),
9
- include_bodies: z.boolean().default(false).optional(),
10
- };
11
- export async function findDocsHandler(input) {
12
- const res = await callApi('GET', '/knowledge/search', undefined, {
13
- q: input.query,
14
- types: input.types?.join(',') ?? '',
15
- limit: String(input.limit ?? 5),
16
- include_bodies: String(input.include_bodies ?? false),
17
- });
18
- if (!res.ok)
19
- return {
20
- content: [{ type: 'text', text: JSON.stringify({ error: 'Failed to find knowledge docs', status: res.status, data: res.data }) }],
21
- isError: true,
22
- };
23
- return { content: [{ type: 'text', text: JSON.stringify({ data: res.data }) }] };
24
- }
25
- //# sourceMappingURL=find-docs.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"find-docs.js","sourceRoot":"","sources":["../../src/tools/find-docs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAA;AACvC,MAAM,CAAC,MAAM,mBAAmB,GAAG,6MAA6M,CAAA;AAChP,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,wCAAwC,CAAC;IAC7F,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC1E,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC5D,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE;CACtD,CAAA;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAAoF;IACxH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,mBAAmB,EAAE,SAAS,EAAE;QAC/D,CAAC,EAAE,KAAK,CAAC,KAAK;QACd,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;QACnC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;QAC/B,cAAc,EAAE,MAAM,CAAC,KAAK,CAAC,cAAc,IAAI,KAAK,CAAC;KACtD,CAAC,CAAA;IACF,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,OAAO;YAClB,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,+BAA+B,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YAC1I,OAAO,EAAE,IAAI;SACd,CAAA;IACD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAA;AAC3F,CAAC"}
@@ -1,25 +0,0 @@
1
- import { z } from 'zod'
2
- import { callApi } from '../client.js'
3
-
4
- export const findDocsName = 'find_docs'
5
- export const findDocsDescription = 'Find the relevant ColdIQ GTM knowledge docs for a task — plays with endpoint waterfalls, provider deep-docs, and strategy guides. Describe what you are trying to do; returns the most relevant docs. FREE.'
6
- export const findDocsSchema = {
7
- query: z.string().trim().min(1).max(2_000).describe('Natural-language GTM task or question.'),
8
- types: z.array(z.enum(['play', 'provider', 'strategy'])).max(3).optional(),
9
- limit: z.number().int().min(1).max(15).default(5).optional(),
10
- include_bodies: z.boolean().default(false).optional(),
11
- }
12
-
13
- export async function findDocsHandler(input: { query: string; types?: string[]; limit?: number; include_bodies?: boolean }) {
14
- const res = await callApi('GET', '/knowledge/search', undefined, {
15
- q: input.query,
16
- types: input.types?.join(',') ?? '',
17
- limit: String(input.limit ?? 5),
18
- include_bodies: String(input.include_bodies ?? false),
19
- })
20
- if (!res.ok) return {
21
- content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Failed to find knowledge docs', status: res.status, data: res.data }) }],
22
- isError: true,
23
- }
24
- return { content: [{ type: 'text' as const, text: JSON.stringify({ data: res.data }) }] }
25
- }
@@ -1,28 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
2
- import { initClient } from '../../src/client.js'
3
- import { findDocsHandler } from '../../src/tools/find-docs.js'
4
-
5
- describe('find_docs', () => {
6
- const originalFetch = globalThis.fetch
7
- beforeEach(() => initClient('http://test-api.local', 'test-key'))
8
- afterEach(() => { globalThis.fetch = originalFetch })
9
-
10
- it('calls the free knowledge search endpoint with filters', async () => {
11
- let requested = ''
12
- globalThis.fetch = vi.fn(async (url: string | URL | Request) => {
13
- requested = url.toString()
14
- return new Response(JSON.stringify({ hits: [{ slug: 'find-influencers' }] }), { status: 200 })
15
- }) as typeof fetch
16
- const result = await findDocsHandler({ query: 'find creators', types: ['play'], limit: 3, include_bodies: true })
17
- expect(requested).toContain('/v1/knowledge/search?')
18
- expect(requested).toContain('q=find+creators')
19
- expect(requested).toContain('types=play')
20
- expect(result.isError).toBeFalsy()
21
- })
22
-
23
- it('returns a structured MCP error for API failures', async () => {
24
- globalThis.fetch = vi.fn(async () => new Response(JSON.stringify({ error: 'nope' }), { status: 401 })) as typeof fetch
25
- expect(await findDocsHandler({ query: 'find creators' })).toMatchObject({ isError: true })
26
- })
27
- })
28
-