@coldiq/mcp 0.2.5 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"find-signals.d.ts","sourceRoot":"","sources":["../../src/tools/find-signals.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,eAAO,MAAM,eAAe,iBAAiB,CAAA;AAE7C,eAAO,MAAM,sBAAsB,QAM+G,CAAA;AAElJ,eAAO,MAAM,iBAAiB;;;;;;;;;CAqC7B,CAAA;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;GA+CtE"}
1
+ {"version":3,"file":"find-signals.d.ts","sourceRoot":"","sources":["../../src/tools/find-signals.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,eAAO,MAAM,eAAe,iBAAiB,CAAA;AAE7C,eAAO,MAAM,sBAAsB,QAOyL,CAAA;AAE5N,eAAO,MAAM,iBAAiB;;;;;;;;;;;CA6C7B,CAAA;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;GAsEtE"}
@@ -5,14 +5,15 @@ export const findSignalsName = 'find_signals';
5
5
  export const findSignalsDescription = 'Retrieve sales intelligence signals — funding rounds, acquisitions, hiring activity, job changes, buying intent, news, and startup posts. ' +
6
6
  'Each call targets one signal type. Two modes: ' +
7
7
  'Company-targeted (funding | acquisition | hiring | job_change | intent): accepts companies/domains/industries/countries/since filters. ' +
8
- 'intent REQUIRES at least one of companies or domains. ' +
8
+ 'funding additionally accepts `round_type` (e.g. ["Series A", "Seed"]). ' +
9
+ 'intent REQUIRES at least one of companies or domains and additionally accepts `topics` (e.g. ["sales-automation"]) to narrow by intent keyword. ' +
9
10
  'Feed-style (news | startup_post): country and since only — does NOT filter by company. Passing companies/domains for these types is rejected. ' +
10
- 'hiring returns aggregated company-level hiring activity (e.g. "X is rapidly expanding") — for individual job postings use search_jobs instead.';
11
+ 'hiring returns individual job postings with company context (title, location, descriptionText, company industries) — for richer job-board queries with description/seniority/easy-apply filters use search_jobs instead.';
11
12
  export const findSignalsSchema = {
12
13
  signal_type: z
13
14
  .enum(['funding', 'acquisition', 'hiring', 'job_change', 'news', 'intent', 'startup_post'])
14
15
  .describe('Signal type to retrieve. ' +
15
- 'Company-targeted: "funding" (fundraising rounds), "acquisition" (M&A), "hiring" (company hiring surge not individual postings), ' +
16
+ 'Company-targeted: "funding" (fundraising rounds), "acquisition" (M&A), "hiring" (individual job postings indexed by Signalbase, with company context), ' +
16
17
  '"job_change" (people who recently changed roles), "intent" (companies showing buying intent). ' +
17
18
  'Feed-style (country/date filter only — company filter not supported): "news" (company news events), "startup_post" (Product Hunt, Hacker News, etc.)'),
18
19
  companies: z
@@ -30,11 +31,19 @@ export const findSignalsSchema = {
30
31
  industries: z
31
32
  .array(z.string())
32
33
  .optional()
33
- .describe('Industry names to filter by (e.g. ["Software", "SaaS"]). Only used by company-targeted signal types.'),
34
+ .describe('Industry names to filter by (e.g. ["Software", "SaaS"]). Forwarded to upstream for funding and acquisition. For hiring, filtered client-side against each row\'s `industries` field (case-insensitive substring match). Ignored for job_change, intent, news, startup_post (those signal types have no industry data to filter on).'),
34
35
  countries: z
35
36
  .array(z.string())
36
37
  .optional()
37
38
  .describe('ISO country codes or names to filter by (e.g. ["US", "GB"]). Works for all signal types.'),
39
+ round_type: z
40
+ .array(z.string())
41
+ .optional()
42
+ .describe('Funding round filter (e.g. ["Series A", "Seed"]). Only honored by signal_type=funding. Comma-joined and forwarded to Signalbase\'s `round` filter — case-insensitive substring match upstream, so "Series A" matches "Series A Extension" as well. Silently ignored for other signal types.'),
43
+ topics: z
44
+ .array(z.string())
45
+ .optional()
46
+ .describe('Intent topic / keyword slugs (e.g. ["sales-automation", "lead-generation"]). Only honored by signal_type=intent (forwarded to TheirStack as `keyword_slug_or`). Note: topics is supplemental — TheirStack still requires at least one of `companies` or `domains`, so topics narrows an existing company-targeted search rather than enabling pure topic discovery.'),
38
47
  limit: z
39
48
  .number()
40
49
  .int()
@@ -84,6 +93,30 @@ export async function findSignalsHandler(input) {
84
93
  typed.data.data = typed.data.data.slice(0, limit);
85
94
  }
86
95
  }
96
+ // Signalbase /hiring-signals does not accept an industry filter upstream, so the
97
+ // `industries` param would otherwise be silently dropped. Filter client-side:
98
+ // each hiring row carries an `industries` string (e.g. "Law Practice and Legal
99
+ // Services") which we substring-match against the user-supplied list.
100
+ if (restInput.signal_type === 'hiring' && Array.isArray(restInput.industries) && restInput.industries.length > 0) {
101
+ const wanted = restInput.industries
102
+ .map((s) => (typeof s === 'string' ? s.toLowerCase() : ''))
103
+ .filter((s) => s.length > 0);
104
+ if (wanted.length > 0) {
105
+ const typed = result;
106
+ const rows = typed.data?.data;
107
+ if (Array.isArray(rows)) {
108
+ typed.data.data = rows.filter((row) => {
109
+ if (!row || typeof row !== 'object')
110
+ return false;
111
+ const industriesField = row.industries;
112
+ if (typeof industriesField !== 'string' || industriesField.length === 0)
113
+ return false;
114
+ const haystack = industriesField.toLowerCase();
115
+ return wanted.some((needle) => haystack.includes(needle));
116
+ });
117
+ }
118
+ }
119
+ }
87
120
  return { content: [{ type: 'text', text: JSON.stringify(result) }] };
88
121
  }
89
122
  //# sourceMappingURL=find-signals.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"find-signals.js","sourceRoot":"","sources":["../../src/tools/find-signals.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"find-signals.js","sourceRoot":"","sources":["../../src/tools/find-signals.ts"],"names":[],"mappings":"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"}
@@ -32,6 +32,12 @@ export declare const searchPlacesSchema: {
32
32
  language: z.ZodOptional<z.ZodString>;
33
33
  use_providers: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
34
34
  };
35
+ export declare function applyPlaceFilters(data: unknown, filters: {
36
+ minRating?: number;
37
+ maxRating?: number;
38
+ minReviews?: number;
39
+ maxReviews?: number;
40
+ }): unknown;
35
41
  export declare function searchPlacesHandler(input: Record<string, unknown>): Promise<{
36
42
  content: {
37
43
  type: "text";
@@ -1 +1 @@
1
- {"version":3,"file":"search-places.d.ts","sourceRoot":"","sources":["../../src/tools/search-places.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,eAAO,MAAM,gBAAgB,kBAAkB,CAAA;AAE/C,eAAO,MAAM,uBAAuB,2eACmb,CAAA;AAEvd,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoC9B,CAAA;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;GAWvE"}
1
+ {"version":3,"file":"search-places.d.ts","sourceRoot":"","sources":["../../src/tools/search-places.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,eAAO,MAAM,gBAAgB,kBAAkB,CAAA;AAE/C,eAAO,MAAM,uBAAuB,2eACmb,CAAA;AAEvd,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoC9B,CAAA;AAaD,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,OAAO,EACb,OAAO,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5F,OAAO,CAmBT;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;GAiBvE"}
@@ -15,10 +15,10 @@ export const searchPlacesSchema = {
15
15
  long: z.number().optional().describe('Openmart only. Longitude for radius search.'),
16
16
  geo_radius: z.number().int().positive().optional().describe('Openmart only. Search radius in meters around lat/long.'),
17
17
  tags: z.array(z.string()).max(100).optional().describe('Openmart only. Category tags (mutually exclusive with query upstream — query is ignored if tags non-empty).'),
18
- min_overall_rating: z.number().min(0).max(5).optional().describe('Openmart only.'),
19
- max_overall_rating: z.number().min(0).max(5).optional().describe('Openmart only.'),
20
- min_total_reviews: z.number().int().min(0).optional().describe('Openmart only.'),
21
- max_total_reviews: z.number().int().min(0).optional().describe('Openmart only.'),
18
+ min_overall_rating: z.number().min(0).max(5).optional().describe('Forwarded upstream to Openmart; applied client-side as a post-filter on Google Maps results (each place has a `totalScore` field). Use 0–5.'),
19
+ max_overall_rating: z.number().min(0).max(5).optional().describe('Forwarded upstream to Openmart; applied client-side as a post-filter on Google Maps results (each place has a `totalScore` field). Use 0–5.'),
20
+ min_total_reviews: z.number().int().min(0).optional().describe('Forwarded upstream to Openmart; applied client-side as a post-filter on Google Maps results (each place has a `reviewsCount` field).'),
21
+ max_total_reviews: z.number().int().min(0).optional().describe('Forwarded upstream to Openmart; applied client-side as a post-filter on Google Maps results (each place has a `reviewsCount` field).'),
22
22
  ownership_type: z.enum(['INDEPENDENT', 'FAMILY', 'FRANCHISE', 'CHAIN']).optional().describe('Openmart only.'),
23
23
  has_website: z.boolean().optional().describe('Openmart only.'),
24
24
  has_valid_website: z.boolean().optional().describe('Openmart only.'),
@@ -34,6 +34,44 @@ export const searchPlacesSchema = {
34
34
  language: z.string().optional().describe('Google Maps only. ISO 639-1 (e.g. "en", "fr"). Routes to Google Maps only when set.'),
35
35
  use_providers: z.array(z.string()).optional().describe(`Optional ordered list of providers to use. Leave empty to let ColdIQ automatically pick the best tool for your inputs — recommended for most use cases. Available providers: ${getProvidersForCapability('search_places').join(', ')}. Provider names are matched fuzzily, so minor typos are tolerated.`),
36
36
  };
37
+ function asNumber(v) {
38
+ return typeof v === 'number' && Number.isFinite(v) ? v : undefined;
39
+ }
40
+ // Google Maps Scraper does not honor min/max rating or review-count filters
41
+ // upstream, so the params would otherwise be silently dropped. Filter client-side
42
+ // against the `totalScore` (rating) and `reviewsCount` fields on each place.
43
+ // Applied to all routes for consistency; on Openmart this is a no-op since the
44
+ // filters were already enforced upstream and Openmart returns `{ data: [...] }`
45
+ // rather than `{ places: [...] }`.
46
+ // Pure: returns a new object instead of mutating its argument.
47
+ export function applyPlaceFilters(data, filters) {
48
+ const { minRating, maxRating, minReviews, maxReviews } = filters;
49
+ if (minRating === undefined && maxRating === undefined && minReviews === undefined && maxReviews === undefined)
50
+ return data;
51
+ if (!data || typeof data !== 'object')
52
+ return data;
53
+ const d = data;
54
+ const places = d.places;
55
+ if (!Array.isArray(places))
56
+ return data;
57
+ const filtered = places.filter((place) => {
58
+ if (!place || typeof place !== 'object')
59
+ return false;
60
+ const p = place;
61
+ const rating = asNumber(p.totalScore);
62
+ const reviews = asNumber(p.reviewsCount);
63
+ if (minRating !== undefined && (rating === undefined || rating < minRating))
64
+ return false;
65
+ if (maxRating !== undefined && (rating === undefined || rating > maxRating))
66
+ return false;
67
+ if (minReviews !== undefined && (reviews === undefined || reviews < minReviews))
68
+ return false;
69
+ if (maxReviews !== undefined && (reviews === undefined || reviews > maxReviews))
70
+ return false;
71
+ return true;
72
+ });
73
+ return { ...d, places: filtered };
74
+ }
37
75
  export async function searchPlacesHandler(input) {
38
76
  const { use_providers: rawUseProviders, ...restInput } = input;
39
77
  const resolved = resolvePreferredProviders('search_places', restInput, rawUseProviders);
@@ -44,6 +82,12 @@ export async function searchPlacesHandler(input) {
44
82
  if (isExecutionError(result)) {
45
83
  return { content: [{ type: 'text', text: JSON.stringify(result) }], isError: true };
46
84
  }
85
+ result.data = applyPlaceFilters(result.data, {
86
+ minRating: asNumber(restInput.min_overall_rating),
87
+ maxRating: asNumber(restInput.max_overall_rating),
88
+ minReviews: asNumber(restInput.min_total_reviews),
89
+ maxReviews: asNumber(restInput.max_total_reviews),
90
+ });
47
91
  return { content: [{ type: 'text', text: JSON.stringify(result) }] };
48
92
  }
49
93
  //# sourceMappingURL=search-places.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"search-places.js","sourceRoot":"","sources":["../../src/tools/search-places.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"search-places.js","sourceRoot":"","sources":["../../src/tools/search-places.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,11 @@
1
1
  import { z } from 'zod';
2
2
  export declare const verifyEmailName = "verify_email";
3
- export declare const verifyEmailDescription = "Check whether an email address is valid and deliverable. Returns verification status (valid, invalid, catch-all, unknown). Use before cold outreach to protect sender reputation. ColdIQ automatically picks the best provider \u2014 pass use_providers only if you need a specific tool.";
3
+ export declare const verifyEmailDescription = "Check whether an email address is valid and deliverable. Returns a normalized `status` field \u2014 one of \"valid\", \"invalid\", \"catch_all\", \"risky\", \"disposable\", \"unknown\" \u2014 alongside the raw provider response. Findymail (the default) only distinguishes valid vs invalid; richer status values come from providers like icypeas, instantly, or linkupapi. Use before cold outreach to protect sender reputation. ColdIQ automatically picks the best provider \u2014 pass use_providers only if you need a specific tool.";
4
4
  export declare const verifyEmailSchema: {
5
5
  email: z.ZodString;
6
6
  use_providers: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
7
7
  };
8
+ export declare function addNormalizedStatus(data: unknown, providerId: string): unknown;
8
9
  export declare function verifyEmailHandler(input: Record<string, unknown>): Promise<{
9
10
  content: {
10
11
  type: "text";
@@ -1 +1 @@
1
- {"version":3,"file":"verify-email.d.ts","sourceRoot":"","sources":["../../src/tools/verify-email.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,eAAO,MAAM,eAAe,iBAAiB,CAAA;AAE7C,eAAO,MAAM,sBAAsB,+RACsP,CAAA;AAEzR,eAAO,MAAM,iBAAiB;;;CAG7B,CAAA;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;GAWtE"}
1
+ {"version":3,"file":"verify-email.d.ts","sourceRoot":"","sources":["../../src/tools/verify-email.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,eAAO,MAAM,eAAe,iBAAiB,CAAA;AAE7C,eAAO,MAAM,sBAAsB,shBACud,CAAA;AAE1f,eAAO,MAAM,iBAAiB;;;CAG7B,CAAA;AA8BD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAiC9E;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;GAYtE"}
@@ -2,11 +2,66 @@ import { z } from 'zod';
2
2
  import { executeWithFallback, isExecutionError } from '../executor.js';
3
3
  import { resolvePreferredProviders, getProvidersForCapability } from '../utils/provider-resolver.js';
4
4
  export const verifyEmailName = 'verify_email';
5
- export const verifyEmailDescription = 'Check whether an email address is valid and deliverable. Returns verification status (valid, invalid, catch-all, unknown). Use before cold outreach to protect sender reputation. ColdIQ automatically picks the best provider — pass use_providers only if you need a specific tool.';
5
+ export const verifyEmailDescription = 'Check whether an email address is valid and deliverable. Returns a normalized `status` field — one of "valid", "invalid", "catch_all", "risky", "disposable", "unknown" — alongside the raw provider response. Findymail (the default) only distinguishes valid vs invalid; richer status values come from providers like icypeas, instantly, or linkupapi. Use before cold outreach to protect sender reputation. ColdIQ automatically picks the best provider — pass use_providers only if you need a specific tool.';
6
6
  export const verifyEmailSchema = {
7
7
  email: z.string().email().describe('Email address to verify'),
8
8
  use_providers: z.array(z.string()).optional().describe(`Optional ordered list of providers to use. Leave empty to let ColdIQ automatically pick the best tool for your inputs — recommended for most use cases. Available providers: ${getProvidersForCapability('verify_email').join(', ')}. Provider names are matched fuzzily, so minor typos are tolerated.`),
9
9
  };
10
+ const STATUS_ALIASES = {
11
+ valid: 'valid',
12
+ deliverable: 'valid',
13
+ ok: 'valid',
14
+ invalid: 'invalid',
15
+ undeliverable: 'invalid',
16
+ bounce: 'invalid',
17
+ bounced: 'invalid',
18
+ catch_all: 'catch_all',
19
+ catchall: 'catch_all',
20
+ 'catch-all': 'catch_all',
21
+ accept_all: 'catch_all',
22
+ acceptall: 'catch_all',
23
+ risky: 'risky',
24
+ disposable: 'disposable',
25
+ unknown: 'unknown',
26
+ };
27
+ function normalizeStatus(raw) {
28
+ if (typeof raw !== 'string' || raw.length === 0)
29
+ return 'unknown';
30
+ return STATUS_ALIASES[raw.toLowerCase()] ?? 'unknown';
31
+ }
32
+ // Provider responses are heterogeneous. We surface a single `status` field on the
33
+ // payload while leaving the raw upstream fields untouched so callers that already
34
+ // depend on them keep working.
35
+ export function addNormalizedStatus(data, providerId) {
36
+ if (!data || typeof data !== 'object' || Array.isArray(data))
37
+ return data;
38
+ const d = { ...data };
39
+ // If the response already carries a status string, normalize it and stop.
40
+ if (typeof d.status === 'string') {
41
+ d.status = normalizeStatus(d.status);
42
+ return d;
43
+ }
44
+ if (providerId === 'findymail') {
45
+ // Findymail's /api/verify returns { email, verified: boolean, provider } — no
46
+ // catch_all/unknown distinction. Map the boolean to valid/invalid only.
47
+ d.status = d.verified === true ? 'valid' : 'invalid';
48
+ return d;
49
+ }
50
+ if (providerId === 'icypeas') {
51
+ const item = d.item;
52
+ d.status = normalizeStatus(item?.status);
53
+ return d;
54
+ }
55
+ if (providerId === 'linkupapi-validate') {
56
+ // LinkupAPI shapes vary; accept any of the common signal fields.
57
+ const truthy = d.verified === true || d.valid === true || d.result === 'valid';
58
+ d.status = truthy ? 'valid' : normalizeStatus(d.result ?? d.valid ?? d.verified);
59
+ return d;
60
+ }
61
+ // Unknown provider — leave the original payload alone and flag the gap.
62
+ d.status = 'unknown';
63
+ return d;
64
+ }
10
65
  export async function verifyEmailHandler(input) {
11
66
  const { use_providers: rawUseProviders, ...restInput } = input;
12
67
  const resolved = resolvePreferredProviders('verify_email', restInput, rawUseProviders);
@@ -17,6 +72,7 @@ export async function verifyEmailHandler(input) {
17
72
  if (isExecutionError(result)) {
18
73
  return { content: [{ type: 'text', text: JSON.stringify(result) }], isError: true };
19
74
  }
75
+ result.data = addNormalizedStatus(result.data, result._meta.provider);
20
76
  return { content: [{ type: 'text', text: JSON.stringify(result) }] };
21
77
  }
22
78
  //# sourceMappingURL=verify-email.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"verify-email.js","sourceRoot":"","sources":["../../src/tools/verify-email.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACtE,OAAO,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAA;AAEpG,MAAM,CAAC,MAAM,eAAe,GAAG,cAAc,CAAA;AAE7C,MAAM,CAAC,MAAM,sBAAsB,GACjC,uRAAuR,CAAA;AAEzR,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC;IAC7D,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gLAAgL,yBAAyB,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,qEAAqE,CAAC;CAClW,CAAA;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAA8B;IACrE,MAAM,EAAE,aAAa,EAAE,eAAe,EAAE,GAAG,SAAS,EAAE,GAAG,KAAK,CAAA;IAC9D,MAAM,QAAQ,GAAG,yBAAyB,CAAC,cAAc,EAAE,SAAS,EAAE,eAAe,CAAC,CAAA;IACtF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;IACtG,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,cAAc,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAA;IACzI,IAAI,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;IAC9F,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAA;AAC/E,CAAC"}
1
+ {"version":3,"file":"verify-email.js","sourceRoot":"","sources":["../../src/tools/verify-email.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coldiq/mcp",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/registry.ts CHANGED
@@ -2909,8 +2909,22 @@ const enrichPersonProviders: ProviderEntry[] = [
2909
2909
  },
2910
2910
  }),
2911
2911
  hasResult: (data) => {
2912
+ // Apollo /people/match always returns { person: {...} } even on weak matches
2913
+ // where every useful field is null (just echoing back the input first/last name).
2914
+ // Treat as a real match only when at least one enrichment field is populated,
2915
+ // so the executor falls through to the next provider instead of returning a
2916
+ // silent empty record.
2912
2917
  const d = data as Record<string, unknown>
2913
- return !!d.person && typeof d.person === 'object'
2918
+ const person = d.person as Record<string, unknown> | undefined
2919
+ if (!person || typeof person !== 'object') return false
2920
+ return Boolean(
2921
+ person.linkedin_url ||
2922
+ person.email ||
2923
+ person.title ||
2924
+ person.headline ||
2925
+ person.organization_id ||
2926
+ isNonEmptyArray(person.employment_history),
2927
+ )
2914
2928
  },
2915
2929
  },
2916
2930
  {
@@ -3049,6 +3063,7 @@ const findSignalsProviders: ProviderEntry[] = [
3049
3063
  if (typeof input.since === 'string') qp.dateFrom = input.since
3050
3064
  if (isNonEmptyArray(input.industries)) qp.industry = (input.industries as string[]).join(',')
3051
3065
  if (isNonEmptyArray(input.countries)) qp.countries = (input.countries as string[]).join(',')
3066
+ if (isNonEmptyArray(input.round_type)) qp.round = (input.round_type as string[]).join(',')
3052
3067
  qp.limit = String(Math.min((input.limit as number | undefined) ?? 25, 100))
3053
3068
  return { queryParams: qp }
3054
3069
  },
@@ -3157,6 +3172,7 @@ const findSignalsProviders: ProviderEntry[] = [
3157
3172
  body: {
3158
3173
  ...(isNonEmptyArray(input.companies) && { company_name_or: input.companies }),
3159
3174
  ...(isNonEmptyArray(input.domains) && { company_domain: (input.domains as string[])[0] }),
3175
+ ...(isNonEmptyArray(input.topics) && { keyword_slug_or: input.topics }),
3160
3176
  },
3161
3177
  }),
3162
3178
  hasResult: (data) => isNonEmptyArray((data as Record<string, unknown>).data),
@@ -8,16 +8,17 @@ export const findSignalsDescription =
8
8
  'Retrieve sales intelligence signals — funding rounds, acquisitions, hiring activity, job changes, buying intent, news, and startup posts. ' +
9
9
  'Each call targets one signal type. Two modes: ' +
10
10
  'Company-targeted (funding | acquisition | hiring | job_change | intent): accepts companies/domains/industries/countries/since filters. ' +
11
- 'intent REQUIRES at least one of companies or domains. ' +
11
+ 'funding additionally accepts `round_type` (e.g. ["Series A", "Seed"]). ' +
12
+ 'intent REQUIRES at least one of companies or domains and additionally accepts `topics` (e.g. ["sales-automation"]) to narrow by intent keyword. ' +
12
13
  'Feed-style (news | startup_post): country and since only — does NOT filter by company. Passing companies/domains for these types is rejected. ' +
13
- 'hiring returns aggregated company-level hiring activity (e.g. "X is rapidly expanding") — for individual job postings use search_jobs instead.'
14
+ 'hiring returns individual job postings with company context (title, location, descriptionText, company industries) — for richer job-board queries with description/seniority/easy-apply filters use search_jobs instead.'
14
15
 
15
16
  export const findSignalsSchema = {
16
17
  signal_type: z
17
18
  .enum(['funding', 'acquisition', 'hiring', 'job_change', 'news', 'intent', 'startup_post'])
18
19
  .describe(
19
20
  'Signal type to retrieve. ' +
20
- 'Company-targeted: "funding" (fundraising rounds), "acquisition" (M&A), "hiring" (company hiring surge not individual postings), ' +
21
+ 'Company-targeted: "funding" (fundraising rounds), "acquisition" (M&A), "hiring" (individual job postings indexed by Signalbase, with company context), ' +
21
22
  '"job_change" (people who recently changed roles), "intent" (companies showing buying intent). ' +
22
23
  'Feed-style (country/date filter only — company filter not supported): "news" (company news events), "startup_post" (Product Hunt, Hacker News, etc.)'
23
24
  ),
@@ -36,11 +37,19 @@ export const findSignalsSchema = {
36
37
  industries: z
37
38
  .array(z.string())
38
39
  .optional()
39
- .describe('Industry names to filter by (e.g. ["Software", "SaaS"]). Only used by company-targeted signal types.'),
40
+ .describe('Industry names to filter by (e.g. ["Software", "SaaS"]). Forwarded to upstream for funding and acquisition. For hiring, filtered client-side against each row\'s `industries` field (case-insensitive substring match). Ignored for job_change, intent, news, startup_post (those signal types have no industry data to filter on).'),
40
41
  countries: z
41
42
  .array(z.string())
42
43
  .optional()
43
44
  .describe('ISO country codes or names to filter by (e.g. ["US", "GB"]). Works for all signal types.'),
45
+ round_type: z
46
+ .array(z.string())
47
+ .optional()
48
+ .describe('Funding round filter (e.g. ["Series A", "Seed"]). Only honored by signal_type=funding. Comma-joined and forwarded to Signalbase\'s `round` filter — case-insensitive substring match upstream, so "Series A" matches "Series A Extension" as well. Silently ignored for other signal types.'),
49
+ topics: z
50
+ .array(z.string())
51
+ .optional()
52
+ .describe('Intent topic / keyword slugs (e.g. ["sales-automation", "lead-generation"]). Only honored by signal_type=intent (forwarded to TheirStack as `keyword_slug_or`). Note: topics is supplemental — TheirStack still requires at least one of `companies` or `domains`, so topics narrows an existing company-targeted search rather than enabling pure topic discovery.'),
44
53
  limit: z
45
54
  .number()
46
55
  .int()
@@ -97,5 +106,28 @@ export async function findSignalsHandler(input: Record<string, unknown>) {
97
106
  }
98
107
  }
99
108
 
109
+ // Signalbase /hiring-signals does not accept an industry filter upstream, so the
110
+ // `industries` param would otherwise be silently dropped. Filter client-side:
111
+ // each hiring row carries an `industries` string (e.g. "Law Practice and Legal
112
+ // Services") which we substring-match against the user-supplied list.
113
+ if (restInput.signal_type === 'hiring' && Array.isArray(restInput.industries) && restInput.industries.length > 0) {
114
+ const wanted = (restInput.industries as unknown[])
115
+ .map((s) => (typeof s === 'string' ? s.toLowerCase() : ''))
116
+ .filter((s) => s.length > 0)
117
+ if (wanted.length > 0) {
118
+ const typed = result as { data?: { data?: unknown[] } }
119
+ const rows = typed.data?.data
120
+ if (Array.isArray(rows)) {
121
+ typed.data!.data = rows.filter((row) => {
122
+ if (!row || typeof row !== 'object') return false
123
+ const industriesField = (row as Record<string, unknown>).industries
124
+ if (typeof industriesField !== 'string' || industriesField.length === 0) return false
125
+ const haystack = industriesField.toLowerCase()
126
+ return wanted.some((needle) => haystack.includes(needle))
127
+ })
128
+ }
129
+ }
130
+ }
131
+
100
132
  return { content: [{ type: 'text' as const, text: JSON.stringify(result) }] }
101
133
  }
@@ -22,10 +22,10 @@ export const searchPlacesSchema = {
22
22
 
23
23
  tags: z.array(z.string()).max(100).optional().describe('Openmart only. Category tags (mutually exclusive with query upstream — query is ignored if tags non-empty).'),
24
24
 
25
- min_overall_rating: z.number().min(0).max(5).optional().describe('Openmart only.'),
26
- max_overall_rating: z.number().min(0).max(5).optional().describe('Openmart only.'),
27
- min_total_reviews: z.number().int().min(0).optional().describe('Openmart only.'),
28
- max_total_reviews: z.number().int().min(0).optional().describe('Openmart only.'),
25
+ min_overall_rating: z.number().min(0).max(5).optional().describe('Forwarded upstream to Openmart; applied client-side as a post-filter on Google Maps results (each place has a `totalScore` field). Use 0–5.'),
26
+ max_overall_rating: z.number().min(0).max(5).optional().describe('Forwarded upstream to Openmart; applied client-side as a post-filter on Google Maps results (each place has a `totalScore` field). Use 0–5.'),
27
+ min_total_reviews: z.number().int().min(0).optional().describe('Forwarded upstream to Openmart; applied client-side as a post-filter on Google Maps results (each place has a `reviewsCount` field).'),
28
+ max_total_reviews: z.number().int().min(0).optional().describe('Forwarded upstream to Openmart; applied client-side as a post-filter on Google Maps results (each place has a `reviewsCount` field).'),
29
29
 
30
30
  ownership_type: z.enum(['INDEPENDENT', 'FAMILY', 'FRANCHISE', 'CHAIN']).optional().describe('Openmart only.'),
31
31
  has_website: z.boolean().optional().describe('Openmart only.'),
@@ -45,6 +45,41 @@ export const searchPlacesSchema = {
45
45
  use_providers: z.array(z.string()).optional().describe(`Optional ordered list of providers to use. Leave empty to let ColdIQ automatically pick the best tool for your inputs — recommended for most use cases. Available providers: ${getProvidersForCapability('search_places').join(', ')}. Provider names are matched fuzzily, so minor typos are tolerated.`),
46
46
  }
47
47
 
48
+ function asNumber(v: unknown): number | undefined {
49
+ return typeof v === 'number' && Number.isFinite(v) ? v : undefined
50
+ }
51
+
52
+ // Google Maps Scraper does not honor min/max rating or review-count filters
53
+ // upstream, so the params would otherwise be silently dropped. Filter client-side
54
+ // against the `totalScore` (rating) and `reviewsCount` fields on each place.
55
+ // Applied to all routes for consistency; on Openmart this is a no-op since the
56
+ // filters were already enforced upstream and Openmart returns `{ data: [...] }`
57
+ // rather than `{ places: [...] }`.
58
+ // Pure: returns a new object instead of mutating its argument.
59
+ export function applyPlaceFilters(
60
+ data: unknown,
61
+ filters: { minRating?: number; maxRating?: number; minReviews?: number; maxReviews?: number },
62
+ ): unknown {
63
+ const { minRating, maxRating, minReviews, maxReviews } = filters
64
+ if (minRating === undefined && maxRating === undefined && minReviews === undefined && maxReviews === undefined) return data
65
+ if (!data || typeof data !== 'object') return data
66
+ const d = data as Record<string, unknown>
67
+ const places = d.places
68
+ if (!Array.isArray(places)) return data
69
+ const filtered = places.filter((place) => {
70
+ if (!place || typeof place !== 'object') return false
71
+ const p = place as Record<string, unknown>
72
+ const rating = asNumber(p.totalScore)
73
+ const reviews = asNumber(p.reviewsCount)
74
+ if (minRating !== undefined && (rating === undefined || rating < minRating)) return false
75
+ if (maxRating !== undefined && (rating === undefined || rating > maxRating)) return false
76
+ if (minReviews !== undefined && (reviews === undefined || reviews < minReviews)) return false
77
+ if (maxReviews !== undefined && (reviews === undefined || reviews > maxReviews)) return false
78
+ return true
79
+ })
80
+ return { ...d, places: filtered }
81
+ }
82
+
48
83
  export async function searchPlacesHandler(input: Record<string, unknown>) {
49
84
  const { use_providers: rawUseProviders, ...restInput } = input
50
85
  const resolved = resolvePreferredProviders('search_places', restInput, rawUseProviders)
@@ -55,5 +90,11 @@ export async function searchPlacesHandler(input: Record<string, unknown>) {
55
90
  if (isExecutionError(result)) {
56
91
  return { content: [{ type: 'text' as const, text: JSON.stringify(result) }], isError: true }
57
92
  }
93
+ result.data = applyPlaceFilters(result.data, {
94
+ minRating: asNumber(restInput.min_overall_rating),
95
+ maxRating: asNumber(restInput.max_overall_rating),
96
+ minReviews: asNumber(restInput.min_total_reviews),
97
+ maxReviews: asNumber(restInput.max_total_reviews),
98
+ })
58
99
  return { content: [{ type: 'text' as const, text: JSON.stringify(result) }] }
59
100
  }
@@ -5,13 +5,76 @@ import { resolvePreferredProviders, getProvidersForCapability } from '../utils/p
5
5
  export const verifyEmailName = 'verify_email'
6
6
 
7
7
  export const verifyEmailDescription =
8
- 'Check whether an email address is valid and deliverable. Returns verification status (valid, invalid, catch-all, unknown). Use before cold outreach to protect sender reputation. ColdIQ automatically picks the best provider — pass use_providers only if you need a specific tool.'
8
+ 'Check whether an email address is valid and deliverable. Returns a normalized `status` field — one of "valid", "invalid", "catch_all", "risky", "disposable", "unknown" — alongside the raw provider response. Findymail (the default) only distinguishes valid vs invalid; richer status values come from providers like icypeas, instantly, or linkupapi. Use before cold outreach to protect sender reputation. ColdIQ automatically picks the best provider — pass use_providers only if you need a specific tool.'
9
9
 
10
10
  export const verifyEmailSchema = {
11
11
  email: z.string().email().describe('Email address to verify'),
12
12
  use_providers: z.array(z.string()).optional().describe(`Optional ordered list of providers to use. Leave empty to let ColdIQ automatically pick the best tool for your inputs — recommended for most use cases. Available providers: ${getProvidersForCapability('verify_email').join(', ')}. Provider names are matched fuzzily, so minor typos are tolerated.`),
13
13
  }
14
14
 
15
+ type NormalizedStatus = 'valid' | 'invalid' | 'catch_all' | 'risky' | 'disposable' | 'unknown'
16
+
17
+ const STATUS_ALIASES: Record<string, NormalizedStatus> = {
18
+ valid: 'valid',
19
+ deliverable: 'valid',
20
+ ok: 'valid',
21
+ invalid: 'invalid',
22
+ undeliverable: 'invalid',
23
+ bounce: 'invalid',
24
+ bounced: 'invalid',
25
+ catch_all: 'catch_all',
26
+ catchall: 'catch_all',
27
+ 'catch-all': 'catch_all',
28
+ accept_all: 'catch_all',
29
+ acceptall: 'catch_all',
30
+ risky: 'risky',
31
+ disposable: 'disposable',
32
+ unknown: 'unknown',
33
+ }
34
+
35
+ function normalizeStatus(raw: unknown): NormalizedStatus {
36
+ if (typeof raw !== 'string' || raw.length === 0) return 'unknown'
37
+ return STATUS_ALIASES[raw.toLowerCase()] ?? 'unknown'
38
+ }
39
+
40
+ // Provider responses are heterogeneous. We surface a single `status` field on the
41
+ // payload while leaving the raw upstream fields untouched so callers that already
42
+ // depend on them keep working.
43
+ export function addNormalizedStatus(data: unknown, providerId: string): unknown {
44
+ if (!data || typeof data !== 'object' || Array.isArray(data)) return data
45
+ const d = { ...(data as Record<string, unknown>) }
46
+
47
+ // If the response already carries a status string, normalize it and stop.
48
+ if (typeof d.status === 'string') {
49
+ d.status = normalizeStatus(d.status)
50
+ return d
51
+ }
52
+
53
+ if (providerId === 'findymail') {
54
+ // Findymail's /api/verify returns { email, verified: boolean, provider } — no
55
+ // catch_all/unknown distinction. Map the boolean to valid/invalid only.
56
+ d.status = d.verified === true ? 'valid' : 'invalid'
57
+ return d
58
+ }
59
+
60
+ if (providerId === 'icypeas') {
61
+ const item = d.item as Record<string, unknown> | undefined
62
+ d.status = normalizeStatus(item?.status)
63
+ return d
64
+ }
65
+
66
+ if (providerId === 'linkupapi-validate') {
67
+ // LinkupAPI shapes vary; accept any of the common signal fields.
68
+ const truthy = d.verified === true || d.valid === true || d.result === 'valid'
69
+ d.status = truthy ? 'valid' : normalizeStatus(d.result ?? d.valid ?? d.verified)
70
+ return d
71
+ }
72
+
73
+ // Unknown provider — leave the original payload alone and flag the gap.
74
+ d.status = 'unknown'
75
+ return d
76
+ }
77
+
15
78
  export async function verifyEmailHandler(input: Record<string, unknown>) {
16
79
  const { use_providers: rawUseProviders, ...restInput } = input
17
80
  const resolved = resolvePreferredProviders('verify_email', restInput, rawUseProviders)
@@ -22,5 +85,6 @@ export async function verifyEmailHandler(input: Record<string, unknown>) {
22
85
  if (isExecutionError(result)) {
23
86
  return { content: [{ type: 'text' as const, text: JSON.stringify(result) }], isError: true }
24
87
  }
88
+ result.data = addNormalizedStatus(result.data, result._meta.provider)
25
89
  return { content: [{ type: 'text' as const, text: JSON.stringify(result) }] }
26
90
  }