@coldiq/mcp 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/dist/executor.d.ts +8 -1
  2. package/dist/executor.d.ts.map +1 -1
  3. package/dist/executor.js +26 -12
  4. package/dist/executor.js.map +1 -1
  5. package/dist/registry.d.ts +9 -0
  6. package/dist/registry.d.ts.map +1 -1
  7. package/dist/registry.js +16 -0
  8. package/dist/registry.js.map +1 -1
  9. package/dist/tools/enrich-company.d.ts +2 -1
  10. package/dist/tools/enrich-company.d.ts.map +1 -1
  11. package/dist/tools/enrich-company.js +10 -3
  12. package/dist/tools/enrich-company.js.map +1 -1
  13. package/dist/tools/enrich-person.d.ts +1 -0
  14. package/dist/tools/enrich-person.d.ts.map +1 -1
  15. package/dist/tools/enrich-person.js +10 -2
  16. package/dist/tools/enrich-person.js.map +1 -1
  17. package/dist/tools/fetch-page-content.d.ts +1 -0
  18. package/dist/tools/fetch-page-content.d.ts.map +1 -1
  19. package/dist/tools/fetch-page-content.js +8 -1
  20. package/dist/tools/fetch-page-content.js.map +1 -1
  21. package/dist/tools/find-email.d.ts +1 -0
  22. package/dist/tools/find-email.d.ts.map +1 -1
  23. package/dist/tools/find-email.js +9 -2
  24. package/dist/tools/find-email.js.map +1 -1
  25. package/dist/tools/find-emails.d.ts +8 -0
  26. package/dist/tools/find-emails.d.ts.map +1 -1
  27. package/dist/tools/find-emails.js +64 -33
  28. package/dist/tools/find-emails.js.map +1 -1
  29. package/dist/tools/find-influencers.d.ts +1 -0
  30. package/dist/tools/find-influencers.d.ts.map +1 -1
  31. package/dist/tools/find-influencers.js +8 -1
  32. package/dist/tools/find-influencers.js.map +1 -1
  33. package/dist/tools/find-people.d.ts +1 -0
  34. package/dist/tools/find-people.d.ts.map +1 -1
  35. package/dist/tools/find-people.js +12 -5
  36. package/dist/tools/find-people.js.map +1 -1
  37. package/dist/tools/find-phone.d.ts +1 -0
  38. package/dist/tools/find-phone.d.ts.map +1 -1
  39. package/dist/tools/find-phone.js +9 -2
  40. package/dist/tools/find-phone.js.map +1 -1
  41. package/dist/tools/find-signals.d.ts +1 -0
  42. package/dist/tools/find-signals.d.ts.map +1 -1
  43. package/dist/tools/find-signals.js +14 -7
  44. package/dist/tools/find-signals.js.map +1 -1
  45. package/dist/tools/search-ads.d.ts +1 -0
  46. package/dist/tools/search-ads.d.ts.map +1 -1
  47. package/dist/tools/search-ads.js +8 -1
  48. package/dist/tools/search-ads.js.map +1 -1
  49. package/dist/tools/search-companies.d.ts +2 -1
  50. package/dist/tools/search-companies.d.ts.map +1 -1
  51. package/dist/tools/search-companies.js +9 -2
  52. package/dist/tools/search-companies.js.map +1 -1
  53. package/dist/tools/search-jobs.d.ts +1 -0
  54. package/dist/tools/search-jobs.d.ts.map +1 -1
  55. package/dist/tools/search-jobs.js +9 -2
  56. package/dist/tools/search-jobs.js.map +1 -1
  57. package/dist/tools/search-places.d.ts +1 -0
  58. package/dist/tools/search-places.d.ts.map +1 -1
  59. package/dist/tools/search-places.js +8 -1
  60. package/dist/tools/search-places.js.map +1 -1
  61. package/dist/tools/search-reddit.d.ts +1 -0
  62. package/dist/tools/search-reddit.d.ts.map +1 -1
  63. package/dist/tools/search-reddit.js +8 -1
  64. package/dist/tools/search-reddit.js.map +1 -1
  65. package/dist/tools/search-seo.d.ts +1 -0
  66. package/dist/tools/search-seo.d.ts.map +1 -1
  67. package/dist/tools/search-seo.js +8 -1
  68. package/dist/tools/search-seo.js.map +1 -1
  69. package/dist/tools/search-web.d.ts +1 -0
  70. package/dist/tools/search-web.d.ts.map +1 -1
  71. package/dist/tools/search-web.js +10 -2
  72. package/dist/tools/search-web.js.map +1 -1
  73. package/dist/tools/verify-email.d.ts +2 -1
  74. package/dist/tools/verify-email.d.ts.map +1 -1
  75. package/dist/tools/verify-email.js +9 -2
  76. package/dist/tools/verify-email.js.map +1 -1
  77. package/dist/utils/fuzzy.d.ts +13 -0
  78. package/dist/utils/fuzzy.d.ts.map +1 -0
  79. package/dist/utils/fuzzy.js +46 -0
  80. package/dist/utils/fuzzy.js.map +1 -0
  81. package/dist/utils/provider-resolver.d.ts +34 -0
  82. package/dist/utils/provider-resolver.d.ts.map +1 -0
  83. package/dist/utils/provider-resolver.js +235 -0
  84. package/dist/utils/provider-resolver.js.map +1 -0
  85. package/package.json +1 -1
  86. package/src/executor.ts +36 -8
  87. package/src/registry.ts +20 -0
  88. package/src/tools/enrich-company.ts +10 -3
  89. package/src/tools/enrich-person.ts +10 -2
  90. package/src/tools/fetch-page-content.ts +8 -1
  91. package/src/tools/find-email.ts +9 -2
  92. package/src/tools/find-emails.ts +78 -41
  93. package/src/tools/find-influencers.ts +8 -1
  94. package/src/tools/find-people.ts +12 -5
  95. package/src/tools/find-phone.ts +9 -2
  96. package/src/tools/find-signals.ts +15 -7
  97. package/src/tools/search-ads.ts +8 -1
  98. package/src/tools/search-companies.ts +9 -2
  99. package/src/tools/search-jobs.ts +9 -2
  100. package/src/tools/search-places.ts +8 -1
  101. package/src/tools/search-reddit.ts +8 -1
  102. package/src/tools/search-seo.ts +8 -1
  103. package/src/tools/search-web.ts +10 -2
  104. package/src/tools/verify-email.ts +9 -2
  105. package/src/utils/fuzzy.ts +57 -0
  106. package/src/utils/provider-resolver.ts +306 -0
  107. package/tests/executor.test.ts +112 -0
  108. package/tests/registry.test.ts +22 -1
  109. package/tests/tools/find-email.test.ts +43 -0
  110. package/tests/tools/find-emails.test.ts +87 -0
  111. package/tests/tools/search-companies.test.ts +40 -0
  112. package/tests/utils/fuzzy.test.ts +63 -0
  113. package/tests/utils/provider-resolver.test.ts +145 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fuzzy.d.ts","sourceRoot":"","sources":["../../src/utils/fuzzy.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3C;AAkBD,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,YAAY,GAAG,OAAO,CAAA;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,QAAQ,CAAA;CACd;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,gBAAgB,GAAG,IAAI,CAwBvF"}
@@ -0,0 +1,46 @@
1
+ export function normalize(s) {
2
+ return s.toLowerCase().replace(/[^a-z0-9]/g, '');
3
+ }
4
+ function levenshtein(a, b) {
5
+ const m = a.length;
6
+ const n = b.length;
7
+ const dp = Array.from({ length: m + 1 }, (_, i) => Array.from({ length: n + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0)));
8
+ for (let i = 1; i <= m; i++) {
9
+ for (let j = 1; j <= n; j++) {
10
+ dp[i][j] = a[i - 1] === b[j - 1]
11
+ ? dp[i - 1][j - 1]
12
+ : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
13
+ }
14
+ }
15
+ return dp[m][n];
16
+ }
17
+ /**
18
+ * Match a user-supplied string against a list of candidate IDs.
19
+ * Precedence: exact → normalized (case + punctuation stripped) → Levenshtein ≤ threshold.
20
+ * Threshold = min(2, floor(len/3)) where len = normalized candidate length.
21
+ */
22
+ export function fuzzyMatch(input, candidates) {
23
+ // Exact
24
+ if (candidates.includes(input))
25
+ return { matched: input, via: 'exact' };
26
+ const normInput = normalize(input);
27
+ // Normalized exact
28
+ for (const c of candidates) {
29
+ if (normalize(c) === normInput)
30
+ return { matched: c, via: 'normalized' };
31
+ }
32
+ // Levenshtein
33
+ let best = null;
34
+ for (const c of candidates) {
35
+ const normC = normalize(c);
36
+ const threshold = Math.min(2, Math.floor(normC.length / 3));
37
+ if (threshold === 0)
38
+ continue;
39
+ const dist = levenshtein(normInput, normC);
40
+ if (dist <= threshold && (best === null || dist < best.dist)) {
41
+ best = { matched: c, dist };
42
+ }
43
+ }
44
+ return best ? { matched: best.matched, via: 'fuzzy' } : null;
45
+ }
46
+ //# sourceMappingURL=fuzzy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fuzzy.js","sourceRoot":"","sources":["../../src/utils/fuzzy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { Capability } from '../registry.js';
2
+ export declare const FIND_EMAILS_PROVIDERS: string[];
3
+ export declare function getProvidersForCapability(capability: Capability | 'find_emails'): string[];
4
+ export interface ToolErrorPayload {
5
+ error: string;
6
+ available_providers: string[];
7
+ }
8
+ export declare function buildUnrecognizedError(input: string, capability: Capability | 'find_emails', available: string[]): ToolErrorPayload;
9
+ export declare function buildGatedError(provider: string, gate: GateDesc, capability: Capability | 'find_emails', available: string[], input: Record<string, unknown>): ToolErrorPayload;
10
+ export declare function buildAllFailedError(tried: string[], capability: Capability | 'find_emails', available: string[]): ToolErrorPayload;
11
+ export interface ResolveOk {
12
+ ok: true;
13
+ providers: string[];
14
+ matchedFrom?: Record<string, string>;
15
+ }
16
+ export interface ResolveError {
17
+ ok: false;
18
+ error: ToolErrorPayload;
19
+ }
20
+ export type ResolveResult = ResolveOk | ResolveError;
21
+ /**
22
+ * Resolve `use_providers` field against the capability's known provider IDs.
23
+ * - Empty/absent → auto-route (ok: true, providers: [])
24
+ * - Unknown name → unrecognized error
25
+ * - isApplicable fails → gated error
26
+ * - Otherwise → resolved IDs in user order
27
+ */
28
+ export declare function resolvePreferredProviders(capability: Capability | 'find_emails', input: Record<string, unknown>, useProviders: unknown): ResolveResult;
29
+ interface GateDesc {
30
+ kind: 'requires' | 'incompatible_with';
31
+ fields: string;
32
+ }
33
+ export {};
34
+ //# sourceMappingURL=provider-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/provider-resolver.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAKhD,eAAO,MAAM,qBAAqB,UAAoD,CAAA;AAEtF,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,UAAU,GAAG,aAAa,GAAG,MAAM,EAAE,CAG1F;AAMD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,mBAAmB,EAAE,MAAM,EAAE,CAAA;CAC9B;AAKD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,UAAU,GAAG,aAAa,EACtC,SAAS,EAAE,MAAM,EAAE,GAClB,gBAAgB,CAKlB;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,UAAU,GAAG,aAAa,EACtC,SAAS,EAAE,MAAM,EAAE,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,gBAAgB,CAoBlB;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,UAAU,GAAG,aAAa,EACtC,SAAS,EAAE,MAAM,EAAE,GAClB,gBAAgB,CASlB;AAMD,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,IAAI,CAAA;IACR,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACrC;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,KAAK,CAAA;IACT,KAAK,EAAE,gBAAgB,CAAA;CACxB;AAED,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,YAAY,CAAA;AAEpD;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,UAAU,GAAG,aAAa,EACtC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,YAAY,EAAE,OAAO,GACpB,aAAa,CAkDf;AAaD,UAAU,QAAQ;IAChB,IAAI,EAAE,UAAU,GAAG,mBAAmB,CAAA;IACtC,MAAM,EAAE,MAAM,CAAA;CACf"}
@@ -0,0 +1,235 @@
1
+ import { listCapabilityProviderIds, getProviderApplicabilityGuard } from '../registry.js';
2
+ import { fuzzyMatch } from './fuzzy.js';
3
+ // find_emails uses a custom waterfall — its providers are not in the registry.
4
+ // Exported so find-emails.ts stays in sync without a second hardcoded list.
5
+ export const FIND_EMAILS_PROVIDERS = ['prospeo', 'fullenrich', 'findymail', 'icypeas'];
6
+ export function getProvidersForCapability(capability) {
7
+ if (capability === 'find_emails')
8
+ return FIND_EMAILS_PROVIDERS;
9
+ return listCapabilityProviderIds(capability);
10
+ }
11
+ const TRUST_LINE = 'Or just run the tool without specifying providers — ColdIQ will automatically pick the best tool for your needs.';
12
+ export function buildUnrecognizedError(input, capability, available) {
13
+ return {
14
+ error: `'${input}' is not a recognized provider for this tool. Available providers: ${available.join(', ')}. ${TRUST_LINE}`,
15
+ available_providers: available,
16
+ };
17
+ }
18
+ export function buildGatedError(provider, gate, capability, available, input) {
19
+ const others = available.filter((p) => {
20
+ if (p === provider)
21
+ return false;
22
+ const guard = getProviderApplicabilityGuard(capability, p);
23
+ try {
24
+ return !guard || guard(input);
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ });
30
+ const othersText = others.length > 0
31
+ ? `Other providers that work with your inputs: ${others.join(', ')}. `
32
+ : '';
33
+ const clause = gate.kind === 'incompatible_with'
34
+ ? `is not compatible with ${gate.fields}`
35
+ : `requires ${gate.fields}, which wasn't provided`;
36
+ return {
37
+ error: `'${provider}' ${clause}. ${othersText}${TRUST_LINE}`,
38
+ available_providers: others,
39
+ };
40
+ }
41
+ export function buildAllFailedError(tried, capability, available) {
42
+ const remaining = available.filter((p) => !tried.includes(p));
43
+ const remainingText = remaining.length > 0
44
+ ? `You can retry with one of these instead: ${remaining.join(', ')}. `
45
+ : '';
46
+ return {
47
+ error: `Couldn't fulfill your request with the providers you specified (${tried.join(', ')}). ${remainingText}${TRUST_LINE}`,
48
+ available_providers: remaining,
49
+ };
50
+ }
51
+ /**
52
+ * Resolve `use_providers` field against the capability's known provider IDs.
53
+ * - Empty/absent → auto-route (ok: true, providers: [])
54
+ * - Unknown name → unrecognized error
55
+ * - isApplicable fails → gated error
56
+ * - Otherwise → resolved IDs in user order
57
+ */
58
+ export function resolvePreferredProviders(capability, input, useProviders) {
59
+ if (!Array.isArray(useProviders) || useProviders.length === 0) {
60
+ return { ok: true, providers: [] };
61
+ }
62
+ const available = getProvidersForCapability(capability);
63
+ const resolved = [];
64
+ const matchedFrom = {};
65
+ for (const raw of useProviders) {
66
+ const userInput = typeof raw === 'string' ? raw.trim() : String(raw);
67
+ const match = fuzzyMatch(userInput, available);
68
+ if (!match) {
69
+ return { ok: false, error: buildUnrecognizedError(userInput, capability, available) };
70
+ }
71
+ if (match.via !== 'exact') {
72
+ matchedFrom[userInput] = match.matched;
73
+ }
74
+ if (!resolved.includes(match.matched)) {
75
+ resolved.push(match.matched);
76
+ }
77
+ }
78
+ // Check isApplicable for each resolved provider (registry-based capabilities only)
79
+ if (capability !== 'find_emails') {
80
+ for (const id of resolved) {
81
+ const guard = getProviderApplicabilityGuard(capability, id);
82
+ if (guard) {
83
+ let applicable;
84
+ try {
85
+ applicable = guard(input);
86
+ }
87
+ catch {
88
+ applicable = false;
89
+ }
90
+ if (!applicable) {
91
+ const gate = getGateDescription(capability, id);
92
+ return {
93
+ ok: false,
94
+ error: buildGatedError(id, gate, capability, available, input),
95
+ };
96
+ }
97
+ }
98
+ }
99
+ }
100
+ return {
101
+ ok: true,
102
+ providers: resolved,
103
+ ...(Object.keys(matchedFrom).length > 0 ? { matchedFrom } : {}),
104
+ };
105
+ }
106
+ const GENERIC_REQUIRES = { kind: 'requires', fields: 'specific input fields' };
107
+ const GATED_DESCRIPTIONS = {
108
+ // -------------------------------------------------------------------------
109
+ search_companies: {
110
+ theirstack: { kind: 'requires', fields: 'technologies, industries, or funding stage filters' },
111
+ signalbase: { kind: 'incompatible_with', fields: 'technology, funding, revenue, or hiring signal filters' },
112
+ blitzapi: { kind: 'incompatible_with', fields: 'advanced filters (tech stack, funding, revenue, exclusion lists, or workforce growth)' },
113
+ apollo: { kind: 'incompatible_with', fields: 'year, tech stack, funding, revenue, exclusion, or workforce growth filters' },
114
+ predictleads: { kind: 'requires', fields: 'both a location filter and an employee size filter' },
115
+ sumble: { kind: 'requires', fields: 'keywords, industries, or technologies' },
116
+ 'limadata-prospect-filter': { kind: 'requires', fields: 'specific employee count range filters (min_employees / max_employees)' },
117
+ 'limadata-prospect-url': { kind: 'requires', fields: 'linkedin_search_url' },
118
+ 'linkupapi-fundraising': { kind: 'requires', fields: 'funding filters and keywords or industries' },
119
+ 'linkupapi-hiring': { kind: 'requires', fields: 'is_hiring: true' },
120
+ 'prospeo-search-company': { kind: 'requires', fields: 'keywords, industries, or countries' },
121
+ 'ai-ark-companies': { kind: 'requires', fields: 'keywords, industries, or countries' },
122
+ },
123
+ // -------------------------------------------------------------------------
124
+ find_people: {
125
+ 'linkupapi-search-profiles': { kind: 'incompatible_with', fields: 'company_linkedin_urls or company_domains (this provider only works without company filters)' },
126
+ 'sumble-people-find': { kind: 'requires', fields: 'company_domains or company_linkedin_urls, and job_titles or seniorities' },
127
+ 'prospeo-search-person': { kind: 'requires', fields: 'job_titles or company_domains' },
128
+ 'ai-ark-people': { kind: 'requires', fields: 'job_titles, seniorities, or keywords' },
129
+ 'findymail-search-employees': { kind: 'requires', fields: 'company_domains and job_titles' },
130
+ },
131
+ // -------------------------------------------------------------------------
132
+ find_email: {
133
+ findymail: { kind: 'requires', fields: 'first_name and (domain or company_name)' },
134
+ 'limadata-work-email': { kind: 'requires', fields: 'first_name, last_name, and domain' },
135
+ blitzapi: { kind: 'requires', fields: 'linkedin_url' },
136
+ 'limadata-work-email-linkedin': { kind: 'requires', fields: 'linkedin_url' },
137
+ },
138
+ // -------------------------------------------------------------------------
139
+ verify_email: {},
140
+ // -------------------------------------------------------------------------
141
+ find_phone: {
142
+ findymail: { kind: 'requires', fields: 'linkedin_url' },
143
+ limadata: { kind: 'requires', fields: 'linkedin_url or (first_name, last_name, and company domain)' },
144
+ 'ai-ark': { kind: 'requires', fields: 'linkedin_url or (first_name, last_name, and company domain)' },
145
+ },
146
+ // -------------------------------------------------------------------------
147
+ enrich_company: {
148
+ companyenrich: { kind: 'requires', fields: 'domain' },
149
+ apollo: { kind: 'requires', fields: 'domain' },
150
+ limadata: { kind: 'requires', fields: 'domain or linkedin_url' },
151
+ wiza: { kind: 'requires', fields: 'domain or name' },
152
+ companyenrich_props: { kind: 'requires', fields: 'name or linkedin_url' },
153
+ blitzapi: { kind: 'requires', fields: 'linkedin_url' },
154
+ icypeas: { kind: 'requires', fields: 'linkedin_url' },
155
+ builtwith: { kind: 'requires', fields: 'domain' },
156
+ openmart: { kind: 'requires', fields: 'domain' },
157
+ 'linkupapi-by-domain': { kind: 'requires', fields: 'domain' },
158
+ 'linkupapi-by-url': { kind: 'requires', fields: 'linkedin_url' },
159
+ },
160
+ // -------------------------------------------------------------------------
161
+ enrich_person: {
162
+ 'linkupapi-profile-enrich': { kind: 'requires', fields: 'first_name, last_name, and (company_name or domain)' },
163
+ 'linkupapi-email-reverse': { kind: 'requires', fields: 'email' },
164
+ 'blitzapi-reverse-email': { kind: 'requires', fields: 'email' },
165
+ 'findymail-business-profile': { kind: 'requires', fields: 'linkedin_url (must be a linkedin.com URL)' },
166
+ 'findymail-reverse-email': { kind: 'requires', fields: 'email' },
167
+ 'icypeas-scrape-profile': { kind: 'requires', fields: 'linkedin_url (must be a linkedin.com URL)' },
168
+ 'icypeas-url-search-profile': { kind: 'requires', fields: 'first_name, last_name, and (company_name or domain)' },
169
+ 'ai-ark-reverse-lookup': { kind: 'requires', fields: 'email or phone' },
170
+ 'icypeas-reverse-email-lookup': { kind: 'requires', fields: 'email' },
171
+ },
172
+ // -------------------------------------------------------------------------
173
+ search_web: {},
174
+ // -------------------------------------------------------------------------
175
+ search_jobs: {
176
+ career_site_jobs: { kind: 'incompatible_with', fields: 'LinkedIn-only filters (seniority_levels, industries, organization_slugs, min/max_employees, easy_apply_only, exclude_easy_apply)' },
177
+ linkedin_jobs_api: { kind: 'incompatible_with', fields: 'Career Site-only filters (ats_slugs, company_domains)' },
178
+ 'theirstack-jobs': { kind: 'requires', fields: 'specific job filter conditions' },
179
+ },
180
+ // -------------------------------------------------------------------------
181
+ search_ads: {
182
+ google_ads: { kind: 'incompatible_with', fields: 'search_urls (LinkedIn-specific input); use query, domains, or advertiser_ids instead' },
183
+ linkedin_ad_library: { kind: 'requires', fields: 'search_urls (pre-built LinkedIn Ad Library URLs)' },
184
+ meta_ads: { kind: 'incompatible_with', fields: 'search_urls (LinkedIn-specific input)' },
185
+ twitter_ads: { kind: 'incompatible_with', fields: 'search_urls (LinkedIn-specific input)' },
186
+ reddit_ads: { kind: 'incompatible_with', fields: 'search_urls (LinkedIn-specific input)' },
187
+ },
188
+ // -------------------------------------------------------------------------
189
+ search_places: {
190
+ openmart: { kind: 'requires', fields: 'country in [US, CA, AU, PR, NZ] or structured Openmart filters' },
191
+ google_maps: { kind: 'requires', fields: 'a query or start_urls' },
192
+ },
193
+ // -------------------------------------------------------------------------
194
+ find_influencers: {
195
+ influencers_similar: { kind: 'requires', fields: 'handle (a creator handle for lookalike search)' },
196
+ },
197
+ // -------------------------------------------------------------------------
198
+ search_reddit: {},
199
+ // -------------------------------------------------------------------------
200
+ search_seo: {
201
+ kw_search_volume: { kind: 'requires', fields: 'category: "keywords"' },
202
+ kw_trends: { kind: 'requires', fields: 'category: "keywords"' },
203
+ serp_google: { kind: 'requires', fields: 'category: "serp" and engine: "google"' },
204
+ serp_bing: { kind: 'requires', fields: 'category: "serp" and engine: "bing"' },
205
+ serp_youtube: { kind: 'requires', fields: 'category: "serp" and engine: "youtube"' },
206
+ bl_summary: { kind: 'requires', fields: 'category: "backlinks"' },
207
+ bl_backlinks: { kind: 'requires', fields: 'category: "backlinks"' },
208
+ bl_referring: { kind: 'requires', fields: 'category: "backlinks"' },
209
+ domain_tech: { kind: 'requires', fields: 'category: "domain" and action: "technologies"' },
210
+ domain_whois: { kind: 'requires', fields: 'category: "domain" and action: "whois"' },
211
+ labs_rank_overview: { kind: 'requires', fields: 'category: "labs" and target' },
212
+ labs_ranked_kw: { kind: 'requires', fields: 'category: "labs" and target' },
213
+ labs_competitors: { kind: 'requires', fields: 'category: "labs" and target' },
214
+ labs_kw_ideas: { kind: 'requires', fields: 'category: "labs" and keywords[]' },
215
+ page_lighthouse: { kind: 'requires', fields: 'category: "page", url, and page_action: "lighthouse"' },
216
+ page_content: { kind: 'requires', fields: 'category: "page", url, and page_action: "content"' },
217
+ },
218
+ // -------------------------------------------------------------------------
219
+ find_signals: {
220
+ 'signalbase-funding': { kind: 'requires', fields: 'signal_type: "funding"' },
221
+ 'signalbase-acquisition': { kind: 'requires', fields: 'signal_type: "acquisition"' },
222
+ 'signalbase-hiring': { kind: 'requires', fields: 'signal_type: "hiring"' },
223
+ 'signalbase-job-change': { kind: 'requires', fields: 'signal_type: "job_change"' },
224
+ 'theirstack-buying-intents': { kind: 'requires', fields: 'signal_type: "intent" and at least one of companies or domains' },
225
+ 'predictleads-financing': { kind: 'requires', fields: 'signal_type: "funding"' },
226
+ 'predictleads-news': { kind: 'requires', fields: 'signal_type: "news"' },
227
+ 'predictleads-startup-posts': { kind: 'requires', fields: 'signal_type: "startup_post"' },
228
+ },
229
+ // -------------------------------------------------------------------------
230
+ fetch_page_content: {},
231
+ };
232
+ function getGateDescription(capability, providerId) {
233
+ return GATED_DESCRIPTIONS[capability]?.[providerId] ?? GENERIC_REQUIRES;
234
+ }
235
+ //# sourceMappingURL=provider-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-resolver.js","sourceRoot":"","sources":["../../src/utils/provider-resolver.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.1.9",
3
+ "version": "0.1.10",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/executor.ts CHANGED
@@ -7,6 +7,7 @@ export interface ExecutionResult {
7
7
  _meta: {
8
8
  provider: string
9
9
  latencyMs: number
10
+ matchedFrom?: Record<string, string>
10
11
  }
11
12
  }
12
13
 
@@ -138,20 +139,41 @@ async function executeSingle(
138
139
  // Fallback execution — try providers in order, return first success
139
140
  // ---------------------------------------------------------------------------
140
141
 
142
+ export interface ExecuteOptions {
143
+ /** When set, only these provider IDs are tried (in this order). No fallback to others. */
144
+ providers?: string[]
145
+ /** Fuzzy-match aliases to surface in success _meta */
146
+ matchedFrom?: Record<string, string>
147
+ }
148
+
141
149
  export async function executeWithFallback(
142
150
  capability: Capability,
143
151
  input: Record<string, unknown>,
152
+ options?: ExecuteOptions,
144
153
  ): Promise<ExecutionResult | ExecutionError> {
145
- const providers =
154
+ const preferredIds = options?.providers ?? []
155
+ const isConstrained = preferredIds.length > 0
156
+
157
+ let allProviders =
146
158
  capability === 'search_web'
147
159
  ? getSearchWebProviders(input.search_type === 'neural')
148
160
  : getProviders(capability)
149
161
 
162
+ // When the caller pinned specific providers, filter to those in order.
163
+ if (isConstrained) {
164
+ const ordered: typeof allProviders = []
165
+ for (const id of preferredIds) {
166
+ const entry = allProviders.find((p) => p.id === id)
167
+ if (entry) ordered.push(entry)
168
+ }
169
+ allProviders = ordered
170
+ }
171
+
150
172
  const errors: Array<{ id: string; status: number; error: string }> = []
151
173
  let billingUrl: string | undefined
152
174
  let zeroBalance = false
153
175
 
154
- for (const provider of providers) {
176
+ for (const provider of allProviders) {
155
177
  if (provider.isApplicable) {
156
178
  let applicable: boolean
157
179
  try {
@@ -187,7 +209,11 @@ export async function executeWithFallback(
187
209
 
188
210
  if (hasResult) {
189
211
  log(`${capability} → ${provider.id} ✓ (${result.latencyMs}ms)`)
190
- return { data: payload, _meta: { provider: provider.id, latencyMs: result.latencyMs } }
212
+ const meta: ExecutionResult['_meta'] = { provider: provider.id, latencyMs: result.latencyMs }
213
+ if (options?.matchedFrom && Object.keys(options.matchedFrom).length > 0) {
214
+ meta.matchedFrom = options.matchedFrom
215
+ }
216
+ return { data: payload, _meta: meta }
191
217
  }
192
218
 
193
219
  // Record the failure
@@ -221,11 +247,13 @@ export async function executeWithFallback(
221
247
  }
222
248
 
223
249
  debug(`${capability}: all ${errors.length} providers failed — ${JSON.stringify(errors)}`)
224
- const sanitized = errors.map((e, i) => ({
225
- provider: `provider_${i + 1}`,
226
- status: e.status,
227
- error: e.error.slice(0, 200),
228
- }))
250
+
251
+ // When the user pinned specific providers, expose their real IDs in the error
252
+ // (they already know what they asked for). Otherwise anonymize to avoid leaking
253
+ // internal provider names on auto-route failures.
254
+ const sanitized = isConstrained
255
+ ? errors.map((e) => ({ provider: e.id, status: e.status, error: e.error.slice(0, 200) }))
256
+ : errors.map((e, i) => ({ provider: `provider_${i + 1}`, status: e.status, error: e.error.slice(0, 200) }))
229
257
 
230
258
  const allOutOfCredits = zeroBalance
231
259
  || (errors.length > 0 && errors.every((e) => e.status === 402))
package/src/registry.ts CHANGED
@@ -3073,6 +3073,26 @@ const registry: Record<Capability, ProviderEntry[]> = {
3073
3073
  fetch_page_content: fetchPageContentProviders,
3074
3074
  }
3075
3075
 
3076
+ /**
3077
+ * Return the ordered provider IDs for a capability (for documentation / resolver).
3078
+ */
3079
+ export function listCapabilityProviderIds(capability: Capability): string[] {
3080
+ return getProviders(capability).map((p) => p.id)
3081
+ }
3082
+
3083
+ /**
3084
+ * Return the isApplicable guard for a specific provider within a capability.
3085
+ * Returns undefined when the provider has no gate.
3086
+ */
3087
+ export function getProviderApplicabilityGuard(
3088
+ capability: Capability,
3089
+ providerId: string,
3090
+ ): ((input: Record<string, unknown>) => boolean) | undefined {
3091
+ const providers = registry[capability]
3092
+ if (!providers) return undefined
3093
+ return providers.find((p) => p.id === providerId)?.isApplicable
3094
+ }
3095
+
3076
3096
  /**
3077
3097
  * Get the ordered provider list for a capability.
3078
3098
  * Returns a copy sorted by priority (lower = first).
@@ -1,23 +1,30 @@
1
1
  import { z } from 'zod'
2
2
  import { executeWithFallback, isExecutionError } from '../executor.js'
3
+ import { resolvePreferredProviders, getProvidersForCapability } from '../utils/provider-resolver.js'
3
4
 
4
5
  export const enrichCompanyName = 'enrich_company'
5
6
 
6
7
  export const enrichCompanyDescription =
7
- 'Get detailed information about a company. Provide at least one of: domain, linkedin_url, or name. Returns company size, industry, funding, technologies, social profiles, and more. Use this to research a specific company before outreach or to fill in missing company data.'
8
+ 'Get detailed information about a company. Provide at least one of: domain, linkedin_url, or name. Returns company size, industry, funding, technologies, social profiles, and more. ColdIQ automatically picks the best provider based on which input fields are present pass use_providers only if you need a specific tool.'
8
9
 
9
10
  export const enrichCompanySchema = {
10
11
  domain: z.string().optional().describe('Company domain (e.g. "stripe.com")'),
11
12
  linkedin_url: z.string().url().optional().describe('Company LinkedIn URL (e.g. "https://www.linkedin.com/company/stripe")'),
12
13
  name: z.string().optional().describe('Company name (e.g. "Stripe")'),
14
+ 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('enrich_company').join(', ')}. Provider names are matched fuzzily, so minor typos are tolerated.`),
13
15
  }
14
16
 
15
17
  export async function enrichCompanyHandler(input: Record<string, unknown>) {
16
- if (!input.domain && !input.linkedin_url && !input.name) {
18
+ const { use_providers: rawUseProviders, ...restInput } = input
19
+ if (!restInput.domain && !restInput.linkedin_url && !restInput.name) {
17
20
  const err = { error: 'At least one of domain, linkedin_url, or name is required', providers_tried: [] }
18
21
  return { content: [{ type: 'text' as const, text: JSON.stringify(err, null, 2) }], isError: true }
19
22
  }
20
- const result = await executeWithFallback('enrich_company', input)
23
+ const resolved = resolvePreferredProviders('enrich_company', restInput, rawUseProviders)
24
+ if (!resolved.ok) {
25
+ return { content: [{ type: 'text' as const, text: JSON.stringify(resolved.error, null, 2) }], isError: true }
26
+ }
27
+ const result = await executeWithFallback('enrich_company', restInput, { providers: resolved.providers, matchedFrom: resolved.matchedFrom })
21
28
  if (isExecutionError(result)) {
22
29
  return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }], isError: true }
23
30
  }
@@ -1,12 +1,14 @@
1
1
  import { z } from 'zod'
2
2
  import { executeWithFallback, isExecutionError } from '../executor.js'
3
+ import { resolvePreferredProviders, getProvidersForCapability } from '../utils/provider-resolver.js'
3
4
 
4
5
  export const enrichPersonName = 'enrich_person'
5
6
 
6
7
  export const enrichPersonDescription =
7
8
  "Look up a person's LinkedIn profile and professional details. " +
8
9
  'Accepts either an email address (reverse lookup) or name + company (profile enrichment). ' +
9
- 'Returns LinkedIn URL, headline, location, current role, and profile data.'
10
+ 'Returns LinkedIn URL, headline, location, current role, and profile data. ' +
11
+ 'ColdIQ automatically picks the best provider based on which input fields are present — pass use_providers only if you need a specific tool.'
10
12
 
11
13
  export const enrichPersonSchema = {
12
14
  email: z.string().email().optional().describe('Email address for reverse lookup — identify the person behind the email'),
@@ -16,10 +18,16 @@ export const enrichPersonSchema = {
16
18
  domain: z.string().optional().describe('Company domain — alternative to company_name (e.g. "coldiq.com")'),
17
19
  linkedin_url: z.string().url().optional().describe('LinkedIn profile URL for direct profile lookup (e.g. "https://www.linkedin.com/in/michel-lieben")'),
18
20
  phone: z.string().optional().describe('Phone number for reverse phone lookup'),
21
+ 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('enrich_person').join(', ')}. Provider names are matched fuzzily, so minor typos are tolerated.`),
19
22
  }
20
23
 
21
24
  export async function enrichPersonHandler(input: Record<string, unknown>) {
22
- const result = await executeWithFallback('enrich_person', input)
25
+ const { use_providers: rawUseProviders, ...restInput } = input
26
+ const resolved = resolvePreferredProviders('enrich_person', restInput, rawUseProviders)
27
+ if (!resolved.ok) {
28
+ return { content: [{ type: 'text' as const, text: JSON.stringify(resolved.error, null, 2) }], isError: true }
29
+ }
30
+ const result = await executeWithFallback('enrich_person', restInput, { providers: resolved.providers, matchedFrom: resolved.matchedFrom })
23
31
  if (isExecutionError(result)) {
24
32
  return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }], isError: true }
25
33
  }
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { executeWithFallback, isExecutionError } from '../executor.js'
3
+ import { resolvePreferredProviders, getProvidersForCapability } from '../utils/provider-resolver.js'
3
4
 
4
5
  export const fetchPageContentName = 'fetch_page_content'
5
6
 
@@ -25,10 +26,16 @@ export const fetchPageContentSchema = {
25
26
  .optional()
26
27
  .default(false)
27
28
  .describe('Generate a summary for each page in addition to the text (default: false).'),
29
+ 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('fetch_page_content').join(', ')}. Provider names are matched fuzzily, so minor typos are tolerated.`),
28
30
  }
29
31
 
30
32
  export async function fetchPageContentHandler(input: Record<string, unknown>) {
31
- const result = await executeWithFallback('fetch_page_content', input)
33
+ const { use_providers: rawUseProviders, ...restInput } = input
34
+ const resolved = resolvePreferredProviders('fetch_page_content', restInput, rawUseProviders)
35
+ if (!resolved.ok) {
36
+ return { content: [{ type: 'text' as const, text: JSON.stringify(resolved.error, null, 2) }], isError: true }
37
+ }
38
+ const result = await executeWithFallback('fetch_page_content', restInput, { providers: resolved.providers, matchedFrom: resolved.matchedFrom })
32
39
  if (isExecutionError(result)) {
33
40
  return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }], isError: true }
34
41
  }
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { executeWithFallback, isExecutionError } from '../executor.js'
3
+ import { resolvePreferredProviders, getProvidersForCapability } from '../utils/provider-resolver.js'
3
4
 
4
5
  export const findEmailName = 'find_email'
5
6
 
@@ -15,6 +16,7 @@ export const findEmailSchema = {
15
16
  domain: z.string().optional().describe('Company domain (e.g. "stripe.com")'),
16
17
  company_name: z.string().optional().describe('Company name — alternative to domain'),
17
18
  linkedin_url: z.string().optional().describe('LinkedIn profile URL — alternative to name+domain'),
19
+ 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('find_email').join(', ')}. Provider names are matched fuzzily, so minor typos are tolerated.`),
18
20
  }
19
21
 
20
22
  const inputSchema = z
@@ -40,7 +42,12 @@ const inputSchema = z
40
42
  })
41
43
 
42
44
  export async function findEmailHandler(input: Record<string, unknown>) {
43
- const validation = inputSchema.safeParse(input)
45
+ const { use_providers: rawUseProviders, ...restInput } = input
46
+ const resolved = resolvePreferredProviders('find_email', restInput, rawUseProviders)
47
+ if (!resolved.ok) {
48
+ return { content: [{ type: 'text' as const, text: JSON.stringify(resolved.error, null, 2) }], isError: true }
49
+ }
50
+ const validation = inputSchema.safeParse(restInput)
44
51
  if (!validation.success) {
45
52
  const msg = validation.error.issues.map((i) => i.message).join('; ')
46
53
  return {
@@ -48,7 +55,7 @@ export async function findEmailHandler(input: Record<string, unknown>) {
48
55
  isError: true,
49
56
  }
50
57
  }
51
- const result = await executeWithFallback('find_email', input)
58
+ const result = await executeWithFallback('find_email', restInput, { providers: resolved.providers, matchedFrom: resolved.matchedFrom })
52
59
  if (isExecutionError(result)) {
53
60
  return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }], isError: true }
54
61
  }