@coldiq/mcp 5.4.6 → 5.4.8

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.
@@ -2,13 +2,13 @@ import { z } from 'zod';
2
2
  import { callApi } from '../client.js';
3
3
  export const getWebsiteVisitorsName = 'get_website_visitors';
4
4
  export const getWebsiteVisitorsDescription = 'Read the leads ColdIQ Visitor ID identified on one of your websites — the people who visited (with verified work email, title and LinkedIn where available) or the companies they belong to (with firmographics). ' +
5
- 'Use `type: "people"` for enriched contacts and `type: "companies"` for enriched accounts. To find `website_id`, call the free `GET /visitor-id/websites` endpoint through call_endpoint (api "visitor-id"). ' +
5
+ 'Use `type: "people"` for enriched contacts and `type: "companies"` for enriched accounts. To find `website_id`, call `list_website_visitors_sites`; to add a site, call `setup_website_visitors`. ' +
6
6
  'Results are paginated: pass the `cursor` from the previous response to fetch the next page. Free — identifications are billed when they are delivered, not when you read them.';
7
7
  export const getWebsiteVisitorsSchema = {
8
8
  website_id: z
9
9
  .number()
10
10
  .int()
11
- .describe('The tracked website to read leads from, as returned by GET /visitor-id/websites.'),
11
+ .describe('The tracked website to read leads from, as returned by `list_website_visitors_sites`.'),
12
12
  type: z
13
13
  .enum(['people', 'companies'])
14
14
  .describe('"people" returns identified contacts (email, title, LinkedIn); "companies" returns identified accounts (firmographics).'),
@@ -1 +1 @@
1
- {"version":3,"file":"get-website-visitors.js","sourceRoot":"","sources":["../../src/tools/get-website-visitors.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"get-website-visitors.js","sourceRoot":"","sources":["../../src/tools/get-website-visitors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { z } from 'zod';
2
+ export declare const listWebsiteVisitorsSitesName = "list_website_visitors_sites";
3
+ export declare const listWebsiteVisitorsSitesDescription: string;
4
+ export declare const listWebsiteVisitorsSitesSchema: {
5
+ website_id: z.ZodOptional<z.ZodNumber>;
6
+ };
7
+ export declare function listWebsiteVisitorsSitesHandler(input: Record<string, unknown>): Promise<{
8
+ content: {
9
+ type: "text";
10
+ text: string;
11
+ }[];
12
+ isError: boolean;
13
+ } | {
14
+ content: {
15
+ type: "text";
16
+ text: string;
17
+ }[];
18
+ }>;
19
+ //# sourceMappingURL=list-website-visitors-sites.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-website-visitors-sites.d.ts","sourceRoot":"","sources":["../../src/tools/list-website-visitors-sites.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,4BAA4B,gCAAgC,CAAA;AAEzE,eAAO,MAAM,mCAAmC,QAImE,CAAA;AAEnH,eAAO,MAAM,8BAA8B;;CAM1C,CAAA;AASD,wBAAsB,+BAA+B,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;GAsBnF"}
@@ -0,0 +1,43 @@
1
+ import { z } from 'zod';
2
+ import { callApi } from '../client.js';
3
+ export const listWebsiteVisitorsSitesName = 'list_website_visitors_sites';
4
+ export const listWebsiteVisitorsSitesDescription = 'List the websites ColdIQ Visitor ID tracks for this account — each with its `id`, `domain`, `status`, whether it is `identifying` right now, and its `monthly_limit` (credits, companies, people, used_credits). ' +
5
+ 'Pass `website_id` to get one website in full (pixel `embed_html` and `install` state; `install` null = state unknown, re-check later) together with its usage this period (people and companies identified, what it cost, and the live `credits_per_person` / `credits_per_company` — a person costs 6× a company; always quote these live values, never numbers from memory) — returned as `{ website, usage }`; when the usage read fails, `usage` is null and `usage_error` carries the status and message. ' +
6
+ 'Free. Use the `id` as `website_id` in `get_website_visitors`; add a new site with `setup_website_visitors` (a 409 there whose message mentions a deactivated workspace means the domain is held by a deactivated workspace — do not retry, tell the user to contact ColdIQ support; any other 409 means the domain is already in this list — reuse it). ' +
7
+ 'Any write (POST/PATCH/PUT/DELETE) returns 403 for a workspace member key — ask the workspace owner or an admin.';
8
+ export const listWebsiteVisitorsSitesSchema = {
9
+ website_id: z
10
+ .number()
11
+ .int()
12
+ .optional()
13
+ .describe('Return this one website with its install state and usage instead of the list.'),
14
+ };
15
+ function failure(error, status, data) {
16
+ return {
17
+ content: [{ type: 'text', text: JSON.stringify({ error, status, data }) }],
18
+ isError: true,
19
+ };
20
+ }
21
+ export async function listWebsiteVisitorsSitesHandler(input) {
22
+ if (input.website_id === undefined) {
23
+ const res = await callApi('GET', '/visitor-id/websites');
24
+ if (!res.ok)
25
+ return failure('Failed to list tracked websites', res.status, res.data);
26
+ return { content: [{ type: 'text', text: JSON.stringify({ data: res.data }) }] };
27
+ }
28
+ const websiteId = input.website_id;
29
+ const [website, usage] = await Promise.all([
30
+ callApi('GET', `/visitor-id/websites/${websiteId}`),
31
+ callApi('GET', `/visitor-id/websites/${websiteId}/usage`),
32
+ ]);
33
+ if (!website.ok)
34
+ return failure('Failed to read tracked website', website.status, website.data);
35
+ // A failed usage read must not discard the website the caller asked for: the pixel
36
+ // and install state stay useful on their own, so the usage gap is reported next to
37
+ // them instead of replacing them.
38
+ const data = usage.ok
39
+ ? { website: website.data, usage: usage.data }
40
+ : { website: website.data, usage: null, usage_error: { status: usage.status, data: usage.data } };
41
+ return { content: [{ type: 'text', text: JSON.stringify({ data }) }] };
42
+ }
43
+ //# sourceMappingURL=list-website-visitors-sites.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-website-visitors-sites.js","sourceRoot":"","sources":["../../src/tools/list-website-visitors-sites.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,MAAM,CAAC,MAAM,4BAA4B,GAAG,6BAA6B,CAAA;AAEzE,MAAM,CAAC,MAAM,mCAAmC,GAC9C,mNAAmN;IACnN,ifAAif;IACjf,0VAA0V;IAC1V,iHAAiH,CAAA;AAEnH,MAAM,CAAC,MAAM,8BAA8B,GAAG;IAC5C,UAAU,EAAE,CAAC;SACV,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,QAAQ,CAAC,+EAA+E,CAAC;CAC7F,CAAA;AAED,SAAS,OAAO,CAAC,KAAa,EAAE,MAAc,EAAE,IAAa;IAC3D,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACnF,OAAO,EAAE,IAAI;KACd,CAAA;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,+BAA+B,CAAC,KAA8B;IAClF,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,sBAAsB,CAAC,CAAA;QACxD,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,OAAO,CAAC,iCAAiC,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAA;QACpF,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;IAC3F,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,UAAoB,CAAA;IAC5C,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACzC,OAAO,CAAC,KAAK,EAAE,wBAAwB,SAAS,EAAE,CAAC;QACnD,OAAO,CAAC,KAAK,EAAE,wBAAwB,SAAS,QAAQ,CAAC;KAC1D,CAAC,CAAA;IACF,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,OAAO,OAAO,CAAC,gCAAgC,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;IAE/F,mFAAmF;IACnF,mFAAmF;IACnF,kCAAkC;IAClC,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE;QACnB,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE;QAC9C,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,CAAA;IAEnG,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAA;AACjF,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { z } from 'zod';
2
+ export declare const setupWebsiteVisitorsName = "setup_website_visitors";
3
+ export declare const setupWebsiteVisitorsDescription: string;
4
+ export declare const setupWebsiteVisitorsSchema: {
5
+ domain: z.ZodString;
6
+ name: z.ZodOptional<z.ZodString>;
7
+ };
8
+ export declare function setupWebsiteVisitorsHandler(input: Record<string, unknown>): Promise<{
9
+ content: {
10
+ type: "text";
11
+ text: string;
12
+ }[];
13
+ isError: boolean;
14
+ } | {
15
+ content: {
16
+ type: "text";
17
+ text: string;
18
+ }[];
19
+ isError?: undefined;
20
+ }>;
21
+ //# sourceMappingURL=setup-website-visitors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-website-visitors.d.ts","sourceRoot":"","sources":["../../src/tools/setup-website-visitors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,wBAAwB,2BAA2B,CAAA;AAEhE,eAAO,MAAM,+BAA+B,QAOyB,CAAA;AAErE,eAAO,MAAM,0BAA0B;;;CAMtC,CAAA;AAED,wBAAsB,2BAA2B,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;GAiB/E"}
@@ -0,0 +1,36 @@
1
+ import { z } from 'zod';
2
+ import { callApi } from '../client.js';
3
+ export const setupWebsiteVisitorsName = 'setup_website_visitors';
4
+ export const setupWebsiteVisitorsDescription = 'Set up ColdIQ Visitor ID on a website the user owns: creates the tracked website for a domain and returns it with the ready-to-paste tracking pixel (`embed_html`, `script_url`) and its install state (`install.is_installed`). ' +
5
+ '`install` can be null: that means the install state is unknown — re-check later, never report "not installed" from null. ' +
6
+ 'One pixel per domain: a 409 whose message mentions a deactivated workspace = the domain is held by a deactivated workspace — do not retry, tell the user to contact ColdIQ support; any other 409 = a pixel already exists for this domain — call `list_website_visitors_sites` and reuse it. ' +
7
+ 'Any write (POST/PATCH/PUT/DELETE) returns 403 for a workspace member key — ask the workspace owner or an admin. ' +
8
+ 'Setup and reads are free. Identifications are billed once the pixel is live, within the site\'s monthly limit; a person costs 6× a company. ' +
9
+ 'Always quote the live `credits_per_person` / `credits_per_company` from `list_website_visitors_sites` with a `website_id`, never numbers from memory. ' +
10
+ 'Read the identified leads afterwards with `get_website_visitors`.';
11
+ export const setupWebsiteVisitorsSchema = {
12
+ domain: z
13
+ .string()
14
+ .min(1)
15
+ .describe('Website domain the tracking pixel will identify visitors for, e.g. "coldiq.com".'),
16
+ name: z.string().optional().describe('Display name for the website (defaults to the domain).'),
17
+ };
18
+ export async function setupWebsiteVisitorsHandler(input) {
19
+ const body = { domain: String(input.domain) };
20
+ if (input.name)
21
+ body.name = String(input.name);
22
+ const res = await callApi('POST', '/visitor-id/websites', body);
23
+ if (!res.ok) {
24
+ return {
25
+ content: [
26
+ {
27
+ type: 'text',
28
+ text: JSON.stringify({ error: 'Failed to set up website visitor identification', status: res.status, data: res.data }),
29
+ },
30
+ ],
31
+ isError: true,
32
+ };
33
+ }
34
+ return { content: [{ type: 'text', text: JSON.stringify({ data: res.data }) }] };
35
+ }
36
+ //# sourceMappingURL=setup-website-visitors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-website-visitors.js","sourceRoot":"","sources":["../../src/tools/setup-website-visitors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,MAAM,CAAC,MAAM,wBAAwB,GAAG,wBAAwB,CAAA;AAEhE,MAAM,CAAC,MAAM,+BAA+B,GAC1C,mOAAmO;IACnO,2HAA2H;IAC3H,gSAAgS;IAChS,kHAAkH;IAClH,8IAA8I;IAC9I,wJAAwJ;IACxJ,mEAAmE,CAAA;AAErE,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,kFAAkF,CAAC;IAC/F,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wDAAwD,CAAC;CAC/F,CAAA;AAED,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,KAA8B;IAC9E,MAAM,IAAI,GAA4B,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAA;IACtE,IAAI,KAAK,CAAC,IAAI;QAAE,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAE9C,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,sBAAsB,EAAE,IAAI,CAAC,CAAA;IAC/D,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,iDAAiD,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;iBACvH;aACF;YACD,OAAO,EAAE,IAAI;SACd,CAAA;IACH,CAAC;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"}
@@ -9,8 +9,8 @@ export const CONFIGURED_FIND_EMAILS_PROVIDERS = [
9
9
  'findymail',
10
10
  'leadmagic',
11
11
  'prospeo',
12
- 'apollo-people-match',
13
12
  'limadata-work-email',
13
+ 'apollo-people-match',
14
14
  'icypeas',
15
15
  'wiza',
16
16
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coldiq/mcp",
3
- "version": "5.4.6",
3
+ "version": "5.4.8",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/index.ts CHANGED
@@ -179,6 +179,20 @@ import {
179
179
  getWebsiteVisitorsHandler,
180
180
  } from './tools/get-website-visitors.js'
181
181
 
182
+ import {
183
+ setupWebsiteVisitorsName,
184
+ setupWebsiteVisitorsDescription,
185
+ setupWebsiteVisitorsSchema,
186
+ setupWebsiteVisitorsHandler,
187
+ } from './tools/setup-website-visitors.js'
188
+
189
+ import {
190
+ listWebsiteVisitorsSitesName,
191
+ listWebsiteVisitorsSitesDescription,
192
+ listWebsiteVisitorsSitesSchema,
193
+ listWebsiteVisitorsSitesHandler,
194
+ } from './tools/list-website-visitors-sites.js'
195
+
182
196
  import {
183
197
  listDataSourcesName,
184
198
  listDataSourcesDescription,
@@ -268,9 +282,11 @@ registerPublicTool(listSkillsName, listSkillsDescription, listSkillsSchema, list
268
282
  registerPublicTool(loadSkillName, loadSkillDescription, loadSkillSchema, loadSkillHandler)
269
283
  registerPublicTool(extractPostEngagementName, withKnowledgeFirstRequirement(extractPostEngagementDescription), extractPostEngagementSchema, extractPostEngagementHandler)
270
284
  registerPublicTool(getPlaceReviewsName, withKnowledgeFirstRequirement(getPlaceReviewsDescription), getPlaceReviewsSchema, getPlaceReviewsHandler)
271
- // ColdIQ Visitor ID: read the leads identified on the account's own websites.
272
- // Listing the websites themselves needs no tool search_endpoints/call_endpoint
273
- // already reach GET /visitor-id/websites.
285
+ // ColdIQ Visitor ID: set up a tracked website (one pixel per domain), list the
286
+ // account's websites (with install state + usage), read the leads identified on them.
287
+ // All three are free; identifications are billed when delivered.
288
+ registerPublicTool(setupWebsiteVisitorsName, withKnowledgeFirstRequirement(setupWebsiteVisitorsDescription), setupWebsiteVisitorsSchema, setupWebsiteVisitorsHandler)
289
+ registerPublicTool(listWebsiteVisitorsSitesName, withKnowledgeFirstRequirement(listWebsiteVisitorsSitesDescription), listWebsiteVisitorsSitesSchema, listWebsiteVisitorsSitesHandler)
274
290
  registerPublicTool(getWebsiteVisitorsName, withKnowledgeFirstRequirement(getWebsiteVisitorsDescription), getWebsiteVisitorsSchema, getWebsiteVisitorsHandler)
275
291
  // Generic endpoint discovery + invoke: reach any integrated provider without a dedicated tool.
276
292
  registerPublicTool(searchEndpointsName, withKnowledgeFirstRequirement(searchEndpointsDescription), searchEndpointsSchema, searchEndpointsHandler)
package/src/registry.ts CHANGED
@@ -475,9 +475,12 @@ const searchCompaniesProviders: ProviderEntry[] = [
475
475
  id: 'apollo',
476
476
  endpoint: '/apollo/organizations/search',
477
477
  method: 'POST',
478
- priority: 8,
478
+ // Keep Apollo behind the normal company databases. Its limited seat is a
479
+ // recovery source, before the costly Lima Data prospect and DiscoLike tails.
480
+ priority: 19,
479
481
  isApplicable: (input) => {
480
482
  // Apollo has no founded-year, technologies, funding, revenue, exclusion, or hiring filters — skip when set.
483
+ if (input.linkedin_search_url) return false
481
484
  if (typeof input.min_founded_year === 'number' || typeof input.max_founded_year === 'number') return false
482
485
  if (isNonEmptyArray(input.technologies)) return false
483
486
  if (isNonEmptyArray(input.funding_stages)) return false
@@ -891,7 +894,7 @@ const searchCompaniesProviders: ProviderEntry[] = [
891
894
  id: 'limadata-prospect-filter',
892
895
  endpoint: '/limadata/prospect/companies/filter',
893
896
  method: 'POST',
894
- priority: 12,
897
+ priority: 20,
895
898
  mapParams: (input) => {
896
899
  // Lima Data prospect filter uses [{filter_type, values}]. We can only reliably
897
900
  // map company_headcount (size buckets); industries/locations need LinkedIn IDs.
@@ -923,7 +926,7 @@ const searchCompaniesProviders: ProviderEntry[] = [
923
926
  id: 'limadata-prospect-url',
924
927
  endpoint: '/limadata/prospect/companies/search-url',
925
928
  method: 'POST',
926
- priority: 13,
929
+ priority: 21,
927
930
  mapParams: (input) => ({
928
931
  body: { search_url: input.linkedin_search_url as string },
929
932
  }),
@@ -1087,7 +1090,7 @@ const searchCompaniesProviders: ProviderEntry[] = [
1087
1090
  id: 'discolike',
1088
1091
  endpoint: '/discolike/discover',
1089
1092
  method: 'GET',
1090
- priority: 19,
1093
+ priority: 22,
1091
1094
  isApplicable: (input) => {
1092
1095
  if (!isNonEmptyArray(input.similar_to_domains) && !isNonEmptyArray(input.keywords) && !isNonEmptyArray(input.industries)) return false
1093
1096
  if (isNonEmptyArray(input.locations)) return false
@@ -1277,7 +1280,8 @@ const findPeopleProviders: ProviderEntry[] = [
1277
1280
  id: 'apollo',
1278
1281
  endpoint: '/apollo/people/search',
1279
1282
  method: 'POST',
1280
- priority: 2,
1283
+ // Apollo is the last active people-search recovery source in this mirror.
1284
+ priority: 11,
1281
1285
  // Apollo caps per_page at 100 and rejects larger values with a 400. The
1282
1286
  // find_people schema permits limit up to 500 (a waterfall-wide ceiling),
1283
1287
  // so clamp here to keep Apollo from breaking the request when the caller
@@ -1614,7 +1618,7 @@ const findEmailProviders: ProviderEntry[] = [
1614
1618
  id: 'limadata-work-email',
1615
1619
  endpoint: '/limadata/find/work-email',
1616
1620
  method: 'POST',
1617
- priority: 5,
1621
+ priority: 4,
1618
1622
  mapParams: (input) => {
1619
1623
  const fullName = [input.first_name, input.last_name].filter(Boolean).join(' ')
1620
1624
  return {
@@ -1711,7 +1715,7 @@ const findEmailProviders: ProviderEntry[] = [
1711
1715
  id: 'apollo-people-match',
1712
1716
  endpoint: '/apollo/people/match',
1713
1717
  method: 'POST',
1714
- priority: 4,
1718
+ priority: 5,
1715
1719
  isApplicable: (input) =>
1716
1720
  (typeof input.linkedin_url === 'string' && (input.linkedin_url as string).length > 0) ||
1717
1721
  (!!input.first_name && !!input.last_name && !!(input.domain || input.company_name)),
@@ -2066,7 +2070,7 @@ const enrichCompanyProviders: ProviderEntry[] = [
2066
2070
  id: 'apollo',
2067
2071
  endpoint: '/apollo/organizations/enrich',
2068
2072
  method: 'POST',
2069
- priority: 3,
2073
+ priority: 7,
2070
2074
  isApplicable: (input) => !!input.domain,
2071
2075
  mapParams: (input) => ({ body: { domain: input.domain } }),
2072
2076
  hasResult: (data) => {
@@ -2095,7 +2099,7 @@ const enrichCompanyProviders: ProviderEntry[] = [
2095
2099
  id: 'findymail',
2096
2100
  endpoint: '/findymail/search/company',
2097
2101
  method: 'POST',
2098
- priority: 9,
2102
+ priority: 6,
2099
2103
  mapParams: (input) => ({
2100
2104
  body: {
2101
2105
  ...(input.domain ? { domain: input.domain } : {}),
@@ -3296,7 +3300,7 @@ const enrichPersonProviders: ProviderEntry[] = [
3296
3300
  id: 'apollo-people-match',
3297
3301
  endpoint: '/apollo/people/match',
3298
3302
  method: 'POST',
3299
- priority: 4,
3303
+ priority: 11,
3300
3304
  mapParams: (input) => ({
3301
3305
  body: {
3302
3306
  first_name: input.first_name,
@@ -3414,7 +3418,7 @@ const enrichPersonProviders: ProviderEntry[] = [
3414
3418
  id: 'icypeas-reverse-email-lookup',
3415
3419
  endpoint: '/icypeas/reverse-email-lookup',
3416
3420
  method: 'POST',
3417
- priority: 11,
3421
+ priority: 12,
3418
3422
  isApplicable: (input) => typeof input.email === 'string' && (input.email as string).includes('@'),
3419
3423
  mapParams: (input) => ({ body: { email: input.email } }),
3420
3424
  hasResult: isIcypeasReverseEmailFound,
@@ -3423,7 +3427,7 @@ const enrichPersonProviders: ProviderEntry[] = [
3423
3427
  id: 'pdl-person-identify',
3424
3428
  endpoint: '/pdl/person/identify',
3425
3429
  method: 'POST',
3426
- priority: 12,
3430
+ priority: 13,
3427
3431
  mapParams: (input) => ({
3428
3432
  body: {
3429
3433
  email: input.email,
@@ -5,14 +5,14 @@ export const getWebsiteVisitorsName = 'get_website_visitors'
5
5
 
6
6
  export const getWebsiteVisitorsDescription =
7
7
  'Read the leads ColdIQ Visitor ID identified on one of your websites — the people who visited (with verified work email, title and LinkedIn where available) or the companies they belong to (with firmographics). ' +
8
- 'Use `type: "people"` for enriched contacts and `type: "companies"` for enriched accounts. To find `website_id`, call the free `GET /visitor-id/websites` endpoint through call_endpoint (api "visitor-id"). ' +
8
+ 'Use `type: "people"` for enriched contacts and `type: "companies"` for enriched accounts. To find `website_id`, call `list_website_visitors_sites`; to add a site, call `setup_website_visitors`. ' +
9
9
  'Results are paginated: pass the `cursor` from the previous response to fetch the next page. Free — identifications are billed when they are delivered, not when you read them.'
10
10
 
11
11
  export const getWebsiteVisitorsSchema = {
12
12
  website_id: z
13
13
  .number()
14
14
  .int()
15
- .describe('The tracked website to read leads from, as returned by GET /visitor-id/websites.'),
15
+ .describe('The tracked website to read leads from, as returned by `list_website_visitors_sites`.'),
16
16
  type: z
17
17
  .enum(['people', 'companies'])
18
18
  .describe('"people" returns identified contacts (email, title, LinkedIn); "companies" returns identified accounts (firmographics).'),
@@ -0,0 +1,49 @@
1
+ import { z } from 'zod'
2
+ import { callApi } from '../client.js'
3
+
4
+ export const listWebsiteVisitorsSitesName = 'list_website_visitors_sites'
5
+
6
+ export const listWebsiteVisitorsSitesDescription =
7
+ 'List the websites ColdIQ Visitor ID tracks for this account — each with its `id`, `domain`, `status`, whether it is `identifying` right now, and its `monthly_limit` (credits, companies, people, used_credits). ' +
8
+ 'Pass `website_id` to get one website in full (pixel `embed_html` and `install` state; `install` null = state unknown, re-check later) together with its usage this period (people and companies identified, what it cost, and the live `credits_per_person` / `credits_per_company` — a person costs 6× a company; always quote these live values, never numbers from memory) — returned as `{ website, usage }`; when the usage read fails, `usage` is null and `usage_error` carries the status and message. ' +
9
+ 'Free. Use the `id` as `website_id` in `get_website_visitors`; add a new site with `setup_website_visitors` (a 409 there whose message mentions a deactivated workspace means the domain is held by a deactivated workspace — do not retry, tell the user to contact ColdIQ support; any other 409 means the domain is already in this list — reuse it). ' +
10
+ 'Any write (POST/PATCH/PUT/DELETE) returns 403 for a workspace member key — ask the workspace owner or an admin.'
11
+
12
+ export const listWebsiteVisitorsSitesSchema = {
13
+ website_id: z
14
+ .number()
15
+ .int()
16
+ .optional()
17
+ .describe('Return this one website with its install state and usage instead of the list.'),
18
+ }
19
+
20
+ function failure(error: string, status: number, data: unknown) {
21
+ return {
22
+ content: [{ type: 'text' as const, text: JSON.stringify({ error, status, data }) }],
23
+ isError: true,
24
+ }
25
+ }
26
+
27
+ export async function listWebsiteVisitorsSitesHandler(input: Record<string, unknown>) {
28
+ if (input.website_id === undefined) {
29
+ const res = await callApi('GET', '/visitor-id/websites')
30
+ if (!res.ok) return failure('Failed to list tracked websites', res.status, res.data)
31
+ return { content: [{ type: 'text' as const, text: JSON.stringify({ data: res.data }) }] }
32
+ }
33
+
34
+ const websiteId = input.website_id as number
35
+ const [website, usage] = await Promise.all([
36
+ callApi('GET', `/visitor-id/websites/${websiteId}`),
37
+ callApi('GET', `/visitor-id/websites/${websiteId}/usage`),
38
+ ])
39
+ if (!website.ok) return failure('Failed to read tracked website', website.status, website.data)
40
+
41
+ // A failed usage read must not discard the website the caller asked for: the pixel
42
+ // and install state stay useful on their own, so the usage gap is reported next to
43
+ // them instead of replacing them.
44
+ const data = usage.ok
45
+ ? { website: website.data, usage: usage.data }
46
+ : { website: website.data, usage: null, usage_error: { status: usage.status, data: usage.data } }
47
+
48
+ return { content: [{ type: 'text' as const, text: JSON.stringify({ data }) }] }
49
+ }
@@ -0,0 +1,40 @@
1
+ import { z } from 'zod'
2
+ import { callApi } from '../client.js'
3
+
4
+ export const setupWebsiteVisitorsName = 'setup_website_visitors'
5
+
6
+ export const setupWebsiteVisitorsDescription =
7
+ 'Set up ColdIQ Visitor ID on a website the user owns: creates the tracked website for a domain and returns it with the ready-to-paste tracking pixel (`embed_html`, `script_url`) and its install state (`install.is_installed`). ' +
8
+ '`install` can be null: that means the install state is unknown — re-check later, never report "not installed" from null. ' +
9
+ 'One pixel per domain: a 409 whose message mentions a deactivated workspace = the domain is held by a deactivated workspace — do not retry, tell the user to contact ColdIQ support; any other 409 = a pixel already exists for this domain — call `list_website_visitors_sites` and reuse it. ' +
10
+ 'Any write (POST/PATCH/PUT/DELETE) returns 403 for a workspace member key — ask the workspace owner or an admin. ' +
11
+ 'Setup and reads are free. Identifications are billed once the pixel is live, within the site\'s monthly limit; a person costs 6× a company. ' +
12
+ 'Always quote the live `credits_per_person` / `credits_per_company` from `list_website_visitors_sites` with a `website_id`, never numbers from memory. ' +
13
+ 'Read the identified leads afterwards with `get_website_visitors`.'
14
+
15
+ export const setupWebsiteVisitorsSchema = {
16
+ domain: z
17
+ .string()
18
+ .min(1)
19
+ .describe('Website domain the tracking pixel will identify visitors for, e.g. "coldiq.com".'),
20
+ name: z.string().optional().describe('Display name for the website (defaults to the domain).'),
21
+ }
22
+
23
+ export async function setupWebsiteVisitorsHandler(input: Record<string, unknown>) {
24
+ const body: Record<string, unknown> = { domain: String(input.domain) }
25
+ if (input.name) body.name = String(input.name)
26
+
27
+ const res = await callApi('POST', '/visitor-id/websites', body)
28
+ if (!res.ok) {
29
+ return {
30
+ content: [
31
+ {
32
+ type: 'text' as const,
33
+ text: JSON.stringify({ error: 'Failed to set up website visitor identification', status: res.status, data: res.data }),
34
+ },
35
+ ],
36
+ isError: true,
37
+ }
38
+ }
39
+ return { content: [{ type: 'text' as const, text: JSON.stringify({ data: res.data }) }] }
40
+ }
@@ -11,8 +11,8 @@ export const CONFIGURED_FIND_EMAILS_PROVIDERS = [
11
11
  'findymail',
12
12
  'leadmagic',
13
13
  'prospeo',
14
- 'apollo-people-match',
15
14
  'limadata-work-email',
15
+ 'apollo-people-match',
16
16
  'icypeas',
17
17
  'wiza',
18
18
  ]
@@ -310,7 +310,7 @@ describe('executor postFilter integration', () => {
310
310
  })
311
311
 
312
312
  it('invokes Apollo postFilter — strips accounts and drops wrong-country orgs → falls through to error', async () => {
313
- // Apollo is at priority 7. Mock all higher-priority providers to return empty so Apollo is reached.
313
+ // Apollo is a late recovery source. Mock earlier providers so Apollo is reached.
314
314
  // Apollo returns orgs with wrong country + accounts key.
315
315
  // postFilter should: strip accounts, drop German org, keep no French orgs → hasResult false → error.
316
316
  let callCount = 0
@@ -348,7 +348,7 @@ describe('executor postFilter integration', () => {
348
348
  })
349
349
 
350
350
  it('passes raw data through unchanged when postFilter is not set (enrich_company)', async () => {
351
- // enrich_company providers (companyenrich, apollo, pdl) have no postFilter.
351
+ // Company enrichment providers have no postFilter.
352
352
  // Verify a successful response is returned as-is.
353
353
  globalThis.fetch = vi.fn(async () =>
354
354
  new Response(JSON.stringify({ name: 'ColdIQ', domain: 'coldiq.com' }), { status: 200 })
@@ -133,8 +133,8 @@ describe('apollo-people-match', () => {
133
133
  expect(p().hasResult({})).toBe(false)
134
134
  })
135
135
 
136
- it('priority is 4', () => {
137
- expect(p().priority).toBe(4)
136
+ it('priority is 11', () => {
137
+ expect(p().priority).toBe(11)
138
138
  })
139
139
  })
140
140
 
@@ -450,8 +450,8 @@ describe('pdl-person-identify', () => {
450
450
  expect(p().hasResult({ status: 404, matches: [] })).toBe(false)
451
451
  })
452
452
 
453
- it('priority is 12 (last fallback)', () => {
454
- expect(p().priority).toBe(12)
453
+ it('priority is 13 (last dormant fallback)', () => {
454
+ expect(p().priority).toBe(13)
455
455
  })
456
456
  })
457
457
 
@@ -897,8 +897,9 @@ describe('find_people priority ordering', () => {
897
897
  expect(providers()[0].id).toBe('leadsfactory')
898
898
  })
899
899
 
900
- it('findymail-search-employees is last in the wired set (priority 10)', () => {
900
+ it('Apollo is last in the active wired set', () => {
901
901
  const ps = providers()
902
- expect(ps[ps.length - 1].id).toBe('findymail-search-employees')
902
+ expect(ps[ps.length - 1].id).toBe('apollo')
903
+ expect(ps[ps.length - 1].priority).toBe(11)
903
904
  })
904
905
  })
@@ -291,6 +291,11 @@ describe('apollo search_companies', () => {
291
291
  const body = p().mapParams({ keywords: ['SaaS'] }).body as Record<string, unknown>
292
292
  expect(body.limit).toBe(25)
293
293
  })
294
+
295
+ it('is a recovery source and does not handle a LinkedIn search URL', () => {
296
+ expect(p().priority).toBe(19)
297
+ expect(p().isApplicable?.({ linkedin_search_url: 'https://www.linkedin.com/sales/search/company' })).toBe(false)
298
+ })
294
299
  })
295
300
 
296
301
  describe('theirstack search_companies', () => {
@@ -418,9 +423,13 @@ describe('search_companies priority ordering', () => {
418
423
  }
419
424
  })
420
425
 
421
- it('discolike is last (priority 19), after ai-ark-companies (18)', () => {
426
+ it('keeps Apollo before the costly tail and DiscoLike last', () => {
422
427
  const ps = providers()
423
428
  expect(ps[ps.length - 1].id).toBe('discolike')
424
- expect(ps[ps.length - 2].id).toBe('ai-ark-companies')
429
+ expect(ps[ps.length - 1].priority).toBe(22)
430
+ expect(ps.findIndex((provider) => provider.id === 'ai-ark-companies'))
431
+ .toBeLessThan(ps.findIndex((provider) => provider.id === 'apollo'))
432
+ expect(ps.findIndex((provider) => provider.id === 'apollo'))
433
+ .toBeLessThan(ps.findIndex((provider) => provider.id === 'limadata-prospect-filter'))
425
434
  })
426
435
  })
@@ -472,17 +472,17 @@ describe('registry', () => {
472
472
  'fullenrich',
473
473
  'theirstack',
474
474
  'signalbase',
475
- 'apollo',
476
475
  'limadata',
477
476
  'predictleads',
478
477
  'sumble',
479
- 'limadata-prospect-filter',
480
- 'limadata-prospect-url',
481
478
  'linkupapi-search',
482
479
  'linkupapi-fundraising',
483
480
  'linkupapi-hiring',
484
481
  'prospeo-search-company',
485
482
  'ai-ark-companies',
483
+ 'apollo',
484
+ 'limadata-prospect-filter',
485
+ 'limadata-prospect-url',
486
486
  'discolike',
487
487
  ])
488
488
  })
@@ -695,11 +695,11 @@ describe('registry', () => {
695
695
  expect(wiza.priority).toBe(7)
696
696
  })
697
697
 
698
- it('mirrors the server order: findymail → leadmagic → prospeo → apollolimadata → icypeas → wiza', () => {
698
+ it('mirrors the server order: findymail → leadmagic → prospeo → limadataapollo → icypeas → wiza', () => {
699
699
  const ids = getProviders('find_email').map((p) => p.id)
700
700
  expect(ids).toEqual([
701
- 'findymail', 'leadmagic', 'prospeo', 'apollo-people-match',
702
- 'limadata-work-email', 'icypeas', 'wiza',
701
+ 'findymail', 'leadmagic', 'prospeo', 'limadata-work-email',
702
+ 'apollo-people-match', 'icypeas', 'wiza',
703
703
  ])
704
704
  })
705
705
 
@@ -798,8 +798,8 @@ describe('registry', () => {
798
798
  it('providers preserve their order with disabled providers omitted', () => {
799
799
  const ids = getProviders('enrich_company').map((p) => p.id)
800
800
  expect(ids).toEqual([
801
- 'apollo', 'limadata', 'prospeo',
802
- 'wiza', 'findymail', 'icypeas',
801
+ 'limadata', 'prospeo', 'findymail',
802
+ 'apollo', 'wiza', 'icypeas',
803
803
  'builtwith', 'openmart', 'linkupapi-by-domain', 'linkupapi-by-url', 'discolike',
804
804
  ])
805
805
  })
@@ -1215,6 +1215,7 @@ describe('registry', () => {
1215
1215
  expect(apollo.isApplicable!({ exclude_countries: ['CN'] })).toBe(false)
1216
1216
  expect(apollo.isApplicable!({ min_workforce_growth_pct: 10 })).toBe(false)
1217
1217
  expect(apollo.isApplicable!({ is_hiring: true })).toBe(false)
1218
+ expect(apollo.isApplicable?.({ linkedin_search_url: 'https://www.linkedin.com/sales/search/company' })).toBe(false)
1218
1219
  })
1219
1220
 
1220
1221
  it('SignalBase isApplicable: skips for advanced filters it cannot apply', () => {