contact_enrich next channel
Status: Modified (behaviour/billing fix; parameters and output shape unchanged) This tool is on the private
nextMCP channel for allow-listed orgs. When promoted, this file moves tomcp-tools/contact-enrich.md.
Enrich a known contact with full details (email, phone, social profiles, employment history) via Apollo/ZoomInfo. Use it only after contact_search has identified the contact — pass the contactId from that result for the most accurate match. Single or bulk (up to 25 per call).
The next variant is a correctness + billing-integrity fix kept entirely off the v1 base tool (the base tool and the shared apollo.service.ts stay byte-identical to master). It closes a case where Apollo's /people/match returned a company-wrapped stub — the endpoint fuzzy-matched the company from a domain hint and wrapped an empty person around it (organization name populated, every person field null/empty). The v1 no-match gate was too narrow and let these through, so a caller was billed for a record with no usable person identity — and a bulk call of 25 could multiply the leak.
Integration required: apollo (Contact Data — configure in the Integrations Marketplace)
Cache schema version: super.getCacheSchemaVersion() + 1 → 2 (the billed/returned payload changes for stubs, so a next caller must not be served a v1-shaped cached payload; manifest.test.ts enforces next ≠ v1)
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
contactId | string | Optional | Contact ID from contact_search (most accurate). |
firstName | string | Optional | Contact's first name. |
lastName | string | Optional | Contact's last name. |
email | string | Optional | Known email address. |
companyDomain | string | Optional | Current employer's domain. |
companyName | string | Optional | Current employer's name. |
linkedinUrl | string | Optional | LinkedIn profile URL. |
contacts | array | Optional | Multiple contacts in one call (max 25). |
revealEmail | boolean | Optional | Reveal email addresses (default true). |
revealPhone | boolean | Optional | Reveal phone numbers (default false — opt-in). |
provider | string | Optional | Provider override (default auto-select). |
At least one identifying input is required —
contactId(recommended), orname+ company info, or acontacts[]array. Supplying none now raises a clear, caller-fixable error before any upstream call (was a wasted request).
Response Format
Output shape (contact, contacts[], metadata) is unchanged from v1. An identity-less record (no email, no title, no linkedinUrl, empty employmentHistory) is now treated as no-match: the single path returns contact: undefined and the bulk path filters that record out, so a bulk call of 25 inputs may legitimately return fewer (or zero) matched contacts.
Credits
Unchanged from v1: billed per matched contact; a no-match result — now including company-wrapped stubs and any bulk record without an identity signal — bills 0 credits. creditsUsed / dynamicCreditCost are recomputed from survivors only, so a batch cannot multiply the leak.
Related Tools
contact_search— identify contacts first, then enrich the best matches here.company_enrich— company-level background (thenext-channel rename ofcompany_research).
Changelog
2026-08-13 — No-match gate widened + confidence honesty (PR 1/2)
Availability: live on
nextonly (allow-listed orgs viaPOST /api/mcp/next). The v1 base tool and the sharedapollo.service.tsstay byte-identical to master; this entry graduates to the public channel when promoted in a follow-up PR.
Why this changed: A live audit found Apollo's /people/match can return a company-wrapped stub (populated organization, empty person). The v1 no-match gate (isGhostStub: title === "" && organization.name === "") was too narrow and let these through, so callers were billed for records with no usable person identity — a bulk call of up to 25 could multiply the leak. Confidence was also not verified locally, so an identity-less stub could still surface high.
What changed (breaking for billing/results)
- Identity-less records now bill as no-match (0 credits). The gate widened from
isGhostStubtohasNoIdentitySignal— a record with no email, no title, nolinkedinUrl, and emptyemploymentHistoryis treated as no-match, on BOTH the single path (contact: undefined, 0 credits) and the bulk path (each record filtered individually;creditsUsedrecomputed from survivors). matchConfidencedowngraded tolowfor identity-less records — even if the provider forwardshigh, reproduced locally since the shared service is untouched onnext.- Missing-identifier error raised before any upstream call — supplying no identifying input throws a clear, caller-fixable error instead of a wasted request.
What stayed the same
- Parameters and input schema are unchanged.
- Output schema (
contact,contacts[],metadata) is unchanged. - Tool key (
contact_enrich), the Apollo/ZoomInfo providers, the credit formula, and the bulk batch limit (max 25) are identical to v1. - Cache schema version bumped 1 → 2.