@coldiq/mcp 0.2.4 → 0.2.6
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/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +15 -2
- package/dist/registry.js.map +1 -1
- package/dist/tools/find-signals.d.ts.map +1 -1
- package/dist/tools/find-signals.js +27 -3
- package/dist/tools/find-signals.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 +57 -1
- package/dist/tools/verify-email.js.map +1 -1
- package/dist/utils/compact-people.d.ts.map +1 -1
- package/dist/utils/compact-people.js +5 -0
- package/dist/utils/compact-people.js.map +1 -1
- package/package.json +1 -1
- package/src/registry.ts +16 -2
- package/src/tools/find-signals.ts +26 -3
- package/src/tools/verify-email.ts +65 -1
- package/src/utils/compact-people.ts +5 -0
- package/tests/registry-enrich-person.test.ts +30 -2
- package/tests/registry-find-people.test.ts +8 -4
- package/tests/tools/find-signals.test.ts +62 -0
- package/tests/tools/verify-email.test.ts +77 -3
- package/tests/utils/compact-people.test.ts +27 -2
|
@@ -1,6 +1,66 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
2
2
|
import { initClient } from '../../src/client.js'
|
|
3
|
-
import { verifyEmailHandler } from '../../src/tools/verify-email.js'
|
|
3
|
+
import { verifyEmailHandler, addNormalizedStatus } from '../../src/tools/verify-email.js'
|
|
4
|
+
|
|
5
|
+
describe('addNormalizedStatus', () => {
|
|
6
|
+
it('findymail verified=true → status valid', () => {
|
|
7
|
+
const out = addNormalizedStatus({ email: 'michel@coldiq.com', verified: true, provider: 'Google' }, 'findymail') as Record<string, unknown>
|
|
8
|
+
expect(out.status).toBe('valid')
|
|
9
|
+
// Original fields preserved for back-compat.
|
|
10
|
+
expect(out.verified).toBe(true)
|
|
11
|
+
expect(out.provider).toBe('Google')
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('findymail verified=false → status invalid', () => {
|
|
15
|
+
const out = addNormalizedStatus({ email: 'bad@example.com', verified: false, provider: 'Other' }, 'findymail') as Record<string, unknown>
|
|
16
|
+
expect(out.status).toBe('invalid')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('icypeas item.status=catch_all → catch_all', () => {
|
|
20
|
+
const out = addNormalizedStatus({ success: true, item: { status: 'catch_all', email: 'x@y.com' } }, 'icypeas') as Record<string, unknown>
|
|
21
|
+
expect(out.status).toBe('catch_all')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('icypeas missing item → unknown', () => {
|
|
25
|
+
const out = addNormalizedStatus({ success: true }, 'icypeas') as Record<string, unknown>
|
|
26
|
+
expect(out.status).toBe('unknown')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('upstream status="catchall" is normalized to catch_all', () => {
|
|
30
|
+
const out = addNormalizedStatus({ status: 'catchall', email: 'x@y.com' }, 'instantly') as Record<string, unknown>
|
|
31
|
+
expect(out.status).toBe('catch_all')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('upstream status="risky" stays risky', () => {
|
|
35
|
+
const out = addNormalizedStatus({ status: 'risky' }, 'instantly') as Record<string, unknown>
|
|
36
|
+
expect(out.status).toBe('risky')
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('upstream status="disposable" stays disposable', () => {
|
|
40
|
+
const out = addNormalizedStatus({ status: 'disposable' }, 'instantly') as Record<string, unknown>
|
|
41
|
+
expect(out.status).toBe('disposable')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('upstream status="VALID" is lowercased to valid', () => {
|
|
45
|
+
const out = addNormalizedStatus({ status: 'VALID' }, 'instantly') as Record<string, unknown>
|
|
46
|
+
expect(out.status).toBe('valid')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('unknown provider id with no signal fields → unknown', () => {
|
|
50
|
+
const out = addNormalizedStatus({ foo: 'bar' }, 'mystery-provider') as Record<string, unknown>
|
|
51
|
+
expect(out.status).toBe('unknown')
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('null / non-object data is returned unchanged', () => {
|
|
55
|
+
expect(addNormalizedStatus(null, 'findymail')).toBe(null)
|
|
56
|
+
expect(addNormalizedStatus('a string', 'findymail')).toBe('a string')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('linkupapi-validate verified=true → valid', () => {
|
|
60
|
+
const out = addNormalizedStatus({ verified: true }, 'linkupapi-validate') as Record<string, unknown>
|
|
61
|
+
expect(out.status).toBe('valid')
|
|
62
|
+
})
|
|
63
|
+
})
|
|
4
64
|
|
|
5
65
|
describe('verify_email handler', () => {
|
|
6
66
|
const originalFetch = globalThis.fetch
|
|
@@ -13,9 +73,9 @@ describe('verify_email handler', () => {
|
|
|
13
73
|
globalThis.fetch = originalFetch
|
|
14
74
|
})
|
|
15
75
|
|
|
16
|
-
it('
|
|
76
|
+
it('normalizes findymail real upstream shape (verified boolean) to status=valid', async () => {
|
|
17
77
|
globalThis.fetch = vi.fn(async () =>
|
|
18
|
-
new Response(JSON.stringify({
|
|
78
|
+
new Response(JSON.stringify({ email: 'michel@coldiq.com', verified: true, provider: 'Google' }), { status: 200 })
|
|
19
79
|
) as typeof fetch
|
|
20
80
|
|
|
21
81
|
const result = await verifyEmailHandler({ email: 'michel@coldiq.com' })
|
|
@@ -23,6 +83,20 @@ describe('verify_email handler', () => {
|
|
|
23
83
|
const parsed = JSON.parse(result.content[0].text)
|
|
24
84
|
expect(parsed._meta.provider).toBe('findymail')
|
|
25
85
|
expect(parsed.data.status).toBe('valid')
|
|
86
|
+
// Raw fields still surfaced for callers that already depend on them.
|
|
87
|
+
expect(parsed.data.verified).toBe(true)
|
|
88
|
+
expect(parsed.data.provider).toBe('Google')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('normalizes findymail verified=false to status=invalid', async () => {
|
|
92
|
+
globalThis.fetch = vi.fn(async () =>
|
|
93
|
+
new Response(JSON.stringify({ email: 'bad@example.com', verified: false, provider: 'Other' }), { status: 200 })
|
|
94
|
+
) as typeof fetch
|
|
95
|
+
|
|
96
|
+
const result = await verifyEmailHandler({ email: 'bad@example.com' })
|
|
97
|
+
|
|
98
|
+
const parsed = JSON.parse(result.content[0].text)
|
|
99
|
+
expect(parsed.data.status).toBe('invalid')
|
|
26
100
|
})
|
|
27
101
|
|
|
28
102
|
it('falls back to IcyPeas on FindyMail failure', async () => {
|
|
@@ -35,12 +35,15 @@ describe('extractPeopleArray', () => {
|
|
|
35
35
|
expect(extractPeopleArray({ people: [{ name: 'A' }] }, 'fullenrich-people-search')).toHaveLength(1)
|
|
36
36
|
})
|
|
37
37
|
|
|
38
|
-
it('pdl /
|
|
38
|
+
it('pdl / companyenrich: top-level data[]', () => {
|
|
39
39
|
expect(extractPeopleArray({ data: [{ full_name: 'A' }] }, 'pdl')).toHaveLength(1)
|
|
40
|
-
expect(extractPeopleArray({ data: [{ name: 'B' }] }, 'prospeo-search-person')).toHaveLength(1)
|
|
41
40
|
expect(extractPeopleArray({ data: [{ name: 'C' }] }, 'companyenrich')).toHaveLength(1)
|
|
42
41
|
})
|
|
43
42
|
|
|
43
|
+
it('prospeo-search-person: top-level results[] (real upstream shape)', () => {
|
|
44
|
+
expect(extractPeopleArray({ results: [{ person: { full_name: 'B' } }] }, 'prospeo-search-person')).toHaveLength(1)
|
|
45
|
+
})
|
|
46
|
+
|
|
44
47
|
it('ai-ark-people: reads content[]', () => {
|
|
45
48
|
expect(extractPeopleArray({ content: [{ name: 'A' }] }, 'ai-ark-people')).toHaveLength(1)
|
|
46
49
|
})
|
|
@@ -286,6 +289,28 @@ describe('normalizePerson — ai-ark profile-nested shape', () => {
|
|
|
286
289
|
})
|
|
287
290
|
})
|
|
288
291
|
|
|
292
|
+
describe('normalizePerson — prospeo person-nested shape', () => {
|
|
293
|
+
// Prospeo /search-person returns { results: [{ person: { full_name, current_job_title, ... } }] }.
|
|
294
|
+
// Verified against the live api.prospeo.io/search-person response.
|
|
295
|
+
it('unwraps record.person and reads current_job_title as title', () => {
|
|
296
|
+
const p = normalizePerson({
|
|
297
|
+
person: {
|
|
298
|
+
person_id: 'aaaa07ad691a3a7f2ac039c6',
|
|
299
|
+
full_name: 'Catherine Powlett',
|
|
300
|
+
first_name: 'Catherine',
|
|
301
|
+
last_name: 'Powlett',
|
|
302
|
+
current_job_title: 'Marketing Manager',
|
|
303
|
+
linkedin_url: 'https://www.linkedin.com/in/catherinepowlett',
|
|
304
|
+
},
|
|
305
|
+
})!
|
|
306
|
+
expect(p.full_name).toBe('Catherine Powlett')
|
|
307
|
+
expect(p.first_name).toBe('Catherine')
|
|
308
|
+
expect(p.last_name).toBe('Powlett')
|
|
309
|
+
expect(p.title).toBe('Marketing Manager')
|
|
310
|
+
expect(p.linkedin_url).toBe('https://www.linkedin.com/in/catherinepowlett')
|
|
311
|
+
})
|
|
312
|
+
})
|
|
313
|
+
|
|
289
314
|
describe('normalizePerson — leadsfactory persona-level company sibling', () => {
|
|
290
315
|
// LF persona items have shape { contact, company, persona_index, persona_job_title }.
|
|
291
316
|
// Verified against src/providers/leadsfactory/schema.ts:134-141. The company sibling
|