Skip to main content

contact_enrich next channel

Status: Modified (behaviour/billing fix; parameters and output shape unchanged) This tool is on the private next MCP channel for allow-listed orgs. When promoted, this file moves to mcp-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() + 12 (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

NameTypeRequiredDescription
contactIdstringOptionalContact ID from contact_search (most accurate).
firstNamestringOptionalContact's first name.
lastNamestringOptionalContact's last name.
emailstringOptionalKnown email address.
companyDomainstringOptionalCurrent employer's domain.
companyNamestringOptionalCurrent employer's name.
linkedinUrlstringOptionalLinkedIn profile URL.
contactsarrayOptionalMultiple contacts in one call (max 25).
revealEmailbooleanOptionalReveal email addresses (default true).
revealPhonebooleanOptionalReveal phone numbers (default false — opt-in).
providerstringOptionalProvider override (default auto-select).

At least one identifying input is required — contactId (recommended), or name + company info, or a contacts[] 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.

  • contact_search — identify contacts first, then enrich the best matches here.
  • company_enrich — company-level background (the next-channel rename of company_research).

Changelog

2026-08-13 — No-match gate widened + confidence honesty (PR 1/2)

Availability: live on next only (allow-listed orgs via POST /api/mcp/next). The v1 base tool and the shared apollo.service.ts stay 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 isGhostStub to hasNoIdentitySignal — a record with no email, no title, no linkedinUrl, and empty employmentHistory is treated as no-match, on BOTH the single path (contact: undefined, 0 credits) and the bulk path (each record filtered individually; creditsUsed recomputed from survivors).
  • matchConfidence downgraded to low for identity-less records — even if the provider forwards high, reproduced locally since the shared service is untouched on next.
  • 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.