@agifyai/leadify-mcp 8.7.3 → 8.7.5
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/README.md +51 -6
- package/dist/tools/lead_groups.js +14 -0
- package/dist/tools/leads.js +7 -2
- package/dist/tools/person_employment.js +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -229,9 +229,9 @@ Leadify et ne doit jamais être envoyée par un agent MCP.
|
|
|
229
229
|
| `test_api_key` | Vérifier que la clé API configurée est valide (health check). |
|
|
230
230
|
| `list_lead_groups` | Lister les groupes accessibles d'une organisation explicitement sélectionnée, en vue compacte. |
|
|
231
231
|
| `get_lead_group` | Consulter les métadonnées compactes d'un groupe accessible par son ID. |
|
|
232
|
-
| `create_lead_group` | Créer un groupe
|
|
233
|
-
| `update_lead_group` | Modifier un groupe,
|
|
234
|
-
| `add_leads` |
|
|
232
|
+
| `create_lead_group` | Créer un groupe, choisir son type canonique optionnel et rattacher atomiquement `persona_id` avec l’`offer_id` ACTIVE du même tenant. |
|
|
233
|
+
| `update_lead_group` | Modifier un groupe avec réconciliation du schéma ; lorsque le serveur exige un contexte complet, transmettre ensemble `persona_id` et `offer_id` sans inférer l’offre. |
|
|
234
|
+
| `add_leads` | Import plat générique vers un groupe : ne crée aucune relation d’emploi, aucun rattachement parent ni identité canonique. Ne jamais l’utiliser pour rattacher une Personne à une Company (voir `upsert_person_leads_with_employment`). |
|
|
235
235
|
| `upsert_person_leads_with_employment` | Créer ou réconcilier atomiquement des Person Leads, PERSON, EMPLOYED_BY, projections et historique, sans effet commercial. |
|
|
236
236
|
| `create_person_employment_evidence` / `get_person_employment_evidence` | Créer puis relire une preuve d’emploi individuelle tenant-scopée ; transmettre l’`evidence.id` retourné tel quel à l’upsert Person. |
|
|
237
237
|
| `get_leads` | Rechercher et lister des leads avec filtres, recherche et pagination, dont le critère composé `event_filter`. |
|
|
@@ -325,6 +325,51 @@ Leadify et ne doit jamais être envoyée par un agent MCP.
|
|
|
325
325
|
| `set_fine_tuning_output_config` | Remplacer uniquement la configuration métier de sorties d’un Fine Tuning (`linkedinConnection`, `linkedinMessage`, `email`, activations et quantités). Préserve Markdown, langues et fallback ; ne génère, ne planifie, n’active ni n’envoie aucun outreach. |
|
|
326
326
|
| `pipeline_next_lead` | Sélectionner le prochain lead à traiter (score descendant, sans message). Exclusion des IDs déjà vus, limit 1-5. |
|
|
327
327
|
|
|
328
|
-
### `source-icp-prospects` :
|
|
329
|
-
|
|
330
|
-
|
|
328
|
+
### `source-icp-prospects` : sourcing Personne rattachée à une Company
|
|
329
|
+
|
|
330
|
+
`add_leads` est un import générique de leads plats et ne garantit aucune
|
|
331
|
+
relation d’emploi : une Personne sourcée pour une entreprise doit toujours
|
|
332
|
+
passer par `upsert_person_leads_with_employment`, seul chemin d’écriture
|
|
333
|
+
Person admis pour le sourcing. Ce tool crée ou réutilise la Person Lead et la
|
|
334
|
+
Person, crée ou réutilise une relation active `EMPLOYED_BY` vers la cible
|
|
335
|
+
légale de la Company (Legal Entity ou Establishment compatible), maintient les
|
|
336
|
+
projections `parent_lead` / `child_leads` consommées par l’interface, écrit
|
|
337
|
+
uniquement les champs admis par le schéma du groupe Personne, ajoute
|
|
338
|
+
l’historique sans écraser l’existant, applique l’idempotence par entrée et
|
|
339
|
+
relit les objets avant de répondre. Chaque entrée est transactionnelle
|
|
340
|
+
(échec = rollback complet de l’entrée) ; un batch peut mêler succès et échecs
|
|
341
|
+
par entrée mais ne rend jamais un succès ambigu.
|
|
342
|
+
|
|
343
|
+
Chaîne obligatoire, dans l’ordre :
|
|
344
|
+
|
|
345
|
+
1. Vérifier l’identité canonique de la Company Lead. Si elle n’expose ni
|
|
346
|
+
Account ni Legal Entity / Establishment compatible, la préparer d’abord
|
|
347
|
+
avec `preview_canonical_identity_backfill` puis
|
|
348
|
+
`apply_canonical_identity_backfill` en rejouant exactement le digest
|
|
349
|
+
prévisualisé. Toute contradiction d’identité bloque proprement : jamais de
|
|
350
|
+
rapprochement au nom approximatif.
|
|
351
|
+
2. Pour chaque candidat accepté, créer `create_person_employment_evidence`
|
|
352
|
+
avec le tenant, la Company Lead exacte, l’identité Person, la provenance
|
|
353
|
+
et une clé idempotente. Relire si nécessaire avec
|
|
354
|
+
`get_person_employment_evidence`, puis transmettre exclusivement
|
|
355
|
+
l’`evidence.id` retourné dans `evidence_ids` de
|
|
356
|
+
`upsert_person_leads_with_employment`.
|
|
357
|
+
3. Appeler `upsert_person_leads_with_employment` avec les IDs exacts
|
|
358
|
+
(`organization_id`, `person_group_id`, `company_lead_id` identiques au
|
|
359
|
+
même tenant), les seuls champs admis par le schéma Personne,
|
|
360
|
+
`card_history_append`, et une `idempotency_key` unique par entrée.
|
|
361
|
+
Pour réparer une Person Lead orpheline exacte sans doublon, passer son
|
|
362
|
+
`person_lead_id` : la réparation suit la même transaction et les mêmes
|
|
363
|
+
contrôles, et préserve LinkedIn, sources, `card_history`,
|
|
364
|
+
`qualification_status`, `hide_from_campaign` et les champs sans lien avec
|
|
365
|
+
la relation.
|
|
366
|
+
4. Vérifier dans les deux sens avant de considérer la Personne rattachée :
|
|
367
|
+
`get_lead` sur la Personne (parent résolvable) et
|
|
368
|
+
`list_canonical_relationships` filtré sur la Person puis sur la cible
|
|
369
|
+
légale (lien `EMPLOYED_BY` actif). Une relation ou projection manquante
|
|
370
|
+
est un échec explicite, jamais un succès partiel.
|
|
371
|
+
|
|
372
|
+
Aucune mutation de sourcing ne qualifie le lead, n’enrichit les contacts
|
|
373
|
+
(email/téléphone), ne génère de message, n’inscrit en campagne et n’active
|
|
374
|
+
aucun canal externe : le readback porte `noSend: true` et
|
|
375
|
+
`externalActivation: 0`.
|
|
@@ -48,6 +48,11 @@ export function registerLeadGroupTools(server, writeClient) {
|
|
|
48
48
|
.optional()
|
|
49
49
|
.describe("ICP persona ID to assign. Must belong to the same org or be global. " +
|
|
50
50
|
"Omit to create the group without a persona."),
|
|
51
|
+
offer_id: z
|
|
52
|
+
.string()
|
|
53
|
+
.nullable()
|
|
54
|
+
.optional()
|
|
55
|
+
.describe("Active offer ID to assign with persona_id. Must belong to the same organization."),
|
|
51
56
|
}, async (params) => {
|
|
52
57
|
try {
|
|
53
58
|
const body = {
|
|
@@ -66,6 +71,8 @@ export function registerLeadGroupTools(server, writeClient) {
|
|
|
66
71
|
body.disabledTools = params.disabled_tools;
|
|
67
72
|
if (params.persona_id !== undefined)
|
|
68
73
|
body.personaId = params.persona_id;
|
|
74
|
+
if (params.offer_id !== undefined)
|
|
75
|
+
body.offerId = params.offer_id;
|
|
69
76
|
const data = await (writeClient ?? getClient()).post("/api/lead-group", body);
|
|
70
77
|
return toolResult(data);
|
|
71
78
|
}
|
|
@@ -91,6 +98,11 @@ export function registerLeadGroupTools(server, writeClient) {
|
|
|
91
98
|
.nullable()
|
|
92
99
|
.optional()
|
|
93
100
|
.describe("New ICP persona ID. Pass null to unassign. Must belong to the same org or be global."),
|
|
101
|
+
offer_id: z
|
|
102
|
+
.string()
|
|
103
|
+
.nullable()
|
|
104
|
+
.optional()
|
|
105
|
+
.describe("Active offer ID to assign with persona_id. Pass null only when removing the binding."),
|
|
94
106
|
persona_types: z
|
|
95
107
|
.array(z.string())
|
|
96
108
|
.optional()
|
|
@@ -116,6 +128,8 @@ export function registerLeadGroupTools(server, writeClient) {
|
|
|
116
128
|
body.entityKind = params.entity_kind;
|
|
117
129
|
if (params.persona_id !== undefined)
|
|
118
130
|
body.personaId = params.persona_id;
|
|
131
|
+
if (params.offer_id !== undefined)
|
|
132
|
+
body.offerId = params.offer_id;
|
|
119
133
|
if (params.persona_types !== undefined)
|
|
120
134
|
body.personaTypes = params.persona_types;
|
|
121
135
|
if (params.company_profile !== undefined)
|
package/dist/tools/leads.js
CHANGED
|
@@ -116,7 +116,11 @@ export function registerLeadTools(server, client = getClient()) {
|
|
|
116
116
|
});
|
|
117
117
|
}
|
|
118
118
|
// ── add_leads ──────────────────────────────────────────────────────────
|
|
119
|
-
server.tool("add_leads", "
|
|
119
|
+
server.tool("add_leads", "Flat generic lead import only: add one or more leads to a specific lead group in Leadify. " +
|
|
120
|
+
"It stores plain field values and guarantees NO employment relationship, NO parent link and NO canonical identity. " +
|
|
121
|
+
"It must never be used to attach a Person to a Company: Person sourcing attached to a Company must use " +
|
|
122
|
+
"upsert_person_leads_with_employment (after create_person_employment_evidence, and preview_canonical_identity_backfill plus " +
|
|
123
|
+
"apply_canonical_identity_backfill when the Company lacks canonical identity). Provide the group ID " +
|
|
120
124
|
"and an array of lead objects with any combination of fields (email, firstName, " +
|
|
121
125
|
"lastName, company, jobTitle, phone, custom fields, card_* fields, percent_* " +
|
|
122
126
|
"fields, etc.). Canonical relation fields are read-only here: use " +
|
|
@@ -130,7 +134,8 @@ export function registerLeadTools(server, client = getClient()) {
|
|
|
130
134
|
.array(leadInputSchema)
|
|
131
135
|
.describe("Array of lead objects. Each object is a key-value map of field names to values. " +
|
|
132
136
|
"Common fields: email, firstName, lastName, company, jobTitle, phone, linkedin, " +
|
|
133
|
-
"website, seniority, specialty, age.
|
|
137
|
+
"website, seniority, specialty, age. The company field is flat display text only and never creates an employment relationship or parent link. " +
|
|
138
|
+
"location must be {city, region?, postalCode?, countryCode, street?}; geo is computed by Leadify. Also supports card_* fields, " +
|
|
134
139
|
"percent_* fields and boolean flags (decisionMaking, excluded). Canonical relation fields are rejected."),
|
|
135
140
|
is_select_fields: z
|
|
136
141
|
.array(z.string())
|
|
@@ -44,7 +44,7 @@ export function registerPersonEmploymentTools(server, client = getClient()) {
|
|
|
44
44
|
return handleToolError(error);
|
|
45
45
|
}
|
|
46
46
|
});
|
|
47
|
-
server.tool("upsert_person_leads_with_employment", "Atomically create or reconcile Person Leads with their canonical PERSON identity, EMPLOYED_BY relationship, parent_lead/child_leads projections, evidence and append-only card_history. company_lead_id must be exact and already project an Account plus Legal Entity or compatible Establishment. Each batch entry is independently transactional and idempotent. This is the only supported Person write path for sourcing; never fall back to add_leads, canonical backfill, or separate relationship writes. It never
|
|
47
|
+
server.tool("upsert_person_leads_with_employment", "Atomically create or reconcile Person Leads with their canonical PERSON identity, an active EMPLOYED_BY relationship to the Company legal target, parent_lead/child_leads projections, evidence and append-only card_history. company_lead_id must be exact and already project an Account plus Legal Entity or compatible Establishment: when it does not, prepare it first with preview_canonical_identity_backfill then apply_canonical_identity_backfill using the returned digest, and never fuzzy-match identity. Pass person_lead_id to repair one exact orphan Person Lead without creating a duplicate; otherwise a new Person Lead is created. fields must only contain keys admitted by the Person group schema and are forwarded verbatim so the backend rejects unknown keys; history is append-only via card_history_append and existing card_history is never overwritten. Each batch entry is independently transactional (a failure rolls that entry back entirely) and idempotent by idempotency_key; a changed payload under the same key is rejected, never merged. The success readback carries personLeadId, personId, companyLeadId, legalTarget, relationshipId, relationship, projections, cardHistory, noSend and externalActivation: verify the EMPLOYED_BY link in both directions (get_lead plus list_canonical_relationships) before treating the Person as attached. This is the only supported Person write path for sourcing; never fall back to add_leads, direct canonical backfill writes, or separate relationship writes. It never creates emails or phones, never qualifies the lead, never generates messages, never enrolls in a campaign and never activates any external channel.", {
|
|
48
48
|
people: z.array(z.object({
|
|
49
49
|
organization_id: z.string().trim().min(1),
|
|
50
50
|
person_group_id: z.string().trim().min(1),
|
|
@@ -52,7 +52,7 @@ export function registerPersonEmploymentTools(server, client = getClient()) {
|
|
|
52
52
|
person_lead_id: z.string().trim().min(1).optional(),
|
|
53
53
|
fields: z.record(z.unknown()),
|
|
54
54
|
card_history_append: z.string().trim().min(1).max(100_000),
|
|
55
|
-
evidence_ids: z.array(z.string().trim().min(1)).min(1).max(100),
|
|
55
|
+
evidence_ids: z.array(z.string().trim().min(1)).min(1).max(100).optional(),
|
|
56
56
|
idempotency_key: z.string().trim().min(1).max(220),
|
|
57
57
|
}).strict()).min(1).max(100),
|
|
58
58
|
}, async ({ people }) => {
|
|
@@ -65,7 +65,7 @@ export function registerPersonEmploymentTools(server, client = getClient()) {
|
|
|
65
65
|
personLeadId: person.person_lead_id,
|
|
66
66
|
fields: person.fields,
|
|
67
67
|
cardHistoryAppend: person.card_history_append,
|
|
68
|
-
evidenceIds: person.evidence_ids,
|
|
68
|
+
...(person.evidence_ids !== undefined ? { evidenceIds: person.evidence_ids } : {}),
|
|
69
69
|
idempotencyKey: person.idempotency_key,
|
|
70
70
|
})),
|
|
71
71
|
}));
|