@coldiq/mcp 0.4.1 → 0.4.3

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 (98) hide show
  1. package/dist/provider-availability.d.ts +3 -0
  2. package/dist/provider-availability.d.ts.map +1 -0
  3. package/dist/provider-availability.js +25 -0
  4. package/dist/provider-availability.js.map +1 -0
  5. package/dist/registry.d.ts +7 -2
  6. package/dist/registry.d.ts.map +1 -1
  7. package/dist/registry.js +31 -20
  8. package/dist/registry.js.map +1 -1
  9. package/dist/tools/call-endpoint.js +1 -1
  10. package/dist/tools/call-endpoint.js.map +1 -1
  11. package/dist/tools/extract-post-engagement.d.ts.map +1 -1
  12. package/dist/tools/extract-post-engagement.js +2 -2
  13. package/dist/tools/extract-post-engagement.js.map +1 -1
  14. package/dist/tools/fetch-page-content.d.ts.map +1 -1
  15. package/dist/tools/fetch-page-content.js +1 -1
  16. package/dist/tools/fetch-page-content.js.map +1 -1
  17. package/dist/tools/find-email.js +2 -2
  18. package/dist/tools/find-email.js.map +1 -1
  19. package/dist/tools/find-emails-bulk.js +2 -2
  20. package/dist/tools/find-emails-bulk.js.map +1 -1
  21. package/dist/tools/find-emails.js +2 -2
  22. package/dist/tools/find-emails.js.map +1 -1
  23. package/dist/tools/find-influencers.js +2 -2
  24. package/dist/tools/find-influencers.js.map +1 -1
  25. package/dist/tools/find-people.d.ts.map +1 -1
  26. package/dist/tools/find-people.js +2 -2
  27. package/dist/tools/find-people.js.map +1 -1
  28. package/dist/tools/find-phone-bulk.d.ts.map +1 -1
  29. package/dist/tools/find-phone-bulk.js +2 -2
  30. package/dist/tools/find-phone-bulk.js.map +1 -1
  31. package/dist/tools/find-phone.d.ts +1 -1
  32. package/dist/tools/find-phone.d.ts.map +1 -1
  33. package/dist/tools/find-phone.js +2 -2
  34. package/dist/tools/find-phone.js.map +1 -1
  35. package/dist/tools/get-endpoint-details.js +2 -2
  36. package/dist/tools/get-endpoint-details.js.map +1 -1
  37. package/dist/tools/get-place-reviews.d.ts.map +1 -1
  38. package/dist/tools/get-place-reviews.js +2 -2
  39. package/dist/tools/get-place-reviews.js.map +1 -1
  40. package/dist/tools/list-data-sources.js +2 -2
  41. package/dist/tools/list-data-sources.js.map +1 -1
  42. package/dist/tools/search-ads.d.ts +1 -1
  43. package/dist/tools/search-ads.d.ts.map +1 -1
  44. package/dist/tools/search-ads.js +2 -2
  45. package/dist/tools/search-ads.js.map +1 -1
  46. package/dist/tools/search-companies.d.ts.map +1 -1
  47. package/dist/tools/search-companies.js +5 -5
  48. package/dist/tools/search-companies.js.map +1 -1
  49. package/dist/tools/search-places.d.ts +1 -1
  50. package/dist/tools/search-places.js +1 -1
  51. package/dist/tools/search-reddit.d.ts +1 -1
  52. package/dist/tools/search-reddit.d.ts.map +1 -1
  53. package/dist/tools/search-reddit.js +2 -2
  54. package/dist/tools/search-reddit.js.map +1 -1
  55. package/dist/tools/verify-emails-bulk.js +1 -1
  56. package/dist/tools/verify-emails-bulk.js.map +1 -1
  57. package/dist/utils/provider-display.js +5 -5
  58. package/dist/utils/provider-display.js.map +1 -1
  59. package/dist/utils/provider-resolver.d.ts +1 -0
  60. package/dist/utils/provider-resolver.d.ts.map +1 -1
  61. package/dist/utils/provider-resolver.js +3 -1
  62. package/dist/utils/provider-resolver.js.map +1 -1
  63. package/package.json +1 -1
  64. package/src/provider-availability.ts +27 -0
  65. package/src/registry.ts +35 -19
  66. package/src/tools/call-endpoint.ts +1 -1
  67. package/src/tools/extract-post-engagement.ts +2 -2
  68. package/src/tools/fetch-page-content.ts +1 -1
  69. package/src/tools/find-email.ts +2 -2
  70. package/src/tools/find-emails-bulk.ts +2 -2
  71. package/src/tools/find-emails.ts +2 -2
  72. package/src/tools/find-influencers.ts +2 -2
  73. package/src/tools/find-people.ts +2 -2
  74. package/src/tools/find-phone-bulk.ts +2 -2
  75. package/src/tools/find-phone.ts +2 -2
  76. package/src/tools/get-endpoint-details.ts +2 -2
  77. package/src/tools/get-place-reviews.ts +2 -2
  78. package/src/tools/list-data-sources.ts +2 -2
  79. package/src/tools/search-ads.ts +2 -2
  80. package/src/tools/search-companies.ts +5 -5
  81. package/src/tools/search-places.ts +1 -1
  82. package/src/tools/search-reddit.ts +2 -2
  83. package/src/tools/verify-emails-bulk.ts +1 -1
  84. package/src/utils/provider-display.ts +5 -5
  85. package/src/utils/provider-resolver.ts +4 -1
  86. package/test-linkupapi-live.ts +1 -1
  87. package/test-phone-live.ts +1 -1
  88. package/test-search-live.ts +1 -1
  89. package/tests/live/companyenrich-hybrid-probe.ts +1 -1
  90. package/tests/provider-availability.test.ts +83 -0
  91. package/tests/registry-enrich-person.test.ts +7 -5
  92. package/tests/registry-find-people.test.ts +2 -2
  93. package/tests/registry.test.ts +32 -32
  94. package/tests/tools/call-endpoint.test.ts +4 -4
  95. package/tests/tools/get-endpoint-details.test.ts +4 -4
  96. package/tests/tools/list-data-sources.test.ts +1 -1
  97. package/tests/tools/search-endpoints.test.ts +2 -2
  98. package/tests/utils/provider-display.test.ts +2 -2
@@ -46,11 +46,11 @@ const CATALOG: Array<{ capability: InsightCapability; tool: string; label: strin
46
46
  // the accurate coverage. Keyed by capability -> branded display name.
47
47
  const VENDOR_STRENGTH_OVERRIDES: Partial<Record<InsightCapability, Record<string, string>>> = {
48
48
  search_companies: {
49
- LimaData: 'LinkedIn-based company discovery, incl. Sales Navigator URLs',
49
+ 'Lima Data': 'LinkedIn-based company discovery, incl. Sales Navigator URLs',
50
50
  LinkupAPI: 'live LinkedIn company search, incl. recently-funded and hiring companies',
51
51
  },
52
52
  find_email: {
53
- LimaData: 'work-email lookup by name + domain or LinkedIn URL',
53
+ 'Lima Data': 'work-email lookup by name + domain or LinkedIn URL',
54
54
  },
55
55
  enrich_company: {
56
56
  CompanyEnrich: 'firmographic enrichment from a domain, name or LinkedIn URL',
@@ -5,7 +5,7 @@ import { getProvidersForCapability } from '../utils/provider-resolver.js'
5
5
  export const searchAdsName = 'search_ads'
6
6
 
7
7
  export const searchAdsDescription =
8
- 'Search live ad creatives across 4 ad libraries (Google Ads Transparency, LinkedIn Ad Library, Meta Ads Library, Twitter/X Ads) — a high-signal GTM input for competitive intelligence, ICP refinement, and pitch personalization. Routes by input: domains/advertiser_ids → Google only; search_urls → LinkedIn only; bare query → Google → Meta → Twitter waterfall. Use platform="google"|"linkedin"|"meta"|"twitter" to pin to one platform. All providers are async (~10–60s). Cost: ~5 credits per call (Twitter charges 1 credit per ad returned). Credits are fully refunded when a run returns zero ads. NOTE: Google Ads creatives return image URLs + creative IDs, not ad copy text — open the image URLs to read the ad. There is no "currently running only" filter; results can span past campaigns.'
8
+ 'Search live ad creatives across 4 ad libraries (Google Ads Transparency, LinkedIn Ad Library, Meta Ads Library, Twitter/X Ads) — a high-signal GTM input for competitive intelligence, ICP refinement, and pitch personalization. Routes by input: domains/advertiser_ids → Google only; search_urls → LinkedIn only; bare query → Google → Meta → Twitter waterfall. Use platform="google"|"linkedin"|"meta"|"twitter" to pin to one platform. All providers are async (~10–60s). Pricing is returned in the response credit headers; credits are fully refunded when a run returns zero ads. NOTE: Google Ads creatives return image URLs + creative IDs, not ad copy text — open the image URLs to read the ad. There is no "currently running only" filter; results can span past campaigns.'
9
9
 
10
10
  export const searchAdsSchema = {
11
11
  query: z.string().optional().describe('Advertiser/company name or keyword. Routes to Google→Meta→Twitter when no platform-specific input is set.'),
@@ -16,7 +16,7 @@ export const searchAdsSchema = {
16
16
  search_urls: z.array(z.string()).optional().describe('Pre-built LinkedIn Ad Library URLs from linkedin.com/ad-library. Routes to LinkedIn only. Build at linkedin.com/ad-library/search using accountOwner/countries/dateOption filters.'),
17
17
 
18
18
  country: z.string().optional().describe('ISO country code (2-letter for Meta/Twitter; longer accepted by Google as region).'),
19
- max_results: z.number().int().min(1).max(200).default(25).describe('Max ads per provider call (1–200). Per-provider caps: Google 1000, LinkedIn 200, Meta 200, Twitter 100. Twitter charges 1 credit per ad returned.'),
19
+ max_results: z.number().int().min(1).max(200).default(25).describe('Max ads per provider call (1–200). Per-provider caps: Google 1000, LinkedIn 200, Meta 200, Twitter 100. Pricing is returned in the response credit headers.'),
20
20
 
21
21
  ad_type: z.enum(['ALL', 'POLITICAL_AND_ISSUE_ADS']).optional().describe('Meta only. Defaults to ALL.'),
22
22
 
@@ -6,7 +6,7 @@ export const searchCompaniesName = 'search_companies'
6
6
 
7
7
  export const searchCompaniesDescription =
8
8
  'Search B2B companies by industry, size, geography, founding year, tech stack, funding, revenue, exclusion lists, and hiring signals. To build a lookalike TAM from example accounts, pass `similar_to_domains` (seed company domains) — the request routes to DiscoLike\'s lookalike discovery over 70M companies, optionally narrowed by keywords/geo/size. Routing is otherwise automatic: strict firmographic filters (revenue, employee bands, exclusion lists) get high-precision providers first; tech-stack and funding-stage filters route to specialized providers; loose keyword/geo searches fall back to broad providers; a LinkedIn Sales Navigator search URL enables URL-based discovery as a final fallback. For buying-intent topic discovery (find companies associated with intent keywords like "sales automation" or "lead generation"), pass `keywords` with `use_providers: ["theirstack"]` — this routes to TheirStack\'s intent-keyword index instead of the default full-text match. For "fast-growing" companies there is no exact workforce-growth % filter anywhere — pass min_workforce_growth_pct (and/or is_hiring / a recent-funding filter) and ColdIQ routes to active-hiring and recently-funded providers, the practical proxies for growth. ColdIQ automatically picks the best provider — pass use_providers only if you need a specific tool. Default limit: 25. ' +
9
- 'Response is compact by default (name, domain, website, linkedin_url, employees, revenue, industry, country, city, founded_year, categories) — everything needed to act on a company or feed it into find_people. Set fields="verbose" only when you specifically need the full firmographic record (funding history, technologies, keywords, NAICS codes, all socials, descriptions).'
9
+ 'Employee and revenue bounds are exact when the selected provider returns exact values. Bucket-only providers widen non-aligned bounds to overlapping buckets and disclose that in `_meta.filter_approximations`. Response is compact by default (name, domain, website, linkedin_url, employees, revenue, industry, country, city, founded_year, categories) — everything needed to act on a company or feed it into find_people. Set fields="verbose" only when you specifically need the full firmographic record (funding history, technologies, keywords, NAICS codes, all socials, descriptions).'
10
10
 
11
11
  export const searchCompaniesSchema = {
12
12
  keywords: z.array(z.string()).optional().describe('Free-text topics, business models, or themes — e.g. ["SaaS", "fintech", "cybersecurity"]. Best for most discovery: matched broadly across company name, description, and tags. Prefer this over `industries` for terms like "SaaS" that are business models rather than formal industry categories.'),
@@ -15,8 +15,8 @@ export const searchCompaniesSchema = {
15
15
  locations: z.array(z.string()).optional().describe('City/region filters as free-text strings (e.g. ["Paris, France", "San Francisco, California"]). Narrows results beyond country-level.'),
16
16
  industries: z.array(z.string()).optional().describe('Formal industry-taxonomy categories — e.g. ["Financial Services", "Hospitals and Health Care"]. Use only for strict industry classification; for business models or themes like "SaaS" or "fintech", use `keywords` instead. Both fields are searched together when set.'),
17
17
  technologies: z.array(z.string()).optional().describe('Tech stack filters — routes to tech-aware providers. Pass technology names or slugs e.g. ["Salesforce", "HubSpot"].'),
18
- min_employees: z.number().optional().describe('Minimum employee count'),
19
- max_employees: z.number().optional().describe('Maximum employee count'),
18
+ min_employees: z.number().optional().describe('Minimum employee count. Bucket-only providers may widen non-aligned bounds; see _meta.filter_approximations.'),
19
+ max_employees: z.number().optional().describe('Maximum employee count. Bucket-only providers may widen non-aligned bounds; see _meta.filter_approximations.'),
20
20
  min_founded_year: z.number().optional().describe('Earliest founding year'),
21
21
  max_founded_year: z.number().optional().describe('Latest founding year'),
22
22
  funding_stages: z.array(z.string()).optional().describe('Funding stage filters e.g. ["seed", "series_a", "series_b"]. Exact stage IDs vary by provider.'),
@@ -24,8 +24,8 @@ export const searchCompaniesSchema = {
24
24
  max_funding_amount: z.number().optional().describe('Maximum total funding raised (USD)'),
25
25
  min_funding_year: z.number().optional().describe('Earliest year of last funding round'),
26
26
  max_funding_year: z.number().optional().describe('Latest year of last funding round'),
27
- min_revenue: z.number().optional().describe('Minimum estimated annual revenue (USD)'),
28
- max_revenue: z.number().optional().describe('Maximum estimated annual revenue (USD)'),
27
+ min_revenue: z.number().optional().describe('Minimum estimated annual revenue (USD). Bucket-only providers may widen non-aligned bounds; see _meta.filter_approximations.'),
28
+ max_revenue: z.number().optional().describe('Maximum estimated annual revenue (USD). Bucket-only providers may widen non-aligned bounds; see _meta.filter_approximations.'),
29
29
  exclude_domains: z.array(z.string()).optional().describe('Company domains to exclude from results'),
30
30
  exclude_industries: z.array(z.string()).optional().describe('Industry names to exclude from results'),
31
31
  exclude_countries: z.array(z.string()).optional().describe('2-letter ISO country codes to exclude from results'),
@@ -5,7 +5,7 @@ import { getProvidersForCapability } from '../utils/provider-resolver.js'
5
5
  export const searchPlacesName = 'search_places'
6
6
 
7
7
  export const searchPlacesDescription =
8
- 'Search local businesses and places via 2 providers (Openmart Search, Google Maps Scraper) — useful for territory mapping, local-services prospecting, restaurant/retail/vertical research. Routes by input: structured filters or country in {US,CA,AU,PR,NZ} → Openmart (sync, ~1s) first, then Google Maps Scraper (async, ~30–120s) as fallback or for global coverage. Use provider="openmart"|"google_maps" to pin to one. Cost: 1 credit per place returned (both providers). Results do NOT include review text — to fetch a place\'s reviews, pass its `url` to get_place_reviews.'
8
+ 'Search local businesses and places via 2 providers (Openmart Search, Google Maps Scraper) — useful for territory mapping, local-services prospecting, restaurant/retail/vertical research. Routes by input: structured filters or country in {US,CA,AU,PR,NZ} → Openmart (sync, ~1s) first, then Google Maps Scraper (async, ~30–120s) as fallback or for global coverage. Use provider="openmart"|"google_maps" to pin to one. Pricing is returned in the response credit headers. Results do NOT include review text — to fetch a place\'s reviews, pass its `url` to get_place_reviews.'
9
9
 
10
10
  export const searchPlacesSchema = {
11
11
  query: z.string().optional().describe('Free-text query (e.g. "coffee shops in Brooklyn", "law firm New York"). Used by both providers.'),
@@ -5,7 +5,7 @@ import { getProvidersForCapability } from '../utils/provider-resolver.js'
5
5
  export const searchRedditName = 'search_reddit'
6
6
 
7
7
  export const searchRedditDescription =
8
- 'Scrape Reddit posts, comments, communities, or users via 1 provider (Reddit Scraper). Provide subreddit/post/user URLs and/or a keyword query. Optionally scope a query to one community, sort, filter by time, include comments, and limit by date. Async (~30–120s). Cost: 1 credit per item returned.'
8
+ 'Scrape Reddit posts, comments, communities, or users via 1 provider (Reddit Scraper). Provide subreddit/post/user URLs and/or a keyword query. Optionally scope a query to one community, sort, filter by time, include comments, and limit by date. Async (~30–120s). Pricing is returned in the response credit headers.'
9
9
 
10
10
  export const searchRedditSchema = {
11
11
  start_urls: z.array(z.string().url()).max(25).optional()
@@ -21,7 +21,7 @@ export const searchRedditSchema = {
21
21
  time: z.enum(['hour', 'day', 'week', 'month', 'year', 'all']).optional()
22
22
  .describe('Time filter.'),
23
23
  limit: z.number().int().min(1).max(200).default(10)
24
- .describe('Max items to return (1–200). 1 credit per item.'),
24
+ .describe('Max items to return (1–200). Pricing is returned in the response credit headers.'),
25
25
  max_comments: z.number().int().min(0).max(1000).optional()
26
26
  .describe('Max comments to fetch per post. Set 0 to skip comments.'),
27
27
  include_comments: z.boolean().optional()
@@ -12,7 +12,7 @@ export const verifyEmailsBulkDescription =
12
12
  'means the address was SKIPPED (rate-limited / never checked) and is safe to re-submit — do not discard those contacts; ' +
13
13
  'only `provider_indeterminate` (or no reason) is a real "couldn\'t determine". ' +
14
14
  'The 202 response includes `credits_reserved` (worst-case hold, settled to actual on completion) and a `low_balance_warning` when low. ' +
15
- 'PROVIDER: runs on BounceBan by default — it resolves catch-all/risky addresses without sending (~0.2 credits per conclusively-verified email). If the BounceBan submit fails, the job AUTOMATICALLY falls back to Icypeas (0.1 credits/verified, no catch-all resolution) — bulk verify survives a single-provider outage. Pinning use_providers:["bounceban"] or ["icypeas"] is STRICT: a pinned backend\'s failure returns an error instead of switching. Any other verifier returns a clear error (never silently ignored). ' +
15
+ 'PROVIDER: runs on BounceBan by default — it resolves catch-all/risky addresses without sending. If the BounceBan submit fails, the job AUTOMATICALLY falls back to Icypeas (no catch-all resolution) — bulk verify survives a single-provider outage. Pinning use_providers:["bounceban"] or ["icypeas"] is STRICT: a pinned backend\'s failure returns an error instead of switching. Any other verifier returns a clear error (never silently ignored). ' +
16
16
  'For a single email use verify_email instead.'
17
17
 
18
18
  export const verifyEmailsBulkSchema = {
@@ -32,11 +32,11 @@ const DISPLAY_NAMES: Record<string, string> = {
32
32
  'signalbase-job-change': 'SignalBase',
33
33
  blitzapi: 'BlitzAPI',
34
34
  'blitzapi-reverse-email': 'BlitzAPI',
35
- limadata: 'LimaData',
36
- 'limadata-prospect-filter': 'LimaData',
37
- 'limadata-prospect-url': 'LimaData',
38
- 'limadata-work-email': 'LimaData',
39
- 'limadata-work-email-linkedin': 'LimaData',
35
+ limadata: 'Lima Data',
36
+ 'limadata-prospect-filter': 'Lima Data',
37
+ 'limadata-prospect-url': 'Lima Data',
38
+ 'limadata-work-email': 'Lima Data',
39
+ 'limadata-work-email-linkedin': 'Lima Data',
40
40
  predictleads: 'PredictLeads',
41
41
  'predictleads-financing': 'PredictLeads',
42
42
  'predictleads-news': 'PredictLeads',
@@ -1,12 +1,13 @@
1
1
  import { listCapabilityProviderIds, getProviderApplicabilityGuard } from '../registry.js'
2
2
  import type { Capability } from '../registry.js'
3
+ import { isProviderEnabled } from '../provider-availability.js'
3
4
  import { fuzzyMatch } from './fuzzy.js'
4
5
 
5
6
  // find_emails uses a custom waterfall — its providers are not in the registry.
6
7
  // Exported so find-emails.ts stays in sync without a second hardcoded list.
7
8
  // Order = auto-route execution order: bulk providers first (Steps 1-3), then
8
9
  // the single-find_email fallback providers used for stragglers (Step 4).
9
- export const FIND_EMAILS_PROVIDERS = [
10
+ export const CONFIGURED_FIND_EMAILS_PROVIDERS = [
10
11
  'limadata-work-email',
11
12
  'leadmagic',
12
13
  'prospeo',
@@ -20,6 +21,8 @@ export const FIND_EMAILS_PROVIDERS = [
20
21
  'icypeas',
21
22
  ]
22
23
 
24
+ export const FIND_EMAILS_PROVIDERS = CONFIGURED_FIND_EMAILS_PROVIDERS.filter(isProviderEnabled)
25
+
23
26
  export function getProvidersForCapability(capability: Capability | 'find_emails'): string[] {
24
27
  if (capability === 'find_emails') return FIND_EMAILS_PROVIDERS
25
28
  return listCapabilityProviderIds(capability as Capability)
@@ -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)