@coldiq/mcp 0.1.8 → 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.
- package/README.md +1 -1
- package/dist/executor.d.ts +8 -1
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +45 -18
- package/dist/executor.js.map +1 -1
- package/dist/registry.d.ts +9 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +16 -0
- package/dist/registry.js.map +1 -1
- package/dist/tools/enrich-company.d.ts +2 -1
- package/dist/tools/enrich-company.d.ts.map +1 -1
- package/dist/tools/enrich-company.js +10 -3
- package/dist/tools/enrich-company.js.map +1 -1
- package/dist/tools/enrich-person.d.ts +1 -0
- package/dist/tools/enrich-person.d.ts.map +1 -1
- package/dist/tools/enrich-person.js +10 -2
- package/dist/tools/enrich-person.js.map +1 -1
- package/dist/tools/fetch-page-content.d.ts +1 -0
- package/dist/tools/fetch-page-content.d.ts.map +1 -1
- package/dist/tools/fetch-page-content.js +8 -1
- package/dist/tools/fetch-page-content.js.map +1 -1
- package/dist/tools/find-email.d.ts +1 -0
- package/dist/tools/find-email.d.ts.map +1 -1
- package/dist/tools/find-email.js +9 -2
- package/dist/tools/find-email.js.map +1 -1
- package/dist/tools/find-emails.d.ts +8 -0
- package/dist/tools/find-emails.d.ts.map +1 -1
- package/dist/tools/find-emails.js +64 -33
- package/dist/tools/find-emails.js.map +1 -1
- package/dist/tools/find-influencers.d.ts +1 -0
- package/dist/tools/find-influencers.d.ts.map +1 -1
- package/dist/tools/find-influencers.js +8 -1
- package/dist/tools/find-influencers.js.map +1 -1
- package/dist/tools/find-people.d.ts +1 -0
- package/dist/tools/find-people.d.ts.map +1 -1
- package/dist/tools/find-people.js +12 -5
- package/dist/tools/find-people.js.map +1 -1
- package/dist/tools/find-phone.d.ts +1 -0
- package/dist/tools/find-phone.d.ts.map +1 -1
- package/dist/tools/find-phone.js +9 -2
- package/dist/tools/find-phone.js.map +1 -1
- package/dist/tools/find-signals.d.ts +1 -0
- package/dist/tools/find-signals.d.ts.map +1 -1
- package/dist/tools/find-signals.js +14 -7
- package/dist/tools/find-signals.js.map +1 -1
- package/dist/tools/search-ads.d.ts +1 -0
- package/dist/tools/search-ads.d.ts.map +1 -1
- package/dist/tools/search-ads.js +8 -1
- package/dist/tools/search-ads.js.map +1 -1
- package/dist/tools/search-companies.d.ts +2 -1
- package/dist/tools/search-companies.d.ts.map +1 -1
- package/dist/tools/search-companies.js +9 -2
- package/dist/tools/search-companies.js.map +1 -1
- package/dist/tools/search-jobs.d.ts +1 -0
- package/dist/tools/search-jobs.d.ts.map +1 -1
- package/dist/tools/search-jobs.js +9 -2
- package/dist/tools/search-jobs.js.map +1 -1
- package/dist/tools/search-places.d.ts +1 -0
- package/dist/tools/search-places.d.ts.map +1 -1
- package/dist/tools/search-places.js +8 -1
- package/dist/tools/search-places.js.map +1 -1
- package/dist/tools/search-reddit.d.ts +1 -0
- package/dist/tools/search-reddit.d.ts.map +1 -1
- package/dist/tools/search-reddit.js +8 -1
- package/dist/tools/search-reddit.js.map +1 -1
- package/dist/tools/search-seo.d.ts +1 -0
- package/dist/tools/search-seo.d.ts.map +1 -1
- package/dist/tools/search-seo.js +8 -1
- package/dist/tools/search-seo.js.map +1 -1
- package/dist/tools/search-web.d.ts +1 -0
- package/dist/tools/search-web.d.ts.map +1 -1
- package/dist/tools/search-web.js +10 -2
- package/dist/tools/search-web.js.map +1 -1
- package/dist/tools/verify-email.d.ts +2 -1
- package/dist/tools/verify-email.d.ts.map +1 -1
- package/dist/tools/verify-email.js +9 -2
- package/dist/tools/verify-email.js.map +1 -1
- package/dist/utils/fuzzy.d.ts +13 -0
- package/dist/utils/fuzzy.d.ts.map +1 -0
- package/dist/utils/fuzzy.js +46 -0
- package/dist/utils/fuzzy.js.map +1 -0
- package/dist/utils/provider-resolver.d.ts +34 -0
- package/dist/utils/provider-resolver.d.ts.map +1 -0
- package/dist/utils/provider-resolver.js +235 -0
- package/dist/utils/provider-resolver.js.map +1 -0
- package/package.json +1 -1
- package/src/executor.ts +55 -14
- package/src/registry.ts +20 -0
- package/src/tools/enrich-company.ts +10 -3
- package/src/tools/enrich-person.ts +10 -2
- package/src/tools/fetch-page-content.ts +8 -1
- package/src/tools/find-email.ts +9 -2
- package/src/tools/find-emails.ts +78 -41
- package/src/tools/find-influencers.ts +8 -1
- package/src/tools/find-people.ts +12 -5
- package/src/tools/find-phone.ts +9 -2
- package/src/tools/find-signals.ts +15 -7
- package/src/tools/search-ads.ts +8 -1
- package/src/tools/search-companies.ts +9 -2
- package/src/tools/search-jobs.ts +9 -2
- package/src/tools/search-places.ts +8 -1
- package/src/tools/search-reddit.ts +8 -1
- package/src/tools/search-seo.ts +8 -1
- package/src/tools/search-web.ts +10 -2
- package/src/tools/verify-email.ts +9 -2
- package/src/utils/fuzzy.ts +57 -0
- package/src/utils/provider-resolver.ts +306 -0
- package/tests/executor.test.ts +184 -4
- package/tests/registry.test.ts +22 -1
- package/tests/tools/find-email.test.ts +43 -0
- package/tests/tools/find-emails.test.ts +87 -0
- package/tests/tools/search-companies.test.ts +40 -0
- package/tests/utils/fuzzy.test.ts +63 -0
- 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
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,19 +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
|
|
174
|
+
let zeroBalance = false
|
|
152
175
|
|
|
153
|
-
for (const provider of
|
|
176
|
+
for (const provider of allProviders) {
|
|
154
177
|
if (provider.isApplicable) {
|
|
155
178
|
let applicable: boolean
|
|
156
179
|
try {
|
|
@@ -186,7 +209,11 @@ export async function executeWithFallback(
|
|
|
186
209
|
|
|
187
210
|
if (hasResult) {
|
|
188
211
|
log(`${capability} → ${provider.id} ✓ (${result.latencyMs}ms)`)
|
|
189
|
-
|
|
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 }
|
|
190
217
|
}
|
|
191
218
|
|
|
192
219
|
// Record the failure
|
|
@@ -201,23 +228,37 @@ export async function executeWithFallback(
|
|
|
201
228
|
|
|
202
229
|
// Capture the billing URL the API surfaces in 402 responses so the
|
|
203
230
|
// top-level error can include a clickable link for the user.
|
|
204
|
-
if (result.status === 402
|
|
205
|
-
&& result.data && typeof result.data === 'object'
|
|
206
|
-
|
|
207
|
-
billingUrl
|
|
231
|
+
if (result.status === 402
|
|
232
|
+
&& result.data && typeof result.data === 'object') {
|
|
233
|
+
const body = result.data as Record<string, unknown>
|
|
234
|
+
if (!billingUrl && typeof body.billingUrl === 'string') {
|
|
235
|
+
billingUrl = body.billingUrl
|
|
236
|
+
}
|
|
237
|
+
// Short-circuit the waterfall when the user has no credits at all.
|
|
238
|
+
// Every paid provider needs >= 1 credit, so trying the rest is wasted
|
|
239
|
+
// round-trips. Non-zero balances may still match a cheaper provider,
|
|
240
|
+
// so only bail when balance is exactly 0.
|
|
241
|
+
if (typeof body.balance === 'number' && body.balance <= 0) {
|
|
242
|
+
zeroBalance = true
|
|
243
|
+
debug(`${capability} → balance is 0, skipping remaining providers`)
|
|
244
|
+
break
|
|
245
|
+
}
|
|
208
246
|
}
|
|
209
247
|
}
|
|
210
248
|
|
|
211
249
|
debug(`${capability}: all ${errors.length} providers failed — ${JSON.stringify(errors)}`)
|
|
212
|
-
const sanitized = errors.map((e, i) => ({
|
|
213
|
-
provider: `provider_${i + 1}`,
|
|
214
|
-
status: e.status,
|
|
215
|
-
error: e.error.slice(0, 200),
|
|
216
|
-
}))
|
|
217
250
|
|
|
218
|
-
|
|
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) }))
|
|
257
|
+
|
|
258
|
+
const allOutOfCredits = zeroBalance
|
|
259
|
+
|| (errors.length > 0 && errors.every((e) => e.status === 402))
|
|
219
260
|
const topLevelError = allOutOfCredits && billingUrl
|
|
220
|
-
? `Insufficient credits —
|
|
261
|
+
? `Insufficient credits — your account balance is 0 and this request requires credits. Top up at ${billingUrl} to continue.`
|
|
221
262
|
: `All ${errors.length} providers failed for ${capability}`
|
|
222
263
|
|
|
223
264
|
const out: ExecutionError = {
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
}
|
package/src/tools/find-email.ts
CHANGED
|
@@ -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
|
|
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',
|
|
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
|
}
|