@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coldiq/mcp",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
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
- return (d._id as string) ?? (d.search_id as string)
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({ _id: 'abc123', search_id: 'abc', nb_jobs_total: 1, progress_percentage: 0 }), { status: 201 })
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({ _id: 'abc123', search_id: 'abc', nb_jobs_total: 1, progress_percentage: 0 }), { status: 201 })
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({ _id: 'abc123', search_id: 'abc', nb_jobs_total: 2, progress_percentage: 0 }), { status: 201 })
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({ _id: 'abc123', nb_jobs_total: 2, progress_percentage: 0 }), { status: 201 })
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({ _id: 'abc123', nb_jobs_total: 2, progress_percentage: 0 }), { status: 201 })
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({ _id: 'abc123', nb_jobs_total: 1, progress_percentage: 0 }), { status: 201 })
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')) {