@coldiq/mcp 0.1.13 → 0.1.14
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/package.json
CHANGED
package/src/registry.ts
CHANGED
|
@@ -1045,7 +1045,9 @@ const findPeopleProviders: ProviderEntry[] = [
|
|
|
1045
1045
|
},
|
|
1046
1046
|
extractId: (response) => {
|
|
1047
1047
|
const d = response as Record<string, unknown>
|
|
1048
|
-
|
|
1048
|
+
// LeadsFactory's upstream returns the ObjectId as `id`; the short `search_id`
|
|
1049
|
+
// cannot be used as the GET param (upstream rejects it as "not a valid ObjectId").
|
|
1050
|
+
return (d.id as string) ?? (d.search_id as string)
|
|
1049
1051
|
},
|
|
1050
1052
|
},
|
|
1051
1053
|
},
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Direct upstream probe — bypasses the marketplace proxy. Goal: figure out why
|
|
2
|
+
// GET /api/v1/find-people/searches/{id} returns errors from our proxy. Probe with
|
|
3
|
+
// both id formats (mongo _id and short search_id) and both path forms (singular vs plural).
|
|
4
|
+
//
|
|
5
|
+
// Run: LEADSFACTORY_API_KEY=… npx tsx mcp/tests/live/leadsfactory-upstream-probe.ts
|
|
6
|
+
|
|
7
|
+
const KEY = process.env.LEADSFACTORY_API_KEY
|
|
8
|
+
if (!KEY) { console.error('LEADSFACTORY_API_KEY required'); process.exit(1) }
|
|
9
|
+
const BASE = 'https://apiv2.leadsfactory.io'
|
|
10
|
+
|
|
11
|
+
async function call(method: 'GET' | 'POST', path: string, body?: unknown) {
|
|
12
|
+
const t0 = Date.now()
|
|
13
|
+
try {
|
|
14
|
+
const res = await fetch(`${BASE}${path}`, {
|
|
15
|
+
method,
|
|
16
|
+
headers: {
|
|
17
|
+
'X-API-Key': KEY!,
|
|
18
|
+
...(body ? { 'Content-Type': 'application/json' } : {}),
|
|
19
|
+
},
|
|
20
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
21
|
+
signal: AbortSignal.timeout(30_000),
|
|
22
|
+
})
|
|
23
|
+
let data: any
|
|
24
|
+
try { data = await res.json() } catch { data = await res.text().catch(() => '<no body>') }
|
|
25
|
+
return { ok: res.ok, status: res.status, ms: Date.now() - t0, data }
|
|
26
|
+
} catch (err) {
|
|
27
|
+
return { ok: false, status: 0, ms: Date.now() - t0, data: { error: String(err) } }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function summary(r: any, max = 300) {
|
|
32
|
+
const s = typeof r.data === 'string' ? r.data : JSON.stringify(r.data)
|
|
33
|
+
return `status=${r.status} ms=${r.ms} body=${s.slice(0, max)}`
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function main() {
|
|
37
|
+
console.log('=== LeadsFactory upstream probe ===')
|
|
38
|
+
console.log(`BASE=${BASE}\n`)
|
|
39
|
+
|
|
40
|
+
// 1. Create a small search to get fresh IDs to test against
|
|
41
|
+
console.log('▸ Step 1: POST to create a small search (1 contact at ColdIQ)')
|
|
42
|
+
const post = await call('POST', '/api/v1/find-people/search', {
|
|
43
|
+
company_domains: ['coldiq.com'],
|
|
44
|
+
search: {
|
|
45
|
+
max_persona_results: 1,
|
|
46
|
+
personas: [{ job_title: 'CEO' }],
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
console.log(` ${summary(post, 800)}`)
|
|
50
|
+
|
|
51
|
+
if (!post.ok) {
|
|
52
|
+
console.log(' POST failed; aborting GET probes.')
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const mongoId = post.data?._id
|
|
57
|
+
const searchId = post.data?.search_id
|
|
58
|
+
console.log(`\n mongoId=${mongoId}`)
|
|
59
|
+
console.log(` searchId=${searchId}\n`)
|
|
60
|
+
|
|
61
|
+
// 2. Try the GET in every shape we can think of
|
|
62
|
+
console.log('▸ Step 2: GET probes (try every path/id combo)')
|
|
63
|
+
|
|
64
|
+
for (const id of [mongoId, searchId].filter(Boolean)) {
|
|
65
|
+
const idType = id === mongoId ? 'mongoId' : 'searchId'
|
|
66
|
+
for (const path of [
|
|
67
|
+
`/api/v1/find-people/searches/${id}`, // current marketplace shape (plural)
|
|
68
|
+
`/api/v1/find-people/search/${id}`, // singular — matches POST path style
|
|
69
|
+
`/api/v1/find-people/searches/${id}/`, // trailing slash
|
|
70
|
+
`/api/v1/find-people/${id}`, // no segment
|
|
71
|
+
`/api/v1/find-people/search?id=${id}`, // query param
|
|
72
|
+
`/api/v1/find-people/search?search_id=${id}`,
|
|
73
|
+
]) {
|
|
74
|
+
const r = await call('GET', path)
|
|
75
|
+
console.log(` [${idType}] GET ${path}`)
|
|
76
|
+
console.log(` ${summary(r)}`)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 3. Try the GET via different auth shapes (in case X-API-Key vs Bearer matters for GET)
|
|
81
|
+
console.log('\n▸ Step 3: alternate auth headers on the canonical path (plural + mongoId)')
|
|
82
|
+
const probePath = `/api/v1/find-people/searches/${mongoId}`
|
|
83
|
+
for (const [label, headers] of [
|
|
84
|
+
['X-API-Key', { 'X-API-Key': KEY! }],
|
|
85
|
+
['Authorization Bearer', { Authorization: `Bearer ${KEY}` }],
|
|
86
|
+
['Authorization raw', { Authorization: KEY! }],
|
|
87
|
+
['Api-Key', { 'Api-Key': KEY! }],
|
|
88
|
+
] as const) {
|
|
89
|
+
const t0 = Date.now()
|
|
90
|
+
const res = await fetch(`${BASE}${probePath}`, { headers, signal: AbortSignal.timeout(20_000) })
|
|
91
|
+
let body: any
|
|
92
|
+
try { body = await res.json() } catch { body = await res.text() }
|
|
93
|
+
console.log(` [${label}] status=${res.status} ms=${Date.now() - t0} body=${JSON.stringify(body).slice(0, 200)}`)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
main().catch((e) => { console.error('FATAL', e); process.exit(1) })
|
|
@@ -21,7 +21,7 @@ describe('find_people handler', () => {
|
|
|
21
21
|
|
|
22
22
|
// LeadsFactory create search — returns an ID
|
|
23
23
|
if (urlStr.includes('/leadsfactory/contact-finder/searches') && callCount === 1) {
|
|
24
|
-
return new Response(JSON.stringify({
|
|
24
|
+
return new Response(JSON.stringify({ id: 'abc123', search_id: 'abc', nb_jobs_total: 1, progress_percentage: 0 }), { status: 201 })
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
// LeadsFactory poll — always RUNNING (will timeout)
|
|
@@ -70,7 +70,7 @@ describe('find_people handler', () => {
|
|
|
70
70
|
|
|
71
71
|
// LeadsFactory create search
|
|
72
72
|
if (urlStr.includes('/leadsfactory/contact-finder/searches') && !urlStr.includes('abc123')) {
|
|
73
|
-
return new Response(JSON.stringify({
|
|
73
|
+
return new Response(JSON.stringify({ id: 'abc123', search_id: 'abc', nb_jobs_total: 1, progress_percentage: 0 }), { status: 201 })
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
// LeadsFactory poll — first RUNNING, then SUCCESSFUL with contacts
|
|
@@ -121,7 +121,7 @@ describe('find_people handler', () => {
|
|
|
121
121
|
|
|
122
122
|
// LeadsFactory create
|
|
123
123
|
if (urlStr.includes('/leadsfactory/contact-finder/searches') && !urlStr.includes('abc123')) {
|
|
124
|
-
return new Response(JSON.stringify({
|
|
124
|
+
return new Response(JSON.stringify({ id: 'abc123', search_id: 'abc', nb_jobs_total: 2, progress_percentage: 0 }), { status: 201 })
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
// LeadsFactory poll — SUCCESSFUL with one miss
|
|
@@ -179,7 +179,7 @@ describe('find_people handler', () => {
|
|
|
179
179
|
const urlStr = url.toString()
|
|
180
180
|
|
|
181
181
|
if (urlStr.includes('/leadsfactory/contact-finder/searches') && !urlStr.includes('abc123')) {
|
|
182
|
-
return new Response(JSON.stringify({
|
|
182
|
+
return new Response(JSON.stringify({ id: 'abc123', nb_jobs_total: 2, progress_percentage: 0 }), { status: 201 })
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
if (urlStr.includes('/leadsfactory/contact-finder/searches/abc123')) {
|
|
@@ -231,7 +231,7 @@ describe('find_people handler', () => {
|
|
|
231
231
|
|
|
232
232
|
if (urlStr.includes('/leadsfactory/contact-finder/searches') && !urlStr.includes('abc123')) {
|
|
233
233
|
capturedBody = JSON.parse((opts?.body as string) ?? '{}')
|
|
234
|
-
return new Response(JSON.stringify({
|
|
234
|
+
return new Response(JSON.stringify({ id: 'abc123', nb_jobs_total: 2, progress_percentage: 0 }), { status: 201 })
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
if (urlStr.includes('/leadsfactory/contact-finder/searches/abc123')) {
|
|
@@ -277,7 +277,7 @@ describe('find_people handler', () => {
|
|
|
277
277
|
|
|
278
278
|
if (urlStr.includes('/leadsfactory/contact-finder/searches') && !urlStr.includes('abc123')) {
|
|
279
279
|
capturedBody = JSON.parse((opts?.body as string) ?? '{}')
|
|
280
|
-
return new Response(JSON.stringify({
|
|
280
|
+
return new Response(JSON.stringify({ id: 'abc123', nb_jobs_total: 1, progress_percentage: 0 }), { status: 201 })
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
if (urlStr.includes('/leadsfactory/contact-finder/searches/abc123')) {
|